diff --git a/app/build.gradle b/app/build.gradle index a80aac7..812d19b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,4 +68,5 @@ dependencies { implementation 'com.github.esafirm.android-image-picker:imagepicker:1.11.1' compile 'id.zelory:compressor:2.1.0' compile 'com.vincent.filepicker:MultiTypeFilePicker:1.0.7' + compile 'com.andrognito.patternlockview:patternlockview:1.0.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3b5bc5e..2a633fc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,6 +47,9 @@ + + > generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList(); + + final PatternRequestActivity thisActivity = this; + + generalSettingsLiveData.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List generalSettings) { + patternEncrypted = ""; + + if (generalSettings != null){ + for (GeneralSetting generalSetting:generalSettings) { + if (generalSetting.getName().equals(GeneralSetting.SETTING_PATTERN)){ + if (!generalSetting.getValue().isEmpty()){ + patternEncrypted = generalSetting.getValue(); + + patternLockView.addPatternLockListener(new PatternLockViewListener() { + @Override + public void onStarted() { + + } + + @Override + public void onProgress(List progressPattern) { + + } + + @Override + public void onComplete(List pattern) { + if (PasswordManager.checkPassword(patternEncrypted,patternToString(pattern))){ + thisActivity.finish(); + } else { + patternLockView.clearPattern(); + patternLockView.requestFocus(); + } + } + + @Override + public void onCleared() { + + } + }); + } + break; + } + } + } + } + }); + } + + public String patternToString(List pattern){ + String patternString = ""; + for (PatternLockView.Dot nextDot : pattern){ + patternString = patternString+(nextDot.getRow()*3+nextDot.getColumn()); + } + + return patternString; + } +} + + diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/SettingsActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/SettingsActivity.java index 222c74a..8f708f9 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/SettingsActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/SettingsActivity.java @@ -100,8 +100,8 @@ public class SettingsActivity extends AppCompatActivity{ return new SecuritySettingsFragment(); case 2: return new BackupsSettingsFragment(); - case 3: - return new AccountsSettingsFragment(); + //case 3: + // return new AccountsSettingsFragment(); } @@ -110,7 +110,7 @@ public class SettingsActivity extends AppCompatActivity{ @Override public int getCount() { - return 4; + return 3; } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java index 852daa9..b08726c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java +++ b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java @@ -13,6 +13,7 @@ import android.support.v4.app.FragmentActivity; import java.util.List; +import cy.agorise.crystalwallet.activities.PatternRequestActivity; import cy.agorise.crystalwallet.activities.PinRequestActivity; import cy.agorise.crystalwallet.models.GeneralSetting; import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel; @@ -24,6 +25,7 @@ import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel; public class CrystalSecurityMonitor implements Application.ActivityLifecycleCallbacks { private int numStarted = 0; private String passwordEncrypted; + private String patternEncrypted; public CrystalSecurityMonitor(final FragmentActivity fragmentActivity){ GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of(fragmentActivity).get(GeneralSettingListViewModel.class); @@ -38,12 +40,17 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall if (generalSettings != null){ for (GeneralSetting generalSetting:generalSettings) { if (generalSetting.getName().equals(GeneralSetting.SETTING_PASSWORD)){ - founded = true; if (!generalSetting.getValue().isEmpty()){ passwordEncrypted = generalSetting.getValue(); callPasswordRequest(fragmentActivity); } break; + } else if (generalSetting.getName().equals(GeneralSetting.SETTING_PATTERN)){ + if (!generalSetting.getValue().isEmpty()){ + patternEncrypted = generalSetting.getValue(); + callPasswordRequest(fragmentActivity); + } + break; } } } @@ -54,7 +61,10 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall @Override public void onActivityStarted(Activity activity) { if (numStarted == 0) { - if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) { + if ( + ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) + || ((this.patternEncrypted != null) && (!this.patternEncrypted.equals(""))) + ) { callPasswordRequest(activity); } } @@ -65,7 +75,10 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall public void onActivityStopped(Activity activity) { numStarted--; if (numStarted == 0) { - if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) { + if ( + ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) + || ((this.patternEncrypted != null) && (!this.patternEncrypted.equals(""))) + ) { callPasswordRequest(activity); } } @@ -73,9 +86,16 @@ public class CrystalSecurityMonitor implements Application.ActivityLifecycleCall public void callPasswordRequest(Activity activity){ if ((!activity.getIntent().hasExtra("ACTIVITY_TYPE")) || (!activity.getIntent().getStringExtra("ACTIVITY_TYPE").equals("PASSWORD_REQUEST"))) { - Intent intent = new Intent(activity, PinRequestActivity.class); - intent.putExtra("ACTIVITY_TYPE", "PASSWORD_REQUEST"); - activity.startActivity(intent); + Intent intent = null; + if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) { + intent = new Intent(activity, PinRequestActivity.class); + } else if ((this.patternEncrypted != null) && (!this.patternEncrypted.equals(""))) { + intent = new Intent(activity, PatternRequestActivity.class); + } + if (intent != null) { + intent.putExtra("ACTIVITY_TYPE", "PASSWORD_REQUEST"); + activity.startActivity(intent); + } } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/PatternSecurityFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/PatternSecurityFragment.java new file mode 100644 index 0000000..2a53dd7 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/PatternSecurityFragment.java @@ -0,0 +1,223 @@ +package cy.agorise.crystalwallet.fragments; + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.ViewModelProviders; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.andrognito.patternlockview.PatternLockView; +import com.andrognito.patternlockview.listener.PatternLockViewListener; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnTextChanged; +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.GeneralSetting; +import cy.agorise.crystalwallet.util.PasswordManager; +import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel; +import cy.agorise.crystalwallet.viewmodels.validators.PinSecurityValidator; +import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener; +import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField; + +/** + * Created by xd on 1/18/18. + */ + +public class PatternSecurityFragment extends Fragment { + + GeneralSettingListViewModel generalSettingListViewModel; + GeneralSetting patternGeneralSetting; + //PatternSecurityValidator patternSecurityValidator; + + @BindView(R.id.patternLockView) + PatternLockView patternLockView; + @BindView(R.id.tvPatternText) + TextView tvPatternText; + + private PatternLockViewListener actualPatternListener; + private String patternEntered; + + public PatternSecurityFragment() { + // Required empty public constructor + } + + public static PatternSecurityFragment newInstance() { + PatternSecurityFragment fragment = new PatternSecurityFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + return fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View v = inflater.inflate(R.layout.fragment_pattern_security, container, false); + ButterKnife.bind(this, v); + + generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class); + LiveData> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList(); + + generalSettingsLiveData.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List generalSettings) { + boolean founded = false; + + if (generalSettings != null){ + for (GeneralSetting generalSetting:generalSettings) { + if (generalSetting.getName().equals(GeneralSetting.SETTING_PATTERN)){ + founded = true; + if (!generalSetting.getValue().isEmpty()){ + patternGeneralSetting = generalSetting; + showEnterPatternUI(); + } else { + showNewPatternUI(); + } + break; + } + } + if (!founded){ + showNewPatternUI(); + } + } else { + showNewPatternUI(); + } + } + }); + + return v; + } + + public String patternToString(List pattern){ + String patternString = ""; + for (PatternLockView.Dot nextDot : pattern){ + patternString = patternString+(nextDot.getRow()*3+nextDot.getColumn()); + } + + return patternString; + } + + public void removePatternListener(){ + if (actualPatternListener != null){ + patternLockView.removePatternLockListener(actualPatternListener); + actualPatternListener = null; + } + } + + public void showNewPatternUI(){ + removePatternListener(); + patternLockView.clearPattern(); + tvPatternText.setText("Enter new pattern"); + + actualPatternListener = new PatternLockViewListener() { + @Override + public void onStarted() { + + } + + @Override + public void onProgress(List progressPattern) { + + } + + @Override + public void onComplete(List pattern) { + patternEntered = patternToString(pattern); + showConfirmPatternUI(); + } + + @Override + public void onCleared() { + + } + }; + patternLockView.addPatternLockListener(actualPatternListener); + } + + public void showConfirmPatternUI(){ + removePatternListener(); + patternLockView.clearPattern(); + patternLockView.requestFocus(); + tvPatternText.setText("Confirm new pattern"); + + actualPatternListener = new PatternLockViewListener() { + @Override + public void onStarted() { + + } + + @Override + public void onProgress(List progressPattern) { + + } + + @Override + public void onComplete(List pattern) { + if (patternEntered.equals(patternToString(pattern))){ + savePattern(patternEntered); + showEnterPatternUI(); + } + } + + @Override + public void onCleared() { + + } + }; + patternLockView.addPatternLockListener(actualPatternListener); + } + + public void savePattern(String pattern){ + String patternEncripted = PasswordManager.encriptPassword(pattern); + + if (patternGeneralSetting == null) { + patternGeneralSetting = new GeneralSetting(); + patternGeneralSetting.setName(GeneralSetting.SETTING_PATTERN); + } + + patternGeneralSetting.setValue(patternEncripted); + generalSettingListViewModel.saveGeneralSetting(patternGeneralSetting); + } + + public void showEnterPatternUI(){ + removePatternListener(); + patternLockView.clearPattern(); + tvPatternText.setText("Enter old pattern"); + + actualPatternListener = new PatternLockViewListener() { + @Override + public void onStarted() { + + } + + @Override + public void onProgress(List progressPattern) { + + } + + @Override + public void onComplete(List pattern) { + if (PasswordManager.checkPassword(patternGeneralSetting.getValue(),patternToString(pattern))){ + showNewPatternUI(); + } + } + + @Override + public void onCleared() { + + } + }; + patternLockView.addPatternLockListener(actualPatternListener); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/SecuritySettingsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/SecuritySettingsFragment.java index 0407bcf..39e4a25 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/SecuritySettingsFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/SecuritySettingsFragment.java @@ -69,7 +69,7 @@ public class SecuritySettingsFragment extends Fragment { case 1: return new PinSecurityFragment(); case 2: - return new BackupsSettingsFragment(); + return new PatternSecurityFragment(); } return null; //new OnConstructionFragment(); diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java b/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java index 62bab19..80edd47 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java +++ b/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java @@ -20,6 +20,7 @@ public class GeneralSetting { public final static String SETTING_NAME_PREFERRED_LANGUAGE = "PREFERRED_LANGUAGE"; public final static String SETTING_NAME_TIME_ZONE = "TIME_ZONE"; public final static String SETTING_PASSWORD = "PASSWORD"; + public final static String SETTING_PATTERN = "PATTERN"; public final static String SETTING_NAME_RECEIVED_FUNDS_SOUND_PATH = "RECEIVED_FUNDS_SOUND_PATH"; /** diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/GeneralSettingListViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/GeneralSettingListViewModel.java index 22b2feb..b3d3c35 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/GeneralSettingListViewModel.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/GeneralSettingListViewModel.java @@ -31,7 +31,7 @@ public class GeneralSettingListViewModel extends AndroidViewModel { } public void saveGeneralSetting(GeneralSetting generalSetting){ - this.db.generalSettingDao().insertGeneralSetting(generalSetting); + this.db.generalSettingDao ().insertGeneralSetting(generalSetting); } public void saveGeneralSettings(GeneralSetting... generalSettings){ diff --git a/app/src/main/res/layout/activity_pattern_request.xml b/app/src/main/res/layout/activity_pattern_request.xml new file mode 100644 index 0000000..9eedb0f --- /dev/null +++ b/app/src/main/res/layout/activity_pattern_request.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 2e76881..7b91bba 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -104,11 +104,12 @@ android:layout_height="wrap_content" android:text="@string/backups" /> - + android:text="@string/accounts" + android:visibility="gone" />--> diff --git a/app/src/main/res/layout/fragment_pattern_security.xml b/app/src/main/res/layout/fragment_pattern_security.xml new file mode 100644 index 0000000..643a48f --- /dev/null +++ b/app/src/main/res/layout/fragment_pattern_security.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_security_settings.xml b/app/src/main/res/layout/fragment_security_settings.xml index 19840d0..d3ae66f 100644 --- a/app/src/main/res/layout/fragment_security_settings.xml +++ b/app/src/main/res/layout/fragment_security_settings.xml @@ -68,6 +68,7 @@ android:layout_width="match_parent" android:layout_height="140dp" android:background="@color/lightGray" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" />