com.bonsai.wallet32.WalletApplication.java Source code

Java tutorial

Introduction

Here is the source code for com.bonsai.wallet32.WalletApplication.java

Source

// Copyright (C) 2013-2014  Bonsai Software, Inc.
// 
// 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 com.bonsai.wallet32;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;

import android.app.Application;
import android.app.NotificationManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.PreferenceManager;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.android.LogcatAppender;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;

import com.google.bitcoin.crypto.KeyCrypter;
import hashengineering.groestlcoin.wallet32.R;

public class WalletApplication extends Application implements OnSharedPreferenceChangeListener {

    private static Logger mLogger = LoggerFactory.getLogger(WalletApplication.class);

    public String mPasscode;
    public KeyCrypter mKeyCrypter;
    public KeyParameter mAesKey;

    public String mIntentURI = null;

    private BTCFmt mBTCFmt = null;

    private String mWalletDirName;

    private WalletService mWalletService = null;

    private long mPasscodeValidTimestamp = 0L;

    private boolean mEntered = false; // Came through lobby?
    private boolean mLoggedIn = false; // Past the passcode?

    @Override
    public void onCreate() {
        // Apply PRNGFixes.
        PRNGFixes.apply();

        initLogging();

        super.onCreate();

        // Log the About contents so we have the version string.
        mLogger.info(getResources().getString(R.string.about_contents));

        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        String btcUnits = sharedPref.getString(SettingsActivity.KEY_BTC_UNITS, "");
        setBTCUnits(btcUnits);

        // Register for future preference changes.
        sharedPref.registerOnSharedPreferenceChangeListener(this);

        mLogger.info("WalletApplication created");
    }

    public void doExit() {
        mLogger.info("Application exiting");

        if (mWalletService != null)
            mWalletService.shutdown();

        mLogger.info("Stopping WalletService");
        stopService(new Intent(this, WalletService.class));

        // Cancel any remaining notifications.
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.cancelAll();

        // Kill everything
        mLogger.info("Exiting");
        System.exit(0);
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        mLogger.info("saw pref key " + key);
        if (key.equals(SettingsActivity.KEY_BTC_UNITS)) {
            SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
            String btcUnits = sharedPref.getString(SettingsActivity.KEY_BTC_UNITS, "");
            setBTCUnits(btcUnits);
        }
    }

    public void setWalletService(WalletService walletService) {
        mWalletService = walletService;
    }

    public WalletService getWalletService() {
        return mWalletService;
    }

    public void startBackgroundTimeout() {
        if (mWalletService != null)
            mWalletService.startBackgroundTimeout();
    }

    public void cancelBackgroundTimeout() {
        if (mWalletService != null)
            mWalletService.cancelBackgroundTimeout();
    }

    public void setPasscodeValidTimestamp() {
        mPasscodeValidTimestamp = System.currentTimeMillis();
    }

    public boolean passcodeFreshlyEntered() {
        long delta = System.currentTimeMillis() - mPasscodeValidTimestamp;
        return delta < (5 * 60 * 1000);
    }

    public void setEntered() {
        mEntered = true;
    }

    public boolean hasEntered() {
        return mEntered;
    }

    public void setLoggedIn() {
        mLoggedIn = true;
    }

    public boolean isLoggedIn() {
        return mLoggedIn;
    }

    private void setBTCUnits(String src) {
        if (src.equals("UGRS")) {
            mLogger.info("Setting GRS units to uGRS");
            mBTCFmt = new BTCFmt(BTCFmt.SCALE_UBTC, this);
        } else if (src.equals("MGRS")) {
            mLogger.info("Setting GRS units to MGRS");
            mBTCFmt = new BTCFmt(BTCFmt.SCALE_MBTC, this);
        } else if (src.equals("GRS")) {
            mLogger.info("Setting GRS units to GRS");
            mBTCFmt = new BTCFmt(BTCFmt.SCALE_BTC, this);
        } else if (src.equals("")) {
            mLogger.info("Defaulting GRS units to MGRS");
            mBTCFmt = new BTCFmt(BTCFmt.SCALE_MBTC, this);
        } else {
            mLogger.warn("Unknown grs units " + src);
            return;
        }
    }

