Download Kotlin Source from Github: Download
Platform: Android 4.1 +
Language: Kotlin
Package AAR: Download
Maven:
Build Via Gradle: implementation 'com.dbh4ck:supernova-emoji-kotlin:1.0'
Origin: supernova emoji for android
Python GUI & Scripts, Nimbuzz Apps and Other Softwares for Windows Desktop, Android Devices, Cross-Platform and more, including source codes [c-sharp , python, java, etc.] just for information purpose only! All apps on this Blog are self-coded, tested and few might be referenced to Thirty Party sources as well. All Source Codes are available at my GitHub: https://github.com/dbh4ck
package com.db.smsotpautofillkotlin.editable | |
import android.annotation.SuppressLint | |
import android.content.Context | |
import android.graphics.Canvas | |
import android.graphics.Paint | |
import android.graphics.Rect | |
import android.graphics.Typeface | |
import android.support.annotation.ColorInt | |
import android.support.v4.content.ContextCompat | |
import android.text.* | |
import android.util.AttributeSet | |
import android.view.View | |
import android.view.inputmethod.EditorInfo | |
import android.view.inputmethod.InputConnection | |
import android.view.inputmethod.InputMethodManager | |
import com.db.smsotpautofillkotlin.R | |
import com.db.smsotpautofillkotlin.interfaces.EditCodeListener | |
import android.text.Editable | |
import com.db.smsotpautofillkotlin.interfaces.EditCodeWatcher | |
/** | |
* Created by DB on 04-01-2018. | |
*/ | |
class EditCodeView: View, View.OnClickListener, View.OnFocusChangeListener { | |
private val textWatcher = CodeTextWatcher() | |
private var inputmethodmanager: InputMethodManager? = null | |
private var editCodeInputConnection: EditCodeInputConnection? = null | |
private var editCodeListener: EditCodeListener? = null | |
private var editCodeWatcher: EditCodeWatcher? = null | |
private var editable: Editable? = null | |
private var textPaint: Paint? = null | |
private var underlinePaint: Paint? = null | |
private var cursorPaint: Paint? = null | |
private var textSize: Float = 0.toFloat() | |
private var textPosY: Float = 0.toFloat() | |
private var textColor: Int = 0 | |
private var sectionWidth: Float = 0.toFloat() | |
private var codeLength: Int = 0 | |
private var symbolWidth: Float = 0.toFloat() | |
private var symbolMaskedWidth: Float = 0.toFloat() | |
private var underlineHorizontalPadding: Float = 0.toFloat() | |
private var underlineReductionScale: Float = 0.toFloat() | |
private var underlineStrokeWidth: Float = 0.toFloat() | |
private var underlineBaseColor: Int = 0 | |
private var underlineSelectedColor: Int = 0 | |
private var underlineFilledColor: Int = 0 | |
private var underlineCursorColor: Int = 0 | |
private var underlinePosY: Float = 0.toFloat() | |
private var fontStyle: Int = 0 | |
private var cursorEnabled: Boolean = false | |
private var codeHiddenMode: Boolean = false | |
private var _isSelected: Boolean = false | |
private var codeHiddenMask: String? = null | |
private val textBounds = Rect() | |
constructor(context: Context): this(context, null){ | |
} | |
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0){ | |
} | |
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr){ | |
init(context, attrs) | |
} | |
private val cursorAnimation = object : Runnable { | |
override fun run() { | |
val color = if (cursorPaint!!.getColor() === underlineSelectedColor) | |
underlineCursorColor | |
else | |
underlineSelectedColor | |
cursorPaint!!.setColor(color) | |
invalidate() | |
postDelayed(this, 500) | |
} | |
} | |
private fun init(context: Context, attrs: AttributeSet?) { | |
initDefaultAttrs(context) | |
initCustomAttrs(context, attrs) | |
initPaints() | |
initViewsOptions(context) | |
if (isInEditMode) { | |
editModePreview() | |
} | |
} | |
private fun initDefaultAttrs(context: Context) { | |
val resources = context.resources | |
underlineReductionScale = DEFAULT_REDUCTION_SCALE!! | |
underlineStrokeWidth = resources.getDimension(R.dimen.underline_stroke_width) | |
underlineBaseColor = ContextCompat.getColor(context, R.color.underline_base_color) | |
underlineFilledColor = ContextCompat.getColor(context, R.color.underline_filled_color) | |
underlineCursorColor = ContextCompat.getColor(context, R.color.underline_cursor_color) | |
underlineSelectedColor = ContextCompat.getColor(context, R.color.underline_selected_color) | |
textSize = resources.getDimension(R.dimen.code_text_size) | |
textColor = ContextCompat.getColor(context, R.color.text_main_color) | |
codeLength = DEFAULT_CODE_LENGTH!! | |
codeHiddenMask = DEFAULT_CODE_MASK | |
} | |
private fun initCustomAttrs(context: Context, attributeSet: AttributeSet?) { | |
if (attributeSet == null) return | |
val attributes = context.obtainStyledAttributes( | |
attributeSet, R.styleable.EditCodeView) | |
underlineStrokeWidth = attributes.getDimension( | |
R.styleable.EditCodeView_underlineStroke, underlineStrokeWidth) | |
underlineReductionScale = attributes.getFloat( | |
R.styleable.EditCodeView_underlineReductionScale, underlineReductionScale) | |
underlineBaseColor = attributes.getColor( | |
R.styleable.EditCodeView_underlineBaseColor, underlineBaseColor) | |
underlineSelectedColor = attributes.getColor( | |
R.styleable.EditCodeView_underlineSelectedColor, underlineSelectedColor) | |
underlineFilledColor = attributes.getColor( | |
R.styleable.EditCodeView_underlineFilledColor, underlineFilledColor) | |
underlineCursorColor = attributes.getColor( | |
R.styleable.EditCodeView_underlineCursorColor, underlineCursorColor) | |
cursorEnabled = attributes.getBoolean( | |
R.styleable.EditCodeView_underlineCursorEnabled, cursorEnabled) | |
textSize = attributes.getDimension( | |
R.styleable.EditCodeView_textSize, textSize) | |
textColor = attributes.getColor( | |
R.styleable.EditCodeView_textColor, textColor) | |
fontStyle = attributes.getInt( | |
R.styleable.EditCodeView_fontStyle, fontStyle) | |
codeLength = attributes.getInt( | |
R.styleable.EditCodeView_codeLength, DEFAULT_CODE_LENGTH!!) | |
codeHiddenMode = attributes.getBoolean( | |
R.styleable.EditCodeView_codeHiddenMode, codeHiddenMode) | |
val mask = attributes.getString(R.styleable.EditCodeView_codeHiddenMask) | |
if (mask != null && mask.length > 0) { | |
codeHiddenMask = mask.substring(0, 1) | |
} | |
attributes.recycle() | |
} | |
private fun editModePreview() { | |
for (i in 0 until codeLength) { | |
if (codeHiddenMode) { | |
editable!!.append(codeHiddenMask) | |
} else { | |
editable!!.append(DEFAULT_CODE_SYMBOL) | |
} | |
} | |
} | |
@SuppressLint("WrongConstant") | |
private fun initPaints() { | |
textPaint = Paint() | |
textPaint!!.setColor(textColor) | |
textPaint!!.setTextSize(textSize) | |
textPaint!!.setTypeface(Typeface.create(Typeface.DEFAULT, fontStyle)) | |
textPaint!!.setAntiAlias(true) | |
underlinePaint = Paint() | |
underlinePaint!!.setColor(underlineBaseColor) | |
underlinePaint!!.setStrokeWidth(underlineStrokeWidth) | |
cursorPaint = Paint() | |
cursorPaint!!.setColor(underlineBaseColor) | |
cursorPaint!!.setStrokeWidth(underlineStrokeWidth) | |
} | |
private fun initViewsOptions(context: Context) { | |
setOnClickListener(this) | |
isFocusable = true | |
isFocusableInTouchMode = true | |
onFocusChangeListener = this | |
inputmethodmanager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager | |
editable = Editable.Factory.getInstance().newEditable("") | |
editable!!.setSpan(textWatcher, 0, editable!!.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) | |
Selection.setSelection(editable, 0) | |
editCodeInputConnection = EditCodeInputConnection(this, true, codeLength) | |
} | |
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { | |
super.onSizeChanged(w, h, oldw, oldh) | |
measureSizes(w, h) | |
} | |
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | |
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)) | |
} | |
override fun onDraw(canvas: Canvas) { | |
drawUnderline(canvas) | |
drawText(canvas) | |
} | |
override fun onFocusChange(v: View, hasFocus: Boolean) { | |
_isSelected = hasFocus | |
if (hasFocus) { | |
if (cursorEnabled) { | |
post(cursorAnimation) | |
} | |
showKeyboard() | |
} else { | |
if (cursorEnabled) { | |
removeCallbacks(cursorAnimation) | |
} | |
hideKeyboard() | |
} | |
} | |
private fun drawText(canvas: Canvas) { | |
if (codeHiddenMode) { | |
val symbol = charArrayOf(codeHiddenMask!!.get(0)) | |
for (i in 0 until editable!!.length) { | |
val textPosX = sectionWidth * i + sectionWidth / 2 - symbolMaskedWidth / 2 | |
canvas.drawText(symbol, 0, 1, textPosX, textPosY, textPaint) | |
} | |
} else { | |
for (i in 0 until editable!!.length) { | |
val symbol = charArrayOf(editable!!.get(i)) | |
val textPosX = sectionWidth * i + sectionWidth / 2 - symbolWidth / 2 | |
canvas.drawText(symbol, 0, 1, textPosX, textPosY, textPaint) | |
} | |
} | |
} | |
private fun drawUnderline(canvas: Canvas) { | |
for (i in 0 until codeLength) { | |
val startPosX = sectionWidth * i + underlineHorizontalPadding | |
val endPosX = startPosX + sectionWidth - underlineHorizontalPadding * 2 | |
if (cursorEnabled && _isSelected && editable!!.length == i) { | |
canvas.drawLine(startPosX, underlinePosY, endPosX, underlinePosY, cursorPaint) | |
} else { | |
if (editable!!.length <= i && _isSelected) { | |
underlinePaint!!.setColor(underlineSelectedColor) | |
} else if (editable!!.length <= i && !_isSelected) { | |
underlinePaint!!.setColor(underlineBaseColor) | |
} else { | |
underlinePaint!!.setColor(underlineFilledColor) | |
} | |
canvas.drawLine(startPosX, underlinePosY, endPosX, underlinePosY, underlinePaint) | |
} | |
} | |
} | |
private fun measureSizes(viewWidth: Int, viewHeight: Int) { | |
if (underlineReductionScale > 1) underlineReductionScale = 1f | |
if (underlineReductionScale < 0) underlineReductionScale = 0f | |
if (codeLength <= 0) { | |
throw IllegalArgumentException("Code length must be over than zero") | |
} | |
symbolWidth = textPaint!!.measureText(DEFAULT_CODE_SYMBOL) | |
symbolMaskedWidth = textPaint!!.measureText(codeHiddenMask) | |
textPaint!!.getTextBounds(DEFAULT_CODE_SYMBOL, 0, 1, textBounds) | |
sectionWidth = (viewWidth / codeLength).toFloat() | |
underlinePosY = (viewHeight - paddingBottom).toFloat() | |
underlineHorizontalPadding = sectionWidth * underlineReductionScale / 2 | |
textPosY = (viewHeight / 2 + textBounds.height() / 2).toFloat() | |
} | |
private fun measureHeight(measureSpec: Int): Int { | |
val size = (paddingBottom.toFloat() | |
+ paddingTop.toFloat() | |
+ textBounds.height().toFloat() | |
+ textSize | |
+ underlineStrokeWidth).toInt() | |
return View.resolveSizeAndState(size, measureSpec, 0) | |
} | |
private fun measureWidth(measureSpec: Int): Int { | |
val size = ((paddingLeft.toFloat() + paddingRight.toFloat() + textSize) * codeLength.toFloat() * 2f).toInt() | |
return View.resolveSizeAndState(size, measureSpec, 0) | |
} | |
fun setEditCodeListener(EditCodeListener: EditCodeListener) { | |
this.editCodeListener = EditCodeListener | |
} | |
fun setCode(code: String) { | |
var code = code | |
code = code.replace(DEFAULT_REGEX.toRegex(), "") | |
editCodeInputConnection!!.setComposingText(code, 1) | |
editCodeInputConnection!!.finishComposingText() | |
} | |
fun clearCode() { | |
editCodeInputConnection!!.setComposingRegion(0, codeLength) | |
editCodeInputConnection!!.setComposingText("", 0) | |
editCodeInputConnection!!.finishComposingText() | |
} | |
fun getCode(): String { | |
return editable.toString() | |
} | |
fun setReductionScale(scale: Float) { | |
var scale = scale | |
if (scale > 1) scale = 1f | |
if (scale < 0) scale = 0f | |
underlineReductionScale = scale | |
invalidate() | |
} | |
fun setCodeHiddenMode(hiddenMode: Boolean) { | |
codeHiddenMode = hiddenMode | |
invalidate() | |
} | |
fun setUnderlineBaseColor(@ColorInt colorId: Int) { | |
underlineBaseColor = colorId | |
invalidate() | |
} | |
fun setUnderlineFilledColor(@ColorInt colorId: Int) { | |
underlineFilledColor = colorId | |
invalidate() | |
} | |
fun setUnderlineSelectedColor(@ColorInt colorId: Int) { | |
underlineSelectedColor = colorId | |
invalidate() | |
} | |
fun setUnderlineCursorColor(@ColorInt colorId: Int) { | |
underlineCursorColor = colorId | |
invalidate() | |
} | |
fun setTextColor(@ColorInt colorId: Int) { | |
textColor = colorId | |
invalidate() | |
} | |
fun setUnderlineStrokeWidth(underlineStrokeWidth: Float) { | |
this.underlineStrokeWidth = underlineStrokeWidth | |
invalidate() | |
} | |
fun setCodeLength(length: Int) { | |
codeLength = length | |
editCodeInputConnection = EditCodeInputConnection(this, true, codeLength) | |
editable!!.clear() | |
inputmethodmanager!!.restartInput(this) | |
invalidate() | |
} | |
fun setEditCodeWatcher(editCodeWatcher: EditCodeWatcher) { | |
this.editCodeWatcher = editCodeWatcher | |
} | |
fun getCodeLength(): Int { | |
return codeLength | |
} | |
fun showKeyboard() { | |
inputmethodmanager!!.showSoftInput(this, 0) | |
} | |
fun hideKeyboard() { | |
inputmethodmanager!!.hideSoftInputFromWindow( | |
windowToken, | |
InputMethodManager.RESULT_UNCHANGED_SHOWN) | |
} | |
override fun onClick(v: View) { | |
showKeyboard() | |
} | |
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection { | |
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER | |
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | |
outAttrs.initialSelStart = 0 | |
return this.editCodeInputConnection!! | |
} | |
override fun setSelected(selected: Boolean) { | |
_isSelected = selected | |
invalidate() | |
} | |
fun getEditable(): Editable { | |
return editable!! | |
} | |
override fun onCheckIsTextEditor(): Boolean { | |
return true | |
} | |
private inner class CodeTextWatcher : TextWatcher { | |
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} | |
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { | |
} | |
override fun afterTextChanged(s: Editable) { | |
invalidate() | |
if (editCodeWatcher != null) { | |
editCodeWatcher!!.onCodeChanged(s.toString()) | |
} | |
if (editable!!.length == codeLength) { | |
if (editCodeListener != null) { | |
editCodeListener!!.onCodeReady(editable.toString()) | |
} | |
} | |
} | |
} | |
companion object { | |
private val DEFAULT_CODE_LENGTH: Int? = 4 | |
private val DEFAULT_CODE_MASK = "*" | |
private val DEFAULT_CODE_SYMBOL = "0" | |
private val DEFAULT_REGEX = "[^0-9]" | |
private val DEFAULT_REDUCTION_SCALE: Float? = 0.5f | |
} | |
} |
package com.db.smsotpautofillkotlin.editable | |
import android.view.View | |
import android.view.inputmethod.BaseInputConnection | |
import android.text.Editable | |
import android.view.KeyEvent | |
/** | |
* Created by DB on 03-01-2018. | |
*/ | |
class EditCodeInputConnection(targetView: View, fullEditor: Boolean, textLength: Int): BaseInputConnection(targetView, fullEditor) { | |
private var _editable: Editable? = null | |
private var textLength: Int = 0 | |
init { | |
val view = targetView as EditCodeView | |
this.textLength = textLength | |
this._editable = view.getEditable() | |
} | |
override fun getEditable(): Editable { | |
return this._editable!! | |
} | |
override fun sendKeyEvent(event: KeyEvent): Boolean { | |
if (event.getAction() === KeyEvent.ACTION_DOWN) { | |
if (event.getKeyCode() >= KeyEvent.KEYCODE_0 && event.getKeyCode() <= KeyEvent.KEYCODE_9) { | |
val c = event.getKeyCharacterMap().getNumber(event.getKeyCode()) | |
commitText(c.toString(), 1) | |
} else if (event.getKeyCode() === KeyEvent.KEYCODE_DEL) { | |
deleteSurroundingText(1, 0) | |
} | |
} | |
return super.sendKeyEvent(event) | |
} | |
override fun commitText(text: CharSequence, newCursorPosition: Int): Boolean { | |
return _editable!!.length + text.length <= textLength && super.commitText(text.subSequence(0, 1), newCursorPosition) | |
} | |
override fun setComposingText(text: CharSequence, newCursorPosition: Int): Boolean { | |
var text = text | |
if (text.length > textLength) { | |
text = text.subSequence(0, textLength) | |
} | |
return super.setComposingText(text, newCursorPosition) | |
} | |
override fun setComposingRegion(start: Int, end: Int): Boolean { | |
return super.setComposingRegion(start, end) | |
} | |
override fun finishComposingText(): Boolean { | |
return super.finishComposingText() | |
} | |
} |
package com.db.smsotpautofillkotlin.receivers | |
import android.content.BroadcastReceiver | |
import android.content.Context | |
import android.content.Intent | |
import android.support.v4.app.NotificationCompat.getExtras | |
import android.os.Bundle | |
import android.R.attr.data | |
import android.telephony.SmsMessage | |
import java.util.regex.Pattern | |
import com.db.smsotpautofillkotlin.interfaces.SmsListener | |
/** | |
* Created by DB on 03-01-2018. | |
*/ | |
class SmsReceiver: BroadcastReceiver() { | |
@Suppress("DEPRECATION") | |
override fun onReceive(context: Context?, intent: Intent?) { | |
val data = intent!!.getExtras() | |
val pdus = data.get("pdus") as Array<Any> | |
for (i in 0 until pdus.size) { | |
val smsMessage = SmsMessage.createFromPdu(pdus[i] as ByteArray) | |
val sender = smsMessage.getDisplayOriginatingAddress() | |
//Check the sender to filter messages which we require to read | |
val pattern = Pattern.compile(OTP_SENDER_REGEX) | |
val matcher = pattern.matcher(sender) | |
while (matcher.find()) { | |
val messageBody: String? = smsMessage.getMessageBody() | |
//Pass the message text to interface | |
if (messageBody != null) { | |
mListener!!.messageReceived(messageBody) | |
} | |
} | |
} | |
} | |
companion object { | |
val OTP_SENDER_REGEX: String? = "[0-9a-zA-Z]" | |
//interface | |
private var mListener: SmsListener? = null | |
fun bindListener(listener: SmsListener) { | |
mListener = listener | |
} | |
} | |
} |
package com.db.smsotpautofillkotlin | |
import android.support.v7.app.AppCompatActivity | |
import android.os.Bundle | |
import android.util.Log | |
import com.db.smsotpautofillkotlin.interfaces.EditCodeWatcher | |
import com.db.smsotpautofillkotlin.interfaces.EditCodeListener | |
import android.view.WindowManager | |
import com.db.smsotpautofillkotlin.editable.EditCodeView | |
import com.db.smsotpautofillkotlin.interfaces.SmsListener | |
import com.db.smsotpautofillkotlin.receivers.SmsReceiver | |
import java.util.regex.Pattern | |
class DBSmsOTPActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_dbsms_otp) | |
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) | |
val editCodeView = findViewById(R.id.dbotp) as EditCodeView | |
editCodeView.setEditCodeListener(object : EditCodeListener { | |
override fun onCodeReady(code: String) { | |
} | |
}) | |
editCodeView.setEditCodeWatcher(object : EditCodeWatcher { | |
override fun onCodeChanged(code: String) { | |
Log.e("CodeWatcher", " changed : " + code) | |
} | |
}) | |
editCodeView.requestFocus() | |
SmsReceiver.Companion.bindListener(object : SmsListener { | |
override fun messageReceived(messageText: String) { | |
//From the received text string you may do string operations to get the required OTP | |
//It depends on your SMS format | |
Log.e("Message", messageText) | |
// Toast.makeText(DbSmsOTPActivity.this,"Message: " + messageText, Toast.LENGTH_LONG).show(); | |
// If your OTP is six digits number, you may use the below code | |
val pattern = Pattern.compile(OTP_REGEX) | |
val matcher = pattern.matcher(messageText) | |
var otp: String? = null | |
while (matcher.find()) { | |
otp = matcher.group() | |
} | |
// Toast.makeText(DbSmsOTPActivity.this, "OTP: " + otp , Toast.LENGTH_LONG).show(); | |
assert(otp != null) | |
editCodeView.setCode(otp!!) | |
} | |
}) | |
} | |
companion object { | |
val OTP_REGEX: String? = "[0-9]{1,6}" | |
} | |
} |
package com.db.smsotpautofillkotlin.interfaces | |
/** | |
* Created by DB on 03-01-2018. | |
*/ | |
interface EditCodeListener { | |
fun onCodeReady(code: String) | |
} |
package com.db.smsotpautofillkotlin.interfaces | |
/** | |
* Created by DB on 03-01-2018. | |
*/ | |
interface EditCodeWatcher { | |
fun onCodeChanged(code: String) | |
} |
package com.db.smsotpautofillkotlin.interfaces | |
/** | |
* Created by DB on 03-01-2018. | |
*/ | |
interface SmsListener { | |
fun messageReceived(messageText: String) | |
} |