Partially implemented multiple addresses per profile and profile password.

This commit is contained in:
Adrian Tiberius 2015-08-04 22:40:36 +02:00
parent 80656a219b
commit 36036c5568
7 changed files with 263 additions and 38 deletions

View File

@ -14,6 +14,7 @@ import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -24,6 +25,8 @@ import android.support.v7.widget.Toolbar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -39,12 +42,17 @@ public class BaseActivity extends AppCompatActivity {
protected List<Profile> profiles; protected List<Profile> profiles;
Spinner spinner; Spinner spinner;
SpinnerAdapter spinnerAdapter;
EditText search; EditText search;
ListView drawerList; ListView drawerList;
TextView settings; TextView settings;
TextView profileManager; TextView profileManager;
Profile requestProfile;
int currentPosition;
protected ArrayAdapter<String> drawerListAdapter; protected ArrayAdapter<String> drawerListAdapter;
@Override @Override
@ -130,21 +138,74 @@ public class BaseActivity extends AppCompatActivity {
}); });
} }
protected void changeProfile(Profile profile) {
/*
*/
Syng application = (Syng)getApplication();
List<String> privateKeys = profile.getPrivateKeys();
application.ethereum.init(privateKeys);
currentPosition = spinnerAdapter.getPosition(profile);
}
protected void requestChangeProfile(Profile profile) {
requestProfile = profile;
new MaterialDialog.Builder(BaseActivity.this)
.title(R.string.request_profile_password)
.customView(R.layout.profile_password, true)
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.contentColor(R.color.accent) // notice no 'res' postfix for literal color
.dividerColorRes(R.color.accent)
.backgroundColorRes(R.color.primary_dark)
.positiveColorRes(R.color.accent)
.negativeColorRes(R.color.accent)
.widgetColorRes(R.color.accent)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
View view = dialog.getCustomView();
EditText passwordInput = (EditText) view.findViewById(R.id.passwordInput);
if (requestProfile.decrypt(passwordInput.getText().toString())) {
changeProfile(requestProfile);
} else {
dialog.hide();
spinner.setSelection(currentPosition, false);
}
}
@Override
public void onNegative(MaterialDialog dialog) {
dialog.hide();
spinner.setSelection(currentPosition, false);
}
})
.build()
.show();
}
public void initSpinner() { public void initSpinner() {
profiles = ((Syng)getApplication()).preferenceManager.getProfiles(); profiles = ((Syng)getApplication()).preferenceManager.getProfiles();
ArrayList<String> spinnerItems = new ArrayList<>(); spinnerAdapter = new SpinnerAdapter(this, android.R.layout.simple_dropdown_item_1line, profiles);
for (Profile profile: profiles) { spinner.setAdapter(spinnerAdapter);
spinnerItems.add(profile.getName());
}
spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, spinnerItems.toArray(new String[spinnerItems.size()])));
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
//String item = (String) adapterView.getItemAtPosition(i);
if (adapterView != null && adapterView.getChildAt(0) != null) { if (adapterView != null && adapterView.getChildAt(0) != null) {
((TextView) adapterView.getChildAt(0)).setTextColor(Color.parseColor("#ffffff")); ((TextView) adapterView.getChildAt(0)).setTextColor(Color.parseColor("#ffffff"));
} }
Profile profile = spinnerAdapter.getItem(i);
if (profile.getPasswordProtectedProfile()) {
requestChangeProfile(profile);
} else {
changeProfile(profile);
}
} }
@Override @Override
@ -230,4 +291,54 @@ public class BaseActivity extends AppCompatActivity {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig); drawerToggle.onConfigurationChanged(newConfig);
} }
public class SpinnerAdapter extends ArrayAdapter<Profile> {
private Context context;
private List<Profile> values;
public SpinnerAdapter(Context context, int textViewResourceId, List<Profile> values) {
super(context, textViewResourceId, values);
this.context = context;
this.values = values;
}
public int getCount() {
return values.size();
}
public Profile getItem(int position){
return values.get(position);
}
public long getItemId(int position){
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
public View getCustomView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = getLayoutInflater();
View row=inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
TextView label=(TextView)row.findViewById(android.R.id.text1);
label.setText(spinnerAdapter.getItem(position).getName());
return row;
}
}
} }

View File

