diff --git a/README.md b/README.md index 83bf370c..5d5ad95f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This library is available in **jCenter** which is the default Maven repository u dependencies { // other dependencies here - compile 'com.andrognito.pinlockview:pinlockview:2.0.1' + implementation 'com.andrognito.pinlockview:pinlockview:2.1.0' } ``` @@ -31,7 +31,7 @@ dependencies { com.andrognito.pinlockview pinlockview - 2.0.1 + 2.1.0 pom ``` @@ -164,7 +164,8 @@ Android & Backend Developer. Blogger. Designer. Fitness Enthusiast. - + + # License diff --git a/app/build.gradle b/app/build.gradle index a56194f5..de9556a1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 24 - buildToolsVersion "24.0.3" + compileSdkVersion 25 + buildToolsVersion "25.0.3" defaultConfig { applicationId "com.andrognito.pinlockviewapp" minSdkVersion 11 - targetSdkVersion 24 + targetSdkVersion 25 versionCode 1 versionName "1.0" } @@ -22,6 +22,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:24.2.1' + compile 'com.android.support:appcompat-v7:25.3.1' compile project(':pinlockview') } diff --git a/app/src/main/java/com/andrognito/pinlockviewapp/SampleActivity.java b/app/src/main/java/com/andrognito/pinlockviewapp/SampleActivity.java index 25d628b8..1e846109 100644 --- a/app/src/main/java/com/andrognito/pinlockviewapp/SampleActivity.java +++ b/app/src/main/java/com/andrognito/pinlockviewapp/SampleActivity.java @@ -1,7 +1,7 @@ package com.andrognito.pinlockviewapp; +import android.graphics.Color; import android.os.Bundle; -import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Window; @@ -9,10 +9,14 @@ import android.widget.ImageView; import android.view.View; import android.view.View.*; +import android.widget.RelativeLayout; +import android.widget.Toast; import com.andrognito.pinlockview.IndicatorDots; +import com.andrognito.pinlockview.InputField; import com.andrognito.pinlockview.PinLockListener; import com.andrognito.pinlockview.PinLockView; +import com.andrognito.pinlockview.SeparateDeleteButton; public class SampleActivity extends AppCompatActivity { @@ -20,13 +24,16 @@ public class SampleActivity extends AppCompatActivity { private PinLockView mPinLockView; private IndicatorDots mIndicatorDots; + private InputField mInputField; + private SeparateDeleteButton mSeparateDeleteButton; private ImageView logo; - private boolean isShowing = true; + private boolean isEnterButtonEnabled = true; private PinLockListener mPinLockListener = new PinLockListener() { @Override public void onComplete(String pin) { Log.d(TAG, "Pin complete: " + pin); + Toast.makeText(SampleActivity.this, "Pin complete: " + pin, Toast.LENGTH_SHORT).show(); } @Override @@ -49,28 +56,69 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_sample); logo = (ImageView) findViewById(R.id.profile_image); mPinLockView = (PinLockView) findViewById(R.id.pin_lock_view); + mInputField = (InputField) findViewById(R.id.input_field); mIndicatorDots = (IndicatorDots) findViewById(R.id.indicator_dots); + mSeparateDeleteButton = (SeparateDeleteButton) findViewById(R.id.separate_delete_button); + mPinLockView.attachInputField(mInputField); mPinLockView.attachIndicatorDots(mIndicatorDots); + mPinLockView.attachSeparateDeleteButton(mSeparateDeleteButton); + mPinLockView.setPinLockListener(mPinLockListener); - //mPinLockView.setCustomKeySet(new int[]{2, 3, 1, 5, 9, 6, 7, 0, 8, 4}); - //mPinLockView.enableLayoutShuffling(); + mPinLockView.setPinLength(6); + mPinLockView.setShowDeleteButton(false); + ((RelativeLayout.LayoutParams) mPinLockView.getLayoutParams()).addRule(RelativeLayout.BELOW, R.id.input_field); + mInputField.setVisibility(View.VISIBLE); + mInputField.requestFocus(); + + mSeparateDeleteButton.setShowSeparateDeleteButton(true); + mSeparateDeleteButton.setSeparateDeleteButtonColor(Color.TRANSPARENT); + mSeparateDeleteButton.setSeparateDeleteButtonPressedColor(Color.GRAY); + mSeparateDeleteButton.setImageResource(R.drawable.ic_keyboard_backspace); - mPinLockView.setPinLength(4); + mPinLockView.setUseCustomEnterButtonImages(true); + mPinLockView.setEnterButtonEnabledDrawableId(R.drawable.ic_check_box); + mPinLockView.setEnterButtonDisabledDrawableId(R.drawable.ic_check_box_outline); + mPinLockView.setEnterButtonPressedDrawableId(R.drawable.ic_check_box); + mPinLockView.setDeleteButtonDrawable(getResources().getDrawable(R.drawable.ic_cheveron_left)); - mIndicatorDots.setIndicatorType(IndicatorDots.IndicatorType.FILL_WITH_ANIMATION); + mPinLockView.detachIndicatorDots(); + mIndicatorDots.setVisibility(View.GONE); + mIndicatorDots.setIndicatorType(IndicatorDots.IndicatorType.FIXED); logo.setOnClickListener(new OnClickListener() { - public void onClick(View v) - { - if(isShowing) { - mPinLockView.setVisibility(View.GONE); - mIndicatorDots.setVisibility(View.GONE); - isShowing = false; - } else { - mPinLockView.setVisibility(View.VISIBLE); + public void onClick(View v) { + if (isEnterButtonEnabled) { + + isEnterButtonEnabled = false; + mPinLockView.setShowEnterButton(false); + mPinLockView.setSwapEnterDeleteButtons(false); + mPinLockView.setShowDeleteButton(true); + mSeparateDeleteButton.setShowSeparateDeleteButton(false); + mInputField.setVisibility(View.GONE); mIndicatorDots.setVisibility(View.VISIBLE); - isShowing = true; + ((RelativeLayout.LayoutParams) mPinLockView.getLayoutParams()).addRule(RelativeLayout.BELOW, R.id.indicator_dots); + mPinLockView.resetPinLockView(); + + mPinLockView.detachInputField(); + mPinLockView.detachSeparateDeleteButton(); + mPinLockView.attachIndicatorDots(mIndicatorDots); + } else{ + + isEnterButtonEnabled = true; + mPinLockView.setShowEnterButton(true); + mPinLockView.setSwapEnterDeleteButtons(true); + mPinLockView.setShowDeleteButton(false); + mSeparateDeleteButton.setShowSeparateDeleteButton(true); + mInputField.setVisibility(View.VISIBLE); + mIndicatorDots.setVisibility(View.GONE); + ((RelativeLayout.LayoutParams) mPinLockView.getLayoutParams()).addRule(RelativeLayout.BELOW, R.id.input_field); + mInputField.requestFocus(); + mPinLockView.resetPinLockView(); + + mPinLockView.attachInputField(mInputField); + mPinLockView.attachSeparateDeleteButton(mSeparateDeleteButton); + mPinLockView.detachIndicatorDots(); } } }); diff --git a/app/src/main/res/drawable-hdpi/ic_check_box.png b/app/src/main/res/drawable-hdpi/ic_check_box.png new file mode 100644 index 00000000..dfde503b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_box.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_check_box_outline.png b/app/src/main/res/drawable-hdpi/ic_check_box_outline.png new file mode 100644 index 00000000..ea0e7e63 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_box_outline.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_cheveron_left.png b/app/src/main/res/drawable-hdpi/ic_cheveron_left.png new file mode 100644 index 00000000..fb87e635 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_cheveron_left.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_backspace.png b/app/src/main/res/drawable-hdpi/ic_keyboard_backspace.png new file mode 100644 index 00000000..8d7c3a7d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_keyboard_backspace.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_box.png b/app/src/main/res/drawable-mdpi/ic_check_box.png new file mode 100644 index 00000000..ad595bcc Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_box.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_box_outline.png b/app/src/main/res/drawable-mdpi/ic_check_box_outline.png new file mode 100644 index 00000000..a7598b03 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_box_outline.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_cheveron_left.png b/app/src/main/res/drawable-mdpi/ic_cheveron_left.png new file mode 100644 index 00000000..e301accb Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_cheveron_left.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_keyboard_backspace.png b/app/src/main/res/drawable-mdpi/ic_keyboard_backspace.png new file mode 100644 index 00000000..81678c64 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_keyboard_backspace.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_box.png b/app/src/main/res/drawable-xhdpi/ic_check_box.png new file mode 100644 index 00000000..b1bc4430 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_box.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_box_outline.png b/app/src/main/res/drawable-xhdpi/ic_check_box_outline.png new file mode 100644 index 00000000..20ed7755 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_box_outline.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_cheveron_left.png b/app/src/main/res/drawable-xhdpi/ic_cheveron_left.png new file mode 100644 index 00000000..764df638 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_cheveron_left.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_keyboard_backspace.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_backspace.png new file mode 100644 index 00000000..5abe5bcc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_keyboard_backspace.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box.png b/app/src/main/res/drawable-xxhdpi/ic_check_box.png new file mode 100644 index 00000000..5529859e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_box.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box_outline.png b/app/src/main/res/drawable-xxhdpi/ic_check_box_outline.png new file mode 100644 index 00000000..8d34740c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_box_outline.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_cheveron_left.png b/app/src/main/res/drawable-xxhdpi/ic_cheveron_left.png new file mode 100644 index 00000000..e5f8c0db Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_cheveron_left.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_keyboard_backspace.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_backspace.png new file mode 100644 index 00000000..58343f19 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_keyboard_backspace.png differ diff --git a/app/src/main/res/layout/activity_sample.xml b/app/src/main/res/layout/activity_sample.xml index 95b96595..a72f15df 100644 --- a/app/src/main/res/layout/activity_sample.xml +++ b/app/src/main/res/layout/activity_sample.xml @@ -15,6 +15,8 @@ android:layout_marginTop="104dp" android:src="@drawable/img_no_avatar" /> + + + + + + + app:keypadShowLetters="true" + app:keypadDeleteButtonColor="#E1BEE7" + app:keypadDefaultDeleteColor="false" + app:keypadDeleteButtonPressedColor="@color/white" + app:keypadShowEnterButton="true" + app:keypadSwapEnterDeleteButtons="true" + app:keypadEnterButtonColor="@android:color/transparent" + app:keypadEnterButtonDisabledColor="@android:color/transparent" + app:keypadEnterButtonPressedColor="@color/greyish" + app:indicatorType="fixed" + /> diff --git a/build.gradle b/build.gradle index b249cff4..2976defb 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 660db70a..568ab3c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Mar 10 00:08:20 IST 2017 +#Tue Dec 12 09:32:28 EET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/pinlockview/build.gradle b/pinlockview/build.gradle index 618feb4e..2fe6adbf 100644 --- a/pinlockview/build.gradle +++ b/pinlockview/build.gradle @@ -13,7 +13,7 @@ ext { siteUrl = 'https://github.com/aritraroy/PinLockView' gitUrl = 'https://github.com/aritraroy/PinLockView.git' - libraryVersion = '2.0.1' + libraryVersion = '2.1.0' developerId = 'aritraroy' developerName = 'Aritra Roy' @@ -27,13 +27,13 @@ ext { android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 11 targetSdkVersion 25 - versionCode 4 - versionName "2.0.1" + versionCode 5 + versionName "2.1.0" } buildTypes { release { @@ -46,8 +46,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support:recyclerview-v7:25.2.0' + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' } apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/CustomizationOptionsBundle.java b/pinlockview/src/main/java/com/andrognito/pinlockview/CustomizationOptionsBundle.java index d2fe486b..1e9f939a 100644 --- a/pinlockview/src/main/java/com/andrognito/pinlockview/CustomizationOptionsBundle.java +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/CustomizationOptionsBundle.java @@ -5,11 +5,10 @@ /** * The customization options for the buttons in {@link PinLockView} * passed to the {@link PinLockAdapter} to decorate the individual views - * + *

* Created by aritraroy on 01/06/16. */ public class CustomizationOptionsBundle { - private int numberTextColor; private int lettersTextColor; private int deleteButtonColor; @@ -29,6 +28,19 @@ public class CustomizationOptionsBundle { private boolean useDeprecated; private int deleteButtonPressesColor; + private boolean showEnterButton; + private int pinLength; + private boolean swapEnterDeleteButtons; + + private int enterButtonColor; + private int enterButtonDisabledColor; + private int enterButtonPressesColor; + private boolean useCustomEnterButtomImages; + private int enterButtonEnabledDrawableId; + private int enterButtonDisabledDrawableId; + private int enterButtonPressedDrawableId; + + public CustomizationOptionsBundle() { } @@ -176,4 +188,84 @@ public int getDeleteButtonPressesColor() { public void setDeleteButtonPressesColor(int deleteButtonPressesColor) { this.deleteButtonPressesColor = deleteButtonPressesColor; } + + public boolean isShowEnterButton() { + return showEnterButton; + } + + public void setShowEnterButton(boolean showEnterButton) { + this.showEnterButton = showEnterButton; + } + + public int getEnterButtonColor() { + return this.enterButtonColor; + } + + public void setEnterButtonColor(int enterButtonColor) { + this.enterButtonColor = enterButtonColor; + } + + public int getEnterButtonDisabledColor() { + return this.enterButtonDisabledColor; + } + + public void setEnterButtonDisabledColor(int enterButtonDisabledColor) { + this.enterButtonDisabledColor = enterButtonDisabledColor; + } + + public int getEnterButtonPressesColor() { + return this.enterButtonPressesColor; + } + + public void setEnterButtonPressesColor(int enterButtonPressesColor) { + this.enterButtonPressesColor = enterButtonPressesColor; + } + + public int getPinLength() { + return pinLength; + } + + public void setPinLength(int pinLength) { + this.pinLength = pinLength; + } + + public boolean isSwapEnterDeleteButtons() { + return swapEnterDeleteButtons; + } + + public void setSwapEnterDeleteButtons(boolean swapEnterDeleteButtons) { + this.swapEnterDeleteButtons = swapEnterDeleteButtons; + } + + public boolean isUseCustomEnterButtonImages() { + return useCustomEnterButtomImages; + } + + public void setUseCustomEnterButtonImages(boolean useCustomEnterButtomImages) { + this.useCustomEnterButtomImages = useCustomEnterButtomImages; + } + + public int getEnterButtonEnabledDrawableId() { + return enterButtonEnabledDrawableId; + } + + public void setEnterButtonEnabledDrawableId(int enterButtonEnabledDrawableId) { + this.enterButtonEnabledDrawableId = enterButtonEnabledDrawableId; + } + + public int getEnterButtonDisabledDrawableId() { + return enterButtonDisabledDrawableId; + } + + public void setEnterButtonDisabledDrawableId(int enterButtonDisabledDrawableId) { + this.enterButtonDisabledDrawableId = enterButtonDisabledDrawableId; + } + + public int getEnterButtonPressedDrawableId() { + return enterButtonPressedDrawableId; + } + + public void setEnterButtonPressedDrawableId(int enterButtonPressedDrawableId) { + this.enterButtonPressedDrawableId = enterButtonPressedDrawableId; + } } diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/IndicatorDots.java b/pinlockview/src/main/java/com/andrognito/pinlockview/IndicatorDots.java index c8cea3d9..b332d4de 100644 --- a/pinlockview/src/main/java/com/andrognito/pinlockview/IndicatorDots.java +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/IndicatorDots.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.IntDef; +import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -70,6 +71,7 @@ public IndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) { } private void initView(Context context) { + ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR); if (mIndicatorType == 0) { for (int i = 0; i < mPinLength; i++) { View dot = new View(context); diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/InputField.java b/pinlockview/src/main/java/com/andrognito/pinlockview/InputField.java new file mode 100644 index 00000000..87e7068b --- /dev/null +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/InputField.java @@ -0,0 +1,178 @@ +package com.andrognito.pinlockview; + +import android.app.Activity; +import android.content.Context; +import android.os.Handler; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.AppCompatEditText; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +public class InputField extends AppCompatEditText { + + public InputField(Context context) { + super(context); + + initView(context); + } + + public InputField(Context context, AttributeSet attrs) { + super(context, attrs); + + initView(context); + } + + public InputField(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + initView(context); + } + + private void initView(Context context) { + ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR); + + disableKeyboard(context); + setupPasswordDots(); + } + + private void disableKeyboard(Context context) { + setCursorVisible(true); + ((Activity) context).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + setShowSoftInputOnFocus(false); + setTextIsSelectable(true); + setInputType(InputType.TYPE_NULL); + setKeyListener(null); + setRawInputType(InputType.TYPE_CLASS_TEXT); + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + /* do nothing */ + } + }); + setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + /* do nothing */ + return true; + } + }); + setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + /* do nothing */ + return true; + } + }); + setOnEditorActionListener(new OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + /* do nothing */ + return true; + } + }); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return true; + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return true; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return true; + } + + private void setupPasswordDots() { + TextWatcher pinWatcher = new TextWatcher() { + + private Handler pinHandler = new Handler(); + + int oldLength = 0; + boolean editing = false; + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (editing) return; // Do nothing if editing field within the watcher, to prevent infinite loops + + pinHandler.removeCallbacksAndMessages(null); + + Runnable hideText = new Runnable() { + @Override + public void run() { + editing = true; + InputField.this.setText(hideString(InputField.this.getText().toString())); + InputField.this.setSelection(InputField.this.getText().length()); + editing = false; + } + }; + + if (oldLength < s.toString().length()) { // Briefly display last digit if PIN increases + oldLength = s.length(); + + editing = true; + InputField.this.setText(almostHideString(InputField.this.getText().toString())); + InputField.this.setSelection(InputField.this.getText().length()); + editing = false; + + pinHandler.postDelayed(hideText, 1500); + } else { // Otherwise just hide PIN if deleting + oldLength = s.length(); + + hideText.run(); + } + } + + }; + + this.addTextChangedListener(pinWatcher); + } + + private String almostHideString(String s) { + if (s == null || s.isEmpty()) { + return ""; + } + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < s.length() - 1; ++i) { + stringBuilder.append(DOT); + } + stringBuilder.append(s.charAt(s.length() - 1)); + + return stringBuilder.toString(); + } + + private String hideString(String s) { + if (s == null || s.isEmpty()) { + return ""; + } + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < s.length(); ++i) { + stringBuilder.append(DOT); + } + + return stringBuilder.toString(); + } + + private static char DOT = '\u2022'; +} diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/LTRGridLayoutManager.java b/pinlockview/src/main/java/com/andrognito/pinlockview/LTRGridLayoutManager.java new file mode 100644 index 00000000..eac00a54 --- /dev/null +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/LTRGridLayoutManager.java @@ -0,0 +1,31 @@ +package com.andrognito.pinlockview; + +import android.content.Context; +import android.support.v7.widget.GridLayoutManager; +import android.util.AttributeSet; + +/** + * Used to always maintain an LTR layout no matter what is the real device's layout direction + * to avoid an unwanted reversed direction in RTL devices + * Created by Idan on 7/6/2017. + */ + +public class LTRGridLayoutManager extends GridLayoutManager { + + public LTRGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public LTRGridLayoutManager(Context context, int spanCount) { + super(context, spanCount); + } + + public LTRGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { + super(context, spanCount, orientation, reverseLayout); + } + + @Override + protected boolean isLayoutRTL(){ + return false; + } +} diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockAdapter.java b/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockAdapter.java index c90de0d5..57b10089 100644 --- a/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockAdapter.java +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockAdapter.java @@ -1,8 +1,10 @@ package com.andrognito.pinlockview; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.Rect; +import android.graphics.Typeface; import android.os.Build; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; @@ -10,11 +12,10 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import android.graphics.Typeface; /** * Created by aritraroy on 31/05/16. @@ -23,11 +24,18 @@ public class PinLockAdapter extends RecyclerView.Adapter 0) { + holder.mDeleteButton.setEnabled(true); holder.mDeleteButton.setVisibility(View.VISIBLE); if (mCustomizationOptionsBundle.getDeleteButtonDrawable() != null) { holder.mButtonImage.setImageDrawable(mCustomizationOptionsBundle.getDeleteButtonDrawable()); @@ -189,8 +204,49 @@ private void configureDeleteButtonHolder(DeleteViewHolder holder) { mCustomizationOptionsBundle.getButtonSize()); holder.mDeleteButton.setLayoutParams(params); } else { + holder.mDeleteButton.setEnabled(false); holder.mDeleteButton.setVisibility(View.GONE); } + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getButtonSize(), + mCustomizationOptionsBundle.getButtonSize()); + holder.mDeleteButton.setLayoutParams(params); + } + } + + private void configureEnterButtonHolder(EnterViewHolder holder) { + if (holder != null) { + if (mCustomizationOptionsBundle.isShowEnterButton()) { + // Set Enable of Enter Button + if (mPinLength >= mCustomizationOptionsBundle.getPinLength()) { + holder.mEnterButton.setEnabled(true); + if (mCustomizationOptionsBundle.isUseCustomEnterButtonImages()) { + holder.mEnterButton.setImageResource(mCustomizationOptionsBundle.getEnterButtonEnabledDrawableId()); + } + holder.mEnterButton.setColorFilter(mCustomizationOptionsBundle.getEnterButtonColor(), + PorterDuff.Mode.SRC_ATOP); + } else { + holder.mEnterButton.setEnabled(false); + if (mCustomizationOptionsBundle.isUseCustomEnterButtonImages()) { + holder.mEnterButton.setImageResource(mCustomizationOptionsBundle.getEnterButtonDisabledDrawableId()); + } + holder.mEnterButton.setColorFilter(mCustomizationOptionsBundle.getEnterButtonDisabledColor(), + PorterDuff.Mode.SRC_ATOP); + } + + // Set Visibility of Enter Button + if (holder.mEnterButton.getVisibility() != View.VISIBLE) { + holder.mEnterButton.setVisibility(View.VISIBLE); + } + } else { + holder.mEnterButton.setEnabled(false); + holder.mEnterButton.setVisibility(View.GONE); + } + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getButtonSize(), + mCustomizationOptionsBundle.getButtonSize()); + holder.mEnterButton.setLayoutParams(params); } } @@ -201,7 +257,10 @@ public int getItemCount() { @Override public int getItemViewType(int position) { - if (position == getItemCount() - 1) { + if (position == getEnterButtonPosition()) { + return VIEW_TYPE_ENTER; + } + if (position == getDeleteButtonPosition()) { return VIEW_TYPE_DELETE; } return VIEW_TYPE_NUMBER; @@ -253,12 +312,34 @@ public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener this.mOnDeleteClickListener = onDeleteClickListener; } + public void setOnEnterClickListener(OnEnterClickListener onEnterClickListener) { + this.mOnEnterClickListener = onEnterClickListener; + } + public CustomizationOptionsBundle getCustomizationOptions() { return mCustomizationOptionsBundle; } public void setCustomizationOptions(CustomizationOptionsBundle customizationOptionsBundle) { this.mCustomizationOptionsBundle = customizationOptionsBundle; + setEnterButtonPosition(mCustomizationOptionsBundle.isSwapEnterDeleteButtons() ? 11 : 9); + setDeleteButtonPosition(mCustomizationOptionsBundle.isSwapEnterDeleteButtons() ? 9 : 11); + } + + public int getEnterButtonPosition() { + return enterButtonPosition; + } + + public void setEnterButtonPosition(int enterButtonPosition) { + this.enterButtonPosition = enterButtonPosition; + } + + public int getDeleteButtonPosition() { + return deleteButtonPosition; + } + + public void setDeleteButtonPosition(int deleteButtonPosition) { + this.deleteButtonPosition = deleteButtonPosition; } public class NumberViewHolder extends RecyclerView.ViewHolder { @@ -286,64 +367,120 @@ public class DeleteViewHolder extends RecyclerView.ViewHolder { LinearLayout mDeleteButton; ImageView mButtonImage; + @SuppressLint("ClickableViewAccessibility") public DeleteViewHolder(final View itemView) { super(itemView); mDeleteButton = (LinearLayout) itemView.findViewById(R.id.button); mButtonImage = (ImageView) itemView.findViewById(R.id.buttonImage); - if (mCustomizationOptionsBundle.isShowDeleteButton() && mPinLength > 0) { - mDeleteButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mOnDeleteClickListener != null) { - mOnDeleteClickListener.onDeleteClicked(); - } + mDeleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteClicked(); + } + } + }); + + mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteLongClicked(); } - }); + return true; + } + }); + + mDeleteButton.setOnTouchListener(new View.OnTouchListener() { + private Rect rect; - mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mOnDeleteClickListener != null) { - mOnDeleteClickListener.onDeleteLongClicked(); + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mButtonImage.setColorFilter(mCustomizationOptionsBundle + .getDeleteButtonPressesColor()); + rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } + if (event.getAction() == MotionEvent.ACTION_UP) { + if (mCustomizationOptionsBundle.getDeleteButtonDefault()) { + mButtonImage.clearColorFilter(); + } else { + mButtonImage.setColorFilter(mCustomizationOptionsBundle.getDeleteButtonColor(), + PorterDuff.Mode.SRC_ATOP); } - return true; } - }); + if (leftButtonArea(v, event)) { + if (mCustomizationOptionsBundle.getDeleteButtonDefault()) { + mButtonImage.clearColorFilter(); + } else { + mButtonImage.setColorFilter(mCustomizationOptionsBundle.getDeleteButtonColor(), + PorterDuff.Mode.SRC_ATOP); + } + } + return false; + } + + private boolean leftButtonArea(View v, MotionEvent event) { + return rect != null && !rect.contains(v.getLeft() + (int) event.getX(), + v.getTop() + (int) event.getY()); + } + }); + } + } + + public class EnterViewHolder extends RecyclerView.ViewHolder { + ImageButton mEnterButton; + + @SuppressLint("ClickableViewAccessibility") + public EnterViewHolder(final View itemView) { + super(itemView); + mEnterButton = (ImageButton) itemView.findViewById(R.id.button); + + mEnterButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnEnterClickListener != null) { + mOnEnterClickListener.onEnterClicked(); + } + } + }); - mDeleteButton.setOnTouchListener(new View.OnTouchListener() { - private Rect rect; + mEnterButton.setOnTouchListener(new View.OnTouchListener() { + private Rect rect; - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mButtonImage.setColorFilter(mCustomizationOptionsBundle - .getDeleteButtonPressesColor()); - rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (mCustomizationOptionsBundle.isUseCustomEnterButtonImages()) { + mEnterButton.setImageResource(mCustomizationOptionsBundle.getEnterButtonPressedDrawableId()); } - if (event.getAction() == MotionEvent.ACTION_UP) { - if (mCustomizationOptionsBundle.getDeleteButtonDefault()) { - mButtonImage.clearColorFilter(); - } else { - mButtonImage.setColorFilter(mCustomizationOptionsBundle.getDeleteButtonColor(), - PorterDuff.Mode.SRC_ATOP); - } + mEnterButton.setColorFilter(mCustomizationOptionsBundle.getEnterButtonPressesColor(), + PorterDuff.Mode.SRC_ATOP); + rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } + if (event.getAction() == MotionEvent.ACTION_UP) { + if (mCustomizationOptionsBundle.isUseCustomEnterButtonImages()) { + mEnterButton.setImageResource(mCustomizationOptionsBundle.getEnterButtonEnabledDrawableId()); } - if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (!rect.contains(v.getLeft() + (int) event.getX(), - v.getTop() + (int) event.getY())) { - if (mCustomizationOptionsBundle.getDeleteButtonDefault()) { - mButtonImage.clearColorFilter(); - } else { - mButtonImage.setColorFilter(mCustomizationOptionsBundle.getDeleteButtonColor(), - PorterDuff.Mode.SRC_ATOP); - } - } + mEnterButton.setColorFilter(mCustomizationOptionsBundle.getEnterButtonColor(), + PorterDuff.Mode.SRC_ATOP); + } + if (leftButtonArea(v, event)) { + if (mCustomizationOptionsBundle.isUseCustomEnterButtonImages()) { + mEnterButton.setImageResource(mCustomizationOptionsBundle.getEnterButtonEnabledDrawableId()); } - return false; + mEnterButton.setColorFilter(mCustomizationOptionsBundle.getEnterButtonColor(), + PorterDuff.Mode.SRC_ATOP); } - }); - } + return false; + } + + private boolean leftButtonArea(View v, MotionEvent event) { + return rect != null && !rect.contains(v.getLeft() + (int) event.getX(), + v.getTop() + (int) event.getY()); + } + }); } } @@ -356,4 +493,8 @@ public interface OnDeleteClickListener { void onDeleteLongClicked(); } + + public interface OnEnterClickListener { + void onEnterClicked(); + } } diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockView.java b/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockView.java index 816d10ca..553add1f 100644 --- a/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockView.java +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/PinLockView.java @@ -2,11 +2,12 @@ import android.content.Context; import android.content.res.TypedArray; +import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; -import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; +import android.view.View; /** * Represents a numeric lock view which can used to taken numbers as input. @@ -33,7 +34,20 @@ public class PinLockView extends RecyclerView { private boolean mDeleteButtonDefault; private boolean mDeprecatedColorOptions; + private boolean mShowEnterButton; + private boolean mSwapEnterDeleteButtons; + + private int mEnterButtonColor; + private int mEnterButtonDisabledColor; + private int mEnterButtonPressedColor; + + private boolean mUseCustomEnterButtonImages; + private int mEnterButtonEnabledDrawableId; + private int mEnterButtonDisabledDrawableId; + private IndicatorDots mIndicatorDots; + private InputField mInputField; + private SeparateDeleteButton mSeparateDeleteButton; private PinLockAdapter mAdapter; private PinLockListener mPinLockListener; private CustomizationOptionsBundle mCustomizationOptionsBundle; @@ -49,34 +63,33 @@ public void onNumberClicked(int keyValue) { if (isIndicatorDotsAttached()) { mIndicatorDots.updateDot(mPin.length()); } + if (isInputFieldAttached()) { + mInputField.setText(mPin); + } if (mPin.length() == 1) { mAdapter.setPinLength(mPin.length()); - mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + mAdapter.notifyItemChanged(mAdapter.getDeleteButtonPosition()); + if (mSeparateDeleteButton != null && mSeparateDeleteButton.isShowSeparateDeleteButton()) { + mSeparateDeleteButton.setVisibility(View.VISIBLE); + } + } + + if (mPin.length() == mPinLength) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getEnterButtonPosition()); } if (mPinLockListener != null) { - if (mPin.length() == mPinLength) { + if (mPin.length() == mPinLength && !mShowEnterButton) { mPinLockListener.onComplete(mPin); } else { mPinLockListener.onPinChange(mPin.length(), mPin); } } } else { - if (!isShowDeleteButton()) { - resetPinLockView(); - mPin = mPin.concat(String.valueOf(keyValue)); - - if (isIndicatorDotsAttached()) { - mIndicatorDots.updateDot(mPin.length()); - } - - if (mPinLockListener != null) { - mPinLockListener.onPinChange(mPin.length(), mPin); - } - - } else { - if (mPinLockListener != null) { + if (mPinLockListener != null) { + if (!mShowEnterButton) { mPinLockListener.onComplete(mPin); } } @@ -94,10 +107,21 @@ public void onDeleteClicked() { if (isIndicatorDotsAttached()) { mIndicatorDots.updateDot(mPin.length()); } + if (isInputFieldAttached()) { + mInputField.setText(mPin); + } if (mPin.length() == 0) { mAdapter.setPinLength(mPin.length()); - mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + mAdapter.notifyItemChanged(mAdapter.getDeleteButtonPosition()); + if (mSeparateDeleteButton != null) { + mSeparateDeleteButton.setVisibility(View.GONE); + } + } + + if (mPin.length() == mPinLength - 1) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getEnterButtonPosition()); } if (mPinLockListener != null) { @@ -124,6 +148,16 @@ public void onDeleteLongClicked() { } }; + private PinLockAdapter.OnEnterClickListener mOnEnterClickListener + = new PinLockAdapter.OnEnterClickListener() { + @Override + public void onEnterClicked() { + if (mPin.length() >= mPinLength) { + mPinLockListener.onComplete(mPin); + } + } + }; + public PinLockView(Context context) { super(context); init(null, 0); @@ -165,6 +199,15 @@ private void init(AttributeSet attributeSet, int defStyle) { mDeprecatedColorOptions = typedArray.getBoolean(R.styleable.PinLockView_keypadUseDeprecatedColorOptions, true); mTextColor = typedArray.getColor(R.styleable.PinLockView_keypadTextColor, ResourceUtils.getColor(getContext(), R.color.white)); mTextSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadTextSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_text_size)); + mShowEnterButton = typedArray.getBoolean(R.styleable.PinLockView_keypadShowEnterButton, false); + if (mShowEnterButton) { + mShowDeleteButton = true; + } + mSwapEnterDeleteButtons = typedArray.getBoolean(R.styleable.PinLockView_keypadSwapEnterDeleteButtons, false); + + mEnterButtonColor = typedArray.getColor(R.styleable.PinLockView_keypadEnterButtonColor, ResourceUtils.getColor(getContext(), R.color.white)); + mEnterButtonDisabledColor = typedArray.getColor(R.styleable.PinLockView_keypadEnterButtonDisabledColor, ResourceUtils.getColor(getContext(), R.color.greyish)); + mEnterButtonPressedColor = typedArray.getColor(R.styleable.PinLockView_keypadEnterButtonPressedColor, ResourceUtils.getColor(getContext(), R.color.greyish)); } finally { typedArray.recycle(); } @@ -189,15 +232,25 @@ private void init(AttributeSet attributeSet, int defStyle) { mCustomizationOptionsBundle.setDeleteButtonDefault(mDeleteButtonDefault); mCustomizationOptionsBundle.setUseDeprecated(mDeprecatedColorOptions); + mCustomizationOptionsBundle.setShowEnterButton(mShowEnterButton); + mCustomizationOptionsBundle.setPinLength(mPinLength); + mCustomizationOptionsBundle.setSwapEnterDeleteButtons(mSwapEnterDeleteButtons); + + mCustomizationOptionsBundle.setEnterButtonColor(mEnterButtonColor); + mCustomizationOptionsBundle.setEnterButtonDisabledColor(mEnterButtonDisabledColor); + mCustomizationOptionsBundle.setEnterButtonPressesColor(mEnterButtonPressedColor); initView(); } private void initView() { - setLayoutManager(new GridLayoutManager(getContext(), 3)); + setLayoutManager(new LTRGridLayoutManager(getContext(), 3)); mAdapter = new PinLockAdapter(getContext()); mAdapter.setOnItemClickListener(mOnNumberClickListener); mAdapter.setOnDeleteClickListener(mOnDeleteClickListener); + + mAdapter.setOnEnterClickListener(mOnEnterClickListener); + mAdapter.setCustomizationOptions(mCustomizationOptionsBundle); setAdapter(mAdapter); @@ -230,10 +283,14 @@ public int getPinLength() { */ public void setPinLength(int pinLength) { this.mPinLength = pinLength; + mCustomizationOptionsBundle.setPinLength(mPinLength); if (isIndicatorDotsAttached()) { mIndicatorDots.setPinLength(pinLength); } + if (isInputFieldAttached()) { + mInputField.setText(mPin); + } } /** @@ -574,6 +631,7 @@ public boolean isShowDeleteButton() { public void setShowDeleteButton(boolean showDeleteButton) { this.mShowDeleteButton = showDeleteButton; mCustomizationOptionsBundle.setShowDeleteButton(showDeleteButton); + mAdapter.notifyItemChanged(mAdapter.getDeleteButtonPosition()); mAdapter.notifyDataSetChanged(); } @@ -597,6 +655,29 @@ public void setDeleteButtonPressedColor(int deleteButtonPressedColor) { mAdapter.notifyDataSetChanged(); } + public boolean isShowEnterButton() { + return mShowEnterButton; + } + + public void setShowEnterButton(boolean showEnterButton) { + this.mShowEnterButton = showEnterButton; + mCustomizationOptionsBundle.setShowEnterButton(showEnterButton); + mAdapter.notifyItemChanged(mAdapter.getEnterButtonPosition()); + mAdapter.notifyDataSetChanged(); + } + + public boolean isSwapEnterDeleteButtons() { + return mSwapEnterDeleteButtons; + } + + public void setSwapEnterDeleteButtons(boolean swapEnterDeleteButtons) { + this.mSwapEnterDeleteButtons = swapEnterDeleteButtons; + mCustomizationOptionsBundle.setSwapEnterDeleteButtons(swapEnterDeleteButtons); + mAdapter.setEnterButtonPosition(mCustomizationOptionsBundle.isSwapEnterDeleteButtons() ? 11 : 9); + mAdapter.setDeleteButtonPosition(mCustomizationOptionsBundle.isSwapEnterDeleteButtons() ? 9 : 11); + mAdapter.notifyDataSetChanged(); + } + public int[] getCustomKeySet() { @@ -632,11 +713,20 @@ public void resetPinLockView() { clearInternalPin(); mAdapter.setPinLength(mPin.length()); - mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + mAdapter.notifyItemChanged(mAdapter.getEnterButtonPosition()); + mAdapter.notifyItemChanged(mAdapter.getDeleteButtonPosition()); if (mIndicatorDots != null) { mIndicatorDots.updateDot(mPin.length()); } + if (mInputField != null) { + mInputField.setText(""); + } + if (mSeparateDeleteButton != null) { + mSeparateDeleteButton.setVisibility(View.GONE); + mSeparateDeleteButton.setColorFilter(mSeparateDeleteButton.getSeparateDeleteButtonColor(), + PorterDuff.Mode.SRC_ATOP); + } } /** @@ -656,4 +746,82 @@ public boolean isIndicatorDotsAttached() { public void attachIndicatorDots(IndicatorDots mIndicatorDots) { this.mIndicatorDots = mIndicatorDots; } + + public void detachIndicatorDots() { + this.mIndicatorDots = null; + } + + /** + * Returns true if {@link SeparateDeleteButton} is attached to {@link PinLockView} + * + * @return true if attached, false otherwise + */ + public boolean isSeparateDeleteButtonAttached() { + return mSeparateDeleteButton != null; + } + + /** + * Attaches {@link SeparateDeleteButton} to {@link PinLockView} + * + * @param separateDeleteButton the SeparateDeleteButton to attach + */ + public void attachSeparateDeleteButton(SeparateDeleteButton separateDeleteButton) { + this.mSeparateDeleteButton = separateDeleteButton; + this.mSeparateDeleteButton.setOnDeleteClickListener(mOnDeleteClickListener); + if (mPin.length() == 0) { + this.mSeparateDeleteButton.setVisibility(View.GONE); + } else { + this.mSeparateDeleteButton.setVisibility(View.VISIBLE); + } + } + + public void detachSeparateDeleteButton() { + this.mSeparateDeleteButton = null; + } + + /** + * Returns true if {@link InputField} is attached to {@link PinLockView} + * + * @return true if attached, false otherwise + */ + public boolean isInputFieldAttached() { + return mInputField != null; + } + + /** + * Attaches {@link InputField} to {@link PinLockView} + * + * @param inputField the InputField to attach + */ + public void attachInputField(InputField inputField) { + this.mInputField = inputField; + } + + public void detachInputField() { + this.mInputField = null; + } + + public void setUseCustomEnterButtonImages(boolean useCustomEnterButtonImages) { + this.mUseCustomEnterButtonImages = useCustomEnterButtonImages; + mCustomizationOptionsBundle.setUseCustomEnterButtonImages(useCustomEnterButtonImages); + mAdapter.notifyDataSetChanged(); + } + + public void setEnterButtonEnabledDrawableId(int id) { + this.mEnterButtonEnabledDrawableId = id; + mCustomizationOptionsBundle.setEnterButtonEnabledDrawableId(id); + mAdapter.notifyDataSetChanged(); + } + + public void setEnterButtonDisabledDrawableId(int id) { + this.mEnterButtonEnabledDrawableId = id; + mCustomizationOptionsBundle.setEnterButtonDisabledDrawableId(id); + mAdapter.notifyDataSetChanged(); + } + + public void setEnterButtonPressedDrawableId(int id) { + this.mEnterButtonEnabledDrawableId = id; + mCustomizationOptionsBundle.setEnterButtonPressedDrawableId(id); + mAdapter.notifyDataSetChanged(); + } } diff --git a/pinlockview/src/main/java/com/andrognito/pinlockview/SeparateDeleteButton.java b/pinlockview/src/main/java/com/andrognito/pinlockview/SeparateDeleteButton.java new file mode 100644 index 00000000..dca9eaee --- /dev/null +++ b/pinlockview/src/main/java/com/andrognito/pinlockview/SeparateDeleteButton.java @@ -0,0 +1,132 @@ +package com.andrognito.pinlockview; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.AppCompatImageButton; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +@SuppressLint("ClickableViewAccessibility") +public class SeparateDeleteButton extends AppCompatImageButton { + + private PinLockAdapter.OnDeleteClickListener mOnDeleteClickListener; + + private int mSeparateDeleteButtonColor; + private int mSeparateDeleteButtonPressedColor; + private boolean mShowSeparateDeleteButton = true; + + public SeparateDeleteButton(Context context) { + super(context); + initView(context, null); + } + + public SeparateDeleteButton(Context context, AttributeSet attrs) { + super(context, attrs); + initView(context, attrs); + } + + public SeparateDeleteButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initView(context, attrs); + } + + public void initView(Context context, AttributeSet attrs) { + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.PinLockView); + + try { + mSeparateDeleteButtonColor = typedArray.getColor(R.styleable.PinLockView_separateDeleteButtonColor, ResourceUtils.getColor(getContext(), R.color.white)); + mSeparateDeleteButtonPressedColor = typedArray.getColor(R.styleable.PinLockView_separateDeleteButtonPressedColor, ResourceUtils.getColor(getContext(), R.color.greyish)); + } finally { + typedArray.recycle(); + } + + ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR); + + setImageResource(R.drawable.ic_backspace); + setBackgroundColor(Color.TRANSPARENT); + setScaleType(ScaleType.FIT_CENTER); + setColorFilter(getSeparateDeleteButtonColor(), PorterDuff.Mode.SRC_ATOP); + + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteClicked(); + } + } + }); + + setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteLongClicked(); + } + return true; + } + }); + + setOnTouchListener(new OnTouchListener() { + private Rect rect; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + SeparateDeleteButton.this.setColorFilter( + getSeparateDeleteButtonPressedColor(), PorterDuff.Mode.SRC_ATOP); + rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } + if (event.getAction() == MotionEvent.ACTION_UP) { + SeparateDeleteButton.this.setColorFilter( + getSeparateDeleteButtonColor(), PorterDuff.Mode.SRC_ATOP); + } + if (leftButtonArea(v, event)) { + SeparateDeleteButton.this.setColorFilter( + getSeparateDeleteButtonColor(), PorterDuff.Mode.SRC_ATOP); + } + return false; + } + + private boolean leftButtonArea(View v, MotionEvent event) { + return rect != null && !rect.contains(v.getLeft() + (int) event.getX(), + v.getTop() + (int) event.getY()); + } + }); + + } + + public void setOnDeleteClickListener(PinLockAdapter.OnDeleteClickListener mOnDeleteClickListener) { + this.mOnDeleteClickListener = mOnDeleteClickListener; + } + + public int getSeparateDeleteButtonColor() { + return mSeparateDeleteButtonColor; + } + + public void setSeparateDeleteButtonColor(int mSeparateDeleteButtonColor) { + this.mSeparateDeleteButtonColor = mSeparateDeleteButtonColor; + setColorFilter(getSeparateDeleteButtonColor(), PorterDuff.Mode.SRC_ATOP); + } + + public int getSeparateDeleteButtonPressedColor() { + return mSeparateDeleteButtonPressedColor; + } + + public void setSeparateDeleteButtonPressedColor(int mSeparateDeleteButtonPressedColor) { + this.mSeparateDeleteButtonPressedColor = mSeparateDeleteButtonPressedColor; + } + + public boolean isShowSeparateDeleteButton() { + return mShowSeparateDeleteButton; + } + + public void setShowSeparateDeleteButton(boolean mShowSeparateDeleteButton) { + this.mShowSeparateDeleteButton = mShowSeparateDeleteButton; + } +} diff --git a/pinlockview/src/main/res/drawable/ic_check_circle.png b/pinlockview/src/main/res/drawable/ic_check_circle.png new file mode 100644 index 00000000..f88c7828 Binary files /dev/null and b/pinlockview/src/main/res/drawable/ic_check_circle.png differ diff --git a/pinlockview/src/main/res/layout/layout_enter_item.xml b/pinlockview/src/main/res/layout/layout_enter_item.xml new file mode 100644 index 00000000..82dd0aa2 --- /dev/null +++ b/pinlockview/src/main/res/layout/layout_enter_item.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/pinlockview/src/main/res/values/attrs.xml b/pinlockview/src/main/res/values/attrs.xml index 854a761c..bc7311fe 100644 --- a/pinlockview/src/main/res/values/attrs.xml +++ b/pinlockview/src/main/res/values/attrs.xml @@ -23,6 +23,12 @@ + + + + + + @@ -33,6 +39,9 @@ + + + \ No newline at end of file