├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── thinkmobiles │ │ └── bitcoinwalletsample │ │ ├── Constants.java │ │ ├── base │ │ ├── BasePresenter.java │ │ └── BaseView.java │ │ ├── main │ │ ├── MainActivity.java │ │ ├── MainActivityContract.java │ │ └── MainActivityPresenter.java │ │ └── utils │ │ └── HistorySharedPreferences.java │ └── res │ ├── drawable │ ├── app_icon.png │ ├── bg_round_rect.xml │ ├── ic_add_black_24dp.xml │ ├── ic_bitcoin.xml │ ├── ic_camera_white_24dp.xml │ ├── ic_code_white_24dp.xml │ ├── ic_content_copy_black_18dp.xml │ ├── ic_edit_black_24dp.xml │ ├── ic_info_outline_white_24dp.xml │ ├── ic_insert_drive_file_black_24dp.xml │ ├── ic_logo.xml │ └── ic_refresh_refresh_24dp.xml │ ├── layout │ ├── activity_main.xml │ ├── layout_download_peers.xml │ ├── layout_toolbar.xml │ └── view_my_qr_code.xml │ ├── menu │ └── menu_main.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── free.png ├── home.png ├── loading.png └── scan.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | .DS_Store 5 | /build 6 | /captures 7 | *.iml 8 | gradle.xml 9 | 10 | # User-specific configurations 11 | .idea/libraries/ 12 | .idea/workspace.xml 13 | .idea/tasks.xml 14 | .idea/.name 15 | .idea/compiler.xml 16 | .idea/copyright/profiles_settings.xml 17 | .idea/inspectionProfiles/Project_Default.xml 18 | .idea/encodings.xml 19 | .idea/misc.xml 20 | .idea/modules.xml 21 | .idea/scopes/scope_settings.xml 22 | .idea/vcs.xml 23 | .idea/gradle.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BitcoinJ Wallet Sample 2 | 3 | Sample application of using the BitcoinJ SDK (MVP architecture). 4 | 5 | When you launch application first time it needs to download peer network, it may takes up to 15 minutes, but it happens only once, so be patiant and drink some tea or coffee meanwhile. 6 | 7 | # Functionality 8 | 9 | Application works with official bitcoin test network (TestNet3) and contains next features: 10 | * Create wallet 11 | * Get balance 12 | * Receive transactions 13 | * Send transactions 14 | * Get free test bitcoins 15 | * Generating your wallet address QR code 16 | * Scan recipient wallet address QR code 17 | 18 | # Screenshots 19 | Initializaqtion  20 | Main screen  21 | Get coins 22 | Scan QR 23 | 24 | # Service Information 25 | SDK tutorial https://bitcoinj.github.io/getting-started-java 26 | 27 | In `build.gradle`: 28 | ``` 29 | dependencies { 30 | compile 'org.bitcoinj:bitcoinj-core:0.14.4' 31 | 32 | //Logging 33 | compile 'org.slf4j:slf4j-api:1.7.12' 34 | compile 'org.slf4j:slf4j-simple:1.7.12' 35 | } 36 | ``` 37 | Example uses next additinal libraries: 38 | * Android Annotations 39 | * QRGen 40 | * ZXing 41 | 42 | # Developers 43 | 44 | * [Saldan Roman](https://github.com/RomanSaldan) 45 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.2" 7 | defaultConfig { 8 | applicationId "com.example.thinkmobiles.bitcoinwalletsample" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | multiDexEnabled true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | } 27 | 28 | dependencies { 29 | //Android support 30 | compile 'com.android.support:design:25.3.1' 31 | //Android annotations 32 | annotationProcessor "org.androidannotations:androidannotations:4.2.0" 33 | compile "org.androidannotations:androidannotations-api:4.2.0" 34 | //BitcoinJ SDK 35 | compile 'org.bitcoinj:bitcoinj-core:0.14.4' 36 | //QR support 37 | compile 'com.github.kenglxn.QRGen:android:2.2.0' 38 | compile 'com.journeyapps:zxing-android-embedded:3.0.2@aar' 39 | compile 'com.google.zxing:core:3.2.0' 40 | //Logging 41 | compile 'org.slf4j:slf4j-api:1.7.12' 42 | compile 'org.slf4j:slf4j-simple:1.7.12' 43 | } 44 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Dev\SDK\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/Constants.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample; 2 | 3 | /** 4 | * Created by Lynx on 4/11/2017. 5 | */ 6 | 7 | public class Constants { 8 | public static final String WALLET_NAME = "users_wallet"; 9 | 10 | public static boolean IS_PRODUCTION = false; 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.base; 2 | 3 | /** 4 | * Created by Lynx on 4/11/2017. 5 | */ 6 | 7 | public interface BasePresenter { 8 | void subscribe(); 9 | void unsubscribe(); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.base; 2 | 3 | /** 4 | * Created by Lynx on 4/11/2017. 5 | */ 6 | 7 | public interface BaseView { 8 | void setPresenter(T presenter); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.main; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Intent; 6 | import android.graphics.Bitmap; 7 | import android.support.v4.widget.SwipeRefreshLayout; 8 | import android.support.v7.app.AlertDialog; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.support.v7.widget.Toolbar; 11 | import android.text.Editable; 12 | import android.text.Html; 13 | import android.text.TextUtils; 14 | import android.text.TextWatcher; 15 | import android.text.method.LinkMovementMethod; 16 | import android.view.View; 17 | import android.widget.Button; 18 | import android.widget.FrameLayout; 19 | import android.widget.ImageView; 20 | import android.widget.ProgressBar; 21 | import android.widget.TextView; 22 | import android.widget.Toast; 23 | 24 | import com.example.thinkmobiles.bitcoinwalletsample.R; 25 | import com.google.zxing.integration.android.IntentIntegrator; 26 | import com.google.zxing.integration.android.IntentResult; 27 | 28 | import net.glxn.qrgen.android.QRCode; 29 | 30 | import org.androidannotations.annotations.AfterInject; 31 | import org.androidannotations.annotations.AfterViews; 32 | import org.androidannotations.annotations.EActivity; 33 | import org.androidannotations.annotations.OptionsItem; 34 | import org.androidannotations.annotations.OptionsMenu; 35 | import org.androidannotations.annotations.SystemService; 36 | import org.androidannotations.annotations.UiThread; 37 | import org.androidannotations.annotations.ViewById; 38 | import org.androidannotations.annotations.res.ColorRes; 39 | import org.androidannotations.annotations.res.StringRes; 40 | 41 | @EActivity(R.layout.activity_main) 42 | @OptionsMenu(R.menu.menu_main) 43 | public class MainActivity extends AppCompatActivity implements MainActivityContract.MainActivityView { 44 | 45 | private MainActivityContract.MainActivityPresenter presenter; 46 | 47 | @ViewById 48 | protected FrameLayout flDownloadContent_LDP; 49 | @ViewById 50 | protected ProgressBar pbProgress_LDP; 51 | @ViewById 52 | protected TextView tvPercentage_LDP; 53 | 54 | @ViewById 55 | protected Toolbar toolbar_AT; 56 | @ViewById 57 | protected SwipeRefreshLayout srlContent_AM; 58 | @ViewById 59 | protected TextView tvMyBalance_AM; 60 | @ViewById 61 | protected TextView tvMyAddress_AM; 62 | @ViewById 63 | protected ImageView ivMyQRAddress_AM; 64 | @ViewById 65 | protected TextView tvWalletFilePath_AM; 66 | @ViewById 67 | protected TextView tvRecipientAddress_AM; 68 | @ViewById 69 | protected TextView etAmount_AM; 70 | @ViewById 71 | protected Button btnSend_AM; 72 | @ViewById 73 | protected ImageView ivCopy_AM; 74 | 75 | @SystemService 76 | protected ClipboardManager clipboardManager; 77 | 78 | @StringRes(R.string.scan_recipient_qr) 79 | protected String strScanRecipientQRCode; 80 | @StringRes(R.string.about) 81 | protected String strAbout; 82 | 83 | @ColorRes(android.R.color.holo_green_dark) 84 | protected int colorGreenDark; 85 | @ColorRes(android.R.color.darker_gray) 86 | protected int colorGreyDark; 87 | 88 | @AfterInject 89 | protected void initData() { 90 | new MainActivityPresenter(this, getCacheDir()); 91 | } 92 | 93 | @AfterViews 94 | protected void initUI() { 95 | initToolbar(); 96 | setListeners(); 97 | 98 | presenter.subscribe(); 99 | } 100 | 101 | @OptionsItem(R.id.menuScanQR_MM) 102 | protected void clickMenuGetRecipientQR() { 103 | presenter.pickRecipient(); 104 | } 105 | 106 | @OptionsItem(R.id.menuInfo_MM) 107 | protected void clickMenuInfo() { 108 | presenter.getInfoDialog(); 109 | } 110 | 111 | private void initToolbar() { 112 | setSupportActionBar(toolbar_AT); 113 | if(getSupportActionBar() != null) { 114 | getSupportActionBar().setTitle("Wallet"); 115 | } 116 | } 117 | 118 | 119 | @Override 120 | public void setPresenter(MainActivityContract.MainActivityPresenter presenter) { 121 | this.presenter = presenter; 122 | } 123 | 124 | @Override 125 | @UiThread 126 | public void displayDownloadContent(boolean isShown) { 127 | flDownloadContent_LDP.setVisibility(isShown ? View.VISIBLE : View.GONE); 128 | } 129 | 130 | @Override 131 | @UiThread 132 | public void displayProgress(int percent) { 133 | if(pbProgress_LDP.isIndeterminate()) pbProgress_LDP.setIndeterminate(false); 134 | pbProgress_LDP.setProgress(percent); 135 | } 136 | 137 | @Override 138 | @UiThread 139 | public void displayPercentage(int percent) { 140 | tvPercentage_LDP.setText(String.valueOf(percent) + " %"); 141 | } 142 | 143 | @Override 144 | @UiThread 145 | public void displayMyBalance(String myBalance) { 146 | tvMyBalance_AM.setText(myBalance); 147 | } 148 | 149 | @Override 150 | @UiThread 151 | public void displayWalletPath(String walletPath) { 152 | tvWalletFilePath_AM.setText(walletPath); 153 | } 154 | 155 | @Override 156 | @UiThread 157 | public void displayMyAddress(String myAddress) { 158 | tvMyAddress_AM.setText(myAddress); 159 | Bitmap bitmapMyQR = QRCode.from(myAddress).bitmap(); //base58 address 160 | ivMyQRAddress_AM.setImageBitmap(bitmapMyQR); 161 | if(srlContent_AM.isRefreshing()) srlContent_AM.setRefreshing(false); 162 | 163 | } 164 | 165 | @Override 166 | public void displayRecipientAddress(String recipientAddress) { 167 | tvRecipientAddress_AM.setText(TextUtils.isEmpty(recipientAddress) ? strScanRecipientQRCode : recipientAddress); 168 | tvRecipientAddress_AM.setTextColor(TextUtils.isEmpty(recipientAddress) ? colorGreyDark : colorGreenDark); 169 | } 170 | 171 | 172 | @Override 173 | public void showToastMessage(String message) { 174 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 175 | } 176 | 177 | @Override 178 | public String getRecipient() { 179 | return tvRecipientAddress_AM.getText().toString().trim(); 180 | } 181 | 182 | @Override 183 | public String getAmount() { 184 | return etAmount_AM.getText().toString(); 185 | } 186 | 187 | @Override 188 | public void clearAmount() { 189 | etAmount_AM.setText(null); 190 | } 191 | 192 | @Override 193 | public void startScanQR() { 194 | new IntentIntegrator(this).initiateScan(); 195 | } 196 | 197 | @Override 198 | public void displayInfoDialog(String myAddress) { 199 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 200 | builder.setTitle("About"); 201 | builder.setMessage(Html.fromHtml(strAbout)); 202 | builder.setCancelable(true); 203 | builder.setPositiveButton("GOT IT", (dialog, which) -> dialog.dismiss()); 204 | AlertDialog alertDialog = builder.create(); 205 | alertDialog.show(); 206 | TextView msgTxt = (TextView) alertDialog.findViewById(android.R.id.message); 207 | msgTxt.setMovementMethod(LinkMovementMethod.getInstance()); 208 | } 209 | 210 | @Override 211 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 212 | super.onActivityResult(requestCode, resultCode, data); 213 | IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); 214 | if (scanResult != null) { 215 | displayRecipientAddress(scanResult.getContents()); 216 | } 217 | } 218 | 219 | private void setListeners() { 220 | srlContent_AM.setOnRefreshListener(() -> presenter.refresh()); 221 | tvRecipientAddress_AM.setOnClickListener(v -> presenter.pickRecipient()); 222 | btnSend_AM.setOnClickListener(v -> presenter.send()); 223 | etAmount_AM.addTextChangedListener(new TextWatcher() { 224 | @Override 225 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 226 | 227 | } 228 | 229 | @Override 230 | public void onTextChanged(CharSequence s, int start, int before, int count) { 231 | 232 | } 233 | 234 | @Override 235 | public void afterTextChanged(Editable s) { 236 | if(s.toString().trim().length() == 0) 237 | etAmount_AM.setText("0.00"); 238 | } 239 | }); 240 | ivCopy_AM.setOnClickListener(v -> { 241 | ClipData clip = ClipData.newPlainText("My wallet address", tvMyAddress_AM.getText().toString()); 242 | clipboardManager.setPrimaryClip(clip); 243 | Toast.makeText(MainActivity.this, "Copied", Toast.LENGTH_SHORT).show(); 244 | }); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/main/MainActivityContract.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.main; 2 | 3 | import com.example.thinkmobiles.bitcoinwalletsample.base.BasePresenter; 4 | import com.example.thinkmobiles.bitcoinwalletsample.base.BaseView; 5 | 6 | /** 7 | * Created by Lynx on 4/11/2017. 8 | */ 9 | 10 | public interface MainActivityContract { 11 | interface MainActivityView extends BaseView { 12 | void displayDownloadContent(boolean isShown); 13 | void displayProgress(int percent); 14 | void displayPercentage(int percent); 15 | 16 | void displayMyBalance(String myBalance); 17 | void displayWalletPath(String walletPath); 18 | 19 | void displayMyAddress(String myAddress); 20 | void displayRecipientAddress(String recipientAddress); 21 | 22 | void showToastMessage(String message); 23 | String getRecipient(); 24 | String getAmount(); 25 | void clearAmount(); 26 | 27 | void startScanQR(); 28 | void displayInfoDialog(String myAddress); 29 | } 30 | interface MainActivityPresenter extends BasePresenter { 31 | void refresh(); 32 | void pickRecipient(); 33 | void send(); 34 | 35 | void getInfoDialog(); 36 | } 37 | interface MainActivityModel { 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/main/MainActivityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.main; 2 | 3 | import android.os.Handler; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import com.example.thinkmobiles.bitcoinwalletsample.Constants; 8 | 9 | import org.bitcoinj.core.Address; 10 | import org.bitcoinj.core.Coin; 11 | import org.bitcoinj.core.ECKey; 12 | import org.bitcoinj.core.InsufficientMoneyException; 13 | import org.bitcoinj.core.NetworkParameters; 14 | import org.bitcoinj.core.Transaction; 15 | import org.bitcoinj.core.TransactionConfidence; 16 | import org.bitcoinj.core.listeners.DownloadProgressTracker; 17 | import org.bitcoinj.kits.WalletAppKit; 18 | import org.bitcoinj.params.MainNetParams; 19 | import org.bitcoinj.params.TestNet3Params; 20 | import org.bitcoinj.utils.BriefLogFormatter; 21 | import org.bitcoinj.utils.Threading; 22 | import org.bitcoinj.wallet.SendRequest; 23 | import org.bitcoinj.wallet.Wallet; 24 | 25 | import java.io.File; 26 | import java.util.Date; 27 | 28 | /** 29 | * Created by Lynx on 4/11/2017. 30 | */ 31 | 32 | public class MainActivityPresenter implements MainActivityContract.MainActivityPresenter { 33 | 34 | private MainActivityContract.MainActivityView view; 35 | private File walletDir; //Context.getCacheDir(); 36 | 37 | private NetworkParameters parameters; 38 | private WalletAppKit walletAppKit; 39 | 40 | public MainActivityPresenter(MainActivityContract.MainActivityView view, File walletDir) { 41 | this.view = view; 42 | this.walletDir = walletDir; 43 | 44 | view.setPresenter(this); 45 | } 46 | 47 | @Override 48 | public void subscribe() { 49 | setBtcSDKThread(); 50 | parameters = Constants.IS_PRODUCTION ? MainNetParams.get() : TestNet3Params.get(); 51 | BriefLogFormatter.init(); 52 | 53 | walletAppKit = new WalletAppKit(parameters, walletDir, Constants.WALLET_NAME) { 54 | @Override 55 | protected void onSetupCompleted() { 56 | if (wallet().getImportedKeys().size() < 1) wallet().importKey(new ECKey()); 57 | wallet().allowSpendingUnconfirmedTransactions(); 58 | view.displayWalletPath(vWalletFile.getAbsolutePath()); 59 | setupWalletListeners(wallet()); 60 | 61 | Log.d("myLogs", "My address = " + wallet().freshReceiveAddress()); 62 | } 63 | }; 64 | walletAppKit.setDownloadListener(new DownloadProgressTracker() { 65 | @Override 66 | protected void progress(double pct, int blocksSoFar, Date date) { 67 | super.progress(pct, blocksSoFar, date); 68 | int percentage = (int) pct; 69 | view.displayPercentage(percentage); 70 | view.displayProgress(percentage); 71 | } 72 | 73 | @Override 74 | protected void doneDownload() { 75 | super.doneDownload(); 76 | view.displayDownloadContent(false); 77 | refresh(); 78 | } 79 | }); 80 | walletAppKit.setBlockingStartup(false); 81 | walletAppKit.startAsync(); 82 | } 83 | 84 | @Override 85 | public void unsubscribe() { 86 | 87 | } 88 | 89 | @Override 90 | public void refresh() { 91 | String myAddress = walletAppKit.wallet().freshReceiveAddress().toBase58(); 92 | 93 | view.displayMyBalance(walletAppKit.wallet().getBalance().toFriendlyString()); 94 | view.displayMyAddress(myAddress); 95 | 96 | } 97 | 98 | @Override 99 | public void pickRecipient() { 100 | view.displayRecipientAddress(null); 101 | view.startScanQR(); 102 | } 103 | 104 | @Override 105 | public void send() { 106 | String recipientAddress = view.getRecipient(); 107 | String amount = view.getAmount(); 108 | if(TextUtils.isEmpty(recipientAddress) || recipientAddress.equals("Scan recipient QR")) { 109 | view.showToastMessage("Select recipient"); 110 | return; 111 | } 112 | if(TextUtils.isEmpty(amount) | Double.parseDouble(amount) <= 0) { 113 | view.showToastMessage("Select valid amount"); 114 | return; 115 | } 116 | if(walletAppKit.wallet().getBalance().isLessThan(Coin.parseCoin(amount))) { 117 | view.showToastMessage("You got not enough coins"); 118 | view.clearAmount(); 119 | return; 120 | } 121 | SendRequest request = SendRequest.to(Address.fromBase58(parameters, recipientAddress), Coin.parseCoin(amount)); 122 | try { 123 | walletAppKit.wallet().completeTx(request); 124 | walletAppKit.wallet().commitTx(request.tx); 125 | walletAppKit.peerGroup().broadcastTransaction(request.tx).broadcast(); 126 | } catch (InsufficientMoneyException e) { 127 | e.printStackTrace(); 128 | view.showToastMessage(e.getMessage()); 129 | } 130 | 131 | } 132 | 133 | @Override 134 | public void getInfoDialog() { 135 | view.displayInfoDialog(walletAppKit.wallet().currentReceiveAddress().toBase58()); 136 | } 137 | 138 | private void setBtcSDKThread() { 139 | final Handler handler = new Handler(); 140 | Threading.USER_THREAD = handler::post; 141 | } 142 | 143 | private void setupWalletListeners(Wallet wallet) { 144 | wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> { 145 | view.displayMyBalance(wallet.getBalance().toFriendlyString()); 146 | if(tx.getPurpose() == Transaction.Purpose.UNKNOWN) 147 | view.showToastMessage("Receive " + newBalance.minus(prevBalance).toFriendlyString()); 148 | }); 149 | wallet.addCoinsSentEventListener((wallet12, tx, prevBalance, newBalance) -> { 150 | view.displayMyBalance(wallet.getBalance().toFriendlyString()); 151 | view.clearAmount(); 152 | view.displayRecipientAddress(null); 153 | view.showToastMessage("Sent " + prevBalance.minus(newBalance).minus(tx.getFee()).toFriendlyString()); 154 | }); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/thinkmobiles/bitcoinwalletsample/utils/HistorySharedPreferences.java: -------------------------------------------------------------------------------- 1 | package com.example.thinkmobiles.bitcoinwalletsample.utils; 2 | 3 | import org.androidannotations.annotations.sharedpreferences.DefaultString; 4 | import org.androidannotations.annotations.sharedpreferences.SharedPref; 5 | 6 | /** 7 | * Created by Lynx on 4/11/2017. 8 | */ 9 | 10 | @SharedPref(SharedPref.Scope.UNIQUE) 11 | public interface HistorySharedPreferences { 12 | 13 | @DefaultString("") 14 | String getHistory(); // comma-separated history 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkmobiles/BitcoinJ-Wallet-Sample-Android/a54393e7e7906355c5681d90ecc03f965506afa6/app/src/main/res/drawable/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_round_rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bitcoin.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 71 | 74 | 77 | 80 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_code_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_copy_black_18dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_outline_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_insert_drive_file_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | 20 | 29 | 37 | 42 | 52 | 58 | 64 | 71 | 81 | 88 | 93 | 96 | 99 | 102 | 106 | 110 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh_refresh_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 15 | 16 | 20 | 21 | 24 | 25 | 31 | 32 | 37 | 38 | 42 | 43 | 52 | 53 | 54 | 55 | 60 | 61 | 71 | 72 | 78 | 79 | 80 | 81 | 88 | 89 | 95 | 96 | 108 | 109 | 114 | 115 | 126 | 127 | 131 | 132 | 140 | 141 | 146 | 147 | 154 | 155 | 156 | 157 | 161 | 162 | 170 | 171 | 176 | 177 | 191 | 192 | 197 | 198 | 204 | 205 |