    public BTCFmt getBTCFmt() {
        return mBTCFmt;
    }

    public void setIntentURI(String uri) {
        mIntentURI = uri;
    }

    public String getIntentURI() {
        return mIntentURI;
    }

    public static class WalletEntry {
        public String mName; // User friendly name.
        public String mPath; // Wallet directory path name.

        public WalletEntry(String name, String path) {
            mName = name;
            mPath = path;
        }

        public JSONObject dumpJSON() throws JSONException {
            JSONObject entobj = new JSONObject();
            entobj.put("name", mName);
            entobj.put("path", mPath);
            return entobj;
        }

        public static WalletEntry loadJSON(JSONObject entobj) throws JSONException {
            return new WalletEntry(entobj.getString("name"), entobj.getString("path"));
        }
    }

    public List<WalletEntry> listWallets() {
        List<WalletEntry> wallets = new ArrayList<WalletEntry>();

        File walletDirFile = new File(getFilesDir(), getWalletPrefix() + ".walletdir");

        // Does the directory not exist?
        if (!walletDirFile.exists()) {
            // Yes, we don't have a directory file yet.  Create a
            // default one with a single entry for default wallet.
            mLogger.info("creating default wallet directory in " + walletDirFile.getPath());
            wallets.add(new WalletEntry("Default", "."));
            persistWalletDirectory(wallets);
        } else {
            mLogger.info("reading wallet directory in " + walletDirFile.getPath());
            wallets = loadWalletDirectory();
        }

        return wallets;
    }

    public int nextAvailableWallet() {
        // Find the first available wallet directory.
        for (int ndx = 1; ndx < 64; ++ndx) {
            String path = String.format("wallet%03d", ndx);
            File dir = new File(getFilesDir(), path);
            if (!dir.exists()) {
                mLogger.info("nextAvailableWallet " + path);
                return ndx;
            }
        }
        throw new RuntimeException("no wallet space available");
    }

    public void renameWallet(String path, String newName) {
        mLogger.info("renmaing wallet " + path + " to " + newName);
        List<WalletEntry> wallets = loadWalletDirectory();
        for (WalletEntry entry : wallets) {
            if (entry.mPath.equals(path)) {
                entry.mName = newName;
                persistWalletDirectory(wallets);
                return;
            }
        }
        throw new RuntimeException("wallet " + path + " not found");
    }

    public void deleteWallet(String path) {
        mLogger.info("deleting wallet " + path);

        File dir = new File(getFilesDir(), path);

        // Special case, the root wallet is not in a subdirectory.
        File child = null;
        if (path.equals(".")) {
            try {
                child = new File(dir, "salt");
                child.delete();
                child = new File(dir, "wallet32.spvchain");
                child.delete();
                child = new File(dir, "wallet32.hdwallet");
                child.delete();
                child = new File(dir, "wallet32.wallet");
                child.delete();
            } catch (Exception ex) {
                mLogger.error("delete of " + child.toString() + " failed");
            }
        } else {
            File[] directoryListing = dir.listFiles();
            for (File child2 : directoryListing) {
                try {
                    mLogger.info("deleting " + child2.toString());
                    child2.delete();
                } catch (Exception ex) {
                    mLogger.error("delete of " + child2.toString() + " failed");
                }
            }
            try {
                dir.delete();
            } catch (Exception ex) {
                mLogger.error("delete of " + path + " failed");
            }
        }

        // Re-persist the wallet list without the deleted entry.
        List<WalletEntry> newWalletList = new ArrayList<WalletEntry>();
        List<WalletEntry> walletList = loadWalletDirectory();
        for (WalletEntry entry : walletList)
            if (!entry.mPath.equals(path))
                newWalletList.add(entry);
        persistWalletDirectory(newWalletList);
    }