@ -48,13 +48,14 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
private static int CONSOLE_LENGTH = 10000; private static int CONSOLE_LENGTH = 10000;
private static int CONSOLE_REFRESH = 1000; private static int CONSOLE_REFRESH = 1000;
static EthereumConnector ethereum = null;
protected String handlerIdentifier = UUID.randomUUID().toString(); protected String handlerIdentifier = UUID.randomUUID().toString();
TextViewUpdater consoleUpdater = new TextViewUpdater(); TextViewUpdater consoleUpdater = new TextViewUpdater();
static DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS"); static DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
private boolean isConnectorReady = false;
private class TextViewUpdater implements Runnable { private class TextViewUpdater implements Runnable {
private String txt; private String txt;
@ -85,10 +86,8 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
consoleText.setText(MainActivity.consoleLog); consoleText.setText(MainActivity.consoleLog);
consoleText.setMovementMethod(new ScrollingMovementMethod()); consoleText.setMovementMethod(new ScrollingMovementMethod());
if (ethereum == null) { Syng application = (Syng)getApplication();
ethereum = new EthereumConnector(this, EthereumService.class); application.ethereum.registerHandler(this);
ethereum.registerHandler(this);
}
} }
@Override @Override
@ -97,8 +96,8 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
super.onPause(); super.onPause();
isPaused = true; isPaused = true;
timer.cancel(); timer.cancel();
ethereum.removeListener(handlerIdentifier); Syng application = (Syng)getApplication();
ethereum.unbindService(); application.ethereum.removeListener(handlerIdentifier);
} }
@Override @Override
@ -127,14 +126,16 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
} catch (IllegalStateException e){ } catch (IllegalStateException e){
android.util.Log.i("Damn", "resume error"); android.util.Log.i("Damn", "resume error");
} }
ethereum.bindService(); if (isConnectorReady) {
Syng application = (Syng)getApplication();
application.ethereum.addListener(handlerIdentifier, EnumSet.allOf(EventFlag.class));
}
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
ethereum.unbindService();
} }
@Override @Override
@ -231,7 +232,9 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
@Override @Override
public void onConnectorConnected() { public void onConnectorConnected() {
ethereum.addListener(handlerIdentifier, EnumSet.allOf(EventFlag.class)); isConnectorReady = true;
Syng application = (Syng)getApplication();
application.ethereum.addListener(handlerIdentifier, EnumSet.allOf(EventFlag.class));
//ethereum.connect(SystemProperties.CONFIG.activePeerIP(), SystemProperties.CONFIG.activePeerPort(), SystemProperties.CONFIG.activePeerNodeid()); //ethereum.connect(SystemProperties.CONFIG.activePeerIP(), SystemProperties.CONFIG.activePeerPort(), SystemProperties.CONFIG.activePeerNodeid());
ethereum.startJsonRpc(); ethereum.startJsonRpc();
} }
@ -239,6 +242,9 @@ public class MainActivity extends BaseActivity implements ConnectorHandler {
@Override @Override
public void onConnectorDisconnected() { public void onConnectorDisconnected() {
isConnectorReady = false;
Syng application = (Syng)getApplication();
application.ethereum.removeListener(handlerIdentifier);
} }
} }

View File

@ -8,8 +8,11 @@ import android.support.v7.widget.Toolbar;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import io.syng.entities.Profile; import io.syng.entities.Profile;
@ -37,9 +40,44 @@ public class ProfileManagerActivity extends BaseActivity implements OnFragmentIn
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Profile profile = addProfileFragment.getProfile(); final Profile profile = addProfileFragment.getProfile();
profileManagerFragment.addProfile(profile); if (profile.getPasswordProtectedProfile()) {
hideAddProfile(); new MaterialDialog.Builder(ProfileManagerActivity.this)
.title(R.string.request_profile_password)
.customView(R.layout.profile_password, true)
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.contentColor(R.color.accent) // notice no 'res' postfix for literal color
.dividerColorRes(R.color.accent)
.backgroundColorRes(R.color.primary_dark)
.positiveColorRes(R.color.accent)
.negativeColorRes(R.color.accent)
.widgetColorRes(R.color.accent)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
View view = dialog.getCustomView();
EditText passwordInput = (EditText) view.findViewById(R.id.passwordInput);
profile.encrypt(passwordInput.getText().toString());
profileManagerFragment.addProfile(profile);
hideAddProfile();
}
@Override
public void onNegative(MaterialDialog dialog) {
dialog.hide();
spinner.setSelection(currentPosition, false);
}
})
.build()
.show();
} else {
profileManagerFragment.addProfile(profile);
hideAddProfile();
}
} }
}); });
addProfileLink = (TextView)findViewById(R.id.add_profile_link); addProfileLink = (TextView)findViewById(R.id.add_profile_link);

View File

