package se.slide.timy;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import android.widget.Toast;
import com.google.analytics.tracking.android.EasyTracker;
import com.google.android.gms.common.GooglePlayServicesUtil;
//import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.CalendarList;
import com.google.api.services.calendar.model.CalendarListEntry;
import com.google.api.services.calendar.model.ColorDefinition;
import com.google.api.services.calendar.model.Colors;
import se.slide.timy.billing.IabHelper;
import se.slide.timy.billing.IabResult;
import se.slide.timy.billing.Purchase;
import se.slide.timy.db.DatabaseManager;
import se.slide.timy.model.Color;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* A {@link PreferenceActivity} that presents a set of application settings. On
* handset devices, settings are presented as a single list. On tablets,
* settings are split by category, with category headers shown to the left of
* the list of settings.
* <p>
* See <a href="http://developer.android.com/design/patterns/settings.html">
* Android Design: Settings</a> for design guidelines and the <a
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
* API Guide</a> for more information on developing a Settings UI.
*/
public class SettingsActivity extends PreferenceActivity {
/**
* In app billing variables
*/
// The helper object
IabHelper mHelper;
// SKUs for our products: the premium upgrade (non-consumable) and gas (consumable)
static final String SKU_SMALL = "donate_small";
static final String SKU_MEDIUM = "donate_medium";
static final String SKU_LARGE = "donate_large";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;
static final int REQUEST_GET_PERMISSIONS = 1;
static final int REQUEST_ACCOUNT_PICKER = 2;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 3;
ProgressDialog mProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActionBar();
}
/**
* Set up the {@link android.app.ActionBar}, if the API is available.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// Show the Up button in the action bar.
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
* (that you got from the Google Play developer console). This is not your
* developer public key, it's the *app-specific* public key.
*
* Instead of just storing the entire literal string here embedded in the
* program, construct the key at runtime from pieces or
* use bit manipulation (for example, XOR with some other string) to hide
* the actual key. The key itself is not secret information, but we don't
* want to make it easy for an attacker to replace the public key with one
* of their own and then fake messages from the server.
*/
/*
* We will ignore the advice given above because this is an open source app; you may what you wish with the code :)
*/
String base64EncodedPublicKey = getString(R.string.google_base64_encoded_rsa_public_key);
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(false);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
// We should send something to Google Analytics...
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
}
});
setupSimplePreferencesScreen();
}
@Override
public void onStart() {
super.onStart();
EasyTracker.getInstance(this).activityStart(this);
}
@Override
public void onStop() {
super.onStop();
EasyTracker.getInstance(this).activityStop(this);
}
// We're being destroyed. It's important to dispose of the helper here!
@Override
public void onDestroy() {
super.onDestroy();
// very important:
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
/**
* Shows the simplified settings UI if the device configuration if the
* device configuration dictates that a simplified, single-pane UI should be
* shown.
*/
@SuppressWarnings("deprecation")
private void setupSimplePreferencesScreen() {
PreferenceCategory fakeHeader;
// Add 'general' preferences.
addPreferencesFromResource(R.xml.pref_general);
fakeHeader = new PreferenceCategory(this);
fakeHeader.setTitle(R.string.pref_header_sync);
getPreferenceScreen().addPreference(fakeHeader);
addPreferencesFromResource(R.xml.pref_data_sync);
fakeHeader = new PreferenceCategory(this);
fakeHeader.setTitle(R.string.pref_header_about);
getPreferenceScreen().addPreference(fakeHeader);
addPreferencesFromResource(R.xml.pref_about);
// Reminder
Preference remindMe = findPreference("remind_me");
remindMe.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean checked = (Boolean) newValue;
Context context = preference.getContext();
if (checked)
sendBroadcast(new Intent(context, BootReceiver.class));
else {
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
mgr.cancel(pi);
}
return true;
}
});
// Remind me at
Preference remindMeAt = findPreference("remind_me_at");
remindMeAt.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String stringValue = getFormattedTime(newValue.toString());
preference.setSummary(stringValue);
sendBroadcast(new Intent(preference.getContext(), BootReceiver.class));
return true;
}
});
String remindMeAtSummary = PreferenceManager
.getDefaultSharedPreferences(this)
.getString(remindMeAt.getKey(), "");
if (remindMeAtSummary != null && remindMeAtSummary.length() > 0) {
remindMeAt.setSummary(getFormattedTime(remindMeAtSummary));
}
// Remind me at
Preference remindMeWhen = findPreference("remind_me_when");
remindMeWhen.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@SuppressWarnings("unchecked")
String value = getFormattedDays((Set<String>) newValue);
preference.setSummary(value);
return true;
}
});
Set<String> values = PreferenceManager
.getDefaultSharedPreferences(this)
.getStringSet(remindMeWhen.getKey(), new HashSet<String>());
String remindMeWhenSummary = getFormattedDays(values);
remindMeWhen.setSummary(remindMeWhenSummary);
// Account
Preference syncGoogleAccountPref = findPreference("sync_google_calendar_account");
syncGoogleAccountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
String accountName = PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), "");
GoogleAccountCredential credential;
credential = GoogleAccountCredential.usingOAuth2(preference.getContext(),
CalendarScopes.CALENDAR);
credential.setSelectedAccountName(accountName);
// Check for Google Play
if (checkGooglePlayServicesAvailable()) {
try {
startActivityForResult(credential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
} catch (ActivityNotFoundException e) {
// I'm not sure but I think this is the error (when
// testing on AVD)
Toast.makeText(preference.getContext(), R.string.no_google_play_on_device,
Toast.LENGTH_SHORT).show();
}
}
return false;
}
});
String accountName = PreferenceManager
.getDefaultSharedPreferences(this)
.getString(syncGoogleAccountPref.getKey(), "");
syncGoogleAccountPref.setSummary(accountName);
// Google Calendar
Preference syncGoogleCalendarPref = findPreference("sync_google_calendar_calendar");
syncGoogleCalendarPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
/*
boolean hasPermissions = PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getBoolean("sync_google_permission", false);
if (!hasPermissions) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(preference.getContext());
alertDialogBuilder.setTitle(R.string.google_permission_title);
alertDialogBuilder.setMessage(R.string.google_permission_message);
alertDialogBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialogBuilder.create().show();
return true;
}
*/
String accountName = PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString("sync_google_calendar_account", "");
if (accountName == null || accountName.length() < 1) {
final Preference accountPref = findPreference("sync_google_calendar_account");
accountPref.getOnPreferenceClickListener().onPreferenceClick(accountPref);
return true;
}
getCalendarData(accountName, GetColorsAsyncTask.TASK_MODE_ALL);
return true;
}
});
String calendarName = PreferenceManager
.getDefaultSharedPreferences(this)
.getString("sync_google_calendar_calendar_name", "");
syncGoogleCalendarPref.setSummary(calendarName);
// Donate
Preference prefDonate = findPreference("donate");
prefDonate.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
}
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
if (preference.getKey().equals("donate")) {
if (index == 0) {
// Small
onDonateButtonClicked(SKU_SMALL);
}
else if (index == 1) {
// Medium
onDonateButtonClicked(SKU_MEDIUM);
}
else if (index == 2) {
// Large
onDonateButtonClicked(SKU_LARGE);
}
// Reset to default value; this is not a normal preference we would like to save
return false;
}
else {
// Set the summary to reflect the new value.
preference
.setSummary(index >= 0 ? listPreference.getEntries()[index]
: null);
// Update the alarm
preference.getContext().sendBroadcast(
new Intent(preference.getContext(), BootReceiver.class));
}
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
preference.setSummary(stringValue);
}
return true;
}
};
@SuppressLint("SimpleDateFormat")
private String getFormattedTime(String prefTime) {
String[] pieces = prefTime.split(":");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, Integer.parseInt(pieces[0]));
cal.add(Calendar.MINUTE, Integer.parseInt(pieces[1]));
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
try {
Date time = format.parse(prefTime);
return android.text.format.DateFormat.getTimeFormat(this).format(time);
} catch (ParseException e) {
e.printStackTrace();
}
return "";
}
private String getFormattedDays(Set<String> values) {
StringBuilder builder = new StringBuilder();
if (values.contains("1"))
builder.append("Mon");
if (values.contains("2")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Tue");
}
if (values.contains("3")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Wed");
}
if (values.contains("4")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Thu");
}
if (values.contains("5")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Fri");
}
if (values.contains("6")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Sat");
}
if (values.contains("7")) {
if (builder.length() > 0)
builder.append(", ");
builder.append("Sun");
}
return builder.toString();
}
private class GetColorsAsyncTask extends AsyncTask<Void, Void, List<Color>> {
public static final int TASK_MODE_PERMISSION_ONLY = 0;
public static final int TASK_MODE_ALL = 1;
private String accountName;
private WeakReference<Activity> weakActivity;
private int mode;
private int error;
private Intent intent;
public GetColorsAsyncTask(String accountName, Activity activity, int mode) {
this.accountName = accountName;
this.mode = mode;
this.error = 0;
weakActivity = new WeakReference<Activity>(activity);
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
Activity activity = weakActivity.get();
if (activity != null) {
((SettingsActivity) activity).showProgressDialog(true);
if (mode == TASK_MODE_ALL)
((SettingsActivity) activity).setProgressTitle(R.string.getting_colors);
else
((SettingsActivity) activity).setProgressTitle(R.string.checking_permission);
}
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected List<Color> doInBackground(Void... params) {
/*
try {
final com.google.api.services.calendar.Calendar client;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = new GsonFactory();
GoogleAccountCredential credential;
credential = GoogleAccountCredential.usingOAuth2(weakActivity.get(),
CalendarScopes.CALENDAR);
credential.setSelectedAccountName(accountName);
// Calendar client
client = new com.google.api.services.calendar.Calendar.Builder(
transport, jsonFactory, credential).setApplicationName("Timy/1.0")
.build();
// Fire the request
Colors eventColors = client.colors().get().execute();
Set<Entry<String, ColorDefinition>> sets = eventColors.getEvent().entrySet();
List<Color> colors = new ArrayList<Color>();
for (Entry<String, ColorDefinition> entry : sets) {
Color color = new Color();
color.setId(entry.getKey());
ColorDefinition colorDefinitions = entry.getValue();
color.setBackgroundColor(colorDefinitions.getBackground());
color.setForegroundColor(colorDefinitions.getForeground());
colors.add(color);
}
return colors;
} catch (UserRecoverableAuthIOException e) {
error = 1;
intent = e.getIntent();
} catch (IOException e) {
error = 2;
e.printStackTrace();
}
*/
return null;
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(List<Color> result) {
super.onPostExecute(result);
Activity activity = weakActivity.get();
if (activity != null) {
if (error == 0) {
if (result != null)
((SettingsActivity) activity).saveColors(result);
if (mode == TASK_MODE_ALL)
((SettingsActivity) activity).getCalendars(accountName);
else
((SettingsActivity) activity).showProgressDialog(false);
}
else {
((SettingsActivity) activity).showProgressDialog(false);
if (error == 1)
startActivityForResult(intent, REQUEST_GET_PERMISSIONS);
}
}
}
}
/*
public void showRequestPermissionDialog(Intent intent) {
startActivityForResult(intent, REQUEST_ACCOUNT_PICKER);
}
*/
public void setProgressTitle(int resId) {
if (mProgress != null)
mProgress.setMessage(getString(resId));
}
public void showProgressDialog(boolean show) {
if (show) {
mProgress = ProgressDialog.show(this, getString(R.string.please_wait),
getString(R.string.getting_calendar_info), true);
}
else {
if (mProgress != null && mProgress.isShowing())
mProgress.dismiss();
}
}
public void saveColors(List<Color> colors) {
DatabaseManager.getInstance().saveColors(colors);
}
private class GetCalendarsAsyncTask extends AsyncTask<Void, Void, CalendarList> {
private String accountName;
private WeakReference<Activity> weakActivity;
public GetCalendarsAsyncTask(String accountName, Activity activity) {
this.accountName = accountName;
weakActivity = new WeakReference<Activity>(activity);
}
/* (non-Javadoc)
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
Activity activity = weakActivity.get();
if (activity != null) {
((SettingsActivity) activity).setProgressTitle(R.string.getting_calendar_info);
}
}
@Override
protected CalendarList doInBackground(Void... urls) {
/*
try {
final com.google.api.services.calendar.Calendar client;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = new GsonFactory();
GoogleAccountCredential credential;
credential = GoogleAccountCredential.usingOAuth2(weakActivity.get(),
CalendarScopes.CALENDAR);
credential.setSelectedAccountName(accountName);
// Calendar client
client = new com.google.api.services.calendar.Calendar.Builder(
transport, jsonFactory, credential).setApplicationName(getString(R.string.app_name))
.build();
// Fire the request
String FIELDS = "id,summary";
final String FEED_FIELDS = "items(" + FIELDS + ")";
CalendarList feed = client.calendarList().list().setFields(FEED_FIELDS).execute();
return feed;
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_ACCOUNT_PICKER);
} catch (IOException e) {
e.printStackTrace();
}
*/
return null;
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(CalendarList result) {
super.onPostExecute(result);
Activity activity = weakActivity.get();
if (activity != null && result != null) {
((SettingsActivity) activity).showProgressDialog(false);
((SettingsActivity) activity).displayCalendarList(result);
}
}
}
public void getCalendarData(String accountName, int mode) {
GetColorsAsyncTask getColors = new GetColorsAsyncTask(accountName, SettingsActivity.this, mode);
getColors.execute();
}
public void getCalendars(String accountName) {
GetCalendarsAsyncTask getCalendars = new GetCalendarsAsyncTask(accountName,
SettingsActivity.this);
getCalendars.execute();
}
public void displayCalendarList(CalendarList feed) {
List<CalendarListEntry> list = feed.getItems();
final CharSequence[] entries = new CharSequence[list.size()];
final String[] calendarIds = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
entries[i] = list.get(i).getSummary();
calendarIds[i] = list.get(i).getId();
}
int calendarId = PreferenceManager
.getDefaultSharedPreferences(this)
.getInt("sync_google_calendar_calendar", 0);
if (calendarId > entries.length)
calendarId = 0;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choose a calendar")
.setSingleChoiceItems(entries, calendarId, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int item) {
PreferenceManager
.getDefaultSharedPreferences(SettingsActivity.this)
.edit()
.putString("sync_google_calendar_calendar_name",
entries[item].toString()).commit();
PreferenceManager.getDefaultSharedPreferences(SettingsActivity.this).edit()
.putString("sync_google_calendar_calendar_id", calendarIds[item])
.commit();
PreferenceManager.getDefaultSharedPreferences(SettingsActivity.this).edit()
.putInt("sync_google_calendar_calendar", item).commit();
Preference syncGoogleCalendarPref = findPreference("sync_google_calendar_calendar");
syncGoogleCalendarPref.setSummary(entries[item]);
dialogInterface.dismiss();
}
});
builder.create().show();
}
/** Check that Google Play services APK is installed and up to date. */
private boolean checkGooglePlayServicesAvailable() {
final int connectionStatusCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
return false;
}
return true;
}
void showGooglePlayServicesAvailabilityErrorDialog(final int connectionStatusCode) {
runOnUiThread(new Runnable() {
public void run() {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
connectionStatusCode, SettingsActivity.this, REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
});
}
/*
* (non-Javadoc)
* @see android.preference.PreferenceActivity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Pass on the activity result to the helper for handling
if (mHelper == null || !mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ACCOUNT_PICKER && resultCode == Activity.RESULT_OK
&& data != null && data.getExtras() != null) {
String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
Preference syncGoogleAccountPref = findPreference("sync_google_calendar_account");
syncGoogleAccountPref.getEditor()
.putString("sync_google_calendar_account", accountName).commit();
syncGoogleAccountPref.setSummary(accountName);
// To trigger the permission dialog, request something from the API
getCalendarData(accountName, GetColorsAsyncTask.TASK_MODE_PERMISSION_ONLY);
}
else if (requestCode == REQUEST_GET_PERMISSIONS && resultCode == Activity.RESULT_OK
&& data != null && data.getExtras() != null) {
// Save permission
Preference syncGoogleAccountPref = findPreference("sync_google_calendar_account");
syncGoogleAccountPref.getEditor()
.putBoolean("sync_google_permission", true).commit();
}
else if (requestCode == REQUEST_GET_PERMISSIONS && resultCode == Activity.RESULT_CANCELED) {
// Save permission - make sure MainActivity shows dialog if this is null/bad
Preference syncGoogleAccountPref = findPreference("sync_google_calendar_account");
syncGoogleAccountPref.getEditor()
.putBoolean("sync_google_permission", false).commit();
}
}
else {
// onActivityResult handled by IABUtil
}
}
/**
* User clicked to donate!
*
*/
public void onDonateButtonClicked(String sku) {
/* TODO: for security, generate your payload here for verification. See the comments on
* verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
* an empty string, but on a production app you should carefully generate this. */
/*
* We will ignore this advice (as well) since we do not need to link this purchase to a "specific user" of our app, see this answer: http://stackoverflow.com/questions/14553515/why-is-it-important-to-set-the-developer-payload-with-in-app-billing
*/
String payload = "";
mHelper.launchPurchaseFlow(this, sku, RC_REQUEST, mPurchaseFinishedListener, payload);
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
return;
}
if (!verifyDeveloperPayload(purchase)) {
return;
}
// Purchase successful
if (purchase.getSku().equals(SKU_SMALL) || purchase.getSku().equals(SKU_MEDIUM) || purchase.getSku().equals(SKU_LARGE)) {
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
// We know this is the "gas" sku because it's the only one we consume,
// so we don't check which sku was consumed. If you have more than one
// sku, you probably should check...
if (result.isSuccess()) {
// successfully consumed, thank our friendly user
alert("Thank you! :)");
}
else {
// something went wrong
alert("Something went wrong...");
}
}
};
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
/*
* TODO: verify that the developer payload of the purchase is correct. It will be
* the same one that you sent when initiating the purchase.
*
* WARNING: Locally generating a random string when starting a purchase and
* verifying it here might seem like a good approach, but this will fail in the
* case where the user purchases an item on one device and then uses your app on
* a different device, because on the other device you will not have access to the
* random string you originally generated.
*
* So a good developer payload has these characteristics:
*
* 1. If two different users purchase an item, the payload is different between them,
* so that one user's purchase can't be replayed to another user.
*
* 2. The payload must be such that you can verify it even when the app wasn't the
* one who initiated the purchase flow (so that items purchased by the user on
* one device work on other devices owned by the user).
*
* Using your own server to store and verify developer payloads across app
* installations is recommended.
*/
return true;
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton(R.string.ok, null);
bld.create().show();
}
}