    public String addWallet() {
        int ndx = nextAvailableWallet();
        List<WalletEntry> wallets = loadWalletDirectory();
        String name = String.format("Wallet %d", ndx);
        String path = String.format("wallet%03d", ndx);
        wallets.add(new WalletEntry(name, path));
        makeWalletDirectory(path);
        persistWalletDirectory(wallets);
        return name;
    }

    public String walletName(String walletpath) {
        List<WalletEntry> walletList = loadWalletDirectory();
        for (WalletEntry entry : walletList) {
            if (entry.mPath.equals(walletpath))
                return entry.mName;
        }
        throw new RuntimeException("wallet path " + walletpath + " not found");
    }

    public void persistWalletDirectory(List<WalletEntry> wallets) {
        File walletDirFile = new File(getFilesDir(), getWalletPrefix() + ".walletdir");
        try {
            File tmpFile = new File(walletDirFile.getPath() + ".tmp");
            if (tmpFile.exists()) {
                mLogger.info("deleting existing at " + tmpFile.getPath());
                tmpFile.delete();
            }

            JSONArray listobj = new JSONArray();
            for (WalletEntry entry : wallets)
                listobj.put(entry.dumpJSON());
            JSONObject rootobj = new JSONObject();
            rootobj.put("wallets", listobj);
            String jsonstr = rootobj.toString(4); // indentation
            byte[] jsondata = jsonstr.getBytes(Charset.forName("UTF-8"));

            mLogger.info("persisting: " + jsonstr);

            FileOutputStream ostrm = new FileOutputStream(tmpFile);
            ostrm.write(jsondata);
            ostrm.close();

            // Swap the tmp file into place.
            if (!tmpFile.renameTo(walletDirFile))
                mLogger.warn("failed to rename to " + walletDirFile.getPath());
            else
                mLogger.info("persisted to " + walletDirFile.getPath());
        } catch (Exception ex) {
            mLogger.error("persistWalletDirectory failed: " + ex.toString());
            throw new RuntimeException(ex);
        }
    }

    public List<WalletEntry> loadWalletDirectory() {
        File walletDirFile = new File(getFilesDir(), getWalletPrefix() + ".walletdir");

        try {
            int len = (int) walletDirFile.length();
            DataInputStream dis = new DataInputStream(new FileInputStream(walletDirFile));
            byte[] jsondata = new byte[len];
            dis.readFully(jsondata);
            String jsonstr = new String(jsondata);
            mLogger.info("loading: " + jsonstr);
            JSONObject rootobj = new JSONObject(jsonstr);
            JSONArray listobj = rootobj.getJSONArray("wallets");
            ArrayList<WalletEntry> wallets = new ArrayList<WalletEntry>();
            for (int ii = 0; ii < listobj.length(); ++ii)
                wallets.add(WalletEntry.loadJSON(listobj.getJSONObject(ii)));
            return wallets;
        } catch (Exception ex) {
            mLogger.error("loadWalletDirectory failed: " + ex.toString());
            throw new RuntimeException(ex);
        }
    }

    public void makeWalletDirectory(String walletDirName) {
        File dir = new File(getFilesDir(), walletDirName);
        boolean success = dir.mkdir();
    }

    public void setWalletDirName(String walletDirName) {
        mWalletDirName = walletDirName;
    }

    public File getWalletDir() {
        return new File(getFilesDir(), mWalletDirName);
    }

    public String getWalletPrefix() {
        return "wallet32";
    }

    public File getHDWalletFile(String suffix) {
        String filename = getWalletPrefix() + ".hdwallet";
        if (suffix != null)
            filename = filename + suffix;
        return new File(getWalletDir(), filename);
    }

    private void initLogging() {
        // We can't log into the wallet specific directories because
        // logging is initialized well before we've selected one.

        final File logDir = getDir("log", MODE_PRIVATE); // Context.MODE_WORLD_READABLE
        final File logFile = new File(logDir, "wallet32.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() + "/wallet32.%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);
    }
}

// Local Variables:
// mode: java
// c-basic-offset: 4
// tab-width: 4
// End: