diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 214a3540..28cf83b7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,10 @@ # Release Notes +## 1.3.0 + - New network api + - Fixed UI issues + - Fixed converting issues + ## 1.2.8 - Showing commission for exchanging transactions - Updated SDK diff --git a/app/build.gradle b/app/build.gradle index d71aa64a..37a1281e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,8 +53,8 @@ android { applicationId "network.minter.bipwallet" minSdkVersion minterMinSdk targetSdkVersion minterMaxSdk - versionCode 71 - versionName "1.2.8" + versionCode 75 + versionName "1.3.0-dev05" testInstrumentationRunner "network.minter.bipwallet.tests.internal.WalletTestRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled true @@ -83,10 +83,10 @@ android { signingConfigs { config { - keyAlias getProperty("minter_key_alias") - keyPassword getProperty("minter_key_password") - storeFile file(getProperty("minter_key_store_file")) - storePassword getProperty("minter_key_store_password") + keyAlias localProps['minter_key_alias'] + keyPassword localProps['minter_key_password'] + storeFile file(localProps['minter_key_store_file']) + storePassword localProps['minter_key_store_password'] } } @@ -157,10 +157,10 @@ android { } ext { - minterExplorerSDK = "0.3.1" + minterExplorerSDK = "0.4.0" minterProfileSDK = "0.2.1" - minterBlockchainSDK = "0.5.2" - minterCoreSDK = "0.2.3" + minterBlockchainSDK = "0.5.5" + minterCoreSDK = "0.2.4" } dependencies { diff --git a/app/src/androidTest/java/network/minter/bipwallet/tests/ui/ConvertCoinsTest.java b/app/src/androidTest/java/network/minter/bipwallet/tests/ui/ConvertCoinsTest.java index ca185f2a..3d9f3a0c 100644 --- a/app/src/androidTest/java/network/minter/bipwallet/tests/ui/ConvertCoinsTest.java +++ b/app/src/androidTest/java/network/minter/bipwallet/tests/ui/ConvertCoinsTest.java @@ -67,10 +67,11 @@ import network.minter.core.bip39.MnemonicResult; import network.minter.core.crypto.MinterAddress; import network.minter.explorer.MinterExplorerApi; -import network.minter.explorer.models.BCExplorerResult; import network.minter.explorer.models.CoinItem; import network.minter.explorer.models.ExpResult; +import network.minter.explorer.models.GateResult; import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.profile.models.User; import retrofit2.Response; import timber.log.Timber; @@ -85,8 +86,8 @@ import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static network.minter.bipwallet.apis.reactive.ReactiveMyMinter.rxBc; -import static network.minter.bipwallet.apis.reactive.ReactiveMyMinter.rxExp; +import static network.minter.bipwallet.apis.reactive.ReactiveBlockchain.rxBc; +import static network.minter.bipwallet.apis.reactive.ReactiveExplorer.rxExp; import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; import static network.minter.bipwallet.tests.internal.MyMatchers.withInputLayoutError; import static network.minter.bipwallet.tests.internal.MyMatchers.withInputLayoutHint; @@ -215,7 +216,7 @@ public void testSpendCoin() { } @Test - public void testSpendCommission() { + public void testSpendCommission() throws Throwable { ApiMockInterceptor gateMock = new ApiMockInterceptor("gate", mActivityTestRule.getActivity()); try { MinterExplorerApi.getInstance().getGateApiService().addHttpInterceptor(gateMock); @@ -270,6 +271,7 @@ public void testGetCommission() { public void testSpendInputs() throws Throwable { ExplorerCoinsRepository repo = TestWallet.app().explorerCoinsRepo(); + GateEstimateRepository estimateRepo = TestWallet.app().estimateRepo(); final int tabPos = 0; // spend coins @@ -350,8 +352,8 @@ public void testSpendInputs() throws Throwable { calculationSum.perform(scrollTo()); - Response> estimate1 = repo.getCoinExchangeCurrencyToSell(MinterSDK.DEFAULT_COIN, new BigDecimal(amount), mExchangeCoin.getSymbol()).execute(); - assertTrue(estimate1.body().isSuccess()); + Response> estimate1 = estimateRepo.getCoinExchangeCurrencyToSell(MinterSDK.DEFAULT_COIN, new BigDecimal(amount), mExchangeCoin.getSymbol()).execute(); + assertTrue(estimate1.body().isOk()); // amount without commission String expectEstimate3 = bdHuman(estimate1.body().result.getAmount()); calculationSum.check(matches(withText(String.format("%s %s", expectEstimate3, mExchangeCoin.getSymbol())))); @@ -362,8 +364,8 @@ public void testSpendInputs() throws Throwable { actionUseMax.perform(click()); amountInput.check(matches(withText("100"))); - Response> estimate2 = repo.getCoinExchangeCurrencyToSell(MinterSDK.DEFAULT_COIN, new BigDecimal("100"), mExchangeCoin.getSymbol()).execute(); - assertTrue(estimate2.body().isSuccess()); + Response> estimate2 = estimateRepo.getCoinExchangeCurrencyToSell(MinterSDK.DEFAULT_COIN, new BigDecimal("100"), mExchangeCoin.getSymbol()).execute(); + assertTrue(estimate2.body().isOk()); // amount without commission String expectEstimate2 = bdHuman(estimate2.body().result.getAmount()); calculationSum.check(matches(withText(String.format("%s %s", expectEstimate2, mExchangeCoin.getSymbol())))); @@ -373,6 +375,7 @@ public void testSpendInputs() throws Throwable { public void testGetInputs() throws Throwable { ExplorerCoinsRepository repo = TestWallet.app().explorerCoinsRepo(); + GateEstimateRepository estimateRepo = TestWallet.app().estimateRepo(); final int tabPos = 1; // spend coins @@ -456,8 +459,8 @@ public void testGetInputs() throws Throwable { calculationSum.perform(scrollTo()); - Response> estimate1 = repo.getCoinExchangeCurrencyToBuy(MinterSDK.DEFAULT_COIN, new BigDecimal(amount), mExchangeCoin.getSymbol()).execute(); - assertTrue(estimate1.body().isSuccess()); + Response> estimate1 = estimateRepo.getCoinExchangeCurrencyToBuy(MinterSDK.DEFAULT_COIN, new BigDecimal(amount), mExchangeCoin.getSymbol()).execute(); + assertTrue(estimate1.body().isOk()); // amount without commission String expectEstimate1 = bdHuman(estimate1.body().result.getAmount()); calculationSum.check(matches(withText(String.format("%s %s", expectEstimate1, MinterSDK.DEFAULT_COIN)))); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c94f7cee..e85dbd20 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -167,7 +167,7 @@ diff --git a/app/src/main/java/network/minter/bipwallet/advanced/AdvancedModeModule.java b/app/src/main/java/network/minter/bipwallet/advanced/AdvancedModeModule.java index cef3c5ae..81da1371 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/AdvancedModeModule.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/AdvancedModeModule.java @@ -65,7 +65,6 @@ public interface GenerateView extends MvpView, ProgressTextView, ErrorView { } public interface MainView extends MvpView, ErrorView, ProgressTextView { - void setOnGenerate(View.OnClickListener listener); void setMnemonicTextChangedListener(TextWatcher textWatcher); void setOnActivateMnemonic(View.OnClickListener listener); @StateStrategyType(OneExecutionStateStrategy.class) diff --git a/app/src/main/java/network/minter/bipwallet/advanced/models/AccountItem.java b/app/src/main/java/network/minter/bipwallet/advanced/models/AccountItem.java index 7e5f40bb..e8c6de32 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/models/AccountItem.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/models/AccountItem.java @@ -54,8 +54,6 @@ public class AccountItem implements Serializable, Cloneable { public String coin; public MinterAddress address; public BigDecimal balance; - public BigDecimal balanceBase; - public BigDecimal balanceUsd; int mHashCode; public AccountItem(final AccountItem another) { @@ -63,27 +61,23 @@ public AccountItem(final AccountItem another) { avatar = another.avatar; coin = another.coin; address = another.address; - balanceUsd = another.balanceUsd; balance = another.balance; - balanceBase = another.balanceBase; mHashCode = another.mHashCode; } - public AccountItem(String avatar, String coin, MinterAddress address, BigDecimal balance, BigDecimal balanceUsd, BigDecimal balanceBase) { - this(coin, address, balance, balanceUsd, balanceBase); + public AccountItem(String avatar, String coin, MinterAddress address, BigDecimal balance) { + this(coin, address, balance); this.avatar = avatar; } @SuppressWarnings("NullableProblems") - public AccountItem(@NonNull String coin, MinterAddress address, BigDecimal balance, BigDecimal balanceUsd, BigDecimal balanceBase) { + public AccountItem(@NonNull String coin, MinterAddress address, BigDecimal balance) { this.id = UUID.randomUUID().toString(); this.coin = checkNotNull(coin, "Coin name required"); this.address = checkNotNull(address, "Address required"); this.balance = balance; - this.balanceUsd = balanceUsd; - this.balanceBase = balanceBase; this.avatar = MinterProfileApi.getCoinAvatarUrl(coin); - mHashCode = Objects.hash(id, avatar, coin, address, balance, balanceUsd); + mHashCode = Objects.hash(id, avatar, coin, address, balance); } AccountItem() { @@ -114,19 +108,14 @@ public BigDecimal getBalance() { return balance; } + @Deprecated public BigDecimal getBalanceBase() { - if (balanceBase == null) { - balanceBase = new BigDecimal(0); - } - - return balanceBase; + return getBalance(); } + @Deprecated public BigDecimal getBalanceUsd() { - if (balanceUsd == null) { - balanceUsd = new BigDecimal(0); - } - return balanceUsd; + return getBalance(); } @Override @@ -136,8 +125,7 @@ public boolean equals(Object o) { AccountItem that = (AccountItem) o; return Objects.equals(coin, that.coin) && Objects.equals(address, that.address) && - Objects.equals(balance, that.balance) && - Objects.equals(balanceUsd, that.balanceUsd); + Objects.equals(balance, that.balance); } @Override diff --git a/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedGenerateActivity.java b/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedGenerateActivity.java index bc283cc8..8943e9f8 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedGenerateActivity.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedGenerateActivity.java @@ -53,8 +53,10 @@ import network.minter.bipwallet.home.ui.HomeActivity; import network.minter.bipwallet.internal.BaseMvpInjectActivity; import network.minter.bipwallet.internal.dialogs.WalletInputDialog; +import network.minter.bipwallet.internal.helpers.KeyboardHelper; import network.minter.bipwallet.internal.helpers.forms.validators.RegexValidator; import network.minter.bipwallet.internal.system.ActivityBuilder; +import timber.log.Timber; /** * minter-android-wallet. 2018 diff --git a/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedMainActivity.java b/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedMainActivity.java index f7ea0ab1..d245d3a2 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedMainActivity.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/ui/AdvancedMainActivity.java @@ -53,7 +53,9 @@ import network.minter.bipwallet.internal.BaseMvpInjectActivity; import network.minter.bipwallet.internal.dialogs.WalletInputDialog; import network.minter.bipwallet.internal.dialogs.WalletProgressDialog; +import network.minter.bipwallet.internal.helpers.KeyboardHelper; import network.minter.bipwallet.internal.system.ActivityBuilder; +import timber.log.Timber; /** * minter-android-wallet. 2018 @@ -65,7 +67,6 @@ public class AdvancedMainActivity extends BaseMvpInjectActivity implements Advan public static final String EXTRA_FOR_RESULT = "EXTRA_FOR_RESULT"; public static final String EXTRA_TITLE = "EXTRA_TITLE"; - @BindView(R.id.action_generate) Button actionGenerate; @BindView(R.id.action_activate) Button actionActivate; @BindView(R.id.input_seed) AppCompatEditText seedInput; @BindView(R.id.toolbar) Toolbar toolbar; @@ -74,11 +75,6 @@ public class AdvancedMainActivity extends BaseMvpInjectActivity implements Advan @InjectPresenter AdvancedMainPresenter presenter; private WalletProgressDialog mProgress; - @Override - public void setOnGenerate(View.OnClickListener listener) { - actionGenerate.setOnClickListener(listener); - } - @Override public void setMnemonicTextChangedListener(TextWatcher textWatcher) { seedInput.addTextChangedListener(textWatcher); @@ -123,6 +119,7 @@ public void finishSuccess() { @Override public void startHome() { + KeyboardHelper.hideKeyboard(this); startActivityClearTop(this, HomeActivity.class); finish(); } diff --git a/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedGeneratePresenter.java b/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedGeneratePresenter.java index 85b72d52..60c8c9bc 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedGeneratePresenter.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedGeneratePresenter.java @@ -89,7 +89,6 @@ public void attachView(AdvancedModeModule.GenerateView view) { // default value for basic user mSaveOnServer = session.getRole() == AuthSession.AuthType.Basic; - mMnemonicResult = NativeBip39.encodeBytes(mRandom.generateSeed(16)); getViewState().setMnemonic(mMnemonicResult.getMnemonic()); getViewState().setEnableCopy(true); @@ -120,6 +119,7 @@ public void attachView(AdvancedModeModule.GenerateView view) { protected void onFirstViewAttach() { super.onFirstViewAttach(); getViewState().setEnableLaunch(false); + mMnemonicResult = NativeBip39.encodeBytes(mRandom.generateSeed(16)); } private boolean saveServerAddress(String fieldName, String value, MinterAddress address) { diff --git a/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedMainPresenter.java b/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedMainPresenter.java index 5253780f..cbd91ce6 100644 --- a/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedMainPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/advanced/views/AdvancedMainPresenter.java @@ -115,8 +115,6 @@ public void attachView(AdvancedModeModule.MainView view) { getViewState().startHome(); } }); - - getViewState().setOnGenerate(this::onStartGenerate); } private void onStartGenerate(View view) { diff --git a/app/src/main/java/network/minter/bipwallet/apis/dummies/GateErrorMapped.java b/app/src/main/java/network/minter/bipwallet/apis/dummies/GateErrorMapped.java index d4579828..bd4814dd 100644 --- a/app/src/main/java/network/minter/bipwallet/apis/dummies/GateErrorMapped.java +++ b/app/src/main/java/network/minter/bipwallet/apis/dummies/GateErrorMapped.java @@ -50,9 +50,9 @@ public boolean mapError(Throwable throwable) { } NetworkException e = (NetworkException) NetworkException.convertIfNetworking(throwable); - data = null; + result = null; error = new ErrorResult(); - error.statusCode = e.getStatusCode(); + error.code = statusCode; error.message = e.getUserMessage(); statusCode = e.getStatusCode(); errorMessage = e.getUserMessage(); diff --git a/app/src/main/java/network/minter/bipwallet/apis/reactive/ReactiveGate.java b/app/src/main/java/network/minter/bipwallet/apis/reactive/ReactiveGate.java index 3ece1271..cf6b291a 100644 --- a/app/src/main/java/network/minter/bipwallet/apis/reactive/ReactiveGate.java +++ b/app/src/main/java/network/minter/bipwallet/apis/reactive/ReactiveGate.java @@ -11,6 +11,7 @@ import io.reactivex.ObservableSource; import io.reactivex.functions.Function; import network.minter.bipwallet.apis.dummies.GateErrorMapped; +import network.minter.core.internal.exceptions.NetworkException; import network.minter.explorer.MinterExplorerApi; import network.minter.explorer.models.GateResult; import retrofit2.Call; @@ -31,7 +32,7 @@ public static Observable rxGate(Call call) { @Override public void onResponse(@NonNull Call call1, @NonNull Response response) { if (response.body() == null) { - emitter.onNext((T) createGateErrorResult(response)); + emitter.onNext((T) createGateError(response)); } else { emitter.onNext(response.body()); } @@ -46,12 +47,12 @@ public void onFailure(@NonNull Call call1, @NonNull Throwable t) { })); } - public static Function>> convertToGateErrorResult() { + public static Function>> toGateError() { return (Function>>) throwable -> { if (throwable instanceof HttpException) { - return Observable.just(createGateErrorResult(((HttpException) throwable))); + return Observable.just(createGateError(((HttpException) throwable))); } GateErrorMapped errResult = new GateErrorMapped<>(); @@ -63,7 +64,7 @@ public void onFailure(@NonNull Call call1, @NonNull Throwable t) { }; } - public static GateResult createGateErrorResult(final String json, int code, String message) { + public static GateResult createGateError(final String json, int code, String message) { Gson gson = MinterExplorerApi.getInstance().getGsonBuilder().create(); GateResult out; try { @@ -80,7 +81,7 @@ public static GateResult createGateErrorResult(final String json, int cod return out; } - public static GateResult createGateErrorResult(final Response response) { + public static GateResult createGateError(final Response response) { final String errorBodyString; try { // нельзя после этой строки пытаться вытащить body из ошибки, @@ -92,10 +93,10 @@ public static GateResult createGateErrorResult(final Response response return createGateEmpty(response.code(), response.message()); } - return createGateErrorResult(errorBodyString, response.code(), response.message()); + return createGateError(errorBodyString, response.code(), response.message()); } - public static GateResult createGateErrorResult(final HttpException exception) { + public static GateResult createGateError(final HttpException exception) { final String errorBodyString; try { // нельзя после этой строки пытаться вытащить body из ошибки, @@ -107,14 +108,34 @@ public static GateResult createGateErrorResult(final HttpException except return createGateEmpty(exception.code(), exception.message()); } - return createGateErrorResult(errorBodyString, exception.code(), exception.message()); + return createGateError(errorBodyString, exception.code(), exception.message()); } public static GateResult createGateEmpty(int code, String message) { GateResult out = new GateResult<>(); out.error = new GateResult.ErrorResult(); out.error.message = message; - out.error.statusCode = code; + out.error.code = code; + out.statusCode = code; return out; } + + public static GateResult createGateErrorPlain(Throwable t) { + final Throwable e = NetworkException.convertIfNetworking(t); + if (e instanceof NetworkException) { + return createGateErrorPlain(((NetworkException) e).getUserMessage(), -1, ((NetworkException) e).getStatusCode()); + } + + return createGateErrorPlain(t.getMessage(), -1, -1); + + } + + public static GateResult createGateErrorPlain(final String errorMessage, int code, int statusCode) { + GateResult errorRes = new GateResult<>(); + errorRes.error = new GateResult.ErrorResult(); + errorRes.error.message = errorMessage; + errorRes.error.code = code; + errorRes.statusCode = statusCode; + return errorRes; + } } diff --git a/app/src/main/java/network/minter/bipwallet/auth/AuthModule.java b/app/src/main/java/network/minter/bipwallet/auth/AuthModule.java index f14d5b9a..1d037f32 100644 --- a/app/src/main/java/network/minter/bipwallet/auth/AuthModule.java +++ b/app/src/main/java/network/minter/bipwallet/auth/AuthModule.java @@ -51,12 +51,9 @@ public class AuthModule { public interface AuthView extends MvpView { void setOnCreateWallet(View.OnClickListener listener); - void setOnAdvancedMode(View.OnClickListener listener); void setOnSignin(View.OnClickListener listener); void setOnHelp(View.OnClickListener listener); @StateStrategyType(OneExecutionStateStrategy.class) - void startAdvancedMode(); - @StateStrategyType(OneExecutionStateStrategy.class) void startRegister(); @StateStrategyType(OneExecutionStateStrategy.class) void startSignIn(); diff --git a/app/src/main/java/network/minter/bipwallet/auth/ui/AuthFragment.java b/app/src/main/java/network/minter/bipwallet/auth/ui/AuthFragment.java index 5a351cb3..060181c2 100644 --- a/app/src/main/java/network/minter/bipwallet/auth/ui/AuthFragment.java +++ b/app/src/main/java/network/minter/bipwallet/auth/ui/AuthFragment.java @@ -47,6 +47,7 @@ import butterknife.BindView; import butterknife.ButterKnife; import network.minter.bipwallet.R; +import network.minter.bipwallet.advanced.ui.AdvancedGenerateActivity; import network.minter.bipwallet.advanced.ui.AdvancedMainActivity; import network.minter.bipwallet.auth.AuthModule; import network.minter.bipwallet.auth.views.AuthPresenter; @@ -62,7 +63,6 @@ public class AuthFragment extends BaseInjectFragment implements AuthModule.AuthV @Inject Provider authPresenterProvider; @InjectPresenter AuthPresenter presenter; @BindView(R.id.action_create_wallet) Button actionCreateWallet; - @BindView(R.id.action_advanced_mode) Button actionAdvancedMode; @BindView(R.id.action_signin) Button actionSignin; @BindView(R.id.action_help) Button actionHelp; @BindView(R.id.logo) ImageView logo; @@ -78,11 +78,6 @@ public void setOnCreateWallet(View.OnClickListener listener) { actionCreateWallet.setOnClickListener(listener); } - @Override - public void setOnAdvancedMode(View.OnClickListener listener) { - actionAdvancedMode.setOnClickListener(listener); - } - @Override public void setOnSignin(View.OnClickListener listener) { actionSignin.setOnClickListener(listener); @@ -93,19 +88,14 @@ public void setOnHelp(View.OnClickListener listener) { actionHelp.setOnClickListener(listener); } - @Override - public void startAdvancedMode() { - getActivity().startActivity(new Intent(getActivity(), AdvancedMainActivity.class)); - } - @Override public void startRegister() { - getActivity().startActivity(new Intent(getActivity(), RegisterActivity.class)); + getActivity().startActivity(new Intent(getActivity(), AdvancedGenerateActivity.class)); } @Override public void startSignIn() { - getActivity().startActivity(new Intent(getActivity(), SigninActivity.class)); + getActivity().startActivity(new Intent(getActivity(), AdvancedMainActivity.class)); } @Override diff --git a/app/src/main/java/network/minter/bipwallet/auth/views/AuthPresenter.java b/app/src/main/java/network/minter/bipwallet/auth/views/AuthPresenter.java index 9def0ce2..98dae898 100644 --- a/app/src/main/java/network/minter/bipwallet/auth/views/AuthPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/auth/views/AuthPresenter.java @@ -52,8 +52,6 @@ public AuthPresenter() { @Override public void attachView(AuthModule.AuthView view) { super.attachView(view); - - getViewState().setOnAdvancedMode(this::onClickAdvancedMode); getViewState().setOnCreateWallet(this::onClickCreateWallet); getViewState().setOnSignin(this::onClickSignIn); getViewState().setOnHelp(this::onClickHelp); @@ -70,10 +68,4 @@ private void onClickSignIn(View view) { private void onClickCreateWallet(View view) { getViewState().startRegister(); } - - private void onClickAdvancedMode(View view) { - getViewState().startAdvancedMode(); - } - - } diff --git a/app/src/main/java/network/minter/bipwallet/coins/repos/ExplorerBalanceFetcher.java b/app/src/main/java/network/minter/bipwallet/coins/repos/ExplorerBalanceFetcher.java index b61ac62d..1baf0217 100644 --- a/app/src/main/java/network/minter/bipwallet/coins/repos/ExplorerBalanceFetcher.java +++ b/app/src/main/java/network/minter/bipwallet/coins/repos/ExplorerBalanceFetcher.java @@ -140,9 +140,7 @@ public void subscribe(ObservableEmitter> emitter) throws Excep null, balance.getCoin(), entry.getKey(), - balance.getAmount(), - balance.getUsdAmount(), - balance.baseCoinAmount + balance.getAmount() )); } } diff --git a/app/src/main/java/network/minter/bipwallet/exchange/ExchangeCalculator.java b/app/src/main/java/network/minter/bipwallet/exchange/ExchangeCalculator.java index 837eff6a..a3e0bf4d 100644 --- a/app/src/main/java/network/minter/bipwallet/exchange/ExchangeCalculator.java +++ b/app/src/main/java/network/minter/bipwallet/exchange/ExchangeCalculator.java @@ -40,14 +40,15 @@ import io.reactivex.schedulers.Schedulers; import network.minter.bipwallet.advanced.models.AccountItem; import network.minter.bipwallet.internal.common.Lazy; -import network.minter.bipwallet.internal.exceptions.BCExplorerResponseException; +import network.minter.bipwallet.internal.exceptions.GateResponseException; import network.minter.blockchain.models.operational.OperationType; import network.minter.core.MinterSDK; -import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.models.GateResult; +import network.minter.explorer.repo.GateEstimateRepository; import timber.log.Timber; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.rxExpGate; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.toExpGateError; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.rxGate; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.toGateError; import static network.minter.bipwallet.internal.common.Preconditions.firstNonNull; import static network.minter.bipwallet.internal.helpers.MathHelper.bdGTE; import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; @@ -65,7 +66,7 @@ private ExchangeCalculator(Builder builder) { } public void calculate(OperationType opType, Consumer onResult, Consumer onErrorMessage) { - final ExplorerCoinsRepository repo = mBuilder.mCoinsRepo; + final GateEstimateRepository repo = mBuilder.mCoinsRepo; final Consumer errFunc = onErrorMessage == null ? e -> { } : onErrorMessage; @@ -74,22 +75,22 @@ public void calculate(OperationType opType, Consumer onResult final String targetCoin = mBuilder.mGetCoin.get(); if (opType == OperationType.BuyCoin) { - // get - rxExpGate(repo.getCoinExchangeCurrencyToBuy(sourceCoin, mBuilder.mGetAmount.get(), targetCoin)) + // get (buy) + rxGate(repo.getCoinExchangeCurrencyToBuy(sourceCoin, mBuilder.mGetAmount.get(), targetCoin)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .onErrorResumeNext(toExpGateError()) + .onErrorResumeNext(toGateError()) .doOnSubscribe(mBuilder.mDisposableConsumer) .doFinally(firstNonNull(mBuilder.mOnCompleteListener, () -> { })) .subscribe(res -> { - if (!res.isSuccess()) { - if (res.statusCode == 404 || res.statusCode == 400 || res.getErrorCode() == CoinNotExists) { + if (!res.isOk()) { + if (checkCoinNotExistError(res)) { errFunc.accept(firstNonNull(res.getMessage(), "Coin to buy not exists")); return; } else { - Timber.w(new BCExplorerResponseException(res)); - errFunc.accept(firstNonNull(res.getMessage(), String.format("Error::%s", res.getErrorCode().name()))); + Timber.w(new GateResponseException(res)); + errFunc.accept(firstNonNull(res.getMessage(), String.format("Error::%s", res.error.getResultCode().name()))); return; } } @@ -132,22 +133,22 @@ else if (getAccount.isPresent() && bdGTE(getAccount.get().getBalance(), res.resu errFunc.accept("Unable to get currency"); }); } else { - // spend - rxExpGate(repo.getCoinExchangeCurrencyToSell(sourceCoin, mBuilder.mSpendAmount.get(), targetCoin)) + // spend (sell or sellAll) + rxGate(repo.getCoinExchangeCurrencyToSell(sourceCoin, mBuilder.mSpendAmount.get(), targetCoin)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .onErrorResumeNext(toExpGateError()) + .onErrorResumeNext(toGateError()) .doOnSubscribe(mBuilder.mDisposableConsumer) .doFinally(firstNonNull(mBuilder.mOnCompleteListener, () -> { })) .subscribe(res -> { if (!res.isOk()) { - if (res.statusCode == 404 || res.statusCode == 400 || res.getErrorCode() == CoinNotExists) { + if (checkCoinNotExistError(res)) { errFunc.accept(firstNonNull(res.getMessage(), "Coin to buy not exists")); return; } else { - Timber.w(new BCExplorerResponseException(res)); - errFunc.accept(firstNonNull(res.getMessage(), String.format("Error::%s", res.getErrorCode().name()))); + Timber.w(new GateResponseException(res)); + errFunc.accept(firstNonNull(res.getMessage(), String.format("Error::%s", res.error.getResultCode().name()))); return; } } @@ -188,6 +189,19 @@ else if (getAccount.isPresent() && bdGTE(getAccount.get().getBalance(), res.resu } } + // Error hell TODO + private boolean checkCoinNotExistError(GateResult res) { + if (res == null) { + return false; + } + + if (res.error != null) { + return res.error.code == 404 || res.error.getResultCode() == CoinNotExists; + } + + return res.statusCode == 404 || res.statusCode == 400; + } + private Optional findAccountByCoin(String coin) { return Stream.of(mBuilder.mAccounts.get()) .filter(item -> item.getCoin().equals(coin.toUpperCase())) @@ -195,7 +209,7 @@ private Optional findAccountByCoin(String coin) { } public static final class Builder { - private final ExplorerCoinsRepository mCoinsRepo; + private final GateEstimateRepository mCoinsRepo; private Action mOnCompleteListener; private Consumer mDisposableConsumer; private Lazy> mAccounts; @@ -203,7 +217,7 @@ public static final class Builder { private Lazy mGetAmount, mSpendAmount; private Lazy mGetCoin; - public Builder(ExplorerCoinsRepository repo) { + public Builder(GateEstimateRepository repo) { mCoinsRepo = repo; } diff --git a/app/src/main/java/network/minter/bipwallet/exchange/models/ConvertTransactionData.java b/app/src/main/java/network/minter/bipwallet/exchange/models/ConvertTransactionData.java index d6284319..689bdc12 100644 --- a/app/src/main/java/network/minter/bipwallet/exchange/models/ConvertTransactionData.java +++ b/app/src/main/java/network/minter/bipwallet/exchange/models/ConvertTransactionData.java @@ -31,6 +31,7 @@ import network.minter.blockchain.models.operational.OperationInvalidDataException; import network.minter.blockchain.models.operational.Transaction; +import network.minter.core.MinterSDK; import static com.google.common.base.MoreObjects.firstNonNull; @@ -63,43 +64,57 @@ public ConvertTransactionData(Type type, String gasCoin, String sellCoin, String mGasPrice = gasPrice; } - public Transaction build(BigInteger nonce) throws OperationInvalidDataException { + public Transaction build(BigInteger nonce, BigInteger gasPrice, BigDecimal balance) throws OperationInvalidDataException { final Transaction tx; - if (mType == Type.Sell) { + // if sellAll AND selling coin is a custom coin AND calculator says enough MNT to use as gas coin + boolean customToCustom = mType == Type.SellAll && !mSellCoin.equals(MinterSDK.DEFAULT_COIN) && mGasCoin.equals(MinterSDK.DEFAULT_COIN); + + if (mType == Type.Sell || customToCustom) { + // SELL tx = new Transaction.Builder(nonce) .setGasCoin(mGasCoin) - .setGasPrice(mGasPrice) + .setGasPrice(gasPrice) .sellCoin() .setCoinToSell(mSellCoin) .setValueToSell(mAmount) .setCoinToBuy(mBuyCoin) - .setMinValueToBuy(getEstimate().multiply(new BigDecimal(0.9d))) +// .setMinValueToBuy(getEstimate().multiply(new BigDecimal(0.9d))) + .setMinValueToBuy(0) .build(); } else if (mType == Type.Buy) { + // BUY tx = new Transaction.Builder(nonce) .setGasCoin(mGasCoin) - .setGasPrice(mGasPrice) + .setGasPrice(gasPrice) .buyCoin() .setCoinToSell(mSellCoin) .setValueToBuy(mAmount) .setCoinToBuy(mBuyCoin) .setMaxValueToSell(getEstimate().multiply(new BigDecimal(1.1d))) +// .setMaxValueToSell(balance) .build(); } else { + // this case used ONLY: when not enough mnt to pay fee with mnt + // SELL ALL tx = new Transaction.Builder(nonce) .setGasCoin(mGasCoin) - .setGasPrice(mGasPrice) + .setGasPrice(gasPrice) .sellAllCoins() .setCoinToSell(mSellCoin) .setCoinToBuy(mBuyCoin) - .setMinValueToBuy(getEstimate().multiply(new BigDecimal(0.9d))) +// .setMinValueToBuy(getEstimate().multiply(new BigDecimal(0.9d))) + .setMinValueToBuy(0) .build(); } return tx; } +// public Transaction build(BigInteger nonce) throws OperationInvalidDataException { +// return build(nonce, mGasPrice); +// } + private BigDecimal getEstimate() { return firstNonNull(mEstimate, new BigDecimal(0)); } diff --git a/app/src/main/java/network/minter/bipwallet/exchange/views/BaseCoinTabPresenter.java b/app/src/main/java/network/minter/bipwallet/exchange/views/BaseCoinTabPresenter.java index 5eb347bf..96a90ec4 100644 --- a/app/src/main/java/network/minter/bipwallet/exchange/views/BaseCoinTabPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/exchange/views/BaseCoinTabPresenter.java @@ -26,6 +26,7 @@ package network.minter.bipwallet.exchange.views; import android.support.annotation.CallSuper; +import android.support.v4.util.Pair; import android.view.View; import android.widget.EditText; @@ -64,23 +65,25 @@ import network.minter.bipwallet.internal.dialogs.WalletProgressDialog; import network.minter.bipwallet.internal.mvp.MvpBasePresenter; import network.minter.bipwallet.internal.system.testing.IdlingManager; -import network.minter.blockchain.models.CountableData; import network.minter.blockchain.models.TransactionSendResult; import network.minter.blockchain.models.operational.OperationType; import network.minter.blockchain.models.operational.Transaction; import network.minter.blockchain.models.operational.TransactionSign; import network.minter.core.MinterSDK; -import network.minter.explorer.models.BCExplorerResult; +import network.minter.explorer.models.GasValue; +import network.minter.explorer.models.GateResult; import network.minter.explorer.models.HistoryTransaction; +import network.minter.explorer.models.TxCount; import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import timber.log.Timber; -import static network.minter.bipwallet.apis.reactive.ReactiveBlockchain.rxBc; import static network.minter.bipwallet.apis.reactive.ReactiveExplorer.rxExp; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.createExpGateErrorPlain; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.rxExpGate; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.toExpGateError; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.createGateErrorPlain; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.rxGate; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.toGateError; import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; import static network.minter.bipwallet.internal.helpers.MathHelper.bdNull; @@ -93,8 +96,10 @@ public abstract class BaseCoinTabPresenter mAccountStorage; protected final CachedRepository, CachedExplorerTransactionRepository> mTxRepo; protected final ExplorerCoinsRepository mExplorerCoinsRepo; + protected final GateEstimateRepository mEstimateRepository; protected final IdlingManager mIdlingManager; protected final GateGasRepository mGasRepo; + protected final GateTransactionRepository mGateTxRepo; private AccountItem mAccount; private String mGetCoin = null; @@ -113,7 +118,9 @@ public BaseCoinTabPresenter( CachedRepository, CachedExplorerTransactionRepository> txRepo, ExplorerCoinsRepository explorerCoinsRepository, IdlingManager idlingManager, - GateGasRepository gasRepo + GateGasRepository gasRepo, + GateEstimateRepository estimateRepository, + GateTransactionRepository gateTxRepo ) { mSecretStorage = secretStorage; mAccountStorage = accountStorage; @@ -121,6 +128,8 @@ public BaseCoinTabPresenter( mExplorerCoinsRepo = explorerCoinsRepository; mIdlingManager = idlingManager; mGasRepo = gasRepo; + mEstimateRepository = estimateRepository; + mGateTxRepo = gateTxRepo; } @Override @@ -170,14 +179,14 @@ protected void setCalculation(String calculation) { private void loadAndSetFee() { mIdlingManager.setNeedsWait(BaseCoinTabFragment.IDLE_WAIT_GAS, true); - rxBc(mGasRepo.getMinGas()) + rxGate(mGasRepo.getMinGas()) .subscribeOn(Schedulers.io()) .toFlowable(BackpressureStrategy.LATEST) .debounce(200, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(res -> { if (res.isOk()) { - mGasPrice = res.data.gas; + mGasPrice = res.result.gas; Timber.d("Min Gas price: %s", mGasPrice.toString()); getViewState().setFee(String.format("%s %s", bdHuman(getOperationType().getFee().multiply(new BigDecimal(mGasPrice))), MinterSDK.DEFAULT_COIN.toUpperCase())); @@ -217,6 +226,7 @@ private Optional findAccountByCoin(String coin) { .findFirst(); } + // @TODO REFACTORING!!! this is hell private void onStartExecuteTransaction(final ConvertTransactionData txData) { getViewState().startDialog(ctx -> { WalletProgressDialog dialog = new WalletProgressDialog.Builder(ctx, "Exchanging") @@ -226,30 +236,65 @@ private void onStartExecuteTransaction(final ConvertTransactionData txData) { dialog.setCancelable(false); safeSubscribeIoToUi( - rxExpGate(mTxRepo.getEntity().getTransactionCount(mAccount.address)) - .onErrorResumeNext(toExpGateError()) + // GET nonce + rxGate(mEstimateRepository.getTransactionCount(mAccount.address)) + .onErrorResumeNext(toGateError()) ) .doOnSubscribe(this::unsubscribeOnDestroy) - .switchMap((Function, ObservableSource>>) cntRes -> { - if (!cntRes.isSuccess()) { - return Observable.just(BCExplorerResult.copyError(cntRes)); + // GET min gas value + .switchMap(new Function, ObservableSource>>>() { + @Override + public ObservableSource>> apply(GateResult cntRes) { + // if somewhere occurred error, return error, nothing else + if (!cntRes.isOk()) { + GateResult> errOut = new GateResult<>(); + errOut.error = cntRes.error; + return Observable.just(errOut); + } + + // Map nonce and min gas value + return rxGate(mGasRepo.getMinGas()).switchMap(new Function, ObservableSource>>>() { + @Override + public ObservableSource>> apply(GateResult gasValueGateResult) { + GateResult> out = new GateResult<>(); + out.result = Pair.create(cntRes.result, gasValueGateResult.result); + out.statusCode = gasValueGateResult.statusCode; + out.error = gasValueGateResult.error; + return Observable.just(out); + } + }); + } - final BigInteger nonce = cntRes.result.count.add(new BigInteger("1")); - final Transaction tx = txData.build(nonce); - final SecretData data = mSecretStorage.getSecret(mAccount.address); - final TransactionSign sign = tx.signSingle(data.getPrivateKey()); - data.cleanup(); - - return safeSubscribeIoToUi( - rxExpGate(mTxRepo.getEntity().sendTransaction(sign)) - .onErrorResumeNext(toExpGateError()) - ); - - }) + // build transaction with prefetched nonce and gas + }).switchMap((Function>, ObservableSource>>) cntRes -> { + // if error occurred upper, notify error + if (!cntRes.isOk()) { + GateResult errOut = new GateResult<>(); + errOut.error = cntRes.error; + return Observable.just(errOut); + } + + BigDecimal balance = new BigDecimal("0"); + + if (getOperationType() == OperationType.SellCoin || getOperationType() == OperationType.SellAllCoins) { + balance = mAccount.getBalance(); + } + final BigInteger nonce = cntRes.result.first.count.add(new BigInteger("1")); + final Transaction tx = txData.build(nonce, cntRes.result.second.gas, balance); + final SecretData data = mSecretStorage.getSecret(mAccount.address); + final TransactionSign sign = tx.signSingle(data.getPrivateKey()); + data.cleanup(); + + return safeSubscribeIoToUi( + rxGate(mGateTxRepo.sendTransaction(sign)) + .onErrorResumeNext(toGateError()) + ); + + }) .doOnSubscribe(this::unsubscribeOnDestroy) - .onErrorResumeNext(toExpGateError()) + .onErrorResumeNext(toGateError()) .subscribe(BaseCoinTabPresenter.this::onSuccessExecuteTransaction, t -> { - onErrorExecuteTransaction(createExpGateErrorPlain(t)); + onErrorExecuteTransaction(createGateErrorPlain(t)); }); @@ -257,8 +302,8 @@ private void onStartExecuteTransaction(final ConvertTransactionData txData) { }); } - private void onSuccessExecuteTransaction(BCExplorerResult result) { - if (!result.isSuccess()) { + private void onSuccessExecuteTransaction(GateResult result) { + if (!result.isOk()) { onErrorExecuteTransaction(result); return; } @@ -279,7 +324,7 @@ private void onSuccessExecuteTransaction(BCExplorerResult .create()); } - private void onErrorExecuteTransaction(BCExplorerResult errorResult) { + private void onErrorExecuteTransaction(GateResult errorResult) { Timber.e(errorResult.getMessage(), "Unable to send transaction"); getViewState().startDialog(ctx -> new WalletConfirmDialog.Builder(ctx, "Unable to send transaction") .setText((errorResult.getMessage())) @@ -416,7 +461,7 @@ private void onAmountChangedInternal(Boolean incoming) { return; } - ExchangeCalculator calculator = new ExchangeCalculator.Builder(mExplorerCoinsRepo) + ExchangeCalculator calculator = new ExchangeCalculator.Builder(mEstimateRepository) .setAccount(() -> mAccounts, () -> mAccount) .setGetAmount(() -> mGetAmount) .setSpendAmount(() -> mSpendAmount) diff --git a/app/src/main/java/network/minter/bipwallet/exchange/views/GetCoinTabPresenter.java b/app/src/main/java/network/minter/bipwallet/exchange/views/GetCoinTabPresenter.java index f5e97483..eac49a1a 100644 --- a/app/src/main/java/network/minter/bipwallet/exchange/views/GetCoinTabPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/exchange/views/GetCoinTabPresenter.java @@ -42,7 +42,9 @@ import network.minter.core.MinterSDK; import network.minter.explorer.models.HistoryTransaction; import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; @@ -60,8 +62,10 @@ public GetCoinTabPresenter( CachedRepository, CachedExplorerTransactionRepository> txRepo, ExplorerCoinsRepository explorerCoinsRepository, IdlingManager idlingManager, - GateGasRepository gasRepo) { - super(secretStorage, accountStorage, txRepo, explorerCoinsRepository, idlingManager, gasRepo); + GateGasRepository gasRepo, + GateEstimateRepository estimateRepository, + GateTransactionRepository gateTransactionRepository) { + super(secretStorage, accountStorage, txRepo, explorerCoinsRepository, idlingManager, gasRepo, estimateRepository, gateTransactionRepository); } @Override diff --git a/app/src/main/java/network/minter/bipwallet/exchange/views/SpendCoinTabPresenter.java b/app/src/main/java/network/minter/bipwallet/exchange/views/SpendCoinTabPresenter.java index d842e7ca..828a40f0 100644 --- a/app/src/main/java/network/minter/bipwallet/exchange/views/SpendCoinTabPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/exchange/views/SpendCoinTabPresenter.java @@ -42,7 +42,9 @@ import network.minter.core.MinterSDK; import network.minter.explorer.models.HistoryTransaction; import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; @@ -60,8 +62,10 @@ public SpendCoinTabPresenter( CachedRepository, CachedExplorerTransactionRepository> txRepo, ExplorerCoinsRepository explorerCoinsRepository, IdlingManager idlingManager, - GateGasRepository gasRepo) { - super(secretStorage, accountStorage, txRepo, explorerCoinsRepository, idlingManager, gasRepo); + GateGasRepository gasRepo, + GateEstimateRepository estimateRepository, + GateTransactionRepository gateTransactionRepository) { + super(secretStorage, accountStorage, txRepo, explorerCoinsRepository, idlingManager, gasRepo, estimateRepository, gateTransactionRepository); } @Override diff --git a/app/src/main/java/network/minter/bipwallet/home/views/HomePresenter.java b/app/src/main/java/network/minter/bipwallet/home/views/HomePresenter.java index 2bb6fef0..b2fdd592 100644 --- a/app/src/main/java/network/minter/bipwallet/home/views/HomePresenter.java +++ b/app/src/main/java/network/minter/bipwallet/home/views/HomePresenter.java @@ -139,8 +139,8 @@ protected void onFirstViewAttach() { super.onFirstViewAttach(); ServiceConnector.bind(app().context()); ServiceConnector.onConnected() - .subscribe(res -> res.setOnMessageListener((message, channel) -> { - accountStorage.update(true, account -> app().balanceNotifications().showBalanceUpdate(message)); + .subscribe(res -> res.setOnMessageListener((message, channel, address) -> { + accountStorage.update(true, account -> app().balanceNotifications().showBalanceUpdate(message, address)); app().explorerTransactionsRepoCache().update(true); Timber.d("WS ON MESSAGE[%s]: %s", channel, message); })); diff --git a/app/src/main/java/network/minter/bipwallet/internal/di/InjectorsModule.java b/app/src/main/java/network/minter/bipwallet/internal/di/InjectorsModule.java index eb393322..e4854203 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/di/InjectorsModule.java +++ b/app/src/main/java/network/minter/bipwallet/internal/di/InjectorsModule.java @@ -48,7 +48,7 @@ import network.minter.bipwallet.internal.di.annotations.ActivityScope; import network.minter.bipwallet.internal.di.annotations.FragmentScope; import network.minter.bipwallet.internal.di.annotations.ServiceScope; -import network.minter.bipwallet.services.livebalance.BalanceUpdateService; +import network.minter.bipwallet.services.livebalance.LiveBalanceService; import network.minter.bipwallet.settings.ui.PasswordChangeMigrationActivity; import network.minter.bipwallet.tx.ui.TransactionListActivity; @@ -118,7 +118,7 @@ public interface InjectorsModule { @ContributesAndroidInjector @ServiceScope - BalanceUpdateService balanceUpdateService(); + LiveBalanceService balanceUpdateService(); @ContributesAndroidInjector @ActivityScope diff --git a/app/src/main/java/network/minter/bipwallet/internal/di/NotificationModule.java b/app/src/main/java/network/minter/bipwallet/internal/di/NotificationModule.java index 5f21681c..d2196175 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/di/NotificationModule.java +++ b/app/src/main/java/network/minter/bipwallet/internal/di/NotificationModule.java @@ -31,12 +31,12 @@ import dagger.Module; import dagger.Provides; import network.minter.bipwallet.internal.helpers.NetworkHelper; +import network.minter.bipwallet.internal.settings.SettingsManager; import network.minter.bipwallet.services.livebalance.notification.BalanceNotificationManager; import network.minter.explorer.MinterExplorerApi; /** * minter-android-wallet. 2018 - * * @author Eduard Maximovich */ @Module @@ -44,7 +44,7 @@ public class NotificationModule { @Provides @WalletApp - public BalanceNotificationManager provideBalanceNotificationManager(Context context, MinterExplorerApi api, NetworkHelper networkHelper) { - return new BalanceNotificationManager(context, api.getGsonBuilder(), networkHelper); + public BalanceNotificationManager provideBalanceNotificationManager(Context context, MinterExplorerApi api, NetworkHelper networkHelper, SettingsManager settingsManager) { + return new BalanceNotificationManager(context, api.getGsonBuilder(), networkHelper, settingsManager); } } diff --git a/app/src/main/java/network/minter/bipwallet/internal/di/RepoModule.java b/app/src/main/java/network/minter/bipwallet/internal/di/RepoModule.java index f9080a04..29a8968a 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/di/RepoModule.java +++ b/app/src/main/java/network/minter/bipwallet/internal/di/RepoModule.java @@ -42,7 +42,9 @@ import network.minter.explorer.repo.ExplorerAddressRepository; import network.minter.explorer.repo.ExplorerCoinsRepository; import network.minter.explorer.repo.ExplorerTransactionRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import network.minter.profile.MinterProfileApi; import network.minter.profile.repo.ProfileAddressRepository; import network.minter.profile.repo.ProfileAuthRepository; @@ -140,6 +142,20 @@ public GateGasRepository provideGateGasRepo(MinterExplorerApi api) { return api.gas(); } + @Provides + @WalletApp + public GateEstimateRepository provideGateEstimateRepo(MinterExplorerApi api) { + return api.estimate(); + } + + @Provides + @WalletApp + public GateTransactionRepository provideGateTxRepo(MinterExplorerApi api) { + return api.transactionsGate(); + } + + + @Provides @WalletApp public BlockChainAccountRepository provideBlockChainAccountRepo() { diff --git a/app/src/main/java/network/minter/bipwallet/internal/di/WalletComponent.java b/app/src/main/java/network/minter/bipwallet/internal/di/WalletComponent.java index a22ac94a..d4827704 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/di/WalletComponent.java +++ b/app/src/main/java/network/minter/bipwallet/internal/di/WalletComponent.java @@ -50,6 +50,7 @@ import network.minter.bipwallet.internal.helpers.ImageHelper; import network.minter.bipwallet.internal.helpers.NetworkHelper; import network.minter.bipwallet.internal.helpers.SoundManager; +import network.minter.bipwallet.internal.settings.SettingsManager; import network.minter.bipwallet.internal.storage.KVStorage; import network.minter.bipwallet.internal.system.testing.IdlingManager; import network.minter.bipwallet.sending.repo.RecipientAutocompleteStorage; @@ -64,7 +65,9 @@ import network.minter.explorer.repo.ExplorerAddressRepository; import network.minter.explorer.repo.ExplorerCoinsRepository; import network.minter.explorer.repo.ExplorerTransactionRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import network.minter.profile.models.User; import network.minter.profile.repo.ProfileAddressRepository; import network.minter.profile.repo.ProfileAuthRepository; @@ -105,6 +108,7 @@ public interface WalletComponent { NetworkHelper network(); ImageHelper image(); SharedPreferences prefs(); + SettingsManager settings(); GsonBuilder gsonBuilder(); CacheManager cache(); AnalyticsManager analytics(); @@ -131,6 +135,8 @@ public interface WalletComponent { ExplorerAddressRepository addressExplorerRepo(); ExplorerCoinsRepository explorerCoinsRepo(); GateGasRepository gasRepo(); + GateTransactionRepository txGateRepo(); + GateEstimateRepository estimateRepo(); // blockchain BlockChainAccountRepository accountRepoBlockChain(); BlockChainCoinRepository coinRepoBlockChain(); diff --git a/app/src/main/java/network/minter/bipwallet/internal/di/WalletModule.java b/app/src/main/java/network/minter/bipwallet/internal/di/WalletModule.java index e8e161e3..c3527068 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/di/WalletModule.java +++ b/app/src/main/java/network/minter/bipwallet/internal/di/WalletModule.java @@ -60,6 +60,7 @@ import network.minter.bipwallet.internal.Wallet; import network.minter.bipwallet.internal.auth.AuthSession; import network.minter.bipwallet.internal.helpers.DateHelper; +import network.minter.bipwallet.internal.settings.SettingsManager; import network.minter.bipwallet.internal.storage.KVStorage; import network.minter.bipwallet.internal.system.UnzipUtil; import network.minter.bipwallet.internal.system.testing.IdlingManager; @@ -270,6 +271,12 @@ public SharedPreferences providePreferences(Context context) { Context.MODE_PRIVATE); } + @Provides + @WalletApp + public SettingsManager provideSettingsManager(SharedPreferences prefs) { + return new SettingsManager(prefs); + } + private void initCrashlytics() { CrashlyticsCore core = new CrashlyticsCore.Builder().disabled(!mEnableExternalLog).build(); Fabric.with(mContext, new Crashlytics.Builder().core(core).build()); diff --git a/app/src/main/java/network/minter/bipwallet/internal/exceptions/GateResponseException.java b/app/src/main/java/network/minter/bipwallet/internal/exceptions/GateResponseException.java new file mode 100644 index 00000000..66bb9b4b --- /dev/null +++ b/app/src/main/java/network/minter/bipwallet/internal/exceptions/GateResponseException.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) by MinterTeam. 2018 + * @link Org Github + * @link Maintainer Github + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package network.minter.bipwallet.internal.exceptions; + +import network.minter.explorer.models.GateResult; + +/** + * Dogsy. 2018 + * @author Eduard Maximovich + */ +public final class GateResponseException extends Exception { + private GateResult mError; + + public GateResponseException(GateResult error) { + mError = error; + } + + @Override + public String getMessage() { + if (mError != null && mError.getMessage() != null && !mError.getMessage().isEmpty()) { + return mError.getMessage(); + } + + return super.getMessage(); + } + + public GateResult getError() { + return mError; + } +} diff --git a/app/src/main/java/network/minter/bipwallet/internal/settings/SettingsManager.java b/app/src/main/java/network/minter/bipwallet/internal/settings/SettingsManager.java new file mode 100644 index 00000000..721ef358 --- /dev/null +++ b/app/src/main/java/network/minter/bipwallet/internal/settings/SettingsManager.java @@ -0,0 +1,115 @@ +package network.minter.bipwallet.internal.settings; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +/** + * minter-android-wallet. 2019 + * @author Eduard Maximovich [edward.vstock@gmail.com] + */ +@SuppressLint("CommitPrefEdits") +public class SettingsManager { + public static final Key EnableLiveNotifications = new Key<>("enable_live_notifications", true); + private final static String sPrefsPrefix = "minter_wallet_"; + private final SharedPreferences mPrefs; + + public SettingsManager(SharedPreferences prefs) { + mPrefs = prefs; + } + + public boolean getBool(Key key) { + return mPrefs.getBoolean(key.getName(), key.getDefaultValue()); + } + + public int getInt(Key key) { + return mPrefs.getInt(key.getName(), key.getDefaultValue()); + } + + public float getFloat(Key key) { + return mPrefs.getFloat(key.getName(), key.getDefaultValue()); + } + + public long getLong(Key key) { + return mPrefs.getLong(key.getName(), key.getDefaultValue()); + } + + public String getString(Key key) { + return mPrefs.getString(key.getName(), key.getDefaultValue()); + } + + public void putFloat(Key key, float value, boolean sync) { + put(mPrefs.edit().putFloat(key.getName(), value), sync); + } + + public void putFloat(Key key, float value) { + put(mPrefs.edit().putFloat(key.getName(), value), false); + } + + public void putLong(Key key, long value, boolean sync) { + put(mPrefs.edit().putLong(key.getName(), value), sync); + } + + public void putLong(Key key, long value) { + put(mPrefs.edit().putLong(key.getName(), value), false); + } + + public void putInt(Key key, int value, boolean sync) { + put(mPrefs.edit().putInt(key.getName(), value), sync); + } + + public void putInt(Key key, int value) { + put(mPrefs.edit().putInt(key.getName(), value), false); + } + + public void putString(Key key, String value, boolean sync) { + put(mPrefs.edit().putString(key.getName(), value), sync); + } + + public void putString(Key key, String value) { + put(mPrefs.edit().putString(key.getName(), value), false); + } + + public void putBool(Key key, boolean value, boolean sync) { + put(mPrefs.edit().putBoolean(key.getName(), value), sync); + } + + public void putBool(Key key, boolean value) { + put(mPrefs.edit().putBoolean(key.getName(), value), false); + } + + private void put(SharedPreferences.Editor editor, boolean immediately) { + if (immediately) { + editor.commit(); + return; + } + + editor.apply(); + + + } + + public static final class Key { + private final String mName; + private final T mDefaultValue; + + Key(String name, T defaultValue) { + mName = sPrefsPrefix + name; + mDefaultValue = defaultValue; + } + + public String getName() { + return mName; + } + + public T getDefaultValue() { + return mDefaultValue; + } + + @NonNull + @Override + public String toString() { + return getName(); + } + } +} diff --git a/app/src/main/java/network/minter/bipwallet/internal/system/testing/IdlingManager.java b/app/src/main/java/network/minter/bipwallet/internal/system/testing/IdlingManager.java index c61a1e31..7fcecbc8 100644 --- a/app/src/main/java/network/minter/bipwallet/internal/system/testing/IdlingManager.java +++ b/app/src/main/java/network/minter/bipwallet/internal/system/testing/IdlingManager.java @@ -80,6 +80,10 @@ public IdlingManager add(CallbackIdlingResource resource) { } public void setNeedsWait(String name, boolean active) { + if (!mEnabled) { + return; + } + if (!mContent.containsKey(name)) { Timber.e("Idling resource with name %s does not exists", name); return; diff --git a/app/src/main/java/network/minter/bipwallet/sending/account/WalletAccountSelectorDialog.java b/app/src/main/java/network/minter/bipwallet/sending/account/WalletAccountSelectorDialog.java index 4e812d96..c60399c3 100644 --- a/app/src/main/java/network/minter/bipwallet/sending/account/WalletAccountSelectorDialog.java +++ b/app/src/main/java/network/minter/bipwallet/sending/account/WalletAccountSelectorDialog.java @@ -107,13 +107,13 @@ public Builder setOnClickListener(AccountSelectedAdapter.OnClickListener listene return this; } - public Builder addItem(String avatar, String coin, MinterAddress address, BigDecimal balance, BigDecimal balanceUsd, BigDecimal balanceBase) { - mItems.add(new AccountItem(avatar, coin, address, balance, balanceUsd, balanceBase)); + public Builder addItem(String avatar, String coin, MinterAddress address, BigDecimal balance) { + mItems.add(new AccountItem(avatar, coin, address, balance)); return this; } - public Builder addItem(String coin, MinterAddress address, BigDecimal balance, BigDecimal balanceUsd, BigDecimal balanceBase) { - mItems.add(new AccountItem(coin, address, balance, balanceUsd, balanceBase)); + public Builder addItem(String coin, MinterAddress address, BigDecimal balance) { + mItems.add(new AccountItem(coin, address, balance)); return this; } diff --git a/app/src/main/java/network/minter/bipwallet/sending/views/SendTabPresenter.java b/app/src/main/java/network/minter/bipwallet/sending/views/SendTabPresenter.java index 03767c29..9ee76519 100644 --- a/app/src/main/java/network/minter/bipwallet/sending/views/SendTabPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/sending/views/SendTabPresenter.java @@ -78,7 +78,6 @@ import network.minter.bipwallet.sending.ui.dialogs.WalletTxSendSuccessDialog; import network.minter.bipwallet.sending.ui.dialogs.WalletTxSendWaitingDialog; import network.minter.blockchain.models.BCResult; -import network.minter.blockchain.models.CountableData; import network.minter.blockchain.models.TransactionCommissionValue; import network.minter.blockchain.models.TransactionSendResult; import network.minter.blockchain.models.operational.OperationInvalidDataException; @@ -88,19 +87,21 @@ import network.minter.blockchain.repo.BlockChainTransactionRepository; import network.minter.core.MinterSDK; import network.minter.core.crypto.MinterAddress; -import network.minter.explorer.models.BCExplorerResult; +import network.minter.explorer.models.GateResult; import network.minter.explorer.models.HistoryTransaction; +import network.minter.explorer.models.TxCount; import network.minter.explorer.repo.ExplorerCoinsRepository; +import network.minter.explorer.repo.GateEstimateRepository; import network.minter.explorer.repo.GateGasRepository; +import network.minter.explorer.repo.GateTransactionRepository; import network.minter.profile.MinterProfileApi; import network.minter.profile.models.ProfileResult; import network.minter.profile.repo.ProfileInfoRepository; import timber.log.Timber; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.createExpGateErrorPlain; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.rxExpGate; -import static network.minter.bipwallet.apis.reactive.ReactiveExplorerGate.toExpGateError; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.createGateErrorPlain; import static network.minter.bipwallet.apis.reactive.ReactiveGate.rxGate; +import static network.minter.bipwallet.apis.reactive.ReactiveGate.toGateError; import static network.minter.bipwallet.apis.reactive.ReactiveMyMinter.rxProfile; import static network.minter.bipwallet.apis.reactive.ReactiveMyMinter.toProfileError; import static network.minter.bipwallet.internal.helpers.MathHelper.bdGT; @@ -129,6 +130,8 @@ public class SendTabPresenter extends MvpBasePresenter { @Inject CacheManager cache; @Inject RecipientAutocompleteStorage recipientStorage; @Inject IdlingManager idlingManager; + @Inject GateEstimateRepository estimateRepo; + @Inject GateTransactionRepository gateTxRepo; private AccountItem mFromAccount = null; private BigDecimal mAmount = null; private CharSequence mToAddress = null; @@ -219,7 +222,7 @@ private void loadAndSetFee() { .observeOn(AndroidSchedulers.mainThread()) .subscribe(res -> { if (res.isOk()) { - mGasPrice = res.data.gas; + mGasPrice = res.result.gas; Timber.d("Min Gas price: %s", mGasPrice.toString()); final BigDecimal fee = OperationType.SendCoin.getFee().multiply(new BigDecimal(mGasPrice)); getViewState().setFee(String.format("%s %s", bdHuman(fee), MinterSDK.DEFAULT_COIN.toUpperCase())); @@ -451,10 +454,10 @@ private void onStartExecuteTransaction(boolean express) { Optional sendAccount = findAccountByCoin(mFromAccount.getCoin()); // default coin for pay fee - MNT (base coin) - final BCExplorerResult val = new BCExplorerResult<>(); + final GateResult val = new GateResult<>(); val.result = new TransactionCommissionValue(); val.result.value = OperationType.SendCoin.getFee().multiply(Transaction.VALUE_MUL_DEC).toBigInteger(); - Observable> exchangeResolver = Observable.just(val); + Observable> exchangeResolver = Observable.just(val); final boolean enoughBaseCoinForCommission = bdGTE(mntAccount.get().getBalance(), OperationType.SendCoin.getFee()); @@ -485,10 +488,10 @@ else if (!sendAccount.get().getCoin().equals(MinterSDK.DEFAULT_COIN) && !enoughB final SecretData preData = secretStorage.getSecret(mFromAccount.address); final TransactionSign preSign = preTx.signSingle(preData.getPrivateKey()); - exchangeResolver = rxExpGate(cachedTxRepo.getEntity().getTransactionCommission(preSign)).onErrorResumeNext(toExpGateError()); + exchangeResolver = rxGate(estimateRepo.getTransactionCommission(preSign)).onErrorResumeNext(toGateError()); } catch (OperationInvalidDataException e) { Timber.w(e); - final BCExplorerResult commissionValue = new BCExplorerResult<>(); + final GateResult commissionValue = new GateResult<>(); val.result.value = OperationType.SendCoin.getFee().multiply(Transaction.VALUE_MUL_DEC).toBigInteger(); exchangeResolver = Observable.just(commissionValue); } @@ -497,16 +500,16 @@ else if (!sendAccount.get().getCoin().equals(MinterSDK.DEFAULT_COIN) && !enoughB // creating preparation result to send transaction Disposable d = Observable.combineLatest( exchangeResolver, - rxExpGate(cachedTxRepo.getEntity().getTransactionCount(mFromAccount.address)).onErrorResumeNext(toExpGateError()), - new BiFunction, BCExplorerResult, SendInitData>() { + rxGate(estimateRepo.getTransactionCount(mFromAccount.address)).onErrorResumeNext(toGateError()), + new BiFunction, GateResult, SendInitData>() { @Override - public SendInitData apply(BCExplorerResult txCommissionValue, BCExplorerResult countableDataBCResult) { + public SendInitData apply(GateResult txCommissionValue, GateResult countableDataBCResult) { // if some request failed, returning error result - if (!txCommissionValue.isSuccess()) { - return new SendInitData(BCExplorerResult.copyError(txCommissionValue)); - } else if (!countableDataBCResult.isSuccess()) { - return new SendInitData(BCExplorerResult.copyError(countableDataBCResult)); + if (!txCommissionValue.isOk()) { + return new SendInitData(GateResult.copyError(txCommissionValue)); + } else if (!countableDataBCResult.isOk()) { + return new SendInitData(GateResult.copyError(countableDataBCResult)); } Timber.d("SendInitData: tx commission: %s", txCommissionValue.result.getValue()); @@ -521,10 +524,10 @@ public SendInitData apply(BCExplorerResult txCommiss return data; } }) - .switchMap((Function>>) cntRes -> { + .switchMap((Function>>) cntRes -> { // if in previous request we've got error, returning it if (!cntRes.isSuccess()) { - return Observable.just(BCExplorerResult.copyError(cntRes.errorResult)); + return Observable.just(GateResult.copyError(cntRes.errorResult)); } final BigDecimal amountToSend; @@ -548,19 +551,19 @@ public SendInitData apply(BCExplorerResult txCommiss // if after subtracting fee from sending sum has become less than account balance at all, returning error with message "insufficient funds" if (bdLT(amountToSend, 0)) { - BCExplorerResult errorRes; + GateResult errorRes; final BigDecimal balanceMustBe = cntRes.commission.add(mAmount); if (bdLT(mAmount, mFromAccount.getBalance())) { final BigDecimal notEnough = cntRes.commission.subtract(mFromAccount.getBalance().subtract(mAmount)); Timber.d("Amount: %s, fromAcc: %s, diff: %s", bdHuman(mAmount), bdHuman(mFromAccount.getBalance()), bdHuman(notEnough)); - errorRes = createExpGateErrorPlain( + errorRes = createGateErrorPlain( String.format("Insufficient funds: not enough %s %s, wanted: %s %s", bdHuman(notEnough), mFromAccount.getCoin(), bdHuman(balanceMustBe), mFromAccount.getCoin()), BCResult.ResultCode.InsufficientFunds.getValue(), 400 ); } else { Timber.d("Amount: %s, fromAcc: %s, diff: %s", bdHuman(mAmount), bdHuman(mFromAccount.getBalance()), bdHuman(balanceMustBe)); - errorRes = createExpGateErrorPlain( + errorRes = createGateErrorPlain( String.format("Insufficient funds: wanted %s %s", bdHuman(balanceMustBe), mFromAccount.getCoin()), BCResult.ResultCode.InsufficientFunds.getValue(), 400 @@ -580,6 +583,7 @@ public SendInitData apply(BCExplorerResult txCommiss // creating tx final Transaction tx = new Transaction.Builder(cntRes.nonce) .setGasCoin(mGasCoin) + .setGasPrice(mGasPrice) .sendCoin() .setCoin(mFromAccount.coin) .setTo(mToAddress) @@ -590,8 +594,8 @@ public SendInitData apply(BCExplorerResult txCommiss final TransactionSign sign = tx.signSingle(data.getPrivateKey()); return safeSubscribeIoToUi( - rxExpGate(cachedTxRepo.getEntity().sendTransaction(sign)) - .onErrorResumeNext(toExpGateError()) + rxGate(gateTxRepo.sendTransaction(sign)) + .onErrorResumeNext(toGateError()) ); }) @@ -624,7 +628,7 @@ private void onErrorSearchUser(ProfileResult errorResult) { .create()); } - private void onErrorExecuteTransaction(BCExplorerResult errorResult) { + private void onErrorExecuteTransaction(GateResult errorResult) { Timber.e(errorResult.getMessage(), "Unable to send transaction"); getViewState().startDialog(ctx -> new WalletConfirmDialog.Builder(ctx, "Unable to send transaction") .setText((errorResult.getMessage())) @@ -632,8 +636,8 @@ private void onErrorExecuteTransaction(BCExplorerResult errorResult) { .create()); } - private void onSuccessExecuteTransaction(final BCExplorerResult result) { - if (!result.isSuccess()) { + private void onSuccessExecuteTransaction(final GateResult result) { + if (!result.isOk()) { onErrorExecuteTransaction(result); return; } @@ -692,19 +696,19 @@ private void onAccountSelected(AccountItem accountItem) { private static final class SendInitData { BigInteger nonce; BigDecimal commission; - BCExplorerResult errorResult; + GateResult errorResult; SendInitData(BigInteger nonce, BigDecimal commission) { this.nonce = nonce; this.commission = commission; } - SendInitData(BCExplorerResult err) { + SendInitData(GateResult err) { errorResult = err; } boolean isSuccess() { - return errorResult == null || errorResult.isSuccess(); + return errorResult == null || errorResult.isOk(); } } diff --git a/app/src/main/java/network/minter/bipwallet/services/livebalance/BalanceUpdateService.java b/app/src/main/java/network/minter/bipwallet/services/livebalance/LiveBalanceService.java similarity index 82% rename from app/src/main/java/network/minter/bipwallet/services/livebalance/BalanceUpdateService.java rename to app/src/main/java/network/minter/bipwallet/services/livebalance/LiveBalanceService.java index 5dea81af..00df5936 100644 --- a/app/src/main/java/network/minter/bipwallet/services/livebalance/BalanceUpdateService.java +++ b/app/src/main/java/network/minter/bipwallet/services/livebalance/LiveBalanceService.java @@ -38,6 +38,8 @@ import centrifuge.Client; import centrifuge.ConnectEvent; import centrifuge.ConnectHandler; +import centrifuge.DisconnectEvent; +import centrifuge.DisconnectHandler; import centrifuge.ErrorEvent; import centrifuge.ErrorHandler; import centrifuge.MessageEvent; @@ -50,13 +52,14 @@ import network.minter.bipwallet.advanced.repo.SecretStorage; import network.minter.bipwallet.internal.auth.AuthSession; import network.minter.bipwallet.internal.data.CacheManager; +import network.minter.core.crypto.MinterAddress; import timber.log.Timber; /** * minter-android-wallet. 2018 * @author Eduard Maximovich */ -public class BalanceUpdateService extends Service { +public class LiveBalanceService extends Service { private final IBinder mBinder = new LocalBinder(); @Inject CacheManager cache; @@ -64,16 +67,17 @@ public class BalanceUpdateService extends Service { @Inject AuthSession session; private CompositeDisposable mCompositeDisposable = new CompositeDisposable(); private OnMessageListener mOnMessageListener; + private Client mClient = null; + private MinterAddress mAddress; private final PublishHandler mListener = new PublishHandler() { @Override public void onPublish(Subscription subscription, PublishEvent publishEvent) { Timber.d("OnPublish: sub=%s, ev=%s", subscription.channel(), publishEvent.toString()); if (mOnMessageListener != null) { - mOnMessageListener.onMessage(new String(publishEvent.getData()), subscription.channel()); + mOnMessageListener.onMessage(new String(publishEvent.getData()), subscription.channel(), mAddress); } } }; - private Client mClient = null; @Override public void onLowMemory() { @@ -138,7 +142,8 @@ public Client getClient() { private void connect() { try { - mClient = Centrifuge.new_("wss://rtm.explorer.minter.network/connection/websocket", Centrifuge.defaultConfig()); + mClient = Centrifuge.new_("wss://explorer-rtm.testnet.minter.network/connection/websocket", Centrifuge.defaultConfig()); +// mClient = Centrifuge.new_("wss://rtm.explorer.minter.network/connection/websocket", Centrifuge.defaultConfig()); mClient.onConnect(new ConnectHandler() { @Override public void onConnect(Client client, ConnectEvent connectEvent) { @@ -146,10 +151,16 @@ public void onConnect(Client client, ConnectEvent connectEvent) { } }); + mClient.onDisconnect(new DisconnectHandler() { + @Override + public void onDisconnect(Client client, DisconnectEvent disconnectEvent) { + Timber.w("Disconnected"); + } + }); mClient.onError(new ErrorHandler() { @Override public void onError(Client client, ErrorEvent errorEvent) { - Timber.d("OnError[%d]: %s", errorEvent.incRefnum(), errorEvent.getMessage()); + Timber.w("OnError[%d]: %s", errorEvent.incRefnum(), errorEvent.getMessage()); } }); @@ -161,7 +172,8 @@ public void onMessage(Client p0, MessageEvent p1) { }); mClient.connect(); - Subscription sub = mClient.newSubscription(secretStorage.getAddresses().get(0).toString()); + mAddress = secretStorage.getAddresses().get(0); + Subscription sub = mClient.newSubscription(mAddress.toString()); sub.onPublish(mListener); sub.subscribe(); } catch (Throwable t) { @@ -179,12 +191,12 @@ private void disconnect() { } public interface OnMessageListener { - void onMessage(String message, String channel); + void onMessage(String message, String channel, MinterAddress address); } public final class LocalBinder extends Binder { - public BalanceUpdateService getService() { - return BalanceUpdateService.this; + public LiveBalanceService getService() { + return LiveBalanceService.this; } } } diff --git a/app/src/main/java/network/minter/bipwallet/services/livebalance/ServiceConnector.java b/app/src/main/java/network/minter/bipwallet/services/livebalance/ServiceConnector.java index 719817a3..33c0312d 100644 --- a/app/src/main/java/network/minter/bipwallet/services/livebalance/ServiceConnector.java +++ b/app/src/main/java/network/minter/bipwallet/services/livebalance/ServiceConnector.java @@ -42,15 +42,15 @@ * @author Eduard Maximovich */ public class ServiceConnector implements ServiceConnection { - private static BalanceUpdateService service = null; + private static LiveBalanceService service = null; private static ServiceConnector instance; private static boolean bound = false; - private static ReplaySubject serviceConnected = ReplaySubject.create(1); + private static ReplaySubject serviceConnected = ReplaySubject.create(1); private ServiceConnector() { } - public static BalanceUpdateService get() { + public static LiveBalanceService get() { return service; } @@ -65,7 +65,7 @@ public static void bind(Context context) { try { if (!bound) { - Intent intent = new Intent(context, BalanceUpdateService.class); + Intent intent = new Intent(context, LiveBalanceService.class); context.startService(intent); context.bindService(intent, instance, Context.BIND_AUTO_CREATE); } @@ -86,7 +86,7 @@ public static void release(Context context) { Timber.i(e); } - context.stopService(new Intent(context, BalanceUpdateService.class)); + context.stopService(new Intent(context, LiveBalanceService.class)); service = null; } bound = false; @@ -95,7 +95,7 @@ public static void release(Context context) { } } - public static Observable onConnected() { + public static Observable onConnected() { if (service != null) { return Observable.just(service); } @@ -106,7 +106,7 @@ public static Observable onConnected() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { if (!bound) { - service = ((BalanceUpdateService.LocalBinder) binder).getService(); + service = ((LiveBalanceService.LocalBinder) binder).getService(); bound = true; serviceConnected.onNext(service); Timber.i("Service connected"); diff --git a/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/BalanceNotificationManager.java b/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/BalanceNotificationManager.java index 09dce694..f78c2b66 100644 --- a/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/BalanceNotificationManager.java +++ b/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/BalanceNotificationManager.java @@ -39,9 +39,12 @@ import io.reactivex.schedulers.Schedulers; import network.minter.bipwallet.R; import network.minter.bipwallet.external.ui.ExternalActivity; +import network.minter.bipwallet.internal.common.Lazy; import network.minter.bipwallet.internal.helpers.NetworkHelper; import network.minter.bipwallet.internal.notifications.BaseNotificationManager; +import network.minter.bipwallet.internal.settings.SettingsManager; import network.minter.bipwallet.tx.ui.TransactionListActivity; +import network.minter.core.crypto.MinterAddress; import timber.log.Timber; import static android.os.Build.VERSION.SDK_INT; @@ -60,23 +63,83 @@ public final class BalanceNotificationManager extends BaseNotificationManager { private final GsonBuilder mGsonBuilder; private final NetworkHelper mNetwork; private final Context mContext; + private final Lazy mEnabledNotifications; - public BalanceNotificationManager(Context context, GsonBuilder gsonBuilder, NetworkHelper network) { + public BalanceNotificationManager(Context context, GsonBuilder gsonBuilder, NetworkHelper network, SettingsManager prefs) { mContext = context; mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); mGsonBuilder = gsonBuilder; mNetwork = network; + mEnabledNotifications = () -> prefs.getBool(SettingsManager.EnableLiveNotifications); } - public void showBalanceUpdate(String json) { - LiveBalanceMessage message; - try { - message = mGsonBuilder.create().fromJson(json, LiveBalanceMessage.class); - } catch (Throwable t) { - Timber.e(t, "Unable to decode live balance message: %s", json); + public void showBalanceUpdate(String json, MinterAddress address) { + if (!mEnabledNotifications.get()) { return; } + Timber.d("Balance update message: %s", json); + + showBalanceUpdated(address); + +// List message; +// try { +// message = mGsonBuilder.create().fromJson(json, new TypeToken>(){}.getType()); +// } catch (Throwable t) { +// Timber.e(t, "Unable to decode live balance message: %s", json); +// return; +// } +// +// for(LiveBalanceMessage msg: message) { +// showBalanceForCoin(msg, address); +// } + + + } + + private void showBalanceUpdated(MinterAddress address) { + if (SDK_INT >= android.os.Build.VERSION_CODES.O) { + final String channelName = mContext.getResources().getString(R.string.notification_balance_update_name); + final String channelDescription = mContext.getResources().getString(R.string.notification_balance_update_description); + NotificationChannel channel = new NotificationChannel(WALLET_BALANCE_UPDATE_CHANNEL, channelName, NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(channelDescription); + channel.setShowBadge(true); + mNotificationManager.createNotificationChannel(channel); + } + + Intent contentIntent = ExternalActivity.createAction(mContext, ExternalActivity.ACTION_OPEN_HOME, null); + + final String listTxLabel = mContext.getResources().getString(R.string.notification_balance_list_tx_label); + final Intent listTxIntent = new Intent(mContext, TransactionListActivity.class); + NotificationCompat.Action listTxAction = + new NotificationCompat.Action.Builder( + R.drawable.ic_notify_list_tx, + listTxLabel, + createActionIntent(mContext, listTxIntent) + ).build(); + + NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); + style.addLine("Status: updated").setSummaryText(address.toShortString()); + + final Notification messageNotification = new NotificationCompat.Builder(mContext, WALLET_BALANCE_UPDATE_CHANNEL) + .setContentTitle("Balance") + .setContentText("Status: updated") + .setSmallIcon(R.drawable.ic_notify_coin) + .setColor(mContext.getResources().getColor(R.color.colorPrimaryDark)) + .setAutoCancel(true) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setStyle(style) + .setGroup(address.toString()) + .setContentIntent(createActionIntent(mContext, contentIntent)) + .addAction(listTxAction) + .setNumber(1) + .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL) + .build(); + + mNotificationManager.notify(WALLET_BALANCE_ID, messageNotification); + } + + private void showBalanceForCoin(LiveBalanceMessage message, MinterAddress address) { if (SDK_INT >= android.os.Build.VERSION_CODES.O) { final String channelName = mContext.getResources().getString(R.string.notification_balance_update_name); final String channelDescription = mContext.getResources().getString(R.string.notification_balance_update_description); @@ -105,7 +168,7 @@ public void showBalanceUpdate(String json) { final String text = String.format("%s: %s", message.getCoin(), bdHuman(message.getAmount())); NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - style.addLine(text).setSummaryText(message.getAddress().toShortString()); + style.addLine(text).setSummaryText(address.toShortString()); final Notification messageNotification = new NotificationCompat.Builder(mContext, WALLET_BALANCE_UPDATE_CHANNEL) .setContentTitle("Balance updated") @@ -116,7 +179,7 @@ public void showBalanceUpdate(String json) { .setAutoCancel(true) .setCategory(NotificationCompat.CATEGORY_STATUS) .setStyle(style) - .setGroup(message.getAddress().toString() + "_" + message.getCoin()) + .setGroup(address.toString() + "_" + message.getCoin()) .setContentIntent(createActionIntent(mContext, contentIntent)) .addAction(listTxAction) .setNumber(1) diff --git a/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/LiveBalanceMessage.java b/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/LiveBalanceMessage.java index ba7a2512..182bb27f 100644 --- a/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/LiveBalanceMessage.java +++ b/app/src/main/java/network/minter/bipwallet/services/livebalance/notification/LiveBalanceMessage.java @@ -28,22 +28,20 @@ import java.math.BigDecimal; -import network.minter.core.crypto.MinterAddress; import network.minter.profile.MinterProfileApi; /** * minter-android-wallet. 2018 - * * @author Eduard Maximovich */ public class LiveBalanceMessage { - public MinterAddress address; + // public MinterAddress address; public String coin; public BigDecimal amount; - public MinterAddress getAddress() { - return address; - } +// public MinterAddress getAddress() { +// return address; +// } public String getCoin() { return coin.toUpperCase(); diff --git a/app/src/main/java/network/minter/bipwallet/settings/views/SettingsTabPresenter.java b/app/src/main/java/network/minter/bipwallet/settings/views/SettingsTabPresenter.java index b5efc6ba..519b032f 100644 --- a/app/src/main/java/network/minter/bipwallet/settings/views/SettingsTabPresenter.java +++ b/app/src/main/java/network/minter/bipwallet/settings/views/SettingsTabPresenter.java @@ -65,6 +65,7 @@ import network.minter.bipwallet.internal.helpers.forms.validators.MinterUsernameValidator; import network.minter.bipwallet.internal.helpers.forms.validators.PhoneValidator; import network.minter.bipwallet.internal.mvp.MvpBasePresenter; +import network.minter.bipwallet.internal.settings.SettingsManager; import network.minter.bipwallet.internal.views.list.multirow.MultiRowAdapter; import network.minter.bipwallet.settings.SettingsTabModule; import network.minter.bipwallet.settings.repo.CachedMyProfileRepository; @@ -185,6 +186,8 @@ public void onLogout() { getViewState().startLogin(); } + @Inject SettingsManager settings; + @Override protected void onFirstViewAttach() { super.onFirstViewAttach(); @@ -199,12 +202,18 @@ protected void onFirstViewAttach() { mAdditionalAdapter.addRow(new SettingsButtonRow("My Addresses", "Manage", (view, sharedView, value) -> onClickAddresses()).setInactive(true)); mAdditionalAdapter.addRow(new SettingsSwitchRow("Enable sounds", () -> prefs.getBoolean(PrefKeys.ENABLE_SOUNDS, true), this::onSwitchSounds)); + mAdditionalAdapter.addRow(new SettingsSwitchRow("Enable notifications", () -> settings.getBool(SettingsManager.EnableLiveNotifications), this::onSwitchNotifications)); } else { mMainAdapter.addRow(new SettingsButtonRow("My Addresses", "Manage", (view, sharedView, value) -> onClickAddresses()).setInactive(true)); mMainAdapter.addRow(new SettingsSwitchRow("Enable sounds", () -> prefs.getBoolean(PrefKeys.ENABLE_SOUNDS, true), this::onSwitchSounds)); + mMainAdapter.addRow(new SettingsSwitchRow("Enable notifications", () -> settings.getBool(SettingsManager.EnableLiveNotifications), this::onSwitchNotifications)); } } + private void onSwitchNotifications(View view, Boolean enabled) { + settings.putBool(SettingsManager.EnableLiveNotifications, enabled); + } + private void onSwitchSounds(View view, Boolean isChecked) { prefs.edit().putBoolean(PrefKeys.ENABLE_SOUNDS, isChecked).apply(); if (isChecked) { diff --git a/app/src/main/java/network/minter/bipwallet/tx/adapters/TransactionItem.java b/app/src/main/java/network/minter/bipwallet/tx/adapters/TransactionItem.java index 6e0ebbd8..3c134329 100644 --- a/app/src/main/java/network/minter/bipwallet/tx/adapters/TransactionItem.java +++ b/app/src/main/java/network/minter/bipwallet/tx/adapters/TransactionItem.java @@ -51,6 +51,7 @@ public interface TransactionItem { int TX_REDEEM_CHECK = 9; int TX_SET_CANDIDATE_ONLINE = 10; int TX_SET_CANDIDATE_OFFLINE = 11; + int TX_MULTISEND = 13; @ListType diff --git a/app/src/main/java/network/minter/bipwallet/tx/adapters/TxItem.java b/app/src/main/java/network/minter/bipwallet/tx/adapters/TxItem.java index a27e5169..420b8ff0 100644 --- a/app/src/main/java/network/minter/bipwallet/tx/adapters/TxItem.java +++ b/app/src/main/java/network/minter/bipwallet/tx/adapters/TxItem.java @@ -47,6 +47,7 @@ import network.minter.core.crypto.MinterAddress; import network.minter.explorer.models.HistoryTransaction; import network.minter.profile.MinterProfileApi; +import timber.log.Timber; import static network.minter.bipwallet.internal.common.Preconditions.firstNonNull; @@ -61,12 +62,12 @@ public class TxItem implements TransactionItem { public TxItem(HistoryTransaction tx) { mTx = tx; - if (tx.getAvatar() == null && tx.data instanceof HistoryTransaction.TxSendCoinResult) { - mAvatar = MinterProfileApi.getUserAvatarUrlByAddress(tx.getData().to); - } else { - mAvatar = firstNonNull(tx.getAvatar(), MinterProfileApi.getUserAvatarUrl(1)); - } - +// if (tx.getAvatar() == null && tx.data instanceof HistoryTransaction.TxSendCoinResult) { +// mAvatar = MinterProfileApi.getUserAvatarUrlByAddress(tx.getData().to); +// } else { +// mAvatar = firstNonNull(tx.getAvatar(), MinterProfileApi.getUserAvatarUrl(1)); +// } + mAvatar = MinterProfileApi.getUserAvatarUrl(1); mUsername = tx.username; } diff --git a/app/src/main/java/network/minter/bipwallet/tx/adapters/vh/TxMultiSendCoinViewHolder.java b/app/src/main/java/network/minter/bipwallet/tx/adapters/vh/TxMultiSendCoinViewHolder.java new file mode 100644 index 00000000..e3a69741 --- /dev/null +++ b/app/src/main/java/network/minter/bipwallet/tx/adapters/vh/TxMultiSendCoinViewHolder.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) by MinterTeam. 2018 + * @link Org Github + * @link Maintainer Github + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package network.minter.bipwallet.tx.adapters.vh; + +import android.view.View; +import android.widget.TextView; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import network.minter.bipwallet.R; +import network.minter.bipwallet.internal.Wallet; +import network.minter.bipwallet.tx.adapters.TxItem; +import network.minter.core.crypto.MinterAddress; +import network.minter.explorer.models.HistoryTransaction; + +import static network.minter.bipwallet.internal.helpers.MathHelper.bdHuman; +import static network.minter.bipwallet.internal.helpers.MathHelper.bdNull; + +/** + * minter-android-wallet. 2018 + * @author Eduard Maximovich + */ +public final class TxMultiSendCoinViewHolder extends ExpandableTxViewHolder { + public @BindView(R.id.detail_from_value) TextView fromValue; + public @BindView(R.id.detail_to_value) TextView toValue; + public @BindView(R.id.detail_coin_value) TextView coinValue; + public @BindView(R.id.detail_amount_value) TextView amountValue; + + public TxMultiSendCoinViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + public void bind(TxItem txItem, List myAddresses) { + super.bind(txItem); + + final HistoryTransaction item = txItem.getTx(); + final HistoryTransaction.TxSendCoinResult data = item.getData(); + + + final boolean isIncoming = item.isIncoming(myAddresses); + final boolean isSelfSending = item.from.equals(data.to); + + if (isSelfSending) { + if (txItem.getUsername() != null) { + title.setText(String.format("@%s", txItem.getUsername())); + } else { + title.setText(item.getFrom().toShortString()); + } + + amount.setText(bdHuman(data.amount)); + amount.setTextColor(Wallet.app().res().getColor(R.color.textColorPrimary)); + } else { + if (isIncoming) { + if (txItem.getUsername() != null) { + title.setText(String.format("@%s", txItem.getUsername())); + } else { + title.setText(item.getFrom().toShortString()); + } + + amount.setText(String.format("+ %s", bdHuman(data.amount))); + amount.setTextColor(Wallet.app().res().getColor(R.color.textColorGreen)); + } else { + if (txItem.getUsername() != null) { + title.setText(String.format("@%s", txItem.getUsername())); + } else { + title.setText(data.to.toShortString()); + } + + amount.setText(String.format("- %s", bdHuman(data.amount))); + amount.setTextColor(Wallet.app().res().getColor(R.color.textColorPrimary)); + } + } + + if (bdNull(data.amount)) { + amount.setText(bdHuman(data.amount)); + amount.setTextColor(Wallet.app().res().getColor(R.color.textColorPrimary)); + } + + fromValue.setText(item.getFrom().toString()); + toValue.setText(data.to.toString()); + subamount.setText(data.getCoin()); + coinValue.setText(data.getCoin()); + amountValue.setText(bdHuman(data.amount)); + } +} diff --git a/app/src/main/res/layout/activity_advanced_generate.xml b/app/src/main/res/layout/activity_advanced_generate.xml index 88ab6c56..70353ccc 100644 --- a/app/src/main/res/layout/activity_advanced_generate.xml +++ b/app/src/main/res/layout/activity_advanced_generate.xml @@ -54,7 +54,7 @@ android:theme="@style/WalletPrimaryToolbar" app:layout_collapseMode="none" app:navigationIcon="@drawable/ic_arrow_back_white_compat" - app:title="@string/title_advanced_generate" + app:title="@string/title_register" app:titleTextColor="@android:color/white" /> diff --git a/app/src/main/res/layout/activity_advanced_main.xml b/app/src/main/res/layout/activity_advanced_main.xml index ae4fa818..e5f626ae 100644 --- a/app/src/main/res/layout/activity_advanced_main.xml +++ b/app/src/main/res/layout/activity_advanced_main.xml @@ -54,7 +54,7 @@ android:theme="@style/WalletPrimaryToolbar" app:layout_collapseMode="none" app:navigationIcon="@drawable/ic_arrow_back_white_compat" - app:title="@string/title_advanced_main" + app:title="@string/title_signin" app:titleTextColor="@android:color/white" /> @@ -74,45 +74,6 @@ android:layout_height="wrap_content" > - - - - - - - - - - + app:cardPreventCornerOverlap="false"> - + android:orientation="vertical"> + tools:text="@tools:sample/cities" /> - - + tools:itemCount="15" + tools:listitem="@layout/item_list_dialog_account_selector" /> + \ No newline at end of file diff --git a/build.gradle b/build.gradle index de6cbc25..57659899 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ buildscript { maven { url 'https://plugins.gradle.org/m2/' } } dependencies { - classpath 'com.android.tools.build:gradle:3.3.1' + classpath 'com.android.tools.build:gradle:3.3.2' classpath 'io.fabric.tools:gradle:1.25.4' classpath 'com.google.gms:google-services:4.2.0' classpath 'gradle.plugin.firebase.test.lab:plugin:1.1.2' diff --git a/remuser b/remuser deleted file mode 100755 index e4eb40c1..00000000 --- a/remuser +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -curl -X GET "https://my.beta.minter.network/api/v1/service/delete/by/username/$1"