package com.mygeopay.wallet.ui; import android.app.Activity; import android.content.DialogInterface; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.mygeopay.core.wallet.Wallet; import com.mygeopay.wallet.R; import com.mygeopay.wallet.WalletApplication; import com.mygeopay.wallet.util.Fonts; import com.mygeopay.wallet.util.LayoutUtils; import com.mygeopay.wallet.util.Qr; import com.mygeopay.wallet.util.WeakHandler; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.HDKeyDerivation; import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.wallet.DeterministicSeed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spongycastle.crypto.params.KeyParameter; import static com.mygeopay.core.Preconditions.checkState; /** * @author John L. Jegutanis */ public class ShowSeedFragment extends Fragment { private static final Logger log = LoggerFactory.getLogger(ShowSeedFragment.class); private static final int UPDATE_VIEW = 0; private int maxQrSize; private View seedLayout; private View seedEncryptedLayout; private TextView seedView; private View seedPasswordProtectedView; private ImageView qrView; private Listener listener; private LoadSeedTask decryptSeedTask; private Wallet wallet; private String password; private final Handler handler = new MyHandler(this); private static class MyHandler extends WeakHandler<ShowSeedFragment> { public MyHandler(ShowSeedFragment ref) { super(ref); } @Override protected void weakHandleMessage(ShowSeedFragment ref, Message msg) { switch (msg.what) { case UPDATE_VIEW: ref.updateView(); } } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); maxQrSize = LayoutUtils.calculateMaxQrCodeSize(getResources()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_show_seed, container, false); seedLayout = view.findViewById(R.id.show_seed_layout); seedEncryptedLayout = view.findViewById(R.id.seed_encrypted_layout); seedEncryptedLayout.setVisibility(View.GONE); // Hide layout as maybe we have to show the password dialog seedLayout.setVisibility(View.GONE); seedView = (TextView) view.findViewById(R.id.seed); seedPasswordProtectedView = view.findViewById(R.id.seed_password_protected); Fonts.setTypeface(view.findViewById(R.id.seed_password_protected_lock), Fonts.Font.COINOMI_FONT_ICONS); qrView = (ImageView) view.findViewById(R.id.qr_code_seed); TextView lockIcon = (TextView) view.findViewById(R.id.lock_icon); Fonts.setTypeface(lockIcon, Fonts.Font.COINOMI_FONT_ICONS); lockIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { passwordDialog.show(getFragmentManager(), null); } }); updateView(); return view; } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { listener = (Listener) activity; WalletApplication application = (WalletApplication) activity.getApplication(); wallet = application.getWallet(); } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement " + ShowSeedFragment.Listener.class.getCanonicalName()); } } @Override public void onDetach() { super.onDetach(); listener = null; } private void updateView() { if (wallet != null) { if (wallet.getSeed() == null) { if (listener != null) listener.onSeedNotAvailable(); } else if (wallet.getSeed().isEncrypted()) { seedEncryptedLayout.setVisibility(View.VISIBLE); if (password == null) { passwordDialog.show(getFragmentManager(), null); } else { maybeStartDecryptTask(); } } else { seedEncryptedLayout.setVisibility(View.GONE); maybeStartDecryptTask(); } } } DialogFragment passwordDialog = new UnlockWalletDialog() { @Override public void onPassword(String password) { ShowSeedFragment.this.password = password; handler.sendEmptyMessage(UPDATE_VIEW); } @Override public void onCancel() { } }; private void maybeStartDecryptTask() { if (decryptSeedTask == null) { decryptSeedTask = new LoadSeedTask(); decryptSeedTask.execute(); } } private class LoadSeedTask extends AsyncTask<Void, Void, Void> { private Dialogs.ProgressDialogFragment busyDialog; Bitmap qrCodeBitmap; private String seedString; private boolean isSeedPasswordProtected; @Override protected void onPreExecute() { super.onPreExecute(); busyDialog = Dialogs.ProgressDialogFragment.newInstance( getResources().getString(R.string.seed_working)); busyDialog.show(getFragmentManager(), null); } @Override protected Void doInBackground(Void... params) { KeyParameter aesKey = null; DeterministicKey masterKey = null; DeterministicSeed seed = wallet.getSeed(); if (seed != null) { try { if (wallet.getKeyCrypter() != null) { KeyCrypter crypter = wallet.getKeyCrypter(); aesKey = crypter.deriveKey(password); seed = wallet.getSeed().decrypt(crypter, password, aesKey); masterKey = wallet.getMasterKey().decrypt(crypter, aesKey); } else { masterKey = wallet.getMasterKey(); } checkState(!seed.isEncrypted()); checkState(!masterKey.isEncrypted()); // Use empty password to check if the seed is password protected seed = new DeterministicSeed(seed.getMnemonicCode(), null, "", 0); } catch (Exception e) { log.warn("Failed recovering seed."); } } if (seed != null && masterKey != null) { seedString = Wallet.mnemonicToString(seed.getMnemonicCode()); DeterministicKey testMasterKey = HDKeyDerivation.createMasterPrivateKey(seed.getSeedBytes()); isSeedPasswordProtected = !masterKey.getPrivKey().equals(testMasterKey.getPrivKey()); qrCodeBitmap = Qr.bitmap(seedString, maxQrSize); } return null; } protected void onPostExecute(Void aVoid) { decryptSeedTask = null; password = null; busyDialog.dismissAllowingStateLoss(); if (seedString != null) { seedLayout.setVisibility(View.VISIBLE); seedEncryptedLayout.setVisibility(View.GONE); seedView.setText(seedString); qrView.setImageBitmap(qrCodeBitmap); if (isSeedPasswordProtected) { seedPasswordProtectedView.setVisibility(View.VISIBLE); } else { seedPasswordProtectedView.setVisibility(View.GONE); } } else { seedEncryptedLayout.setVisibility(View.VISIBLE); DialogBuilder.warn(getActivity(), R.string.unlocking_wallet_error_title) .setMessage(R.string.unlocking_wallet_error_detail) .setNegativeButton(R.string.button_cancel, null) .setPositiveButton(R.string.button_retry, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { passwordDialog.show(getFragmentManager(), null); } }) .create().show(); } } } public interface Listener { public void onSeedNotAvailable(); } }