/*
* Copyright (C) 2011-2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sufficientlysecure.donations;
import org.sufficientlysecure.donations.google.util.IabHelper;
import org.sufficientlysecure.donations.google.util.IabResult;
import org.sufficientlysecure.donations.google.util.Purchase;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.webkit.WebView;
import android.webkit.WebView.HitTestResult;
import android.webkit.WebViewClient;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Spinner;
import android.widget.TextView;
public class DonationsFragment extends Fragment {
public static final String ARG_DEBUG = "debug";
public static final String ARG_GOOGLE_ENABLED = "googleEnabled";
public static final String ARG_GOOGLE_PUBKEY = "googlePubkey";
public static final String ARG_GOOGLE_CATALOG = "googleCatalog";
public static final String ARG_GOOGLE_CATALOG_VALUES = "googleCatalogValues";
public static final String ARG_PAYPAL_ENABLED = "paypalEnabled";
public static final String ARG_PAYPAL_USER = "paypalUser";
public static final String ARG_PAYPAL_CURRENCY_CODE = "paypalCurrencyCode";
public static final String ARG_PAYPAL_ITEM_NAME = "mPaypalItemName";
public static final String ARG_FLATTR_ENABLED = "flattrEnabled";
public static final String ARG_FLATTR_PROJECT_URL = "flattrProjectUrl";
public static final String ARG_FLATTR_URL = "flattrUrl";
private static final String TAG = "Donations Library";
// http://developer.android.com/google/play/billing/billing_testing.html
private static final String[] CATALOG_DEBUG = new String[]{"android.test.purchased",
"android.test.canceled", "android.test.refunded", "android.test.item_unavailable"};
private Spinner mGoogleSpinner;
private TextView mFlattrUrlTextView;
// Google Play helper object
private IabHelper mHelper;
protected boolean mDebug = false;
protected boolean mGoogleEnabled = false;
protected String mGooglePubkey = "";
protected String[] mGgoogleCatalog = new String[]{};
protected String[] mGoogleCatalogValues = new String[]{};
protected boolean mPaypalEnabled = false;
protected String mPaypalUser = "";
protected String mPaypalCurrencyCode = "";
protected String mPaypalItemName = "";
protected boolean mFlattrEnabled = false;
protected String mFlattrProjectUrl = "";
protected String mFlattrUrl = "";
/**
* Instantiate DonationsFragment.
*
* @param debug You can use BuildConfig.DEBUG to propagate the debug flag from your app to the Donations library
* @param googleEnabled Enabled Google Play donations
* @param googlePubkey Your Google Play public key
* @param googleCatalog Possible item names that can be purchased from Google Play
* @param googleCatalogValues Values for the names
* @param paypalEnabled Enable PayPal donations
* @param paypalUser Your PayPal email address
* @param paypalCurrencyCode Currency code like EUR. See here for other codes:
* https://developer.paypal.com/webapps/developer/docs/classic/api/currency_codes/#id09A6G0U0GYK
* @param paypalItemName Display item name on PayPal, like "Donation for NTPSync"
* @param flattrEnabled Enable Flattr donations
* @param flattrProjectUrl The project URL used on Flattr
* @param flattrUrl The Flattr URL to your thing. NOTE: Enter without http://
* @return DonationsFragment
*/
public static DonationsFragment newInstance(boolean debug, boolean googleEnabled, String googlePubkey, String[] googleCatalog,
String[] googleCatalogValues, boolean paypalEnabled, String paypalUser,
String paypalCurrencyCode, String paypalItemName, boolean flattrEnabled,
String flattrProjectUrl, String flattrUrl) {
DonationsFragment donationsFragment = new DonationsFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_DEBUG, debug);
args.putBoolean(ARG_GOOGLE_ENABLED, googleEnabled);
args.putString(ARG_GOOGLE_PUBKEY, googlePubkey);
args.putStringArray(ARG_GOOGLE_CATALOG, googleCatalog);
args.putStringArray(ARG_GOOGLE_CATALOG_VALUES, googleCatalogValues);
args.putBoolean(ARG_PAYPAL_ENABLED, paypalEnabled);
args.putString(ARG_PAYPAL_USER, paypalUser);
args.putString(ARG_PAYPAL_CURRENCY_CODE, paypalCurrencyCode);
args.putString(ARG_PAYPAL_ITEM_NAME, paypalItemName);
args.putBoolean(ARG_FLATTR_ENABLED, flattrEnabled);
args.putString(ARG_FLATTR_PROJECT_URL, flattrProjectUrl);
args.putString(ARG_FLATTR_URL, flattrUrl);
donationsFragment.setArguments(args);
return donationsFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDebug = getArguments().getBoolean(ARG_DEBUG);
mGoogleEnabled = getArguments().getBoolean(ARG_GOOGLE_ENABLED);
mGooglePubkey = getArguments().getString(ARG_GOOGLE_PUBKEY);
mGgoogleCatalog = getArguments().getStringArray(ARG_GOOGLE_CATALOG);
mGoogleCatalogValues = getArguments().getStringArray(ARG_GOOGLE_CATALOG_VALUES);
mPaypalEnabled = getArguments().getBoolean(ARG_PAYPAL_ENABLED);
mPaypalUser = getArguments().getString(ARG_PAYPAL_USER);
mPaypalCurrencyCode = getArguments().getString(ARG_PAYPAL_CURRENCY_CODE);
mPaypalItemName = getArguments().getString(ARG_PAYPAL_ITEM_NAME);
mFlattrEnabled = getArguments().getBoolean(ARG_FLATTR_ENABLED);
mFlattrProjectUrl = getArguments().getString(ARG_FLATTR_PROJECT_URL);
mFlattrUrl = getArguments().getString(ARG_FLATTR_URL);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.donations__fragment, container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
/* Flattr */
if (mFlattrEnabled) {
// inflate flattr view into stub
ViewStub flattrViewStub = (ViewStub) getActivity().findViewById(
R.id.donations__flattr_stub);
flattrViewStub.inflate();
buildFlattrView();
}
/* Google */
if (mGoogleEnabled) {
// inflate google view into stub
ViewStub googleViewStub = (ViewStub) getActivity().findViewById(
R.id.donations__google_stub);
googleViewStub.inflate();
// choose donation amount
mGoogleSpinner = (Spinner) getActivity().findViewById(
R.id.donations__google_android_market_spinner);
ArrayAdapter<CharSequence> adapter;
if (mDebug) {
adapter = new ArrayAdapter<CharSequence>(getActivity(),
android.R.layout.simple_spinner_item, CATALOG_DEBUG);
} else {
adapter = new ArrayAdapter<CharSequence>(getActivity(),
android.R.layout.simple_spinner_item, mGoogleCatalogValues);
}
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mGoogleSpinner.setAdapter(adapter);
Button btGoogle = (Button) getActivity().findViewById(
R.id.donations__google_android_market_donate_button);
btGoogle.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
donateGoogleOnClick(v);
}
});
// Create the helper, passing it our context and the public key to verify signatures with
if (mDebug)
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(getActivity(), mGooglePubkey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(mDebug);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
if (mDebug)
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (mDebug)
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
openDialog(android.R.drawable.ic_dialog_alert, R.string.donations__google_android_market_not_supported_title,
getString(R.string.donations__google_android_market_not_supported));
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
}
});
}
/* PayPal */
if (mPaypalEnabled) {
// inflate paypal view into stub
ViewStub paypalViewStub = (ViewStub) getActivity().findViewById(
R.id.donations__paypal_stub);
paypalViewStub.inflate();
Button btPayPal = (Button) getActivity().findViewById(
R.id.donations__paypal_donate_button);
btPayPal.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
donatePayPalOnClick(v);
}
});
}
}
/**
* Open dialog
*
* @param icon
* @param title
* @param message
*/
void openDialog(int icon, int title, String message) {
AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
dialog.setIcon(icon);
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setCancelable(true);
dialog.setNeutralButton(R.string.donations__button_close,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog.show();
}
/**
* Donate button executes donations based on selection in spinner
*
* @param view
*/
public void donateGoogleOnClick(View view) {
final int index;
index = mGoogleSpinner.getSelectedItemPosition();
if (mDebug)
Log.d(TAG, "selected item in spinner: " + index);
if (mDebug) {
// when debugging, choose android.test.x item
mHelper.launchPurchaseFlow(getActivity(),
CATALOG_DEBUG[index], IabHelper.ITEM_TYPE_INAPP,
0, mPurchaseFinishedListener, null);
} else {
mHelper.launchPurchaseFlow(getActivity(),
mGgoogleCatalog[index], IabHelper.ITEM_TYPE_INAPP,
0, mPurchaseFinishedListener, null);
}
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (mDebug)
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isSuccess()) {
if (mDebug)
Log.d(TAG, "Purchase successful.");
// directly consume in-app purchase, so that people can donate multiple times
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
// show thanks openDialog
openDialog(android.R.drawable.ic_dialog_info, R.string.donations__thanks_dialog_title,
getString(R.string.donations__thanks_dialog));
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (mDebug)
Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isSuccess()) {
if (mDebug)
Log.d(TAG, "Consumption successful. Provisioning.");
}
if (mDebug)
Log.d(TAG, "End consumption flow.");
}
};
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mDebug)
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// Pass on the fragment result to the helper for handling
if (!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);
} else {
if (mDebug)
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
/**
* Donate button with PayPal by opening browser with defined URL For possible parameters see:
* https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
*
* @param view
*/
public void donatePayPalOnClick(View view) {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("https").authority("www.paypal.com").path("cgi-bin/webscr");
uriBuilder.appendQueryParameter("cmd", "_donations");
uriBuilder.appendQueryParameter("business", mPaypalUser);
uriBuilder.appendQueryParameter("lc", "US");
uriBuilder.appendQueryParameter("item_name", mPaypalItemName);
uriBuilder.appendQueryParameter("no_note", "1");
// uriBuilder.appendQueryParameter("no_note", "0");
// uriBuilder.appendQueryParameter("cn", "Note to the developer");
uriBuilder.appendQueryParameter("no_shipping", "1");
uriBuilder.appendQueryParameter("currency_code", mPaypalCurrencyCode);
Uri payPalUri = uriBuilder.build();
if (mDebug)
Log.d(TAG, "Opening the browser with the url: " + payPalUri.toString());
// Start your favorite browser
try {
Intent viewIntent = new Intent(Intent.ACTION_VIEW, payPalUri);
startActivity(viewIntent);
} catch (ActivityNotFoundException e) {
openDialog(android.R.drawable.ic_dialog_alert, R.string.donations__alert_dialog_title,
getString(R.string.donations__alert_dialog_no_browser));
}
}
/**
* Build view for Flattr. see Flattr API for more information:
* http://developers.flattr.net/button/
*/
@SuppressLint("SetJavaScriptEnabled")
@TargetApi(11)
private void buildFlattrView() {
final FrameLayout mLoadingFrame;
final WebView mFlattrWebview;
mFlattrWebview = (WebView) getActivity().findViewById(R.id.donations__flattr_webview);
mLoadingFrame = (FrameLayout) getActivity().findViewById(R.id.donations__loading_frame);
// disable hardware acceleration for this webview to get transparent background working
if (Build.VERSION.SDK_INT >= 11) {
mFlattrWebview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
// define own webview client to override loading behaviour
mFlattrWebview.setWebViewClient(new WebViewClient() {
/**
* Open all links in browser, not in webview
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
try {
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(urlNewString)));
} catch (ActivityNotFoundException e) {
openDialog(android.R.drawable.ic_dialog_alert, R.string.donations__alert_dialog_title,
getString(R.string.donations__alert_dialog_no_browser));
}
return false;
}
/**
* Links in the flattr iframe should load in the browser not in the iframe itself,
* http:/
* /stackoverflow.com/questions/5641626/how-to-get-webview-iframe-link-to-launch-the
* -browser
*/
@Override
public void onLoadResource(WebView view, String url) {
if (url.contains("flattr")) {
HitTestResult result = view.getHitTestResult();
if (result != null && result.getType() > 0) {
try {
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
} catch (ActivityNotFoundException e) {
openDialog(android.R.drawable.ic_dialog_alert, R.string.donations__alert_dialog_title,
getString(R.string.donations__alert_dialog_no_browser));
}
view.stopLoading();
}
}
}
/**
* After loading is done, remove frame with progress circle
*/
@Override
public void onPageFinished(WebView view, String url) {
// remove loading frame, show webview
if (mLoadingFrame.getVisibility() == View.VISIBLE) {
mLoadingFrame.setVisibility(View.GONE);
mFlattrWebview.setVisibility(View.VISIBLE);
}
}
});
// get flattr values from xml config
String projectUrl = mFlattrProjectUrl;
String flattrUrl = this.mFlattrUrl;
// make text white and background transparent
String htmlStart = "<html> <head><style type='text/css'>*{color: #FFFFFF; background-color: transparent;}</style>";
// https is not working in android 2.1 and 2.2
String flattrScheme;
if (Build.VERSION.SDK_INT >= 9) {
flattrScheme = "https://";
} else {
flattrScheme = "http://";
}
// set url of flattr link
mFlattrUrlTextView = (TextView) getActivity().findViewById(R.id.donations__flattr_url);
mFlattrUrlTextView.setText(flattrScheme + flattrUrl);
String flattrJavascript = "<script type='text/javascript'>"
+ "/* <![CDATA[ */"
+ "(function() {"
+ "var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];"
+ "s.type = 'text/javascript';" + "s.async = true;" + "s.src = '" + flattrScheme
+ "api.flattr.com/js/0.6/load.js?mode=auto';" + "t.parentNode.insertBefore(s, t);"
+ "})();" + "/* ]]> */" + "</script>";
String htmlMiddle = "</head> <body> <div align='center'>";
String flattrHtml = "<a class='FlattrButton' style='display:none;' href='"
+ projectUrl
+ "' target='_blank'></a> <noscript><a href='"
+ flattrScheme
+ flattrUrl
+ "' target='_blank'> <img src='"
+ flattrScheme
+ "api.flattr.com/button/flattr-badge-large.png' alt='Flattr this' title='Flattr this' border='0' /></a></noscript>";
String htmlEnd = "</div> </body> </html>";
String flattrCode = htmlStart + flattrJavascript + htmlMiddle + flattrHtml + htmlEnd;
mFlattrWebview.getSettings().setJavaScriptEnabled(true);
mFlattrWebview.loadData(flattrCode, "text/html", "utf-8");
// disable scroll on touch
mFlattrWebview.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// already handled (returns true) when moving
return (motionEvent.getAction() == MotionEvent.ACTION_MOVE);
}
});
// make background of webview transparent
// has to be called AFTER loadData
// http://stackoverflow.com/questions/5003156/android-webview-style-background-colortransparent-ignored-on-android-2-2
mFlattrWebview.setBackgroundColor(0x00000000);
}
}