package com.mygeopay.wallet;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.StrictMode;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.widget.Toast;
import com.mygeopay.core.coins.CoinType;
import com.mygeopay.core.exchange.shapeshift.ShapeShift;
import com.mygeopay.core.util.HardwareSoftwareCompliance;
import com.mygeopay.core.wallet.Wallet;
import com.mygeopay.core.wallet.WalletAccount;
import com.mygeopay.core.wallet.WalletProtobufSerializer;
import com.mygeopay.wallet.service.CoinService;
import com.mygeopay.wallet.service.CoinServiceImpl;
import com.mygeopay.wallet.util.Fonts;
import com.mygeopay.wallet.util.LinuxSecureRandom;
import com.mygeopay.wallet.util.NetworkUtils;
import com.google.common.collect.ImmutableList;
import org.acra.annotation.ReportsCrashes;
import org.acra.sender.HttpSender;
import org.bitcoinj.core.Address;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.store.UnreadableWalletException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nullable;
/**
* @author John L. Jegutanis
* @author Andreas Schildbach
*/
@ReportsCrashes(
// Also uncomment ACRA.init(this) in onCreate
httpMethod = HttpSender.Method.PUT,
reportType = HttpSender.Type.JSON,
formKey = ""
)
public class WalletApplication extends Application {
private static final Logger log = LoggerFactory.getLogger(WalletApplication.class);
private static HashMap<String, Typeface> typefaces;
private Configuration config;
private ActivityManager activityManager;
private Intent coinServiceIntent;
private Intent coinServiceConnectIntent;
private Intent coinServiceCancelCoinsReceivedIntent;
private Intent coinServiceResetWalletIntent;
private File walletFile;
@Nullable
private Wallet wallet;
private PackageInfo packageInfo;
private long lastStop;
private ConnectivityManager connManager;
private ShapeShift shapeShift;
@Override
public void onCreate() {
// ACRA.init(this);
new LinuxSecureRandom(); // init proper random number generator
initLogging();
// TODO review this
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder().detectAll().permitDiskReads().permitDiskWrites().penaltyLog().build());
super.onCreate();
packageInfo = packageInfoFromContext(this);
config = new Configuration(PreferenceManager.getDefaultSharedPreferences(this));
activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
coinServiceIntent = new Intent(this, CoinServiceImpl.class);
coinServiceConnectIntent = new Intent(CoinService.ACTION_CONNECT_COIN,
null, this, CoinServiceImpl.class);
coinServiceCancelCoinsReceivedIntent = new Intent(CoinService.ACTION_CANCEL_COINS_RECEIVED,
null, this, CoinServiceImpl.class);
coinServiceResetWalletIntent = new Intent(CoinService.ACTION_RESET_WALLET,
null, this, CoinServiceImpl.class);
// Set MnemonicCode.INSTANCE if needed
if (MnemonicCode.INSTANCE == null) {
try {
MnemonicCode.INSTANCE = new MnemonicCode();
} catch (Exception e) {
log.error("Could not set MnemonicCode.INSTANCE", e);
}
}
config.updateLastVersionCode(packageInfo.versionCode);
performComplianceTests();
connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
walletFile = getFileStreamPath(Constants.WALLET_FILENAME_PROTOBUF);
loadWallet();
afterLoadWallet();
Fonts.initFonts(this.getAssets());
}
public boolean isConnected() {
NetworkInfo activeInfo = connManager.getActiveNetworkInfo();
return activeInfo != null && activeInfo.isConnected();
}
public ShapeShift getShapeShift() {
if (shapeShift == null) {
shapeShift = new ShapeShift(NetworkUtils.getHttpClient(getApplicationContext()));
}
return shapeShift;
}
/**
* Some devices have software bugs that causes the EC crypto to malfunction.
*/
private void performComplianceTests() {
if (!HardwareSoftwareCompliance.isEllipticCurveCryptographyCompliant()) {
config.setDeviceCompatible(false);
}
}
private void afterLoadWallet() {
// wallet.autosaveToFile(walletFile, 1, TimeUnit.SECONDS, new WalletAutosaveEventListener());
//
// clean up spam
// wallet.cleanup();
//
// ensureKey();
//
// migrateBackup();
}
private void initLogging() {
// final File logDir = getDir("log", Constants.TEST ? Context.MODE_WORLD_READABLE : MODE_PRIVATE);
// final File logFile = new File(logDir, "wallet.log");
//
// final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
//
// final PatternLayoutEncoder filePattern = new PatternLayoutEncoder();
// filePattern.setContext(context);
// filePattern.setPattern("%d{HH:mm:ss.SSS} [%thread] %logger{0} - %msg%n");
// filePattern.start();
//
// final RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();
// fileAppender.setContext(context);
// fileAppender.setFile(logFile.getAbsolutePath());
//
// final TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<ILoggingEvent>();
// rollingPolicy.setContext(context);
// rollingPolicy.setParent(fileAppender);
// rollingPolicy.setFileNamePattern(logDir.getAbsolutePath() + "/wallet.%d.log.gz");
// rollingPolicy.setMaxHistory(7);
// rollingPolicy.start();
//
// fileAppender.setEncoder(filePattern);
// fileAppender.setRollingPolicy(rollingPolicy);
// fileAppender.start();
//
// final PatternLayoutEncoder logcatTagPattern = new PatternLayoutEncoder();
// logcatTagPattern.setContext(context);
// logcatTagPattern.setPattern("%logger{0}");
// logcatTagPattern.start();
//
// final PatternLayoutEncoder logcatPattern = new PatternLayoutEncoder();
// logcatPattern.setContext(context);
// logcatPattern.setPattern("[%thread] %msg%n");
// logcatPattern.start();
//
// final LogcatAppender logcatAppender = new LogcatAppender();
// logcatAppender.setContext(context);
// logcatAppender.setTagEncoder(logcatTagPattern);
// logcatAppender.setEncoder(logcatPattern);
// logcatAppender.start();
//
// final ch.qos.logback.classic.Logger log = context.getLogger(Logger.ROOT_LOGGER_NAME);
// log.addAppender(fileAppender);
// log.addAppender(logcatAppender);
// log.setLevel(Level.INFO);
}
public Configuration getConfiguration() {
return config;
}
/**
* Get the current wallet.
*/
@Nullable
public Wallet getWallet() {
return wallet;
}
@Nullable
public WalletAccount getAccount(String accountId) {
if (wallet != null) {
return wallet.getAccount(accountId);
} else {
return null;
}
}
public List<WalletAccount> getAccounts(CoinType type) {
if (wallet != null) {
return wallet.getAccounts(type);
} else {
return ImmutableList.of();
}
}
public List<WalletAccount> getAccounts(List<CoinType> types) {
if (wallet != null) {
return wallet.getAccounts(types);
} else {
return ImmutableList.of();
}
}
public List<WalletAccount> getAccounts(Address address) {
if (wallet != null) {
return wallet.getAccounts(address);
} else {
return ImmutableList.of();
}
}
public List<WalletAccount> getAllAccounts() {
if (wallet != null) {
return wallet.getAllAccounts();
} else {
return ImmutableList.of();
}
}
/**
* Check if account exists
*/
public boolean isAccountExists(String accountId) {
if (wallet != null) {
return wallet.isAccountExists(accountId);
} else {
return false;
}
}
/**
* Check if accounts exists for the spesific coin type
*/
public boolean isAccountExists(CoinType type) {
if (wallet != null) {
return wallet.isAccountExists(type);
} else {
return false;
}
}
public void setWallet(Wallet wallet) {
// Disable auto-save of the previous wallet if exists, so it doesn't override the new one
if (this.wallet != null) {
this.wallet.shutdownAutosaveAndWait();
}
this.wallet = wallet;
this.wallet.autosaveToFile(walletFile,
Constants.WALLET_WRITE_DELAY, Constants.WALLET_WRITE_DELAY_UNIT, null);
}
private void loadWallet() {
if (walletFile.exists()) {
final long start = System.currentTimeMillis();
FileInputStream walletStream = null;
try {
walletStream = new FileInputStream(walletFile);
setWallet(WalletProtobufSerializer.readWallet(walletStream));
log.info("wallet loaded from: '" + walletFile + "', took " + (System.currentTimeMillis() - start) + "ms");
} catch (final FileNotFoundException x) {
log.error("problem loading wallet", x);
Toast.makeText(WalletApplication.this, R.string.error_could_not_read_wallet, Toast.LENGTH_LONG).show();
} catch (final UnreadableWalletException x) {
log.error("problem loading wallet", x);
Toast.makeText(WalletApplication.this, R.string.error_could_not_read_wallet, Toast.LENGTH_LONG).show();
} finally {
if (walletStream != null) {
try {
walletStream.close();
} catch (final IOException x) { /* ignore */ }
}
}
}
}
public void saveWalletNow() {
if (wallet != null) {
wallet.saveNow();
}
}
public void saveWalletLater() {
if (wallet != null) {
wallet.saveLater();
}
}
public void startBlockchainService(CoinService.ServiceMode mode) {
switch (mode) {
case CANCEL_COINS_RECEIVED:
startService(coinServiceCancelCoinsReceivedIntent);
break;
case RESET_WALLET:
startService(coinServiceResetWalletIntent);
break;
case NORMAL:
default:
startService(coinServiceIntent);
break;
}
}
public void stopBlockchainService() {
stopService(coinServiceIntent);
}
public static PackageInfo packageInfoFromContext(final Context context) {
try {
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
} catch (final PackageManager.NameNotFoundException x) {
throw new RuntimeException(x);
}
}
public PackageInfo packageInfo() {
return packageInfo;
}
public void touchLastResume() {
lastStop = -1;
}
public void touchLastStop() {
lastStop = SystemClock.elapsedRealtime();
}
public long getLastStop() {
return lastStop;
}
public void maybeConnectAccount(WalletAccount account) {
if (!account.isConnected()) {
coinServiceConnectIntent.putExtra(Constants.ARG_ACCOUNT_ID, account.getId());
startService(coinServiceConnectIntent);
}
}
}