soralit
5 years ago
committed by
GitHub
108 changed files with 3906 additions and 1774 deletions
@ -1,3 +1,3 @@ |
|||
{ |
|||
"BTC": "BTC.bundle_94e3c1790748ac7fc876.js" |
|||
"BTC": "BTC.bundle_a521a1b83cf8627d0ce5.js" |
|||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,44 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.callables; |
|||
|
|||
import com.cobo.cold.encryption.interfaces.CONSTANTS; |
|||
import com.cobo.cold.encryptioncore.base.Packet; |
|||
import com.cobo.cold.encryptioncore.base.Payload; |
|||
|
|||
import java.util.concurrent.Callable; |
|||
|
|||
public class GetMasterFingerprintCallable implements Callable<String> { |
|||
@Override |
|||
public String call() { |
|||
try { |
|||
final Callable<Packet> callable = new BlockingCallable( |
|||
new Packet.Builder(CONSTANTS.METHODS.GET_MASTER_FINGERPRINT).build()); |
|||
final Packet result = callable.call(); |
|||
final Payload payload = result.getPayload(CONSTANTS.TAGS.MASTER_FINGERPRINT); |
|||
|
|||
if (payload != null) { |
|||
return payload.toHex(); |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
|
|||
return ""; |
|||
} |
|||
} |
@ -1,79 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.databinding.DataBindingUtil; |
|||
import androidx.fragment.app.DialogFragment; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.PickerDialogBinding; |
|||
|
|||
import java.util.Objects; |
|||
import java.util.stream.IntStream; |
|||
|
|||
public class AddressNumberPicker extends DialogFragment { |
|||
|
|||
private NumberPickerCallback mCallback; |
|||
private static final int MIN = 0; |
|||
private static final int MAX = 8; |
|||
|
|||
public AddressNumberPicker(){ |
|||
|
|||
} |
|||
|
|||
public void setCallback(NumberPickerCallback mCallback) { |
|||
this.mCallback = mCallback; |
|||
} |
|||
|
|||
@Nullable |
|||
@Override |
|||
public View onCreateView(@NonNull LayoutInflater inflater, |
|||
@Nullable ViewGroup container, |
|||
@Nullable Bundle savedInstanceState) { |
|||
|
|||
Objects.requireNonNull(getDialog()).requestWindowFeature(STYLE_NO_TITLE); |
|||
|
|||
PickerDialogBinding binding = DataBindingUtil.inflate(inflater, R.layout.picker_dialog, |
|||
container, false); |
|||
String[] displayValue = IntStream.range(MIN, MAX + 1) |
|||
.mapToObj(i -> String.valueOf(i + 1)) |
|||
.toArray(String[]::new); |
|||
binding.setValue(1); |
|||
binding.picker.setValue(0); |
|||
binding.picker.setDisplayedValues(displayValue); |
|||
binding.picker.setMinValue(MIN); |
|||
binding.picker.setMaxValue(MAX); |
|||
binding.picker.setOnValueChangedListener((picker, oldVal, newVal) -> binding.setValue(newVal + 1)); |
|||
binding.confirm.setOnClickListener(v -> { |
|||
dismiss(); |
|||
if (mCallback != null) { |
|||
mCallback.onValueSet(binding.picker.getValue() + 1); |
|||
} |
|||
}); |
|||
binding.cancel.setOnClickListener(v -> dismiss()); |
|||
|
|||
return binding.getRoot(); |
|||
} |
|||
} |
@ -1,153 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.content.Intent; |
|||
import android.net.Uri; |
|||
import android.os.Bundle; |
|||
import android.provider.Settings; |
|||
import android.view.Menu; |
|||
import android.view.MenuInflater; |
|||
import android.view.MenuItem; |
|||
import android.view.View; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.ViewModelProviders; |
|||
|
|||
import com.cobo.cold.AppExecutors; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.AssetListFragmentBinding; |
|||
import com.cobo.cold.db.PresetData; |
|||
import com.cobo.cold.db.entity.CoinEntity; |
|||
import com.cobo.cold.ui.MainActivity; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.viewmodel.CoinListViewModel; |
|||
import com.cobo.cold.viewmodel.SetupVaultViewModel; |
|||
import com.yanzhenjie.permission.AndPermission; |
|||
import com.yanzhenjie.permission.Permission; |
|||
|
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_COIN_CODE; |
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_COIN_ID; |
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_ID; |
|||
import static com.cobo.cold.viewmodel.CoinListViewModel.coinEntityComparator; |
|||
|
|||
public class AssetListFragment extends BaseFragment<AssetListFragmentBinding> { |
|||
|
|||
public static final String TAG = "AssetListFragment"; |
|||
|
|||
private long startTime; |
|||
public static final int REQUEST_CODE_SCAN = 1000; |
|||
private CoinAdapter mCoinAdapter; |
|||
|
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.asset_list_fragment; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
mActivity.setSupportActionBar(mBinding.toolbar); |
|||
mBinding.toolbar.setNavigationOnClickListener(((MainActivity) mActivity)::toggleDrawer); |
|||
mBinding.toolbar.setTitle(""); |
|||
mCoinAdapter = new CoinAdapter(mActivity, mCoinClickCallback, false); |
|||
mBinding.assetList.setAdapter(mCoinAdapter); |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
CoinListViewModel mViewModel = ViewModelProviders.of(mActivity).get(CoinListViewModel.class); |
|||
subscribeUi(mViewModel.getCoins()); |
|||
checkAndAddNewCoins(); |
|||
} |
|||
|
|||
private void checkAndAddNewCoins() { |
|||
SetupVaultViewModel viewModel = ViewModelProviders.of(mActivity) |
|||
.get(SetupVaultViewModel.class); |
|||
AppExecutors.getInstance().diskIO().execute(() |
|||
-> viewModel.presetData(PresetData.generateCoins(mActivity), null) |
|||
); |
|||
|
|||
} |
|||
|
|||
private void subscribeUi(LiveData<List<CoinEntity>> coins) { |
|||
coins.observe(this, coinEntities -> { |
|||
if (coinEntities != null) { |
|||
List<CoinEntity> toShow = coinEntities |
|||
.stream() |
|||
.filter(CoinEntity::isShow) |
|||
.collect(Collectors.toList()); |
|||
if (toShow.isEmpty()) { |
|||
mBinding.setIsEmpty(true); |
|||
} else { |
|||
mBinding.setIsEmpty(false); |
|||
toShow.sort(coinEntityComparator); |
|||
mCoinAdapter.setItems(toShow); |
|||
} |
|||
|
|||
} else { |
|||
mBinding.setIsEmpty(true); |
|||
} |
|||
mBinding.executePendingBindings(); |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { |
|||
inflater.inflate(R.menu.main, menu); |
|||
super.onCreateOptionsMenu(menu, inflater); |
|||
} |
|||
|
|||
@Override |
|||
public boolean onOptionsItemSelected(MenuItem item) { |
|||
int id = item.getItemId(); |
|||
if (id == R.id.action_scan) { |
|||
AndPermission.with(this) |
|||
.permission(Permission.CAMERA, Permission.READ_EXTERNAL_STORAGE) |
|||
.onGranted(permissions -> { |
|||
startTime = System.currentTimeMillis(); |
|||
Bundle bundle = new Bundle(); |
|||
bundle.putLong("starttime", startTime); |
|||
navigate(R.id.action_to_scan, bundle); |
|||
}) |
|||
.onDenied(permissions -> { |
|||
Uri packageURI = Uri.parse("package:" + mActivity.getPackageName()); |
|||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); |
|||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
|||
startActivity(intent); |
|||
Toast.makeText(mActivity, getString(R.string.scan_permission_denied), Toast.LENGTH_LONG).show(); |
|||
}).start(); |
|||
return true; |
|||
} |
|||
|
|||
return super.onOptionsItemSelected(item); |
|||
} |
|||
|
|||
private final CoinClickCallback mCoinClickCallback = coin -> { |
|||
Bundle data = new Bundle(); |
|||
data.putLong(KEY_ID, coin.getId()); |
|||
data.putString(KEY_COIN_ID, coin.getCoinId()); |
|||
data.putString(KEY_COIN_CODE, coin.getCoinCode()); |
|||
navigate(R.id.action_to_assetFragment, data); |
|||
}; |
|||
} |
@ -0,0 +1,130 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
|
|||
import androidx.databinding.DataBindingUtil; |
|||
|
|||
import com.cobo.coinlib.Util; |
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.ExportSdcardModalBinding; |
|||
import com.cobo.cold.databinding.ExportXpubBinding; |
|||
import com.cobo.cold.ui.MainActivity; |
|||
import com.cobo.cold.ui.SetupVaultActivity; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.ui.modal.ModalDialog; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
import com.cobo.cold.viewmodel.GlobalViewModel; |
|||
|
|||
import org.json.JSONException; |
|||
import org.json.JSONObject; |
|||
|
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.exportSuccess; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.getAccount; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.showNoSdcardModal; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.writeToSdcard; |
|||
|
|||
|
|||
public class ExportGenericXpubFragment extends BaseFragment<ExportXpubBinding> { |
|||
|
|||
private JSONObject xpubInfo; |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.export_xpub; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); |
|||
try { |
|||
xpubInfo = GlobalViewModel.getXpubInfo(mActivity); |
|||
String exPub = xpubInfo.getString("ExtPubKey"); |
|||
exPub = convertExtpub(exPub, getAccount(mActivity)); |
|||
xpubInfo.put("ExtPubKey", exPub); |
|||
mBinding.qrcode.setData(xpubInfo.toString()); |
|||
mBinding.expub.setText(exPub); |
|||
} catch (JSONException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
mBinding.addressType.setText(getString(R.string.master_xpub, |
|||
GlobalViewModel.getAddressFormat(mActivity))); |
|||
mBinding.done.setOnClickListener(v -> { |
|||
if (mActivity instanceof SetupVaultActivity) { |
|||
navigate(R.id.action_to_setupCompleteFragment); |
|||
} else { |
|||
MainActivity activity = (MainActivity) mActivity; |
|||
activity.getNavController().popBackStack(R.id.assetFragment, false); |
|||
} |
|||
}); |
|||
mBinding.exportToSdcard.setOnClickListener(v -> { |
|||
Storage storage = Storage.createByEnvironment(mActivity); |
|||
if (storage == null || storage.getExternalDir() == null) { |
|||
showNoSdcardModal(mActivity); |
|||
} else { |
|||
ModalDialog modalDialog = ModalDialog.newInstance(); |
|||
ExportSdcardModalBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mActivity), |
|||
R.layout.export_sdcard_modal, null, false); |
|||
binding.title.setText(R.string.export_xpub_text_file); |
|||
binding.fileName.setText(getFileName()); |
|||
binding.actionHint.setText(R.string.electrum_import_xpub_action); |
|||
binding.cancel.setOnClickListener(vv -> modalDialog.dismiss()); |
|||
binding.confirm.setOnClickListener(vv -> { |
|||
modalDialog.dismiss(); |
|||
if (writeToSdcard(storage, xpubInfo.toString(), getFileName())) { |
|||
exportSuccess(mActivity, null); |
|||
} |
|||
}); |
|||
modalDialog.setBinding(binding); |
|||
modalDialog.show(mActivity.getSupportFragmentManager(), ""); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private String getFileName() { |
|||
Coins.Account account = GlobalViewModel.getAccount(mActivity); |
|||
switch (account) { |
|||
case SegWit: |
|||
return "p2wpkh-pubkey.txt"; |
|||
case P2SH: |
|||
return "p2wpkh-p2sh-pubkey.txt"; |
|||
case P2PKH: |
|||
return "p2pkh-pubkey.txt"; |
|||
} |
|||
return "p2wpkh-p2sh-pubkey.txt"; |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
|
|||
} |
|||
|
|||
private String convertExtpub(String xpub, Coins.Account account) { |
|||
if (account == Coins.Account.SegWit) { |
|||
return Util.convertXpubToZpub(xpub); |
|||
} else if (account == Coins.Account.P2SH) { |
|||
return Util.convertXpubToYpub(xpub); |
|||
} else { |
|||
return xpub; |
|||
} |
|||
} |
|||
} |
@ -1,220 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.content.Context; |
|||
import android.content.Intent; |
|||
import android.graphics.Color; |
|||
import android.graphics.drawable.ColorDrawable; |
|||
import android.os.Bundle; |
|||
import android.view.KeyEvent; |
|||
import android.view.Menu; |
|||
import android.view.MenuInflater; |
|||
import android.view.MenuItem; |
|||
import android.view.View; |
|||
import android.view.inputmethod.InputMethodManager; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.databinding.Observable; |
|||
import androidx.databinding.ObservableField; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.ViewModelProviders; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.cobo.cold.AppExecutors; |
|||
import com.cobo.cold.MainApplication; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.config.FeatureFlags; |
|||
import com.cobo.cold.databinding.ManageCoinFragmentBinding; |
|||
import com.cobo.cold.db.entity.CoinEntity; |
|||
import com.cobo.cold.ui.MainActivity; |
|||
import com.cobo.cold.ui.SetupVaultActivity; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.viewmodel.CoinListViewModel; |
|||
|
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
import static com.cobo.cold.Utilities.IS_SETUP_VAULT; |
|||
import static com.cobo.cold.Utilities.IS_SET_PASSPHRASE; |
|||
import static com.cobo.cold.viewmodel.CoinListViewModel.coinEntityComparator; |
|||
|
|||
public class ManageCoinFragment extends BaseFragment<ManageCoinFragmentBinding> { |
|||
|
|||
private final ObservableField<String> query = new ObservableField<>(); |
|||
public static final String TAG = "ManageCoinFragment"; |
|||
private CoinAdapter mCoinAdapter; |
|||
private CoinListViewModel mViewModel; |
|||
|
|||
private boolean hideConfirmAction = true; |
|||
|
|||
private boolean isInSearch; |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.manage_coin_fragment; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
|
|||
mActivity.setSupportActionBar(mBinding.toolbar); |
|||
|
|||
Bundle data = getArguments(); |
|||
if (data != null && data.getBoolean(IS_SET_PASSPHRASE)) { |
|||
mBinding.toolbarTitle.setText(R.string.add_coins); |
|||
mBinding.toolbar.setNavigationIcon(new ColorDrawable(Color.TRANSPARENT)); |
|||
mBinding.toolbar.setNavigationOnClickListener(null); |
|||
hideConfirmAction = false; |
|||
} else if (mActivity instanceof SetupVaultActivity) { |
|||
mBinding.toolbarTitle.setText(R.string.add_coins); |
|||
mBinding.toolbar.setNavigationIcon(new ColorDrawable(Color.TRANSPARENT)); |
|||
mBinding.toolbar.setNavigationOnClickListener(null); |
|||
hideConfirmAction = false; |
|||
} else { |
|||
mBinding.toolbar.setNavigationOnClickListener(((MainActivity) mActivity)::toggleDrawer); |
|||
} |
|||
|
|||
mBinding.toolbar.setTitle(""); |
|||
mCoinAdapter = new CoinAdapter(mActivity, mCoinClickCallback, true); |
|||
mBinding.assetList.setAdapter(mCoinAdapter); |
|||
initSearchView(); |
|||
} |
|||
|
|||
private void initSearchView() { |
|||
mBinding.btnCancel.setOnClickListener(v -> exitSearch()); |
|||
View.OnKeyListener backListener = (view, keyCode, keyEvent) -> { |
|||
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { |
|||
if (keyCode == KeyEvent.KEYCODE_BACK) { |
|||
if (isInSearch) { |
|||
exitSearch(); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
}; |
|||
mBinding.search.setOnKeyListener(backListener); |
|||
query.set(""); |
|||
mBinding.setQuery(query); |
|||
query.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { |
|||
@Override |
|||
public void onPropertyChanged(Observable sender, int propertyId) { |
|||
mCoinAdapter.getFilter().filter(query.get()); |
|||
} |
|||
}); |
|||
mCoinAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { |
|||
@Override |
|||
public void onChanged() { |
|||
super.onChanged(); |
|||
if (isInSearch) { |
|||
if (mCoinAdapter.getItems().size() == 0) { |
|||
mBinding.empty.setVisibility(View.VISIBLE); |
|||
mBinding.assetList.setVisibility(View.GONE); |
|||
} else { |
|||
mBinding.empty.setVisibility(View.GONE); |
|||
mBinding.assetList.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { |
|||
inflater.inflate(R.menu.manage, menu); |
|||
if (hideConfirmAction) { |
|||
menu.findItem(R.id.action_confirm).setVisible(false); |
|||
} |
|||
super.onCreateOptionsMenu(menu, inflater); |
|||
} |
|||
|
|||
@Override |
|||
public boolean onOptionsItemSelected(MenuItem item) { |
|||
switch (item.getItemId()) { |
|||
case R.id.action_confirm: |
|||
Bundle data = Objects.requireNonNull(getArguments()); |
|||
if (data.getBoolean(IS_SETUP_VAULT)) { |
|||
// if (FeatureFlags.ENABLE_WHITE_LIST) {
|
|||
// navigate(R.id.action_manageCoin_to_manageWhiteList, data);
|
|||
// } else {
|
|||
// navigate(R.id.action_manageCoinFragment_to_setupSyncFragment, data);
|
|||
// }
|
|||
|
|||
} else { |
|||
startActivity(new Intent(mActivity, MainActivity.class)); |
|||
} |
|||
break; |
|||
case R.id.action_search: |
|||
enterSearch(); |
|||
break; |
|||
} |
|||
return super.onOptionsItemSelected(item); |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
mViewModel = ViewModelProviders.of(this).get(CoinListViewModel.class); |
|||
if (mActivity instanceof SetupVaultActivity) { |
|||
subscribeUi(MainApplication.getApplication().getRepository().reloadCoins()); |
|||
} else if (getArguments() != null && getArguments().getBoolean(IS_SET_PASSPHRASE)) { |
|||
subscribeUi(MainApplication.getApplication().getRepository().reloadCoins()); |
|||
} else { |
|||
subscribeUi(mViewModel.getCoins()); |
|||
} |
|||
} |
|||
|
|||
private void subscribeUi(LiveData<List<CoinEntity>> coins) { |
|||
coins.observe(this, coinEntities -> { |
|||
if (coinEntities != null) { |
|||
coinEntities.sort(coinEntityComparator); |
|||
mCoinAdapter.setItems(coinEntities); |
|||
} |
|||
mBinding.executePendingBindings(); |
|||
}); |
|||
} |
|||
|
|||
private final CoinClickCallback mCoinClickCallback = coin -> |
|||
AppExecutors.getInstance().diskIO() |
|||
.execute(() -> mViewModel.toggleCoin(coin)); |
|||
|
|||
private void enterSearch() { |
|||
isInSearch = true; |
|||
mBinding.searchBar.setVisibility(View.VISIBLE); |
|||
mBinding.search.requestFocus(); |
|||
InputMethodManager inputManager = |
|||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); |
|||
if (inputManager != null) { |
|||
inputManager.showSoftInput(mBinding.search, 0); |
|||
} |
|||
} |
|||
|
|||
private void exitSearch() { |
|||
isInSearch = false; |
|||
mBinding.search.setText(""); |
|||
mBinding.searchBar.setVisibility(View.INVISIBLE); |
|||
mBinding.search.clearFocus(); |
|||
mBinding.empty.setVisibility(View.GONE); |
|||
mBinding.assetList.setVisibility(View.VISIBLE); |
|||
InputMethodManager inputManager = |
|||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); |
|||
if (inputManager != null) { |
|||
inputManager.hideSoftInputFromWindow(mBinding.search.getWindowToken(), 0); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,171 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.content.Context; |
|||
import android.os.Bundle; |
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.ViewModelProviders; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.ElectrumTxnBinding; |
|||
import com.cobo.cold.databinding.FileListBinding; |
|||
import com.cobo.cold.ui.common.BaseBindingAdapter; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.ui.fragment.main.electrum.Callback; |
|||
import com.cobo.cold.ui.modal.ModalDialog; |
|||
import com.cobo.cold.update.utils.FileUtils; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
import com.cobo.cold.viewmodel.PsbtViewModel; |
|||
|
|||
import org.spongycastle.util.encoders.Base64; |
|||
import org.spongycastle.util.encoders.Hex; |
|||
|
|||
import java.io.File; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.concurrent.atomic.AtomicBoolean; |
|||
|
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.hasSdcard; |
|||
|
|||
public class PsbtListFragment extends BaseFragment<FileListBinding> |
|||
implements Callback { |
|||
|
|||
public static final String TAG = "ElectrumTxnListFragment"; |
|||
public static final String PSBT_MAGIC_PREFIX = Hex.toHexString("psbt".getBytes(StandardCharsets.UTF_8)); |
|||
private PsbtViewModel viewModel; |
|||
private TxnAdapter adapter; |
|||
private AtomicBoolean showEmpty; |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.file_list; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); |
|||
viewModel = ViewModelProviders.of(mActivity).get(PsbtViewModel.class); |
|||
adapter = new TxnAdapter(mActivity, this); |
|||
initViews(); |
|||
} |
|||
|
|||
private void initViews() { |
|||
showEmpty = new AtomicBoolean(false); |
|||
if (!hasSdcard(mActivity)) { |
|||
showEmpty.set(true); |
|||
mBinding.emptyTitle.setText(R.string.no_sdcard); |
|||
mBinding.emptyMessage.setText(R.string.no_sdcard_hint); |
|||
} else { |
|||
mBinding.list.setAdapter(adapter); |
|||
viewModel.loadUnsignPsbt().observe(this, files -> { |
|||
if (files.size() > 0) { |
|||
adapter.setItems(files); |
|||
} else { |
|||
showEmpty.set(true); |
|||
mBinding.emptyTitle.setText(R.string.no_unsigned_txn); |
|||
mBinding.emptyMessage.setText(R.string.no_unsigned_txn_hint); |
|||
} |
|||
updateUi(); |
|||
}); |
|||
} |
|||
updateUi(); |
|||
} |
|||
|
|||
private void updateUi() { |
|||
if (showEmpty.get()) { |
|||
mBinding.emptyView.setVisibility(View.VISIBLE); |
|||
mBinding.list.setVisibility(View.GONE); |
|||
} else { |
|||
mBinding.emptyView.setVisibility(View.GONE); |
|||
mBinding.list.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void onClick(String file) { |
|||
Storage storage = Storage.createByEnvironment(mActivity); |
|||
byte[] content = FileUtils.bufferlize(new File(storage.getExternalDir(), file)); |
|||
if (content != null) { |
|||
String psbtBase64 = null; |
|||
if (isBase64Psbt(content)) { |
|||
psbtBase64 = new String(content); |
|||
} else if (Hex.toHexString(content).startsWith(PSBT_MAGIC_PREFIX)) { |
|||
psbtBase64 = Base64.toBase64String(content); |
|||
} |
|||
|
|||
if (psbtBase64 != null) { |
|||
Bundle bundle = new Bundle(); |
|||
bundle.putString("psbt_base64", psbtBase64); |
|||
navigate(R.id.action_to_psbtTxConfirmFragment, bundle); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
ModalDialog.showCommonModal(mActivity, |
|||
getString(R.string.electrum_decode_txn_fail), |
|||
getString(R.string.error_txn_file), |
|||
getString(R.string.confirm), |
|||
null); |
|||
|
|||
} |
|||
|
|||
private boolean isBase64Psbt(byte[] content) { |
|||
try { |
|||
byte[] data = Base64.decode(new String(content)); |
|||
return Hex.toHexString(data).startsWith(PSBT_MAGIC_PREFIX); |
|||
}catch (Exception e) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
public static class TxnAdapter extends BaseBindingAdapter<String, ElectrumTxnBinding> { |
|||
private Callback callback; |
|||
|
|||
TxnAdapter(Context context, Callback callback) { |
|||
super(context); |
|||
this.callback = callback; |
|||
} |
|||
|
|||
@Override |
|||
protected int getLayoutResId(int viewType) { |
|||
return R.layout.electrum_txn; |
|||
} |
|||
|
|||
@Override |
|||
protected void onBindItem(ElectrumTxnBinding binding, String item) { |
|||
binding.setFile(item); |
|||
binding.setCallback(callback); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { |
|||
super.onBindViewHolder(holder, position); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,105 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
|
|||
import androidx.appcompat.app.AppCompatActivity; |
|||
import androidx.databinding.DataBindingUtil; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.ExportSdcardModalBinding; |
|||
import com.cobo.cold.ui.fragment.main.electrum.UnsignedTxFragment; |
|||
import com.cobo.cold.ui.modal.ModalDialog; |
|||
import com.cobo.cold.ui.views.AuthenticateModal; |
|||
import com.cobo.cold.update.utils.FileUtils; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
|
|||
import java.io.File; |
|||
import java.util.Objects; |
|||
|
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.exportSuccess; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.hasSdcard; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.showNoSdcardModal; |
|||
|
|||
public class PsbtTxConfirmFragment extends UnsignedTxFragment { |
|||
|
|||
private String psbtBase64; |
|||
@Override |
|||
protected void init(View view) { |
|||
super.init(view); |
|||
} |
|||
|
|||
static void showExportPsbtDialog(AppCompatActivity activity, String txId, String psbt, |
|||
Runnable onExportSuccess) { |
|||
ModalDialog modalDialog = ModalDialog.newInstance(); |
|||
ExportSdcardModalBinding binding = DataBindingUtil.inflate(LayoutInflater.from(activity), |
|||
R.layout.export_sdcard_modal, null, false); |
|||
String fileName = "signed_" + txId.substring(0, 8) + ".psbt"; |
|||
binding.title.setText(R.string.export_signed_txn); |
|||
binding.fileName.setText(fileName); |
|||
binding.actionHint.setVisibility(View.GONE); |
|||
binding.cancel.setOnClickListener(vv -> modalDialog.dismiss()); |
|||
binding.confirm.setOnClickListener(vv -> { |
|||
modalDialog.dismiss(); |
|||
if (hasSdcard(activity)) { |
|||
Storage storage = Storage.createByEnvironment(activity); |
|||
File file = new File(Objects.requireNonNull(storage).getExternalDir(), fileName); |
|||
boolean result = FileUtils.writeString(file, psbt); |
|||
if (result) { |
|||
exportSuccess(activity, onExportSuccess); |
|||
} |
|||
} else { |
|||
showNoSdcardModal(activity); |
|||
} |
|||
}); |
|||
modalDialog.setBinding(binding); |
|||
modalDialog.show(activity.getSupportFragmentManager(), ""); |
|||
} |
|||
|
|||
@Override |
|||
protected AuthenticateModal.OnVerify signWithVerifyInfo() { |
|||
return token -> { |
|||
viewModel.setToken(token); |
|||
viewModel.handleSignPsbt(psbtBase64); |
|||
subscribeSignState(); |
|||
}; |
|||
} |
|||
|
|||
@Override |
|||
protected void parseTx() { |
|||
psbtBase64 = Objects.requireNonNull(getArguments()).getString("psbt_base64"); |
|||
viewModel.parsePsbtBase64(psbtBase64); |
|||
} |
|||
|
|||
protected void onSignSuccess() { |
|||
showExportPsbtDialog(mActivity, viewModel.getTxId(), |
|||
viewModel.getTxHex(), this::navigateUp); |
|||
viewModel.getSignState().removeObservers(this); |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
|
@ -0,0 +1,97 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main; |
|||
|
|||
import android.os.Bundle; |
|||
import android.text.TextUtils; |
|||
import android.view.View; |
|||
|
|||
import androidx.lifecycle.ViewModelProviders; |
|||
|
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.WalletInfoBinding; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.viewmodel.GlobalViewModel; |
|||
import com.cobo.cold.viewmodel.SupportedWatchWallet; |
|||
import com.cobo.cold.viewmodel.WalletInfoViewModel; |
|||
|
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_TITLE; |
|||
|
|||
public class WalletInfoFragment extends BaseFragment<WalletInfoBinding> { |
|||
private Coins.Account account; |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.wallet_info; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); |
|||
mBinding.switchAddress.setOnClickListener(v -> switchAddressFormat()); |
|||
account = GlobalViewModel.getAccount(mActivity); |
|||
SupportedWatchWallet watchOnly = SupportedWatchWallet.getSupportedWatchWallet(mActivity); |
|||
if (watchOnly != SupportedWatchWallet.ELECTRUM |
|||
&& watchOnly != SupportedWatchWallet.GENERIC) { |
|||
mBinding.switchAddress.setVisibility(View.GONE); |
|||
} |
|||
mBinding.addressFormat.setText(getAddressFormat()); |
|||
mBinding.addressType.setText(account.getType()); |
|||
mBinding.path.setText(account.getPath()); |
|||
|
|||
WalletInfoViewModel viewModel = ViewModelProviders.of(this) |
|||
.get(WalletInfoViewModel.class); |
|||
|
|||
viewModel.getFingerprint().observe(this, s -> { |
|||
if (!TextUtils.isEmpty(s)) { |
|||
mBinding.fingerprint.setText(s); |
|||
} |
|||
}); |
|||
|
|||
viewModel.getXpub(account).observe(this, s -> { |
|||
if (!TextUtils.isEmpty(s)) { |
|||
mBinding.xpub.setText(s); |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
private String getAddressFormat() { |
|||
switch (account) { |
|||
case SegWit: |
|||
return getString(R.string.native_segwit); |
|||
case P2PKH: |
|||
return getString(R.string.p2pkh); |
|||
case P2SH: |
|||
return getString(R.string.nested_segwit); |
|||
} |
|||
return ""; |
|||
} |
|||
|
|||
private void switchAddressFormat() { |
|||
Bundle data = new Bundle(); |
|||
data.putInt(KEY_TITLE, R.string.select_address_format); |
|||
navigate(R.id.action_to_selectAddressFormatFragment, data); |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
|
|||
} |
|||
} |
@ -0,0 +1,177 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.main.electrum; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
|
|||
import androidx.databinding.DataBindingUtil; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.ExportSdcardModalBinding; |
|||
import com.cobo.cold.databinding.ExportXpubGuideBinding; |
|||
import com.cobo.cold.ui.MainActivity; |
|||
import com.cobo.cold.ui.SetupVaultActivity; |
|||
import com.cobo.cold.ui.fragment.BaseFragment; |
|||
import com.cobo.cold.ui.modal.ModalDialog; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
import com.cobo.cold.viewmodel.GlobalViewModel; |
|||
import com.cobo.cold.viewmodel.SupportedWatchWallet; |
|||
|
|||
import org.json.JSONObject; |
|||
|
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.exportSuccess; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.showNoSdcardModal; |
|||
import static com.cobo.cold.viewmodel.GlobalViewModel.writeToSdcard; |
|||
|
|||
public class ExportXpubGuideFragment extends BaseFragment<ExportXpubGuideBinding> { |
|||
|
|||
private SupportedWatchWallet watchWallet; |
|||
private JSONObject wasabiXpubJson; |
|||
private static final String WASABI_XPUB_FILENAME = "CoboVault-Wasabi.json"; |
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.export_xpub_guide; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
watchWallet = SupportedWatchWallet.getSupportedWatchWallet(mActivity); |
|||
mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); |
|||
mBinding.toolbarTitle.setText(getTitle()); |
|||
mBinding.export.setOnClickListener(v -> export()); |
|||
if (mActivity instanceof MainActivity) { |
|||
mBinding.skip.setVisibility(View.GONE); |
|||
} else { |
|||
mBinding.skip.setOnClickListener(v -> navigate(R.id.action_to_setupCompleteFragment)); |
|||
} |
|||
|
|||
mBinding.text1.setText(getText1()); |
|||
mBinding.text2.setText(getText2()); |
|||
mBinding.export.setText(getButtonText()); |
|||
wasabiXpubJson = GlobalViewModel.getXpubInfo(mActivity); |
|||
} |
|||
|
|||
private void export() { |
|||
switch (watchWallet) { |
|||
case ELECTRUM: |
|||
navigate(R.id.export_electrum_ypub); |
|||
break; |
|||
case COBO: |
|||
navigate(R.id.export_xpub_cobo); |
|||
break; |
|||
case WASABI: |
|||
exportXpub(); |
|||
break; |
|||
case BLUE: |
|||
//return R.string.show_qrcode;
|
|||
break; |
|||
case GENERIC: |
|||
//navigate(R.id.export_electrum_ypub);
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
public void exportXpub() { |
|||
Storage storage = Storage.createByEnvironment(mActivity); |
|||
if (storage == null || storage.getExternalDir() == null) { |
|||
showNoSdcardModal(mActivity); |
|||
} else { |
|||
ModalDialog modalDialog = ModalDialog.newInstance(); |
|||
ExportSdcardModalBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mActivity), |
|||
R.layout.export_sdcard_modal, null, false); |
|||
binding.title.setText(R.string.export_xpub_text_file); |
|||
binding.fileName.setText(WASABI_XPUB_FILENAME); |
|||
binding.actionHint.setVisibility(View.GONE); |
|||
binding.cancel.setOnClickListener(vv -> modalDialog.dismiss()); |
|||
binding.confirm.setOnClickListener(vv -> { |
|||
modalDialog.dismiss(); |
|||
if (writeToSdcard(storage, wasabiXpubJson.toString(), WASABI_XPUB_FILENAME)) { |
|||
Runnable runnable = null; |
|||
if (mActivity instanceof SetupVaultActivity) { |
|||
runnable = () -> navigate(R.id.action_to_setupCompleteFragment); |
|||
} |
|||
exportSuccess(mActivity, runnable); |
|||
} |
|||
}); |
|||
modalDialog.setBinding(binding); |
|||
modalDialog.show(mActivity.getSupportFragmentManager(), ""); |
|||
} |
|||
} |
|||
|
|||
private int getButtonText() { |
|||
switch (watchWallet) { |
|||
case ELECTRUM: |
|||
return R.string.show_master_public_key_qrcode; |
|||
case WASABI: |
|||
return R.string.export_wallet; |
|||
case COBO: |
|||
case BLUE: |
|||
return R.string.show_qrcode; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
private int getTitle() { |
|||
switch (watchWallet) { |
|||
case ELECTRUM: |
|||
return R.string.export_xpub_guide_title_electrum; |
|||
case WASABI: |
|||
return R.string.export_xpub_guide_title_wasabi; |
|||
case COBO: |
|||
return R.string.export_xpub_guide_title_cobo; |
|||
case BLUE: |
|||
return R.string.export_xpub_guide_title_blue; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
private int getText1() { |
|||
switch (watchWallet) { |
|||
case ELECTRUM: |
|||
return R.string.export_xpub_guide_text1_electrum; |
|||
case WASABI: |
|||
return R.string.export_xpub_guide_text1_wasabi; |
|||
case COBO: |
|||
return R.string.export_xpub_guide_text1_cobo; |
|||
case BLUE: |
|||
return R.string.export_xpub_guide_text1_blue; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
private int getText2() { |
|||
switch (watchWallet) { |
|||
case ELECTRUM: |
|||
return R.string.export_xpub_guide_text2_electrum; |
|||
case WASABI: |
|||
return R.string.export_xpub_guide_text2_wasabi; |
|||
case COBO: |
|||
return R.string.export_xpub_guide_text2_cobo; |
|||
case BLUE: |
|||
return R.string.export_xpub_guide_text2_blue; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
@Override |
|||
protected void initData(Bundle savedInstanceState) { |
|||
|
|||
} |
|||
} |
@ -0,0 +1,99 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.setting; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.View; |
|||
|
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.Utilities; |
|||
import com.cobo.cold.ui.SetupVaultActivity; |
|||
import com.cobo.cold.viewmodel.SupportedWatchWallet; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_TITLE; |
|||
import static com.cobo.cold.ui.fragment.setting.MainPreferenceFragment.SETTING_ADDRESS_FORMAT; |
|||
import static com.cobo.cold.ui.fragment.setting.MainPreferenceFragment.SETTING_CHOOSE_WATCH_WALLET; |
|||
|
|||
public class ChooseWatchWalletFragment extends ListPreferenceFragment { |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
Bundle data = getArguments(); |
|||
if (data != null) { |
|||
mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); |
|||
mBinding.toolbarTitle.setText(data.getInt(KEY_TITLE)); |
|||
} else { |
|||
mBinding.toolbar.setVisibility(View.GONE); |
|||
} |
|||
prefs = Utilities.getPrefs(mActivity); |
|||
entries = getResources().getStringArray(getEntries()); |
|||
values = getResources().getStringArray(getValues()); |
|||
value = prefs.getString(getKey(), defaultValue()); |
|||
adapter = new Adapter(mActivity); |
|||
if (mActivity instanceof SetupVaultActivity) { |
|||
adapter.setItems(Arrays.asList(Arrays.copyOfRange(entries,0,entries.length - 1))); |
|||
} else { |
|||
adapter.setItems(Arrays.asList(entries)); |
|||
} |
|||
mBinding.list.setAdapter(adapter); |
|||
} |
|||
|
|||
@Override |
|||
protected int getEntries() { |
|||
return R.array.watch_wallet_list; |
|||
} |
|||
|
|||
@Override |
|||
protected int getValues() { |
|||
return R.array.watch_wallet_list_values; |
|||
} |
|||
|
|||
@Override |
|||
protected String getKey() { |
|||
return SETTING_CHOOSE_WATCH_WALLET; |
|||
} |
|||
|
|||
@Override |
|||
protected String defaultValue() { |
|||
return SupportedWatchWallet.ELECTRUM.getWalletId(); |
|||
} |
|||
|
|||
@Override |
|||
public void onSelect(int position) { |
|||
String old = value; |
|||
value = values[position].toString(); |
|||
if (!old.equals(value)) { |
|||
setWatchWallet(); |
|||
adapter.notifyDataSetChanged(); |
|||
} |
|||
} |
|||
|
|||
private void setWatchWallet() { |
|||
prefs.edit().putString(SETTING_CHOOSE_WATCH_WALLET, value).apply(); |
|||
if (value.equals(SupportedWatchWallet.COBO.getWalletId())) { |
|||
prefs.edit().putString(SETTING_ADDRESS_FORMAT, Coins.Account.P2SH.getType()).apply(); |
|||
} else if (value.equals(SupportedWatchWallet.WASABI.getWalletId()) |
|||
|| value.equals(SupportedWatchWallet.BLUE.getWalletId())) { |
|||
prefs.edit().putString(SETTING_ADDRESS_FORMAT, Coins.Account.SegWit.getType()).apply(); |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,76 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.setup; |
|||
|
|||
import android.view.View; |
|||
|
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.ui.MainActivity; |
|||
import com.cobo.cold.ui.fragment.setting.ListPreferenceFragment; |
|||
|
|||
import static com.cobo.cold.ui.fragment.setting.MainPreferenceFragment.SETTING_ADDRESS_FORMAT; |
|||
|
|||
public class SelectAddressFormatFragment extends ListPreferenceFragment { |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
super.init(view); |
|||
subTitles = getResources().getStringArray(R.array.address_format_subtitle); |
|||
mBinding.confirm.setVisibility(View.VISIBLE); |
|||
mBinding.confirm.setText(R.string.next); |
|||
mBinding.confirm.setOnClickListener(v -> next()); |
|||
if (mActivity instanceof MainActivity) { |
|||
mBinding.confirm.setVisibility(View.GONE); |
|||
} |
|||
} |
|||
|
|||
private void next() { |
|||
navigate(R.id.action_to_export_xpub_guide); |
|||
} |
|||
|
|||
@Override |
|||
protected int getEntries() { |
|||
return R.array.address_format; |
|||
} |
|||
|
|||
@Override |
|||
protected int getValues() { |
|||
return R.array.address_format_value; |
|||
} |
|||
|
|||
@Override |
|||
protected String getKey() { |
|||
return SETTING_ADDRESS_FORMAT; |
|||
} |
|||
|
|||
@Override |
|||
protected String defaultValue() { |
|||
return Coins.Account.P2SH.getType(); |
|||
} |
|||
|
|||
@Override |
|||
public void onSelect(int position) { |
|||
String old = value; |
|||
value = values[position].toString(); |
|||
if (!old.equals(value)) { |
|||
prefs.edit().putString(getKey(), value).apply(); |
|||
adapter.notifyDataSetChanged(); |
|||
} |
|||
} |
|||
} |
@ -1,65 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.setup; |
|||
|
|||
import android.text.TextUtils; |
|||
import android.view.View; |
|||
|
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.ViewModelProviders; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.SetupSyncBinding; |
|||
import com.cobo.cold.db.entity.CoinEntity; |
|||
import com.cobo.cold.viewmodel.CoinListViewModel; |
|||
import com.cobo.cold.viewmodel.SetupVaultViewModel; |
|||
|
|||
import java.util.List; |
|||
|
|||
public class SetupSyncFragment extends SetupVaultBaseFragment<SetupSyncBinding> { |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.setup_sync; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
super.init(view); |
|||
mBinding.complete.setOnClickListener(this::complete); |
|||
SetupVaultViewModel model = ViewModelProviders.of(mActivity).get(SetupVaultViewModel.class); |
|||
subscribe(model.getCoins()); |
|||
} |
|||
|
|||
private void complete(View view) { |
|||
navigate(R.id.action_to_setupCompleteFragment); |
|||
} |
|||
|
|||
private void subscribe(LiveData<List<CoinEntity>> coins) { |
|||
coins.observe(this, this::generateSyncData); |
|||
} |
|||
|
|||
private void generateSyncData(List<CoinEntity> coinEntities) { |
|||
ViewModelProviders.of(mActivity).get(CoinListViewModel.class) |
|||
.generateSync(coinEntities).observe(this, sync -> { |
|||
if (!TextUtils.isEmpty(sync)) { |
|||
mBinding.sync.qrcodeLayout.qrcode.setData(sync); |
|||
} |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.ui.fragment.setup; |
|||
|
|||
import android.os.Bundle; |
|||
import android.view.View; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.databinding.SetupWatchWalletBinding; |
|||
import com.cobo.cold.viewmodel.SupportedWatchWallet; |
|||
|
|||
import static com.cobo.cold.ui.fragment.Constants.KEY_TITLE; |
|||
import static com.cobo.cold.viewmodel.SupportedWatchWallet.getSupportedWatchWallet; |
|||
|
|||
public class SetupWatchWalletFragment extends SetupVaultBaseFragment<SetupWatchWalletBinding> { |
|||
|
|||
@Override |
|||
protected int setView() { |
|||
return R.layout.setup_watch_wallet; |
|||
} |
|||
|
|||
@Override |
|||
protected void init(View view) { |
|||
super.init(view); |
|||
mBinding.complete.setOnClickListener(v -> complete()); |
|||
} |
|||
|
|||
private void complete() { |
|||
int navId = 0; |
|||
Bundle data = new Bundle(); |
|||
SupportedWatchWallet selectWatchOnlyWallet = getSupportedWatchWallet(mActivity); |
|||
switch (selectWatchOnlyWallet) { |
|||
case ELECTRUM: |
|||
data.putInt(KEY_TITLE, R.string.select_address_format); |
|||
navId = R.id.action_to_selectAddressFormatFragment; |
|||
break; |
|||
case COBO: |
|||
case WASABI: |
|||
case BLUE: |
|||
navId = R.id.action_to_export_xpub_guide; |
|||
break; |
|||
} |
|||
navigate(navId, data); |
|||
} |
|||
} |
@ -0,0 +1,271 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.viewmodel; |
|||
|
|||
import android.app.Application; |
|||
import android.content.Context; |
|||
import android.content.SharedPreferences; |
|||
import android.os.Handler; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.appcompat.app.AppCompatActivity; |
|||
import androidx.databinding.DataBindingUtil; |
|||
import androidx.lifecycle.AndroidViewModel; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
|
|||
import com.cobo.coinlib.Util; |
|||
import com.cobo.coinlib.coins.BTC.Btc; |
|||
import com.cobo.coinlib.coins.BTC.Deriver; |
|||
import com.cobo.coinlib.exception.InvalidPathException; |
|||
import com.cobo.coinlib.path.Account; |
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.AppExecutors; |
|||
import com.cobo.cold.BuildConfig; |
|||
import com.cobo.cold.DataRepository; |
|||
import com.cobo.cold.MainApplication; |
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.Utilities; |
|||
import com.cobo.cold.callables.GetExtendedPublicKeyCallable; |
|||
import com.cobo.cold.callables.GetMasterFingerprintCallable; |
|||
import com.cobo.cold.databinding.CommonModalBinding; |
|||
import com.cobo.cold.db.entity.AccountEntity; |
|||
import com.cobo.cold.db.entity.CoinEntity; |
|||
import com.cobo.cold.ui.modal.ExportToSdcardDialog; |
|||
import com.cobo.cold.ui.modal.ModalDialog; |
|||
import com.cobo.cold.update.utils.FileUtils; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
|
|||
import org.json.JSONException; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.File; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
import static com.cobo.cold.ui.fragment.setting.MainPreferenceFragment.SETTING_ADDRESS_FORMAT; |
|||
|
|||
public class GlobalViewModel extends AndroidViewModel { |
|||
|
|||
private static final int DEFAULT_CHANGE_ADDRESS_NUM = 100; |
|||
|
|||
private final DataRepository mRepo; |
|||
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sp, key) -> { |
|||
if (SETTING_ADDRESS_FORMAT.equals(key)) { |
|||
deriveChangeAddress(); |
|||
} |
|||
}; |
|||
private MutableLiveData<String> exPub = new MutableLiveData<>(); |
|||
private MutableLiveData<List<String>> changeAddress = new MutableLiveData<>(); |
|||
private String xpub; |
|||
private AccountEntity accountEntity; |
|||
|
|||
public GlobalViewModel(@NonNull Application application) { |
|||
super(application); |
|||
mRepo = MainApplication.getApplication().getRepository(); |
|||
deriveChangeAddress(); |
|||
Utilities.getPrefs(application).registerOnSharedPreferenceChangeListener(listener); |
|||
} |
|||
|
|||
public static Coins.Account getAccount(Context context) { |
|||
SharedPreferences pref = Utilities.getPrefs(context); |
|||
String type = pref.getString(SETTING_ADDRESS_FORMAT, Coins.Account.P2SH.getType()); |
|||
for (Coins.Account account: Coins.Account.values()) { |
|||
if (type.equals(account.getType())) { |
|||
return account; |
|||
} |
|||
} |
|||
return Coins.Account.P2SH; |
|||
} |
|||
|
|||
public static Btc.AddressType getAddressType(Context context) { |
|||
switch (getAccount(context)) { |
|||
case P2SH: |
|||
return Btc.AddressType.P2SH; |
|||
case P2PKH: |
|||
return Btc.AddressType.P2PKH; |
|||
case SegWit: |
|||
return Btc.AddressType.SegWit; |
|||
} |
|||
return Btc.AddressType.SegWit; |
|||
} |
|||
|
|||
|
|||
public static String getAddressFormat(Context context) { |
|||
switch (getAccount(context)) { |
|||
case SegWit: |
|||
return context.getString(R.string.native_segwit); |
|||
case P2PKH: |
|||
return context.getString(R.string.p2pkh); |
|||
case P2SH: |
|||
return context.getString(R.string.nested_segwit); |
|||
} |
|||
return context.getString(R.string.nested_segwit); |
|||
} |
|||
|
|||
public static JSONObject getXpubInfo(Context activity) { |
|||
JSONObject xpubInfo = new JSONObject(); |
|||
Coins.Account account = getAccount(activity); |
|||
String xpub = new GetExtendedPublicKeyCallable(account.getPath()).call(); |
|||
String masterKeyFingerprint = new GetMasterFingerprintCallable().call(); |
|||
try { |
|||
xpubInfo.put("ExtPubKey", xpub); |
|||
xpubInfo.put("MasterFingerprint", masterKeyFingerprint); |
|||
xpubInfo.put("CoboVaultVersion", BuildConfig.VERSION_NAME); |
|||
} catch (JSONException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return xpubInfo; |
|||
} |
|||
|
|||
private void deriveChangeAddress() { |
|||
AppExecutors.getInstance().networkIO().execute(()->{ |
|||
ExpubInfo expubInfo = new ExpubInfo().getExPubInfo(); |
|||
xpub = expubInfo.expub; |
|||
String path = expubInfo.hdPath; |
|||
List<String> changes = new ArrayList<>(); |
|||
Btc.AddressType type; |
|||
if (Coins.Account.P2SH.getPath().equals(path)) { |
|||
type = Btc.AddressType.P2SH; |
|||
} else if (Coins.Account.SegWit.getPath().equals(path)) { |
|||
type = Btc.AddressType.SegWit; |
|||
} else { |
|||
type = Btc.AddressType.P2PKH; |
|||
} |
|||
|
|||
Deriver btcDeriver = new Deriver(); |
|||
for (int i = 0; i < DEFAULT_CHANGE_ADDRESS_NUM; i++) { |
|||
changes.add(btcDeriver.derive(xpub,1, i, type)); |
|||
} |
|||
changeAddress.postValue(changes); |
|||
}); |
|||
} |
|||
|
|||
public LiveData<String> getExtendPublicKey() { |
|||
AppExecutors.getInstance().diskIO().execute(() -> { |
|||
ExpubInfo expubInfo = new ExpubInfo().getExPubInfo(); |
|||
String hdPath = expubInfo.getHdPath(); |
|||
String extPub = expubInfo.getExpub(); |
|||
try { |
|||
Account account = Account.parseAccount(hdPath); |
|||
if (account.getParent().getParent().getValue() == 49 |
|||
&& extPub.startsWith("xpub")) { |
|||
exPub.postValue(Util.convertXpubToYpub(extPub)); |
|||
} else if (extPub.startsWith("ypub")) { |
|||
exPub.postValue(extPub); |
|||
} else if (account.getParent().getParent().getValue() == 84 |
|||
&& extPub.startsWith("xpub")) { |
|||
exPub.postValue(Util.convertXpubToZpub(extPub)); |
|||
} |
|||
} catch (InvalidPathException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
|
|||
}); |
|||
return exPub; |
|||
} |
|||
|
|||
public AccountEntity getAccountEntity() { |
|||
return accountEntity; |
|||
} |
|||
|
|||
public LiveData<List<String>> getChangeAddress() { |
|||
deriveChangeAddress(); |
|||
return changeAddress; |
|||
} |
|||
|
|||
public String getXpub() { |
|||
return xpub; |
|||
} |
|||
|
|||
public static boolean hasSdcard(Context context) { |
|||
Storage storage = Storage.createByEnvironment(context); |
|||
return storage != null && storage.getExternalDir() != null; |
|||
} |
|||
|
|||
public static boolean writeToSdcard(Storage storage, String content, String fileName) { |
|||
File file = new File(storage.getElectrumDir(), fileName); |
|||
return FileUtils.writeString(file, content); |
|||
} |
|||
|
|||
public static void showNoSdcardModal(AppCompatActivity activity) { |
|||
ModalDialog modalDialog = ModalDialog.newInstance(); |
|||
CommonModalBinding binding = DataBindingUtil.inflate( |
|||
LayoutInflater.from(activity), R.layout.common_modal, |
|||
null, false); |
|||
binding.title.setText(R.string.hint); |
|||
binding.subTitle.setText(R.string.insert_sdcard_hint); |
|||
binding.close.setVisibility(View.GONE); |
|||
binding.confirm.setText(R.string.know); |
|||
binding.confirm.setOnClickListener(vv -> modalDialog.dismiss()); |
|||
modalDialog.setBinding(binding); |
|||
modalDialog.show(activity.getSupportFragmentManager(), ""); |
|||
} |
|||
|
|||
public static void exportSuccess(AppCompatActivity activity, Runnable runnable) { |
|||
ExportToSdcardDialog dialog = new ExportToSdcardDialog(); |
|||
dialog.show(activity.getSupportFragmentManager(), ""); |
|||
new Handler().postDelayed(() -> { |
|||
dialog.dismiss(); |
|||
if (runnable != null) { |
|||
runnable.run(); |
|||
} |
|||
}, 1000); |
|||
} |
|||
|
|||
|
|||
private class ExpubInfo { |
|||
private String hdPath; |
|||
private String expub; |
|||
|
|||
public String getHdPath() { |
|||
return hdPath; |
|||
} |
|||
|
|||
public String getExpub() { |
|||
return expub; |
|||
} |
|||
|
|||
public ExpubInfo getExPubInfo() { |
|||
CoinEntity btc = mRepo.loadCoinSync(Coins.BTC.coinId()); |
|||
SharedPreferences sp = Utilities.getPrefs(getApplication()); |
|||
List<AccountEntity> accounts = mRepo.loadAccountsForCoin(btc); |
|||
String format = sp.getString(SETTING_ADDRESS_FORMAT, Coins.Account.P2SH.getType()); |
|||
|
|||
Coins.Account account; |
|||
if (Coins.Account.P2SH.getType().equals(format)) { |
|||
account = Coins.Account.P2SH; |
|||
} else if(Coins.Account.SegWit.getType().equals(format)) { |
|||
account = Coins.Account.SegWit; |
|||
} else { |
|||
account = Coins.Account.P2PKH; |
|||
} |
|||
for (AccountEntity entity : accounts) { |
|||
if (entity.getHdPath().equals(account.getPath())) { |
|||
accountEntity = entity; |
|||
hdPath = entity.getHdPath(); |
|||
expub = entity.getExPub(); |
|||
|
|||
} |
|||
} |
|||
return this; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,127 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.viewmodel; |
|||
|
|||
import android.app.Application; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.AndroidViewModel; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
|
|||
import com.cobo.cold.AppExecutors; |
|||
import com.cobo.cold.callables.GetMasterFingerprintCallable; |
|||
import com.cobo.cold.update.utils.Storage; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONException; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.File; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.regex.Matcher; |
|||
import java.util.regex.Pattern; |
|||
|
|||
|
|||
public class PsbtViewModel extends AndroidViewModel { |
|||
|
|||
public static final String WASABI_SIGN_ID = "wasabi_sign_id"; |
|||
private static Pattern signedTxnPattern = Pattern.compile("^signed_[0-9a-fA-F]{8}.psbt$"); |
|||
private Storage storage; |
|||
|
|||
public PsbtViewModel(@NonNull Application application) { |
|||
super(application); |
|||
storage = Storage.createByEnvironment(application); |
|||
} |
|||
|
|||
public static JSONObject adapt(JSONObject psbt) throws JSONException, WatchWalletNotMatchException { |
|||
JSONObject object = new JSONObject(); |
|||
JSONArray inputs = new JSONArray(); |
|||
JSONArray outputs = new JSONArray(); |
|||
adaptInputs(psbt.getJSONArray("inputs"), inputs); |
|||
if (inputs.length() < 1) { |
|||
throw new WatchWalletNotMatchException("no input match masterFingerprint"); |
|||
} |
|||
adaptOutputs(psbt.getJSONArray("outputs"), outputs); |
|||
object.put("inputs", inputs); |
|||
object.put("outputs", outputs); |
|||
return object; |
|||
} |
|||
|
|||
private static void adaptInputs(JSONArray psbtInputs, JSONArray inputs) throws JSONException { |
|||
String masterKeyFingerprint = new GetMasterFingerprintCallable().call(); |
|||
for (int i = 0; i < psbtInputs.length(); i++) { |
|||
JSONObject psbtInput = psbtInputs.getJSONObject(i); |
|||
JSONObject in = new JSONObject(); |
|||
JSONObject utxo = new JSONObject(); |
|||
in.put("hash", psbtInput.getString("txId")); |
|||
in.put("index", psbtInput.getInt("index")); |
|||
JSONArray bip32Derivation = psbtInput.getJSONArray("hdPath"); |
|||
for (int j = 0; j < bip32Derivation.length(); j++) { |
|||
JSONObject item = bip32Derivation.getJSONObject(j); |
|||
if (item.getString("masterFingerprint").equals(masterKeyFingerprint)) { |
|||
utxo.put("publicKey", item.getString("pubkey")); |
|||
utxo.put("value", psbtInput.optInt("value")); |
|||
in.put("utxo", utxo); |
|||
in.put("ownerKeyPath", item.getString("path")); |
|||
in.put("masterFingerprint", item.getString("masterFingerprint")); |
|||
inputs.put(in); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
private static void adaptOutputs(JSONArray psbtOutputs, JSONArray outputs) throws JSONException { |
|||
for(int i = 0; i < psbtOutputs.length(); i++) { |
|||
JSONObject psbtOutput = psbtOutputs.getJSONObject(i); |
|||
JSONObject out = new JSONObject(); |
|||
out.put("address", psbtOutput.getString("address")); |
|||
out.put("value", psbtOutput.getInt("value")); |
|||
outputs.put(out); |
|||
} |
|||
} |
|||
|
|||
private boolean isSignedPsbt(String fileName) { |
|||
Matcher matcher = signedTxnPattern.matcher(fileName); |
|||
return matcher.matches(); |
|||
} |
|||
|
|||
public LiveData<List<String>> loadUnsignPsbt() { |
|||
MutableLiveData<List<String>> result = new MutableLiveData<>(); |
|||
AppExecutors.getInstance().diskIO().execute(() -> { |
|||
List<String> fileList = new ArrayList<>(); |
|||
if (storage != null) { |
|||
File[] files = storage.getElectrumDir().listFiles(); |
|||
if (files != null) { |
|||
for (File f : files) { |
|||
if (f.getName().endsWith(".psbt") |
|||
&& !isSignedPsbt(f.getName())) { |
|||
fileList.add(f.getName()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
result.postValue(fileList); |
|||
}); |
|||
return result; |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.viewmodel; |
|||
|
|||
import android.content.Context; |
|||
|
|||
import com.cobo.cold.R; |
|||
import com.cobo.cold.Utilities; |
|||
|
|||
import static com.cobo.cold.ui.fragment.setting.MainPreferenceFragment.SETTING_CHOOSE_WATCH_WALLET; |
|||
|
|||
public enum SupportedWatchWallet { |
|||
COBO("0"), |
|||
ELECTRUM("1"), |
|||
WASABI("2"), |
|||
BLUE("3"), |
|||
GENERIC("4"); |
|||
|
|||
private String walletId; |
|||
SupportedWatchWallet(String walletId) { |
|||
this.walletId = walletId; |
|||
} |
|||
|
|||
public String getWalletId() { |
|||
return walletId; |
|||
} |
|||
|
|||
public String getWalletName(Context context) { |
|||
return context.getResources() |
|||
.getStringArray(R.array.watch_wallet_list)[Integer.parseInt(walletId)]; |
|||
} |
|||
|
|||
public static SupportedWatchWallet getSupportedWatchWallet(Context context) { |
|||
String wallet = Utilities.getPrefs(context) |
|||
.getString(SETTING_CHOOSE_WATCH_WALLET, ELECTRUM.getWalletId()); |
|||
SupportedWatchWallet selectWatchWallet = ELECTRUM; |
|||
for (SupportedWatchWallet watchWallet: SupportedWatchWallet.values()) { |
|||
if (watchWallet.getWalletId().equals(wallet)) { |
|||
selectWatchWallet = watchWallet; |
|||
break; |
|||
} |
|||
} |
|||
return selectWatchWallet; |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.viewmodel; |
|||
|
|||
import android.app.Application; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.AndroidViewModel; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
|
|||
import com.cobo.coinlib.utils.Coins; |
|||
import com.cobo.cold.AppExecutors; |
|||
import com.cobo.cold.DataRepository; |
|||
import com.cobo.cold.MainApplication; |
|||
import com.cobo.cold.callables.GetMasterFingerprintCallable; |
|||
import com.cobo.cold.db.entity.AccountEntity; |
|||
import com.cobo.cold.db.entity.CoinEntity; |
|||
|
|||
public class WalletInfoViewModel extends AndroidViewModel { |
|||
private final MutableLiveData<String> fingerprint = new MutableLiveData<>(""); |
|||
private final MutableLiveData<String> xpub = new MutableLiveData<>(""); |
|||
|
|||
public WalletInfoViewModel(@NonNull Application application) { |
|||
super(application); |
|||
} |
|||
|
|||
public MutableLiveData<String> getFingerprint() { |
|||
AppExecutors.getInstance().diskIO().execute(() -> { |
|||
String masterFingerprint = new GetMasterFingerprintCallable().call(); |
|||
fingerprint.postValue(masterFingerprint); |
|||
}); |
|||
return fingerprint; |
|||
} |
|||
|
|||
public MutableLiveData<String> getXpub(Coins.Account account) { |
|||
AppExecutors.getInstance().diskIO().execute(() -> { |
|||
DataRepository repo = ((MainApplication)getApplication()).getRepository(); |
|||
CoinEntity coinEntity = repo.loadCoinEntityByCoinCode(Coins.BTC.coinCode()); |
|||
AccountEntity accountEntity = repo.loadAccountsByPath(coinEntity.getId(), account.getPath()); |
|||
xpub.postValue(accountEntity.getExPub()); |
|||
}); |
|||
return xpub; |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.cold.viewmodel; |
|||
|
|||
public class WatchWalletNotMatchException extends Exception { |
|||
public WatchWalletNotMatchException(String message) { |
|||
super(message); |
|||
} |
|||
} |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 699 B |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
|
|||
<solid android:color="@color/colorAccent" /> |
|||
<corners android:radius="10.5dp" /> |
|||
</shape> |
@ -0,0 +1,83 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
|||
|
|||
<data> |
|||
<variable |
|||
name="value" |
|||
type="int" /> |
|||
</data> |
|||
|
|||
<LinearLayout |
|||
android:orientation="vertical" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:background="#181717"> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="53dp" |
|||
android:orientation="horizontal" |
|||
android:background="?attr/selectableItemBackground"> |
|||
<TextView |
|||
android:id="@+id/title" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/select_address_num" |
|||
android:layout_centerVertical="true" |
|||
android:paddingHorizontal="14dp" |
|||
android:textColor="@color/white" /> |
|||
|
|||
<ImageView |
|||
android:id="@+id/close" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:src="@drawable/close" |
|||
android:tint="@color/colorAccent" |
|||
android:padding="10dp" |
|||
android:layout_centerVertical="true" |
|||
android:layout_alignParentEnd="true" |
|||
android:layout_marginEnd="6dp" /> |
|||
</RelativeLayout> |
|||
<cn.carbswang.android.numberpickerview.library.NumberPickerView |
|||
android:id="@+id/picker" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="0dp" |
|||
android:layout_weight="1" |
|||
app:npv_ShowDivider="true" |
|||
app:npv_DividerColor="#0bffffff" |
|||
app:npv_DividerMarginLeft="140dp" |
|||
app:npv_DividerMarginRight="140dp" |
|||
app:npv_ItemPaddingVertical="10dp" |
|||
app:npv_ShownCount="5" |
|||
app:npv_TextColorNormal="#90979c" |
|||
app:npv_TextColorSelected="@color/white" |
|||
app:npv_TextSizeNormal="14sp" |
|||
app:npv_TextSizeSelected="14sp" |
|||
app:npv_WrapSelectorWheel="false" /> |
|||
|
|||
<Button |
|||
android:id="@+id/confirm" |
|||
android:layout_width="match_parent" |
|||
android:layout_margin="16dp" |
|||
style="@style/AcceptButton" |
|||
android:text="@string/confirm_add_address"/> |
|||
</LinearLayout> |
|||
</layout> |
@ -0,0 +1,155 @@ |
|||
<?xml version="1.0" encoding="utf-8"?><!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools"> |
|||
|
|||
<data> |
|||
|
|||
</data> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_horizontal" |
|||
android:orientation="vertical"> |
|||
|
|||
<androidx.appcompat.widget.Toolbar |
|||
android:id="@+id/toolbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="?attr/actionBarSize" |
|||
android:background="?attr/colorPrimary" |
|||
app:navigationIcon="@drawable/arrow_left" |
|||
app:popupTheme="@style/AppTheme.PopupOverlay"> |
|||
|
|||
<TextView |
|||
android:id="@+id/toolbar_title" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center" |
|||
android:layout_marginEnd="20dp" |
|||
android:ellipsize="middle" |
|||
android:singleLine="true" |
|||
android:text="@string/electrum_compatibility" |
|||
android:textColor="@android:color/white" |
|||
android:textSize="15sp" /> |
|||
</androidx.appcompat.widget.Toolbar> |
|||
|
|||
<include layout="@layout/divider" /> |
|||
|
|||
<ScrollView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:gravity="center_horizontal" |
|||
android:orientation="vertical"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="10dp" |
|||
android:gravity="center"> |
|||
|
|||
<TextView |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:gravity="center" |
|||
android:text="@string/scan_xpub" |
|||
android:textColor="@color/white" |
|||
android:textSize="15sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
</LinearLayout> |
|||
|
|||
<com.cobo.cold.ui.views.qrcode.QrCodeView |
|||
android:id="@+id/qrcode" |
|||
android:layout_width="160dp" |
|||
android:layout_height="160dp" |
|||
android:layout_marginHorizontal="34dp" |
|||
android:layout_marginTop="16dp" |
|||
android:background="@color/white" |
|||
android:keepScreenOn="true" |
|||
android:padding="5dp"> |
|||
|
|||
<ImageView |
|||
android:id="@+id/img" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:visibility="visible" |
|||
tools:ignore="ContentDescription" /> |
|||
|
|||
<ProgressBar |
|||
android:id="@+id/progress" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center" |
|||
android:visibility="visible" /> |
|||
</com.cobo.cold.ui.views.qrcode.QrCodeView> |
|||
|
|||
<com.cobo.cold.ui.views.SpanedTextView |
|||
android:id="@+id/export_to_sdcard" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginHorizontal="16dp" |
|||
android:layout_marginTop="10dp" |
|||
android:paddingVertical="10dp" |
|||
android:gravity="center" |
|||
android:text="@string/electrum_qrcode_hint" |
|||
android:textColor="@color/white" |
|||
android:textSize="12sp" /> |
|||
|
|||
|
|||
<androidx.legacy.widget.Space |
|||
android:layout_width="match_parent" |
|||
android:layout_height="0dp" |
|||
android:layout_weight="1" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/address_type" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="10dp" |
|||
android:gravity="center" |
|||
android:textColor="@color/white" |
|||
android:textSize="15sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/expub" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginHorizontal="16dp" |
|||
android:layout_marginTop="8dp" |
|||
android:gravity="center" |
|||
android:textColor="@color/white40" |
|||
android:textSize="15sp" |
|||
tools:text="ypub6D3i46Y43SFfjEBYheBK3btYMRm9Cfb8Tt4M5Bv16tArNBw5ATNyJWjdcMyLxoCdHWTvm3ak7j2BWacq5Lw478aYUeARoYm4dvaQgJBAGsb" /> |
|||
|
|||
<Button |
|||
android:id="@+id/done" |
|||
android:layout_width="match_parent" |
|||
android:layout_margin="16dp" |
|||
style="@style/AcceptButton" |
|||
android:text="@string/complete"/> |
|||
</LinearLayout> |
|||
</ScrollView> |
|||
</LinearLayout> |
|||
</layout> |
@ -1,97 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
|||
|
|||
<data> |
|||
|
|||
<variable |
|||
name="value" |
|||
type="int" /> |
|||
</data> |
|||
|
|||
<FrameLayout |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="263dp" |
|||
android:layout_height="319dp" |
|||
android:layout_gravity="center" |
|||
android:orientation="vertical"> |
|||
|
|||
|
|||
<TextView |
|||
android:id="@+id/title" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="30dp" |
|||
android:gravity="center" |
|||
android:paddingHorizontal="16dp" |
|||
android:background="@color/colorAccent" |
|||
android:text="@string/text_create_address_main_text" |
|||
android:textColor="#4a4a4a" /> |
|||
|
|||
<cn.carbswang.android.numberpickerview.library.NumberPickerView |
|||
android:id="@+id/picker" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="0dp" |
|||
android:layout_weight="1" |
|||
android:background="@color/white" |
|||
app:npv_ShowDivider="false" |
|||
app:npv_ShownCount="7" |
|||
app:npv_TextColorNormal="#90979c" |
|||
app:npv_TextColorSelected="#4a4a4a" |
|||
app:npv_TextSizeNormal="18sp" |
|||
app:npv_TextSizeSelected="23sp" |
|||
app:npv_WrapSelectorWheel="false" /> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="48dp" |
|||
android:orientation="horizontal" |
|||
android:background="@color/colorAccent"> |
|||
|
|||
<TextView |
|||
android:id="@+id/cancel" |
|||
android:layout_width="0dp" |
|||
android:layout_weight="1" |
|||
android:gravity="center" |
|||
android:text="@string/cancel" |
|||
android:textColor="@color/white" |
|||
android:background="?attr/selectableItemBackground" |
|||
android:layout_height="match_parent" /> |
|||
|
|||
<View |
|||
android:layout_width="1dp" |
|||
android:layout_height="match_parent" |
|||
android:background="@color/white" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/confirm" |
|||
android:layout_width="0dp" |
|||
android:layout_weight="1" |
|||
android:gravity="center" |
|||
android:text="@string/confirm" |
|||
android:textColor="@color/white" |
|||
android:background="?attr/selectableItemBackground" |
|||
android:layout_height="match_parent" /> |
|||
</LinearLayout> |
|||
</LinearLayout> |
|||
</FrameLayout> |
|||
</layout> |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:orientation="vertical" android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
</LinearLayout> |
@ -0,0 +1,244 @@ |
|||
<?xml version="1.0" encoding="utf-8"?><!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools"> |
|||
|
|||
<data> |
|||
|
|||
</data> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:orientation="vertical"> |
|||
|
|||
<androidx.appcompat.widget.Toolbar |
|||
android:id="@+id/toolbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="?attr/actionBarSize" |
|||
android:background="?attr/colorPrimary" |
|||
app:navigationIcon="@drawable/arrow_left" |
|||
app:popupTheme="@style/AppTheme.PopupOverlay"> |
|||
|
|||
<TextView |
|||
android:id="@+id/toolbar_title" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center" |
|||
android:text="@string/wallet_info" |
|||
android:textColor="@android:color/white" |
|||
android:textSize="15sp" /> |
|||
</androidx.appcompat.widget.Toolbar> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="60dp"> |
|||
|
|||
<TextView |
|||
android:id="@+id/fingerprint_label" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="12dp" |
|||
android:text="@string/wallet" |
|||
android:textColor="@color/white40" |
|||
android:textSize="14sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/fingerprint" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/fingerprint_label" |
|||
android:layout_alignStart="@id/fingerprint_label" |
|||
android:layout_marginTop="5dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="14sp" |
|||
tools:text="5271c071" /> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
|
|||
<RelativeLayout |
|||
|
|||
android:layout_width="match_parent" |
|||
android:layout_height="60dp"> |
|||
|
|||
<TextView |
|||
android:id="@+id/address_format_label" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="12dp" |
|||
android:text="@string/addressType" |
|||
android:textColor="@color/white40" |
|||
android:textSize="14sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/address_format" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/address_format_label" |
|||
android:layout_alignStart="@id/address_format_label" |
|||
android:layout_marginTop="5dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="14sp" |
|||
tools:text="@string/nested_segwit" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/switch_address" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_centerVertical="true" |
|||
android:layout_marginStart="12dp" |
|||
android:layout_toEndOf="@id/address_format" |
|||
android:layout_alignTop="@id/address_format" |
|||
android:text="@string/switch_address_type" |
|||
android:textSize="9sp" |
|||
android:paddingHorizontal="8dp" |
|||
android:paddingVertical="4dp" |
|||
android:textColor="@color/white" |
|||
android:background="@drawable/text_button_bg" |
|||
/> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="60dp"> |
|||
|
|||
<TextView |
|||
android:id="@+id/address_type_label" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="12dp" |
|||
android:text="@string/script_type" |
|||
android:textColor="@color/white40" |
|||
android:textSize="14sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/address_type" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/address_type_label" |
|||
android:layout_alignStart="@id/address_type_label" |
|||
android:layout_marginTop="5dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="14sp" |
|||
tools:text="P2SH" /> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="60dp"> |
|||
|
|||
<TextView |
|||
android:id="@+id/hdpath" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="12dp" |
|||
android:text="@string/address_path" |
|||
android:textColor="@color/white40" |
|||
android:textSize="14sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/path" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/hdpath" |
|||
android:layout_alignStart="@id/hdpath" |
|||
android:layout_marginTop="5dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="14sp" |
|||
tools:text="M/49'/0'/0'" /> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="120dp"> |
|||
|
|||
<TextView |
|||
android:id="@+id/xpub_label" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="12dp" |
|||
android:text="@string/xpub" |
|||
android:textColor="@color/white40" |
|||
android:textSize="14sp" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/xpub" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/xpub_label" |
|||
android:layout_alignStart="@id/xpub_label" |
|||
android:layout_marginTop="5dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="14sp" |
|||
tools:text="ypub6WizmVHgZZHMNUtq4T84HzzrTakvdpwUzsmSsFT4ZaRoJpavKYmZweKd1A4aDZz3p51KMPbWwfii9UMnUKvsXgVFdmQrgPYhy7APibMeT3Z" /> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
|
|||
<TextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:textColor="@color/colorAccent" |
|||
android:layout_margin="16dp" |
|||
android:textSize="13sp" |
|||
android:text="@string/wallet_info_hint"/> |
|||
|
|||
</LinearLayout> |
|||
</layout> |
@ -0,0 +1,55 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
~ Copyright (c) 2020 Cobo |
|||
~ |
|||
~ This program is free software: you can redistribute it and/or modify |
|||
~ it under the terms of the GNU General Public License as published by |
|||
~ the Free Software Foundation, either version 3 of the License, or |
|||
~ (at your option) any later version. |
|||
~ |
|||
~ This program is distributed in the hope that it will be useful, |
|||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
~ GNU General Public License for more details. |
|||
~ |
|||
~ You should have received a copy of the GNU General Public License |
|||
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
|
|||
<data> |
|||
|
|||
</data> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="45dp" |
|||
android:background="?attr/selectableItemBackground"> |
|||
|
|||
<TextView |
|||
android:id="@+id/title" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:textColor="@color/white" |
|||
android:textSize="15sp" |
|||
android:gravity="center" |
|||
android:layout_centerVertical="true" /> |
|||
|
|||
<ImageView |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentEnd="true" |
|||
android:layout_marginEnd="16dp" |
|||
android:src="@drawable/arrow_right" |
|||
android:layout_centerVertical="true" /> |
|||
|
|||
<include |
|||
layout="@layout/divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_marginHorizontal="16dp" |
|||
android:layout_alignParentBottom="true" /> |
|||
</RelativeLayout> |
|||
</layout> |
@ -1,53 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.coinlib.coins; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import org.bitcoinj.core.NetworkParameters; |
|||
import org.bitcoinj.crypto.DeterministicKey; |
|||
import org.bitcoinj.crypto.HDKeyDerivation; |
|||
import org.bitcoinj.params.MainNetParams; |
|||
|
|||
public abstract class AbsDeriver { |
|||
public static AbsDeriver newInstance(@NonNull String coinCode) { |
|||
try { |
|||
Class clazz = Class.forName(CoinReflect.getCoinClassByCoinCode(coinCode) + "$Deriver"); |
|||
return (AbsDeriver) clazz.newInstance(); |
|||
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
protected static final NetworkParameters MAINNET = MainNetParams.get(); |
|||
|
|||
protected DeterministicKey getAddrDeterministicKey(String accountXpub, int changeIndex, int addressIndex) { |
|||
DeterministicKey account = DeterministicKey.deserializeB58(accountXpub, MAINNET); |
|||
DeterministicKey change = HDKeyDerivation.deriveChildKey(account, changeIndex); |
|||
return HDKeyDerivation.deriveChildKey(change, addressIndex); |
|||
} |
|||
|
|||
protected DeterministicKey getDeterministicKey(String xPub) { |
|||
return DeterministicKey.deserializeB58(xPub, MAINNET); |
|||
} |
|||
|
|||
public abstract String derive(String xPubKey, int changeIndex, int addrIndex); |
|||
|
|||
public abstract String derive(String xPubKey); |
|||
} |
@ -0,0 +1,68 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.coinlib.coins.BTC; |
|||
|
|||
import org.bitcoinj.core.LegacyAddress; |
|||
import org.bitcoinj.core.SegwitAddress; |
|||
import org.bitcoinj.crypto.DeterministicKey; |
|||
import org.bitcoinj.crypto.HDKeyDerivation; |
|||
import org.bitcoinj.params.MainNetParams; |
|||
import org.bitcoinj.script.Script; |
|||
import org.bitcoinj.script.ScriptBuilder; |
|||
|
|||
import static com.cobo.coinlib.coins.BTC.Btc.AddressType.P2PKH; |
|||
import static com.cobo.coinlib.coins.BTC.Btc.AddressType.P2SH; |
|||
import static com.cobo.coinlib.coins.BTC.Btc.AddressType.SegWit; |
|||
|
|||
public class Deriver { |
|||
|
|||
protected DeterministicKey getAddrDeterministicKey(String accountXpub, int changeIndex, int addressIndex) { |
|||
DeterministicKey account = DeterministicKey.deserializeB58(accountXpub, MainNetParams.get()); |
|||
DeterministicKey change = HDKeyDerivation.deriveChildKey(account, changeIndex); |
|||
return HDKeyDerivation.deriveChildKey(change, addressIndex); |
|||
} |
|||
|
|||
|
|||
public String derive(String accountXpub, |
|||
int changeIndex, |
|||
int addressIndex, |
|||
Btc.AddressType type) { |
|||
DeterministicKey address = getAddrDeterministicKey(accountXpub, changeIndex, addressIndex); |
|||
|
|||
if (type == P2PKH) { |
|||
return LegacyAddress.fromPubKeyHash(MainNetParams.get(),address.getPubKeyHash()) |
|||
.toBase58(); |
|||
} else if(type == SegWit) { |
|||
return SegwitAddress.fromHash(MainNetParams.get(), address.getPubKeyHash()).toBech32(); |
|||
} else if (type == P2SH){ |
|||
return LegacyAddress.fromScriptHash(MainNetParams.get(), |
|||
segWitOutputScript(address.getPubKeyHash()).getPubKeyHash()).toBase58(); |
|||
} else { |
|||
throw new IllegalArgumentException(); |
|||
} |
|||
} |
|||
|
|||
protected Script segWitOutputScript(byte[] pubKeyHash) { |
|||
return ScriptBuilder.createP2SHOutputScript(segWitRedeemScript(pubKeyHash)); |
|||
} |
|||
|
|||
private Script segWitRedeemScript(byte[] pubKeyHash) { |
|||
return new ScriptBuilder().smallNum(0).data(pubKeyHash).build(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Copyright (c) 2020 Cobo |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.cobo.coinlib.coins; |
|||
|
|||
import android.text.TextUtils; |
|||
|
|||
public class SignPsbtResult { |
|||
public final String txId; |
|||
public final String psbtB64; |
|||
|
|||
public SignPsbtResult(String txId, String psbtB64) { |
|||
this.txId = txId; |
|||
this.psbtB64 = psbtB64; |
|||
} |
|||
|
|||
public boolean isValid() { |
|||
return !TextUtils.isEmpty(txId) && !TextUtils.isEmpty(psbtB64); |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue