at.bitcoin_austria.bitfluids.BitFluidsMainActivity.java Source code

Java tutorial

Introduction

Here is the source code for at.bitcoin_austria.bitfluids.BitFluidsMainActivity.java

Source

/*
 * Copyright 2012 Bitcoin Austria
 *
 * 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 at.bitcoin_austria.bitfluids;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.http.AndroidHttpClient;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.speech.tts.TextToSpeech;
import android.text.ClipboardManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
import at.bitcoin_austria.bitfluids.trafficSignal.SignalType;
import at.bitcoin_austria.bitfluids.trafficSignal.Status;
import at.bitcoin_austria.bitfluids.trafficSignal.TrafficSignal;
import at.bitcoin_austria.bitfluids.trafficSignal.TrafficSignalReciever;
import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.Sha256Hash;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import java.text.DecimalFormat;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class BitFluidsMainActivity extends Activity {
    public static final String TAG = "BF";
    public static final int TTS_ACTION = 1224;
    private final Environment env = Environment.PROD;

    private final Handler uiHandler = new Handler();

    // wake lock for preventing the screen from sleeping
    private PowerManager.WakeLock wakeLock;

    // list of transactions, change it via the ArrayAdapter
    private ListView list_view_tx;
    private ArrayAdapter<TransactionItem> list_view_adapter;

    private BitFluidsActivityState state;

    private ImageView qr_alk;
    private ImageView qr_nonalk;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    //service dependencies, initialized in constructor, run/shutdown in onCreate onDestroy
    private BitcoinTransactionListener bitcoinTransactionListener;
    private BroadcastReceiver netStatusReciever;
    private TextView netStatus;
    private TextView exchStatus;
    private TextView p2pStatus;
    private TextToSpeech textToSpeech;

    public BitFluidsMainActivity() {
        Log.i(TAG, "starting up");
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable throwable) {
                Log.e(TAG, "excpetion!", throwable);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (textToSpeech != null) {
            textToSpeech.shutdown();
        }
        if (netStatusReciever != null) {
            unregisterReceiver(netStatusReciever);
            netStatusReciever = null;
        }
        bitcoinTransactionListener.shutdown();
        bitcoinTransactionListener = null;
    }

    /**
     * usual binder for ui elements
     */
    private void bind() {
        list_view_tx = (ListView) findViewById(R.id.list_view_tx);
        qr_alk = (ImageView) findViewById(R.id.qr_code_alk);
        qr_nonalk = (ImageView) findViewById(R.id.qr_code_nonalk);

        netStatus = (TextView) findViewById(R.id.NetStatus);
        exchStatus = (TextView) findViewById(R.id.ExchStatus);
        p2pStatus = (TextView) findViewById(R.id.P2PStatus);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (textToSpeech != null) {
            textToSpeech.stop();
            textToSpeech.shutdown();
        }
        wakeLock.release();
    }

    /**
     * Save the state, this will be passed-in again in {@link #onCreate(Bundle)}
     */
    @Override
    public Object onRetainNonConfigurationInstance() {
        return state;
    }

    @Override
    protected void onResume() {
        super.onResume();
        wakeLock.acquire();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putSerializable("state", state);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        restoreState(savedInstanceState);
    }

    private void restoreState(Bundle savedInstanceState) {
        Preconditions.checkNotNull(bitcoinTransactionListener);
        if (savedInstanceState != null) {
            state = (BitFluidsActivityState) savedInstanceState.getSerializable("state");
        }
        if (state == null) {
            state = new BitFluidsActivityState();
        }
        bitcoinTransactionListener.addHashes(state.getTransactionItems());
        list_view_adapter = new ArrayAdapter<TransactionItem>(this, R.layout.list_tx_item,
                Lists.reverse(state.getTransactionItems()));
        list_view_tx.setAdapter(list_view_adapter);
    }

    @Override
    protected void onStop() {
        super.onStop();

    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bind();
        checkForTTS();

        bitcoinTransactionListener = new BitcoinTransactionListener(env);
        restoreState(savedInstanceState);
        final PriceService priceService = new PriceService(AndroidHttpClient.newInstance("Bitfluids 0.1"));

        // what follows is a list of initializations, encapsulated into {} blocks

        { // prevent the display from dimming
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
        }

        { // size of QR codes should be a bit less than 1/3rd of (longest) screen
          // dimension - the layout default is ok, but this should work better.
            int qrSize = 1, displayWidth, displayHeight;
            double scale = 0.8;
            switch (getResources().getConfiguration().orientation) {
            case Configuration.ORIENTATION_LANDSCAPE:
                displayHeight = getWindowManager().getDefaultDisplay().getHeight();
                qrSize = (int) ((displayHeight / 2.0) * scale);
                break;
            case Configuration.ORIENTATION_PORTRAIT:
                displayWidth = getWindowManager().getDefaultDisplay().getWidth();
                qrSize = (int) ((displayWidth / 3.0) * scale);
                break;
            default:
                displayWidth = getWindowManager().getDefaultDisplay().getWidth();
                displayHeight = getWindowManager().getDefaultDisplay().getHeight();
                qrSize = (int) (Math.max(displayWidth, displayHeight) / 3.0 * scale);
            }
            LinearLayout.LayoutParams qr_ll = new LinearLayout.LayoutParams(qrSize, qrSize);
            qr_alk.setLayoutParams(qr_ll);
            qr_nonalk.setLayoutParams(qr_ll);
        }

        { // init the tx list
          // this will be tx objects 
          //  and here we would need a custom array adapter

            list_view_tx.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Toast.makeText(getApplicationContext(), ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
                }
            });
        }

        {
            int unknown = Color.CYAN;
            netStatus.setBackgroundColor(unknown);
            exchStatus.setBackgroundColor(unknown);
            p2pStatus.setBackgroundColor(unknown);
            // start background task to singal Status
            TrafficSignal trafficSignal = new TrafficSignal(this, bitcoinTransactionListener, priceService);
            netStatusReciever = trafficSignal.addNotifier(new TrafficSignalReciever() {
                @Override
                public void onStatusChanged(final SignalType signalType, final Status status) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            switch (signalType) { //switch is generally ugly, perhaps this should move into enum?
                            case NETWORK:
                                netStatus.setBackgroundColor(status.androidColor);
                                break;
                            case EXCHANGERATE:
                                exchStatus.setBackgroundColor(status.androidColor);
                                break;
                            case PEERS:
                                p2pStatus.setBackgroundColor(status.androidColor);
                                break;
                            default:
                                throw new IllegalArgumentException("unexpected signal :" + signalType);

                            }
                        }
                    });
                }
            });
        }

        {
            final Runnable queryBtcEurTask = new Runnable() {
                @Override
                public void run() {
                    QueryBtcEur btcEur = new QueryBtcEur(BitFluidsMainActivity.this, priceService, env);
                    //noinspection unchecked
                    btcEur.execute();
                }
            };
            scheduler.scheduleAtFixedRate(queryBtcEurTask, 0, 10 * 60, TimeUnit.SECONDS);
            //todo soll nicht gleichzeitig laufen!
        }

        {
            Tx2FluidsAdapter adapter = new Tx2FluidsAdapter(priceService, env);
            TxNotifier convert = adapter.convert(new FluidsNotifier() {
                @Override
                public void onFluidPaid(final TransactionItem transactionItem) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            state.getTransactionItems().add(transactionItem);
                            list_view_adapter.notifyDataSetChanged();
                            String sentence = transactionItem.buildSentence();
                            if (textToSpeech != null) {
                                textToSpeech.speak(sentence, TextToSpeech.QUEUE_ADD, null);
                            }
                        }
                    });
                }

                @Override
                public void onError(String message, FluidType type, Bitcoins bitcoins) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            state.getTransactionItems()
                                    .add(new TransactionItem(null, null, 0, Double.NaN, Sha256Hash.ZERO_HASH));
                            list_view_adapter.notifyDataSetChanged();

                        }
                    });
                }
            });
            bitcoinTransactionListener.init(convert);
            final TextView TpsText = (TextView) findViewById(R.id.TPS);
            bitcoinTransactionListener.setStatsNotifier(new Stats() {
                @Override
                public void update(final double tpm) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            TpsText.setText(new DecimalFormat("#.0").format(tpm));
                        }
                    });
                }
            });
        }

        { // click on QR code copies public address (after wallet init!)
            class QrClickListener implements OnClickListener {
                private final Address addr;

                public QrClickListener(Address addr) {
                    this.addr = addr;
                }

                @Override
                public void onClick(View v) {
                    copyToClipboard(addr.toString());
                    String t = "Address " + addr + " copied to clipboard.";
                    Toast.makeText(getApplicationContext(), t, Toast.LENGTH_SHORT).show();
                }
            }

            qr_alk.setOnClickListener(new QrClickListener(env.getKey200()));
            qr_nonalk.setOnClickListener(new QrClickListener(env.getKey150()));
        }
    }

    private void checkForTTS() {
        Intent checkIntent = new Intent();
        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
        startActivityForResult(checkIntent, TTS_ACTION);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        finish();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == TTS_ACTION) {
            if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
                textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
                    @Override
                    public void onInit(int i) {
                        Log.i(TAG, "init of text-to-speech finished: " + i);
                    }
                });
                textToSpeech.setLanguage(Locale.GERMAN);
            } else {
                Intent installTTSIntent = new Intent();
                installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
                startActivity(installTTSIntent);
            }
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // this doesn't work.

        // the idea is to change the view layout when the device is rotated.
        // in the android manifest, the param
        // android:configChanges="orientation|keyboardHidden"
        // already prevents this activity to be restartet (and hence all it's
        // async tasks, but the view isn't updated. this should happen here 

        /*
        * switch (newConfig.orientation) { case Configuration.ORIENTATION_PORTRAIT:
        * setContentView(R.id.base_layout_portrait); break;
        *
        * default: // Configuration.ORIENTATION_LANDSCAPE:
        * setContentView(R.id.base_layout_landscape); break; }
        */
    }

    private void drawOneQrCode(int id, int id_txt, Bitcoins amountBtc, double amountEur, Bitmap qr_bitmap) {
        ImageView qr_image_view = (ImageView) findViewById(id);
        qr_image_view.setImageBitmap(qr_bitmap);
        TextView qr_txt = ((TextView) findViewById(id_txt));
        String txt = amountBtc.toCurrencyString() + "\n" + "(~" + Utils.eurDF.format(amountEur) + ")";
        qr_txt.setText(txt);
    }

    void drawQrCodes(Bitmap qrcode1_5, Bitmap qrcode2_0, Bitcoins btc_15, Bitcoins btc_20) {
        drawOneQrCode(R.id.qr_code_nonalk, R.id.qr_code_nonalk_txt, btc_15, FluidType.COLA.getEuroPrice(),
                qrcode1_5);
        drawOneQrCode(R.id.qr_code_alk, R.id.qr_code_alk_txt, btc_20, FluidType.MATE.getEuroPrice(), qrcode2_0);
    }

    final void copyToClipboard(final String txt) {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        clipboard.setText(txt);
    }
}