/*
* Copyright 2011-2012 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package piuk.blockchain.android.service;
import info.blockchain.wallet.ui.TimeOutUtil;
import info.blockchain.wallet.ui.TxNotifUtil;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import piuk.blockchain.android.EventListeners;
import piuk.blockchain.android.MyTransactionOutput;
import piuk.blockchain.android.Constants;
import piuk.blockchain.android.R;
import piuk.blockchain.android.WalletApplication;
import piuk.blockchain.android.R.drawable;
import piuk.blockchain.android.R.raw;
import piuk.blockchain.android.R.string;
import piuk.blockchain.android.ui.WalletActivity;
import piuk.blockchain.android.util.WalletUtils;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;
import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionInput;
public class WebsocketService extends android.app.Service
{
private WalletApplication application;
private WebSocketHandler webSocketHandler;
Timer timer = new Timer();
private final Handler handler = new Handler();
private NotificationManager nm;
private static final int NOTIFICATION_ID_CONNECTED = 0;
private static final int NOTIFICATION_ID_COINS_RECEIVED = 1;
private static final int NOTIFICATION_ID_COINS_SENT = 3;
public static boolean isRunning = false;
private final EventListeners.EventListener walletEventListener = new EventListeners.EventListener() {
@Override
public String getDescription() {
return "Websocket Service Listener";
}
@Override
public void onWalletDidChange() {
try {
notifyWidgets();
} catch (Exception e) {
e.printStackTrace();
}
};
@Override
public void onCoinsSent(final Transaction tx, final long result)
{
System.out.println("onCoinsSent()");
try {
// final MyTransactionOutput output = (MyTransactionOutput) tx.getOutputs().get(0);
// final Address to = output.getToAddress();
notifyCoinsSent(tx, BigInteger.valueOf(result).abs());
notifyWidgets();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onCoinsReceived(final Transaction tx, final long result)
{
try {
System.out.println("onCoinsReceived()");
if (tx.getInputs() == null || tx.getInputs().size() == 0) {
notifyCoinbaseReceived(BigInteger.valueOf(result));
} else {
final TransactionInput input = tx.getInputs().get(0);
final Address from = input.getFromAddress();
handler.post(new Runnable()
{
public void run()
{
notifyCoinsReceived(tx, BigInteger.valueOf(result));
notifyWidgets();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
private void notifyCoinsSent(final Transaction tx, final BigInteger amount)
{
System.out.println("Notify ");
final MyTransactionOutput output = (MyTransactionOutput) tx.getOutputs().get(0);
final Address to = output.getToAddress();
TxNotifUtil.getInstance().setTx(tx);
BigInteger notificationAccumulatedAmount = BigInteger.ZERO;
notificationAccumulatedAmount = notificationAccumulatedAmount.add(amount);
final List<Address> notificationAddresses = new LinkedList<Address>();
if (to != null && !notificationAddresses.contains(to))
notificationAddresses.add(to);
final String tickerMsg = getString(R.string.notification_coins_sent_msg, WalletUtils.formatValue(amount))
+ (Constants.TEST ? " [testnet]" : "");
final String msg = getString(R.string.notification_coins_sent_msg, WalletUtils.formatValue(notificationAccumulatedAmount))
+ (Constants.TEST ? " [testnet]" : "");
final StringBuilder text = new StringBuilder();
for (final Address address : notificationAddresses)
{
if (text.length() > 0)
text.append(", ");
text.append(address.toString());
}
if (text.length() == 0)
text.append("unknown");
text.insert(0, "To ");
final Notification notification = new Notification(R.drawable.stat_notify_received, tickerMsg, System.currentTimeMillis());
Intent notificationIntent = null;
if(TimeOutUtil.getInstance().isTimedOut()){
notificationIntent = new Intent(WebsocketService.this, info.blockchain.wallet.ui.PinEntryActivity.class);
}
else{
notificationIntent = new Intent(WebsocketService.this, info.blockchain.wallet.ui.MainActivity.class);
}
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(WebsocketService.this, 0, notificationIntent, 0);
notification.setLatestEventInfo(WebsocketService.this, msg, text, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.number = 0;
notification.sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.alert);
nm.notify(NOTIFICATION_ID_COINS_SENT, notification);
Toast.makeText(application, tickerMsg, Toast.LENGTH_LONG).show();
}
private void notifyCoinbaseReceived(final BigInteger amount) {
final Notification notification = new Notification(R.drawable.stat_notify_received, "Newly Generated Coins", System.currentTimeMillis());
final String msg = getString(R.string.notification_coins_received_msg, WalletUtils.formatValue(amount));
notification.setLatestEventInfo(WebsocketService.this, msg, "Newly Generated Coins",
PendingIntent.getActivity(WebsocketService.this, 0, new Intent(WebsocketService.this, WalletActivity.class), 0));
notification.number = 0;
notification.sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.alert);
nm.notify(NOTIFICATION_ID_COINS_RECEIVED, notification);
Toast.makeText(application, "Newly Generated Coins Receive", Toast.LENGTH_LONG).show();
}
private void notifyCoinsReceived(final Transaction tx, final BigInteger amount)
{
System.out.println("Notify ");
final TransactionInput input = tx.getInputs().get(0);
final Address from = input.getFromAddress();
TxNotifUtil.getInstance().setTx(tx);
BigInteger notificationAccumulatedAmount = BigInteger.ZERO;
notificationAccumulatedAmount = notificationAccumulatedAmount.add(amount);
final List<Address> notificationAddresses = new LinkedList<Address>();
if (from != null && !notificationAddresses.contains(from))
notificationAddresses.add(from);
final String tickerMsg = getString(R.string.notification_coins_received_msg, WalletUtils.formatValue(amount))
+ (Constants.TEST ? " [testnet]" : "");
final String msg = getString(R.string.notification_coins_received_msg, WalletUtils.formatValue(notificationAccumulatedAmount))
+ (Constants.TEST ? " [testnet]" : "");
final StringBuilder text = new StringBuilder();
for (final Address address : notificationAddresses)
{
if (text.length() > 0)
text.append(", ");
text.append(address.toString());
}
if (text.length() == 0)
text.append("unknown");
text.insert(0, "From ");
final Notification notification = new Notification(R.drawable.stat_notify_received, tickerMsg, System.currentTimeMillis());
Intent notificationIntent = null;
if(TimeOutUtil.getInstance().isTimedOut()){
notificationIntent = new Intent(WebsocketService.this, info.blockchain.wallet.ui.PinEntryActivity.class);
}
else{
notificationIntent = new Intent(WebsocketService.this, info.blockchain.wallet.ui.MainActivity.class);
}
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(WebsocketService.this, 0, notificationIntent, 0);
notification.setLatestEventInfo(WebsocketService.this, msg, text, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.number = 0;
notification.sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.alert);
nm.notify(NOTIFICATION_ID_COINS_RECEIVED, notification);
Toast.makeText(application, tickerMsg, Toast.LENGTH_LONG).show();
}
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(final Context context, final Intent intent)
{
final String action = intent.getAction();
handler.post(new Runnable() {
public void run() {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action))
{
boolean hasConnectivity = !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (hasConnectivity) {
connectToWebsocketIfNotConnected();
}
}
}
});
}
};
public class LocalBinder extends Binder
{
public WebsocketService getService()
{
return WebsocketService.this;
}
}
private final IBinder mBinder = new LocalBinder();
@Override
public IBinder onBind(final Intent intent)
{
return mBinder;
}
@Override
public void onCreate()
{
super.onCreate();
isRunning = true;
nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
application = (WalletApplication) getApplication();
EventListeners.addEventListener(walletEventListener);
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(broadcastReceiver, intentFilter);
webSocketHandler = new WebSocketHandler(application);
connectToWebsocketIfNotConnected();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
connectToWebsocketIfNotConnected();
}
});
}
}, 10000, 20000);
}
public void connectToWebsocketIfNotConnected()
{
try {
if (!webSocketHandler.isConnected()) {
webSocketHandler.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
webSocketHandler.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onDestroy()
{
isRunning = false;
EventListeners.removeEventListener(walletEventListener);
stop();
unregisterReceiver(broadcastReceiver);
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable()
{
public void run()
{
nm.cancel(NOTIFICATION_ID_CONNECTED);
}
}, Constants.SHUTDOWN_REMOVE_NOTIFICATION_DELAY);
super.onDestroy();
}
public void notifyWidgets()
{
final Context context = getApplicationContext();
// notify widgets
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
for (final AppWidgetProviderInfo providerInfo : appWidgetManager.getInstalledProviders())
{
// limit to own widgets
if (providerInfo.provider.getPackageName().equals(context.getPackageName()))
{
final Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetManager.getAppWidgetIds(providerInfo.provider));
context.sendBroadcast(intent);
}
}
}
}