Commit 6aa17a0e authored by Andreas Schildbach's avatar Andreas Schildbach

Switch to modern code format. Organize imports. No functional changes.

parent fe675901
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.schildbach.wallet.integration.sample"
android:versionCode="1"
android:versionName="1.0" >
package="de.schildbach.wallet.integration.sample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<uses-sdk android:minSdkVersion="15" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
<application android:label="Bitcoin in-app payment sample" >
<activity
android:name="de.schildbach.wallet.integration.sample.SampleActivity"
android:theme="@android:style/Theme.Light" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<application android:label="Bitcoin in-app payment sample" >
<activity
android:name="de.schildbach.wallet.integration.sample.SampleActivity"
android:theme="@android:style/Theme.Light" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:scrollbars="none" >
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:scrollbars="none" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:layout_marginTop="24dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:layout_marginTop="24dp"
android:orientation="vertical" >
<RadioGroup
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal" >
<RadioGroup
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/sample_network_mainnet"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Mainnet" />
<RadioButton
android:id="@+id/sample_network_mainnet"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Mainnet" />
<RadioButton
android:id="@+id/sample_network_testnet"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="Testnet" />
</RadioGroup>
<RadioButton
android:id="@+id/sample_network_testnet"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="Testnet" />
</RadioGroup>
<TextView
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="This demonstrates the integration of a &quot;Donate Bitcoins&quot; button in your app." />
<TextView
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="This demonstrates the integration of a &quot;Donate Bitcoins&quot; button in your app." />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" >
<Button
android:id="@+id/sample_donate_button"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Donate" />
</FrameLayout>
<Button
android:id="@+id/sample_donate_button"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Donate" />
</FrameLayout>
<TextView
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="Here is a more complex request for a payment to two addresses." />
<TextView
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="Here is a more complex request for a payment to two addresses." />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1" >
<Button
android:id="@+id/sample_request_button"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Request" />
</FrameLayout>
<Button
android:id="@+id/sample_request_button"
style="@android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Request" />
</FrameLayout>
<TextView
android:id="@+id/sample_donate_message"
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:minLines="5"
android:visibility="invisible" />
</LinearLayout>
<TextView
android:id="@+id/sample_donate_message"
style="@android:style/TextAppearance.Medium"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:minLines="5"
android:visibility="invisible" />
</LinearLayout>
</ScrollView>
\ No newline at end of file
......@@ -42,124 +42,109 @@ import de.schildbach.wallet.integration.android.BitcoinIntegration;
/**
* @author Andreas Schildbach
*/
public class SampleActivity extends Activity
{
private static final long AMOUNT = 500000;
private static final String[] DONATION_ADDRESSES_MAINNET = { "18CK5k1gajRKKSC7yVSTXT9LUzbheh1XY4", "1PZmMahjbfsTy6DsaRyfStzoWTPppWwDnZ" };
private static final String[] DONATION_ADDRESSES_TESTNET = { "mkCLjaXncyw8eSWJBcBtnTgviU85z5PfwS", "mwEacn7pYszzxfgcNaVUzYvzL6ypRJzB6A" };
private static final String MEMO = "Sample donation";
private static final int REQUEST_CODE = 0;
private Button donateButton, requestButton;
private TextView donateMessage;
@Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_activity);
donateButton = (Button) findViewById(R.id.sample_donate_button);
donateButton.setOnClickListener(new OnClickListener()
{
public void onClick(final View v)
{
handleDonate();
}
});
requestButton = (Button) findViewById(R.id.sample_request_button);
requestButton.setOnClickListener(new OnClickListener()
{
public void onClick(final View v)
{
handleRequest();
}
});
donateMessage = (TextView) findViewById(R.id.sample_donate_message);
}
private String[] donationAddresses()
{
final boolean isMainnet = ((RadioButton) findViewById(R.id.sample_network_mainnet)).isChecked();
return isMainnet ? DONATION_ADDRESSES_MAINNET : DONATION_ADDRESSES_TESTNET;
}
private void handleDonate()
{
final String[] addresses = donationAddresses();
BitcoinIntegration.requestForResult(SampleActivity.this, REQUEST_CODE, addresses[0]);
}
private void handleRequest()
{
try
{
final String[] addresses = donationAddresses();
final NetworkParameters params = Address.getParametersFromAddress(addresses[0]);
final Protos.Output.Builder output1 = Protos.Output.newBuilder();
output1.setAmount(AMOUNT);
output1.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(new Address(params, addresses[0])).getProgram()));
final Protos.Output.Builder output2 = Protos.Output.newBuilder();
output2.setAmount(AMOUNT);
output2.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(new Address(params, addresses[1])).getProgram()));
final Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder();
paymentDetails.setNetwork(params.getPaymentProtocolId());
paymentDetails.addOutputs(output1);
paymentDetails.addOutputs(output2);
paymentDetails.setMemo(MEMO);
paymentDetails.setTime(System.currentTimeMillis());
final Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder();
paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString());
BitcoinIntegration.requestForResult(SampleActivity.this, REQUEST_CODE, paymentRequest.build().toByteArray());
}
catch (final AddressFormatException x)
{
throw new RuntimeException(x);
}
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data)
{
if (requestCode == REQUEST_CODE)
{
if (resultCode == Activity.RESULT_OK)
{
final String txHash = BitcoinIntegration.transactionHashFromResult(data);
if (txHash != null)
{
final SpannableStringBuilder messageBuilder = new SpannableStringBuilder("Transaction hash:\n");
messageBuilder.append(txHash);
messageBuilder.setSpan(new TypefaceSpan("monospace"), messageBuilder.length() - txHash.length(), messageBuilder.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (BitcoinIntegration.paymentFromResult(data) != null)
messageBuilder.append("\n(also a BIP70 payment message was received)");
donateMessage.setText(messageBuilder);
donateMessage.setVisibility(View.VISIBLE);
}
Toast.makeText(this, "Thank you!", Toast.LENGTH_LONG).show();
}
else if (resultCode == Activity.RESULT_CANCELED)
{
Toast.makeText(this, "Cancelled.", Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(this, "Unknown result.", Toast.LENGTH_LONG).show();
}
}
}
public class SampleActivity extends Activity {
private static final long AMOUNT = 500000;
private static final String[] DONATION_ADDRESSES_MAINNET = { "18CK5k1gajRKKSC7yVSTXT9LUzbheh1XY4",
"1PZmMahjbfsTy6DsaRyfStzoWTPppWwDnZ" };
private static final String[] DONATION_ADDRESSES_TESTNET = { "mkCLjaXncyw8eSWJBcBtnTgviU85z5PfwS",
"mwEacn7pYszzxfgcNaVUzYvzL6ypRJzB6A" };
private static final String MEMO = "Sample donation";
private static final int REQUEST_CODE = 0;
private Button donateButton, requestButton;
private TextView donateMessage;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_activity);
donateButton = (Button) findViewById(R.id.sample_donate_button);
donateButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
handleDonate();
}
});
requestButton = (Button) findViewById(R.id.sample_request_button);
requestButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
handleRequest();
}
});
donateMessage = (TextView) findViewById(R.id.sample_donate_message);
}
private String[] donationAddresses() {
final boolean isMainnet = ((RadioButton) findViewById(R.id.sample_network_mainnet)).isChecked();
return isMainnet ? DONATION_ADDRESSES_MAINNET : DONATION_ADDRESSES_TESTNET;
}
private void handleDonate() {
final String[] addresses = donationAddresses();
BitcoinIntegration.requestForResult(SampleActivity.this, REQUEST_CODE, addresses[0]);
}
private void handleRequest() {
try {
final String[] addresses = donationAddresses();
final NetworkParameters params = Address.getParametersFromAddress(addresses[0]);
final Protos.Output.Builder output1 = Protos.Output.newBuilder();
output1.setAmount(AMOUNT);
output1.setScript(ByteString
.copyFrom(ScriptBuilder.createOutputScript(new Address(params, addresses[0])).getProgram()));
final Protos.Output.Builder output2 = Protos.Output.newBuilder();
output2.setAmount(AMOUNT);
output2.setScript(ByteString
.copyFrom(ScriptBuilder.createOutputScript(new Address(params, addresses[1])).getProgram()));
final Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder();
paymentDetails.setNetwork(params.getPaymentProtocolId());
paymentDetails.addOutputs(output1);
paymentDetails.addOutputs(output2);
paymentDetails.setMemo(MEMO);
paymentDetails.setTime(System.currentTimeMillis());
final Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder();
paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString());
BitcoinIntegration.requestForResult(SampleActivity.this, REQUEST_CODE,
paymentRequest.build().toByteArray());
} catch (final AddressFormatException x) {
throw new RuntimeException(x);
}
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
final String txHash = BitcoinIntegration.transactionHashFromResult(data);
if (txHash != null) {
final SpannableStringBuilder messageBuilder = new SpannableStringBuilder("Transaction hash:\n");
messageBuilder.append(txHash);
messageBuilder.setSpan(new TypefaceSpan("monospace"), messageBuilder.length() - txHash.length(),
messageBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (BitcoinIntegration.paymentFromResult(data) != null)
messageBuilder.append("\n(also a BIP70 payment message was received)");
donateMessage.setText(messageBuilder);
donateMessage.setVisibility(View.VISIBLE);
}
Toast.makeText(this, "Thank you!", Toast.LENGTH_LONG).show();
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "Cancelled.", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Unknown result.", Toast.LENGTH_LONG).show();
}
}
}
}
This diff is collapsed.
......@@ -3,24 +3,24 @@ FILES
Your wallet contains your private keys and various transaction related metadata. It is stored in app-private
storage:
Mainnet: /data/data/de.schildbach.wallet/files/wallet-protobuf
Mainnet: /data/data/de.schildbach.wallet/files/wallet-protobuf
Testnet: /data/data/de.schildbach.wallet_test/files/wallet-protobuf-testnet
Testnet: /data/data/de.schildbach.wallet_test/files/wallet-protobuf-testnet
The wallet file format is not compatible to wallet.dat (Satoshi client). Rather, it uses a custom protobuf format
which should be compatible between clients using bitcoinj.
Certain actions cause automatic rolling backups of your wallet to app-private storage:
Mainnet: /data/data/de.schildbach.wallet/files/key-backup-protobuf
Mainnet: /data/data/de.schildbach.wallet/files/key-backup-protobuf
Testnet: /data/data/de.schildbach.wallet_test/files/key-backup-protobuf-testnet
Testnet: /data/data/de.schildbach.wallet_test/files/key-backup-protobuf-testnet
Your wallet can be manually backed up to and restored from external storage:
Mainnet: /sdcard/Download/bitcoin-wallet-backup-<yyyy-MM-dd>
Mainnet: /sdcard/Download/bitcoin-wallet-backup-<yyyy-MM-dd>
Testnet: /sdcard/Download/bitcoin-wallet-backup-testnet-<yyyy-MM-dd>
Testnet: /sdcard/Download/bitcoin-wallet-backup-testnet-<yyyy-MM-dd>
If you want to recover coins from manual backups and for whatever reason you cannot use the app
itself to restore from the backup, see the separate README.recover guide.
......@@ -30,11 +30,11 @@ DEBUGGING
Wallet file for Testnet can be pulled from an (even un-rooted) device using
adb pull /data/data/de.schildbach.wallet_test/files/wallet-protobuf-testnet
adb pull /data/data/de.schildbach.wallet_test/files/wallet-protobuf-testnet
Log messages can be viewed by
adb logcat
adb logcat
The app can send extensive debug information. Use Options > Settings > Report Issue and follow the dialog.
In the generated e-mail, replace the support address with yours.
......@@ -50,27 +50,27 @@ You can probably skip some steps, especially if you built Android apps before.
You'll need git, a Java SDK 6 (or later) and Gradle 2.10 (or later) for this. I'll assume Ubuntu Xenial Linux
for the package installs, which comes with slightly more recent versions.
# first time only
sudo apt install git gradle openjdk-8-jdk libstdc++6:i386 zlib1g:i386
# first time only
sudo apt install git gradle openjdk-8-jdk libstdc++6:i386 zlib1g:i386
Get the Android SDK (Tools only) from
http://developer.android.com/sdk/
http://developer.android.com/sdk/
and unpack it to your workspace directory. Point your ANDROID_HOME variable to the unpacked Android SDK directory
and switch to it. Use
# make sure tools are at the newest version
tools/android update sdk --no-ui --force --filter tools
# make sure tools are at the newest version
tools/android update sdk --no-ui --force --filter tools
# fetch required android dependencies
tools/android update sdk --no-ui --force --filter build-tools-24,android-15,android-23,extra-android-m2repository
# fetch required android dependencies
tools/android update sdk --no-ui --force --filter build-tools-24,android-15,android-23,extra-android-m2repository
to download the necessary API level.
Get the Android NDK from
https://developer.android.com/ndk
https://developer.android.com/ndk
and unpack it to your workspace directory. Point your ANDROID_NDK_HOME variable to the unpacked Android NDK
directory.
......@@ -78,21 +78,21 @@ directory.
Finally, you can build Bitcoin Wallet and sign it with your development key. Again in your workspace,
use
# first time only
git clone -b master https://github.com/bitcoin-wallet/bitcoin-wallet.git bitcoin-wallet
# first time only
git clone -b master https://github.com/bitcoin-wallet/bitcoin-wallet.git bitcoin-wallet
# each time
cd bitcoin-wallet
git pull
gradle clean :native-scrypt:copy test build
# each time
cd bitcoin-wallet
git pull
gradle clean :native-scrypt:copy test build
To install the app on your Android device, use
# first time only
sudo apt install android-tools-adb
# first time only
sudo apt install android-tools-adb
# each time
adb install wallet/build/outputs/apk/bitcoin-wallet-debug.apk
# each time
adb install wallet/build/outputs/apk/bitcoin-wallet-debug.apk
If installing fails, make sure "Developer options" and "USB debugging" are enabled on your Android device, and an ADB
connection is established.
......@@ -108,11 +108,11 @@ The productive version uses Mainnet, is built non-debuggable, space-optimized wi
wallet file is protected against access from non-root users. In the code repository, it lives in a
separate 'prod' branch that gets rebased against master with each released version.
# each time
cd bitcoin-wallet
git fetch origin
git checkout origin/prod
gradle clean :native-scrypt:copy test build
# each time
cd bitcoin-wallet
git fetch origin
git checkout origin/prod
gradle clean :native-scrypt:copy test build
SETTING UP FOR DEVELOPMENT
......@@ -124,26 +124,26 @@ TRANSLATIONS