@ -2,6 +2,8 @@ package io.syng;
import android.content.res.Configuration; import android.content.res.Configuration;
import org.ethereum.android.service.EthereumConnector;
import io.syng.entities.PreferenceManager; import io.syng.entities.PreferenceManager;
@ -9,6 +11,8 @@ public class Syng extends android.support.multidex.MultiDexApplication {
public PreferenceManager preferenceManager; public PreferenceManager preferenceManager;
public static EthereumConnector ethereum = null;
@Override @Override
public void onConfigurationChanged(Configuration newConfig) { public void onConfigurationChanged(Configuration newConfig) {
@ -20,6 +24,10 @@ public class Syng extends android.support.multidex.MultiDexApplication {
super.onCreate(); super.onCreate();
preferenceManager = new PreferenceManager(this); preferenceManager = new PreferenceManager(this);
if (ethereum == null) {
ethereum = new EthereumConnector(this, EthereumService.class);
ethereum.bindService();
}
} }
@Override @Override
@ -27,5 +35,6 @@ public class Syng extends android.support.multidex.MultiDexApplication {
super.onTerminate(); super.onTerminate();
preferenceManager.close(); preferenceManager.close();
ethereum.unbindService();
} }
} }

View File

@ -1,13 +1,17 @@
package io.syng.entities; package io.syng.entities;
import org.ethereum.crypto.HashUtil;
import org.spongycastle.util.encoders.Hex;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Profile implements Serializable { public class Profile implements Serializable {
protected String name; protected String name;
protected String privateKey; protected List<String> privateKeys = new ArrayList<>();
/* "password protect profile" (encrypt the private keys) */ /* "password protect profile" (encrypt the private keys) */
protected boolean passwordProtectedProfile = false; protected boolean passwordProtectedProfile = false;
@ -16,37 +20,49 @@ public class Profile implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
protected String passwordHash;
protected transient boolean isEncrypted = false;
public Profile() { public Profile() {
this.privateKey = createPrivateKey(); this.privateKeys.add(createPrivateKey());
} }
public Profile(String privateKey) { public Profile(String privateKey) {
this.privateKey = privateKey; this.privateKeys.add(privateKey);
}
public Profile(List<String> privateKeys) {
this.privateKeys = privateKeys;
} }
protected String createPrivateKey() { protected String createPrivateKey() {
return ""; byte[] privateKey = HashUtil.sha3(HashUtil.randomPeerId());
return Hex.toHexString(privateKey);
} }
public String getPrivateKey() { public List<String> getPrivateKeys() {
if (passwordProtectedProfile) { return privateKeys;
return decryptPrivateKey(privateKey, null);
} else {
return privateKey;
}
} }
public void setPrivateKey(String privateKey) { public void setPrivateKeys(List<String> privateKeys) {
if (passwordProtectedProfile) { this.privateKeys = privateKeys;
this.privateKey = encryptPrivateKey(privateKey, null); }
} else {
this.privateKey = privateKey; public void addPrivateKey(String privateKey) {
}
this.privateKeys.add(privateKey);
}
public void removePrivateKey(String privateKey) {
this.privateKeys.remove(privateKey);
} }
public List<Dapp> getDapps() { public List<Dapp> getDapps() {
@ -77,9 +93,6 @@ public class Profile implements Serializable {
public void setPasswordProtectedProfile(boolean passwordProtectedProfile) { public void setPasswordProtectedProfile(boolean passwordProtectedProfile) {
this.passwordProtectedProfile = passwordProtectedProfile; this.passwordProtectedProfile = passwordProtectedProfile;
if (passwordProtectedProfile) {
this.privateKey = encryptPrivateKey(privateKey, null);
}
} }
public String getName() { public String getName() {
@ -92,6 +105,40 @@ public class Profile implements Serializable {
this.name = name; this.name = name;
} }
protected void setPassword(String password) {
this.passwordHash = Hex.toHexString(HashUtil.sha3(password.getBytes()));
}
public void encrypt(String password) {
if (!passwordProtectedProfile) {
setPassword(password);
List<String> encrypted = new ArrayList<>();
for (String privateKey : this.privateKeys) {
encrypted.add(encryptPrivateKey(privateKey, password));
}
this.privateKeys = encrypted;
passwordProtectedProfile = true;
}
}
public boolean decrypt(String password) {
if (passwordProtectedProfile) {
if (passwordHash != Hex.toHexString(HashUtil.sha3(password.getBytes()))) {
return false;
}
List<String> decrypted = new ArrayList<>();
for (String privateKey : this.privateKeys) {
decrypted.add(decryptPrivateKey(privateKey, password));
}
this.privateKeys = decrypted;
passwordProtectedProfile = false;
}
return true;
}
protected String encryptPrivateKey(String privateKey, String password) { protected String encryptPrivateKey(String privateKey, String password) {
// TODO: Encrypt private key // TODO: Encrypt private key

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/passwordInput"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="true"/>
</LinearLayout>

View File

@ -32,4 +32,7 @@
<string name="create_new_account">Create new Account</string> <string name="create_new_account">Create new Account</string>
<string name="import_account_from_file">Import account from file</string> <string name="import_account_from_file">Import account from file</string>
<string name="import_account_from_string">Import account from string</string> <string name="import_account_from_string">Import account from string</string>
<string name="request_profile_password">Profile Password</string>
</resources> </resources>