1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-13 16:10:51 +02:00

Compare commits

..

13 Commits

Author SHA1 Message Date
m2049r
61e2d880c2 Merge pull request #6 from m2049r/docs
updated docs
2017-08-14 10:48:38 +02:00
m2049r
72889fcdde Create BUILDING-external-libs.md
Update README.md
2017-08-14 10:47:04 +02:00
m2049r
905ee8b8a9 Merge pull request #5 from m2049r/features
allow only portrait
2017-08-14 10:02:50 +02:00
m2049r
81a9aa6938 allow only portrait
remove some debug messages
2017-08-14 10:01:22 +02:00
m2049r
046238a29f Merge pull request #4 from m2049r/features
lots of tweaks
2017-08-14 09:30:16 +02:00
m2049r
9751825ed7 stack size for background thread set to 5MB (monero dafault)
so no need to use heap for slow_hash
recompiled monero without USE_HEAP
2017-08-14 09:26:59 +02:00
m2049r
c19ee65dd1 UI & progress bar tweaks
service start/stop in activity onCreate/onDestroy
update continues in background if screen turned off
2017-08-13 15:39:11 +02:00
m2049r
282f00959d reverted libwallet crypto CMake 2017-08-13 00:29:58 +02:00
m2049r
104a6063a3 Merge pull request #3 from m2049r/features
check daemon availability & save only on first sync
2017-08-12 19:59:45 +02:00
m2049r
95e47e3407 check daemon availability
show txs immediately
only save on first sync

no store on close

Increased Version
2017-08-12 19:56:06 +02:00
m2049r
cb5795e64b remove .so 2017-08-07 08:58:13 +02:00
m2049r
746da913f0 Merge branch 'master' into feature_async 2017-08-07 08:56:22 +02:00
m2049r
2682399600 save prefs
async wallet load / close by service
wakelock & async loading / closing looks good
progress indicator
ode cleanup
added license boilerplates
2017-08-07 08:13:23 +02:00
87 changed files with 1377 additions and 3414 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.gradle
build

1
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
workspace.xml

3068
.idea/workspace.xml generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,42 +10,40 @@ Another Android Monero Wallet
### Disclaimer
This is my first serious Android App.
### Notes
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
- Special thanks to /u/gkruja for inspiration to pick this project up again
- Based off monero v0.10.3.1 with pull requests #2238 & #2239 applied => so can be used in mainnet!
### Random Notes
- Based off monero v0.10.3.1 with pull requests #2238, #2239 and #2289 applied => so can be used in mainnet!
- currently only android32
- sorry for my messups in github
- this is more of a proof of concept
- currently only use is checking incoming/outgoing transactions
- currently only use is checking incoming/outgoing transactions
- works in testnet & mainnet (please use your own daemons)
- takes forever to sync on mainnet (even with own daemon)
- takes forever to sync on mainnet (even with own daemon) - due to 32-bit architecture
- use your own daemon - it's easy
- screen stays on until first sync is complete
- saves wallet only on first sync
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
### TODO
- don't have the screen on for first sync - use IntentService with WakeLock instead?
- make it pretty
- show current block height - is that relevant?
- License Dialog
- support for right-to-left layouts
- visibility of methods/classes
- adjust layout so we can use bigger font sizes
- adjust layout so we can use bigger font sizes (maybe show only 5 decimal places instead of 12 in main view)
- review visibility of methods/classes
- sensible error dialogs (e.g. when no write permissions granted) instead of just crashing on purpose
- sensible loading/saving progress bars instead of just freezing up
- spend monero - not so difficult with wallet api
- figure out how to make it all flow better (loading/saving takes forever and does not run in background)
- currently loading in background thread produces segfaults in JNI
- check licenses of included libraries
- check licenses of included libraries; License Dialog
- ~~provide detailed build instructions for third party binaries~~
- ~~sensible loading/saving progress bars instead of just freezing up~~
- ~~figure out how to make it all flow better (loading/saving takes forever and does not run in background)~~
- ~~currently loading in background thread produces segfaults in JNI~~
### Issues
- occasional crashes ...
- ~~screen rotation crashes the app~~
- ~~turning the display off/on during sync stops sync~~
### HOW TO BUILD
No need to build. Binaries are included:
- openssl-1.0.2l
- monero-v0.10.3.1 + pull requests #2238 & #2239
- monero-v0.10.3.1 + pull requests #2238, #2239 and #2289
- boost_1_64_0
If you want to build - fire up Android Studio and build. Also you can rebuild all of the above.
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
Then, fire up Android Studio and build the APK.

