package com.todoroo.astrid.billing; import java.util.Locale; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.text.Html; import android.text.Spanned; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebView; import android.widget.Button; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.TextView; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.view.MenuItem; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.billing.BillingConstants.ResponseCode; import com.todoroo.astrid.service.ThemeService; public class BillingActivity extends SherlockFragmentActivity implements AstridPurchaseObserver.RestoreTransactionsListener { private static final int DIALOG_CANNOT_CONNECT_ID = 1; private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2; private static final int DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID = 3; private Handler handler; private BillingService billingService; private AstridPurchaseObserver purchaseObserver; private Button buyMonth; private Button buyYear; private TextView restorePurchases; private ProgressDialog restoreTransactionsDialog; @Autowired private ActFmPreferenceService actFmPreferenceService; @Override protected void onCreate(Bundle savedInstanceState) { ThemeService.applyTheme(this); DependencyInjectionService.getInstance().inject(this); super.onCreate(savedInstanceState); setContentView(R.layout.billing_activity); setupActionBar(); setupButtons(); setupText(); handler = new Handler(); billingService = new BillingService(); billingService.setContext(this); purchaseObserver = new AstridPurchaseObserver(this, handler) { @Override protected void billingSupportedCallback() { restoreTransactions(false); buyMonth.setEnabled(true); buyYear.setEnabled(true); restorePurchases.setEnabled(true); } @Override protected void billingNotSupportedCallback() { showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID); } @Override protected void subscriptionsNotSupportedCallback() { showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); } }; purchaseObserver.setRestoreTransactionsListener(this); } private void setupActionBar() { ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowCustomEnabled(true); actionBar.setCustomView(R.layout.header_title_view); ((TextView) actionBar.getCustomView().findViewById(R.id.title)).setText(R.string.premium_billing_title); } @Override protected void onStart() { super.onStart(); ResponseHandler.register(purchaseObserver); } @Override protected void onStop() { super.onStop(); ResponseHandler.unregister(purchaseObserver); } @Override protected void onDestroy() { super.onDestroy(); billingService.unbind(); } @Override protected void onResume() { super.onResume(); if (!actFmPreferenceService.isLoggedIn()) { // Prompt to log in, but this shouldn't happen anyways since we hide the entry path to this screen when not logged in DialogUtilities.okDialog(this, getString(R.string.premium_login_prompt), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); } else if (!billingService.checkBillingSupported(BillingConstants.ITEM_TYPE_SUBSCRIPTION)) { showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); } else if (ActFmPreferenceService.isPremiumUser()) { DialogUtilities.okDialog(this, getString(R.string.premium_already_subscribed), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); } } private void setupButtons() { buyMonth = (Button) findViewById(R.id.premium_buy_month); buyYear = (Button) findViewById(R.id.premium_buy_year); restorePurchases = (TextView) findViewById(R.id.check_for_purchases); buyMonth.setEnabled(false); buyYear.setEnabled(false); restorePurchases.setEnabled(false); if (!Preferences.getBoolean(BillingConstants.PREF_TRANSACTIONS_INITIALIZED, false)) restorePurchases.setVisibility(View.GONE); buyMonth.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!billingService.requestPurchase(BillingConstants.PRODUCT_ID_MONTHLY, BillingConstants.ITEM_TYPE_SUBSCRIPTION, null)) { showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); } } }); buyYear.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!billingService.requestPurchase(BillingConstants.PRODUCT_ID_YEARLY, BillingConstants.ITEM_TYPE_SUBSCRIPTION, null)) { showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); } } }); restorePurchases.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { restoreTransactions(true); } }); } @SuppressWarnings("nls") private void setupText() { int[] bullets = new int[] { R.string.premium_description_1, /* R.string.premium_description_2,*/ R.string.premium_description_3, R.string.premium_description_4, R.string.premium_description_5, R.string.premium_description_6 }; StringBuilder builder = new StringBuilder("<html><style type=\"text/css\">li { padding-bottom: 13px } </style><body><ul>"); for (int i = 0; i < bullets.length; i++) { String curr = getString(bullets[i]); if (curr.contains("\n")) curr = curr.replace("\n", "<br>"); builder.append("<li><font style='color=#404040; font-size: 18px'>").append(curr); builder.append("</font></li>\n"); } builder.append("</ul></body></html>"); WebView list = (WebView) findViewById(R.id.premium_bullets); list.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "utf-8", null); list.setBackgroundColor(0); View speechBubbleBackground = findViewById(R.id.speech_bubble_container); speechBubbleBackground.setBackgroundColor(0); DisplayMetrics metrics = getResources().getDisplayMetrics(); ImageView icon = (ImageView) findViewById(R.id.astridIcon); int dim = (int) (80 * metrics.density); icon.setLayoutParams(new LinearLayout.LayoutParams(dim, dim)); icon.setScaleType(ScaleType.FIT_CENTER); TextView speechBubble = (TextView) findViewById(R.id.reminder_message); // Construct speech bubble text String html = String.format("%s <font color=\"#%s\">%s</font>", getString(R.string.premium_speech_bubble_1), Integer.toHexString(getResources().getColor(R.color.red_theme_color) - 0xff000000), getString(R.string.premium_speech_bubble_2)); Spanned spanned = Html.fromHtml(html); speechBubble.setText(spanned); speechBubble.setTextSize(17); } /** * Replaces the language and/or country of the device into the given string. * The pattern "%lang%" will be replaced by the device's language code and * the pattern "%region%" will be replaced with the device's country code. * * @param str the string to replace the language/country within * @return a string containing the local language and region codes */ @SuppressWarnings("nls") private String replaceLanguageAndRegion(String str) { // Substitute language and or region if present in string if (str.contains("%lang%") || str.contains("%region%")) { Locale locale = Locale.getDefault(); str = str.replace("%lang%", locale.getLanguage().toLowerCase()); str = str.replace("%region%", locale.getCountry().toLowerCase()); } return str; } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CANNOT_CONNECT_ID: return createDialog(R.string.cannot_connect_title, R.string.cannot_connect_message); case DIALOG_BILLING_NOT_SUPPORTED_ID: return createDialog(R.string.billing_not_supported_title, R.string.billing_not_supported_message); case DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID: return createDialog(R.string.subscriptions_not_supported_title, R.string.subscriptions_not_supported_message); default: return null; } } @SuppressWarnings("nls") private Dialog createDialog(int titleId, int messageId) { String helpUrl = "http://market.android.com/support/bin/answer.py?answer=1050566&hl=%lang%&dl=%region%"; helpUrl = replaceLanguageAndRegion(helpUrl); if (BillingConstants.DEBUG) { Log.i("billing-activity-url", helpUrl); //$NON-NLS-1$ } final Uri helpUri = Uri.parse(helpUrl); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(titleId) .setIcon(android.R.drawable.stat_sys_warning) .setMessage(messageId) .setCancelable(false) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton(R.string.subscriptions_learn_more, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, helpUri); startActivity(intent); } }); return builder.create(); } private void restoreTransactions(boolean force) { boolean initialized = Preferences.getBoolean(BillingConstants.PREF_TRANSACTIONS_INITIALIZED, false); if (!initialized || force) { billingService.restoreTransactions(); } if (force) { restoreTransactionsDialog = DialogUtilities.progressDialog(this, getString(R.string.premium_checking_for_purchases)); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); return true; } return super.onOptionsItemSelected(item); } @Override public void restoreTransactionsResponse(ResponseCode responseCode) { DialogUtilities.dismissDialog(this, restoreTransactionsDialog); restoreTransactionsDialog = null; } }