Java tutorial
/* * 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); } }