2
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.externalNativeBuild
build

View File

@@ -11,13 +11,13 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs)
# OpenSSL
############
add_library(crypto SHARED IMPORTED)
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.so)
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
add_library(ssl SHARED IMPORTED)
add_library(ssl STATIC IMPORTED)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.so)
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
############
# Boost
@@ -63,55 +63,55 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct)
#############
add_library(wallet SHARED IMPORTED)
add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
add_library(cryptonote_core SHARED IMPORTED)
add_library(cryptonote_core STATIC IMPORTED)
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a)
add_library(cryptonote_basic SHARED IMPORTED)
add_library(cryptonote_basic STATIC IMPORTED)
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a)
add_library(mnemonics SHARED IMPORTED)
add_library(mnemonics STATIC IMPORTED)
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
add_library(common SHARED IMPORTED)
add_library(common STATIC IMPORTED)
set_target_properties(common PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
add_library(cncrypto SHARED IMPORTED)
add_library(cncrypto STATIC IMPORTED)
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
add_library(ringct SHARED IMPORTED)
add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
#####
add_library(p2p SHARED IMPORTED)
add_library(p2p STATIC IMPORTED)
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
add_library(blockchain_db SHARED IMPORTED)
add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a)
add_library(lmdb SHARED IMPORTED)
add_library(lmdb STATIC IMPORTED)
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
add_library(easylogging SHARED IMPORTED)
add_library(easylogging STATIC IMPORTED)
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
add_library(unbound SHARED IMPORTED)
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.so)
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
####
add_library(epee STATIC IMPORTED)
@@ -141,9 +141,10 @@ target_link_libraries( monerujo
cryptonote_core
cryptonote_basic
mnemonics
cncrypto
ringct
common
cncrypto
blockchain_db
lmdb
#easylogging # not for 0.10.3.1

View File

@@ -92,13 +92,21 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/cmake" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 1
versionName "0.1"
versionCode 3
versionName "0.3.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

View File

@@ -17,17 +17,28 @@
<activity
android:name=".WalletActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/wallet_activity_name"
android:launchMode="singleTop"></activity>
android:launchMode="singleTop"
android:screenOrientation="portrait"></activity>
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
android:label="@string/app_name"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".service.WalletService"
android:description="@string/service_description"
android:exported="false"
android:label="Monero Wallet Service" />
</application>
</manifest>

View File

@@ -1,3 +1,19 @@
/**
* Copyright (c) 2017 m2049r
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*/
#include <inttypes.h>
#include "monerujo.h"
#include "wallet2_api.h"
@@ -336,15 +352,14 @@ Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env,
const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, JNI_FALSE);
const char *_password = env->GetStringUTFChars(password, JNI_FALSE);
bool passwordOk =
Bitmonero::WalletManagerFactory::getWalletManager()->verifyWalletPassword(
std::string(_keys_file_name), std::string(_password), watch_only);
Bitmonero::WalletManagerFactory::getWalletManager()->verifyWalletPassword(
std::string(_keys_file_name), std::string(_password), watch_only);
env->ReleaseStringUTFChars(keys_file_name, _keys_file_name);
env->ReleaseStringUTFChars(password, _password);
return passwordOk;
}
JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance,
jstring path) {
@@ -365,14 +380,15 @@ Java_com_m2049r_xmrwallet_model_WalletManager_getErrorString(JNIEnv *env, jobjec
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_setDaemonAddressJ(JNIEnv *env, jobject instance,
jstring address) {
jstring address) {
const char *_address = env->GetStringUTFChars(address, JNI_FALSE);
Bitmonero::WalletManagerFactory::getWalletManager()->setDaemonAddress(std::string(_address));
env->ReleaseStringUTFChars(address, _address);
}
// returns whether the daemon can be reached, and its version number
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_getConnectedDaemonVersion(JNIEnv *env,
Java_com_m2049r_xmrwallet_model_WalletManager_getDaemonVersion(JNIEnv *env,
jobject instance) {
uint32_t version;
bool isConnected =
@@ -449,11 +465,12 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj
// actually a WalletManager function, but logically in Wallet
JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_close(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance,
jobject walletInstance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, walletInstance);
bool closeSuccess = Bitmonero::WalletManagerFactory::getWalletManager()->closeWallet(wallet);
if (closeSuccess) {
MyWalletListener *walletListener = getHandle<MyWalletListener>(env, instance,
MyWalletListener *walletListener = getHandle<MyWalletListener>(env, walletInstance,
"listenerHandle");
if (walletListener != nullptr) {
walletListener->deleteGlobalJavaRef(env);
@@ -575,9 +592,9 @@ Java_com_m2049r_xmrwallet_model_Wallet_getFilename(JNIEnv *env, jobject instance
// virtual std::string keysFilename() const = 0;
JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_init(JNIEnv *env, jobject instance,
jstring daemon_address,
long upper_transaction_size_limit) {
Java_com_m2049r_xmrwallet_model_Wallet_initJ(JNIEnv *env, jobject instance,
jstring daemon_address,
long upper_transaction_size_limit) {
// const std::string &daemon_username = "", const std::string &daemon_password = "") = 0;
const char *_daemon_address = env->GetStringUTFChars(daemon_address, JNI_FALSE);
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);

View File

@@ -1,6 +1,18 @@
//
// Created by m2049r on 15.04.2017.
//
/**
* Copyright (c) 2017 m2049r
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
*/
#ifndef XMRWALLET_WALLET_LIB_H
#define XMRWALLET_WALLET_LIB_H

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet;
import android.app.Activity;
@@ -9,6 +25,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.KeyEvent;
@@ -30,11 +47,19 @@ import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class LoginActivity extends Activity {
static final String TAG = "LoginActivity";
static final int MIN_DAEMON_VERSION = 65544;
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
ListView listView;
List<String> walletList = new ArrayList<>();
List<String> displayedList = new ArrayList<>();
@@ -69,8 +94,12 @@ public class LoginActivity extends Activity {
@Override
public void onClick(View v) {
boolean mainnet = ((ToggleButton) v).isChecked(); // current state
Log.d(TAG, "CLICK NET! mainnet=" + mainnet);
savePrefs(true); // use previous state as we just clicked it
if (mainnet) {
setDaemon(daemonMainNet);
} else {
setDaemon(daemonTestNet);
}
filterList();
((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
}
@@ -104,9 +133,17 @@ public class LoginActivity extends Activity {
final int preambleLength = "[123456] ".length();
if (itemValue.length() <= (preambleLength)) {
Toast.makeText(LoginActivity.this, "something's wrong", Toast.LENGTH_LONG).show();
Toast.makeText(LoginActivity.this, getString(R.string.panic), Toast.LENGTH_LONG).show();
return;
}
setWalletDaemon();
if (!checkWalletDaemon()) {
Toast.makeText(LoginActivity.this, getString(R.string.warn_daemon_unavailable), Toast.LENGTH_LONG).show();
return;
}
// looking good
savePrefs(false);
String wallet = itemValue.substring(preambleLength);
promptPassword(wallet);
@@ -180,6 +217,32 @@ public class LoginActivity extends Activity {
}
}
private boolean checkWalletDaemon() {
// if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy prevPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder(prevPolicy).permitNetwork().build();
StrictMode.setThreadPolicy(policy);
String d[] = WalletManager.getInstance().getDaemonAddress().split(":");
String host = d[0];
int port = Integer.parseInt(d[1]);
Socket socket = new Socket();
long a = new Date().getTime();
try {
socket.connect(new InetSocketAddress(host, port), DAEMON_TIMEOUT);
socket.close();
} catch (IOException ex) {
Log.d(TAG, "Cannot reach daemon " + host + ":" + port + " because " + ex.getLocalizedMessage());
return false;
} finally {
StrictMode.setThreadPolicy(prevPolicy);
}
long b = new Date().getTime();
Log.d(TAG, "Daemon is " + (b - a) + "ms away.");
int version = WalletManager.getInstance().getDaemonVersion();
Log.d(TAG, "Daemon is v" + version);
return (version >= MIN_DAEMON_VERSION);
}
private boolean checkWalletPassword(String walletName, String password) {
String walletPath = new File(Helper.getStorageRoot(getApplicationContext()),
walletName + ".keys").getAbsolutePath();
@@ -187,7 +250,6 @@ public class LoginActivity extends Activity {
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
}
@Override
protected void onPause() {
Log.d(TAG, "onPause()");
@@ -195,6 +257,12 @@ public class LoginActivity extends Activity {
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
}
boolean isMainNet() {
ToggleButton tbMainNet = (ToggleButton) findViewById(R.id.tbMainNet);
return tbMainNet.isChecked();
@@ -255,20 +323,22 @@ public class LoginActivity extends Activity {
editor.apply();
}
void startWallet(String walletName, String walletPassword) {
Log.d(TAG, "startWallet()");
savePrefs(false);
EditText tvDaemonAddress = (EditText) findViewById(R.id.etDaemonAddress);
private void setWalletDaemon() {
boolean testnet = !isMainNet();
Intent intent = new Intent(getApplicationContext(), WalletActivity.class);
String daemon = tvDaemonAddress.getText().toString();
String daemon = getDaemon();
if (!daemon.contains(":")) {
daemon = daemon + (testnet ? ":28081" : ":18081");
}
intent.putExtra("daemon", daemon);
intent.putExtra("testnet", testnet);
intent.putExtra("wallet", walletName);
intent.putExtra("password", walletPassword);
WalletManager.getInstance().setDaemon(daemon, testnet);
}
void startWallet(String walletName, String walletPassword) {
Log.d(TAG, "startWallet()");
Intent intent = new Intent(getApplicationContext(), WalletActivity.class);
intent.putExtra(WalletActivity.REQUEST_ID, walletName);
intent.putExtra(WalletActivity.REQUEST_PW, walletPassword);
startActivity(intent);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.layout;
import android.graphics.Color;
@@ -22,10 +38,10 @@ import java.util.List;
import java.util.TimeZone;
public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfoAdapter.ViewHolder> {
static final String TAG = "TransactionInfoAdapter";
private static final String TAG = "TransactionInfoAdapter";
static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
static final int TX_RED = Color.rgb(255, 79, 65);
static final int TX_GREEN = Color.rgb(54, 176, 91);
@@ -84,15 +100,15 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final TextView tvAmount;
public final TextView tvAmountPoint;
public final TextView tvAmountDecimal;
public final TextView tvDate;
public final TextView tvTime;
public TransactionInfo infoItem;
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
final TextView tvAmount;
final TextView tvAmountPoint;
final TextView tvAmountDecimal;
final TextView tvDate;
final TextView tvTime;
TransactionInfo infoItem;
public ViewHolder(View itemView) {
ViewHolder(View itemView) {
super(itemView);
this.tvAmount = (TextView) itemView.findViewById(R.id.tx_amount);
// I know this is stupid but can't be bothered to align decimals otherwise

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.model;
import java.util.List;

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.model;
public class TransactionInfo {
@@ -7,7 +23,7 @@ public class TransactionInfo {
public long handle;
public TransactionInfo(long handle) {
TransactionInfo(long handle) {
this.handle = handle;
}

View File

@@ -1,7 +1,25 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.model;
import android.util.Log;
import java.io.File;
public class Wallet {
static {
System.loadLibrary("monerujo");
@@ -9,10 +27,15 @@ public class Wallet {
static final String TAG = "Wallet";
public String getName() {
String p = getPath();
return new File(p).getName();
}
private long handle = 0;
private long listenerHandle = 0;
public Wallet(long handle) {
Wallet(long handle) {
this.handle = handle;
}
@@ -30,8 +53,6 @@ public class Wallet {
//public native long createWalletListenerJ();
public native boolean close(); // from WalletManager
public native String getSeed();
public native String getSeedLanguage();
@@ -67,10 +88,19 @@ public class Wallet {
public native boolean store(String path);
public boolean close() {
return WalletManager.getInstance().close(this);
}
public native String getFilename();
// virtual std::string keysFilename() const = 0;
public native boolean init(String daemon_address, long upper_transaction_size_limit);
public boolean init(long upper_transaction_size_limit) {
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit);
}
private native boolean initJ(String daemon_address, long upper_transaction_size_limit);
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.model;
public interface WalletListener {

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.model;
import android.util.Log;
@@ -8,10 +24,12 @@ import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WalletManager {
final static String TAG = "WalletManager";
private final static String TAG = "WalletManager";
static {
System.loadLibrary("monerujo");
@@ -27,23 +45,57 @@ public class WalletManager {
return WalletManager.Instance;
}
public Wallet createWallet(String path, String password, String language, boolean isTestNet) {
long walletHandle = createWalletJ(path, password, language, isTestNet);
return new Wallet(walletHandle);
private WalletManager() {
this.managedWallets = new HashMap<>();
}
public Wallet openWallet(String path, String password, boolean isTestNet) {
long walletHandle = openWalletJ(path, password, isTestNet);
return new Wallet(walletHandle);
private Map<String, Wallet> managedWallets;
public Wallet getWallet(String walletId) {
return managedWallets.get(walletId);
}
public Wallet recoveryWallet(String path, String mnemonic, boolean isTestNet) {
return recoveryWallet(path, mnemonic, isTestNet, 0);
private void manageWallet(String walletId, Wallet wallet) {
if (getWallet(walletId) != null) {
throw new IllegalStateException("Wallet already under management!");
}
Log.d(TAG, "Managing " + walletId);
managedWallets.put(walletId, wallet);
}
public Wallet recoveryWallet(String path, String mnemonic, boolean isTestNet, long restoreHeight) {
long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet, restoreHeight);
return new Wallet(walletHandle);
private void unmanageWallet(String walletId) {
if (getWallet(walletId) == null) {
throw new IllegalStateException("Wallet not under management!");
}
Log.d(TAG, "Unmanaging " + walletId);
managedWallets.remove(walletId);
}
public Wallet createWallet(String path, String password, String language) {
long walletHandle = createWalletJ(path, password, language, isTestNet());
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
}
public Wallet openWallet(String path, String password) {
long walletHandle = openWalletJ(path, password, isTestNet());
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
}
public Wallet recoveryWallet(String path, String mnemonic) {
Wallet wallet = recoveryWallet(path, mnemonic, 0);
manageWallet(wallet.getName(), wallet);
return wallet;
}
public Wallet recoveryWallet(String path, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet(), restoreHeight);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
}
private native long createWalletJ(String path, String password, String language, boolean isTestNet);
@@ -59,6 +111,20 @@ public class WalletManager {
String viewKeyString,
String spendKeyString);
public native boolean closeJ(Wallet wallet);
public boolean close(Wallet wallet) {
String walletId = new File(wallet.getFilename()).getName();
unmanageWallet(walletId);
boolean closed = closeJ(wallet);
if (!closed) {
// in case we could not close it
// we unmanage it
manageWallet(walletId, wallet);
}
return closed;
}
public native boolean walletExists(String path);
public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only);
@@ -85,7 +151,7 @@ public class WalletManager {
String filename = found[i].getName();
info.name = filename.substring(0, filename.length() - 5); // 5 is length of ".keys"+1
File addressFile = new File(path, info.name + ".address.txt");
Log.d(TAG, addressFile.getAbsolutePath());
//Log.d(TAG, addressFile.getAbsolutePath());
info.address = "??????";
BufferedReader addressReader = null;
try {
@@ -111,20 +177,34 @@ public class WalletManager {
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
String daemonAddress = null;
private String daemonAddress = null;
private boolean testnet = true;
public void setDaemonAddress(String address) {
public boolean isTestNet() {
if (daemonAddress == null) {
// assume testnet not explicitly initialised
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return testnet;
}
public void setDaemon(String address, boolean testnet) {
this.daemonAddress = address;
this.testnet = testnet;
setDaemonAddressJ(address);
}
public String getDaemonAddress() {
if (daemonAddress == null) {
// assume testnet not explicitly initialised
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return this.daemonAddress;
}
private native void setDaemonAddressJ(String address);
public native int getConnectedDaemonVersion();
public native int getDaemonVersion();
public native long getBlockchainHeight();

View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.service;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
* The started Thread has a stck size of STACK_SIZE (=3MB)
*/
public class MoneroHandlerThread extends Thread {
// from src/cryptonote_config.h
static public final long THREAD_STACK_SIZE = 5 * 1024 * 1024;
int mPriority;
int mTid = -1;
Looper mLooper;
public MoneroHandlerThread(String name) {
super(null, null, name, THREAD_STACK_SIZE);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a MoneroHandlerThread.
*
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public MoneroHandlerThread(String name, int priority) {
super(null, null, name, THREAD_STACK_SIZE);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
*
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* 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 com.m2049r.xmrwallet.util;
import android.Manifest;
@@ -12,8 +28,8 @@ import com.m2049r.xmrwallet.R;
import java.io.File;
public class Helper {
static final String TAG = "Helper";
static final String WALLET_DIR = "Monerujo";
private static final String TAG = "Helper";
private static final String WALLET_DIR = "Monerujo";
static public File getStorageRoot(Context context) {
if (!isExternalStorageWritable()) {
@@ -54,11 +70,7 @@ public class Helper {
static public String getWalletPath(Context context, String aWalletName) {
File walletDir = getStorageRoot(context);
Log.d(TAG, "walletdir=" + walletDir.getAbsolutePath());
if (!walletDir.exists()) {
Log.d(TAG, "walletdir did not exist!");
walletDir.mkdirs();
}
//d(TAG, "walletdir=" + walletDir.getAbsolutePath());
File f = new File(walletDir, aWalletName);
Log.d(TAG, "wallet = " + f.getAbsolutePath() + " size=" + f.length());
return f.getAbsolutePath();
@@ -67,10 +79,7 @@ public class Helper {
/* Checks if external storage is available for read and write */
static public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
return Environment.MEDIA_MOUNTED.equals(state);
}
}

Some files were not shown because too many files have changed in this diff Show More