﻿Type=Class
Version=7.01
ModulesStructureVersion=1
B4A=true
@EndOfDesignText@
'SuggestionsEditText	- Custom View
' v. 2.1

#Region + EVENTS 
#Event: TextChanged (Old As String, New As String)
#Event: EnterPressed ()
#Event: FocusChanged (HasFocus As Boolean)
#Event: AddAllAsyncComplete (Success As Boolean) ' AddAllAsync response
#End Region

#Region + DESIGNER PROPERTIES 
#DesignerProperty: Key: BackTextColor, DisplayName: Back Text Color, FieldType: Color, DefaultValue:0xFF808080
#DesignerProperty: Key: TextColor, DisplayName: Text Color, FieldType: Color, DefaultValue:Null
#DesignerProperty: Key: Color, DisplayName: Color, FieldType: Color, DefaultValue:Null
#DesignerProperty: Key: HintText, DisplayName: Hint text, FieldType: String, DefaultValue:""
#DesignerProperty: Key: KBSuggestions, DisplayName: Keyboard's suggestions On, FieldType: Boolean, DefaultValue: False, Description: if keyboard's suggestions will be shown
#DesignerProperty: Key: KBFullScreen, DisplayName: Keyboard on full screen, FieldType: Boolean, DefaultValue: False, Description: if IME keyboard must be on full screen
#End Region

Sub Class_Globals
	Private EventName As String
	Private CallBack As Object
	Private mBase As Panel
	Private Const DefaultColorConstant As Int = -984833 'ignore
	Private Const DefaultTextColorConstant As Int = -16777216 'ignore
	
	Private etBackground, etForeground As EditText
	
	Private mDB As SQL
	Private QueryX As String
	
#Region + PROPERTIES' MEMBERS VARIABLES 
	Private mBackTextColor As Int
	Private mTextColor As Int
	Private mColor As Int
	Private mFakeBaseColor As Int
	Private mCaseSensitive As Boolean
	Private mDBDir, mDBFileName As String
	Private mInDBOnly As Boolean
	Private mForceDoneButton As Boolean
	Private mHintText As String
	Private mKBSuggestions As Boolean
	Private mKBFullScreen As Boolean
#End Region

	Private mLastWordFound As String
	
	Private Const IME_FLAG_NO_FULLSCREEN As Int = 33554432
	Private Const IME_FLAG_NO_EXTRACT_UI As Int = 268435456
End Sub

Public Sub Initialize (vCallBack As Object, vEventName As String)
	EventName = vEventName
	CallBack = vCallBack
	mDBDir = File.DirInternal
	Dim DBID As Int = Rnd(0, 0x7FFFFFFF)
	mDBFileName = "ACE" & DBID & ".db"
	mCaseSensitive = False
	
	mInDBOnly = True
	mForceDoneButton = False
	
	mTextColor = DefaultTextColorConstant
	mFakeBaseColor = 4892359872
	mColor = mFakeBaseColor
	mKBFullScreen = False
End Sub

Public Sub DesignerCreateView (Base As Panel, Lbl As Label, Props As Map) 'ignore
	mBase = Base
	
	mBackTextColor = Props.Get("BackTextColor")
	
	Dim Col As Int

	Col = Props.Get("TextColor")
	If Col <> DefaultColorConstant Then
		mTextColor = Col
	End If

	Col = Props.Get("TextColor")
	If Col <> DefaultColorConstant Then
		mColor = Col
	End If
	
	mKBFullScreen = Props.Get("KBFullScreen")
	
	CreateGUI
End Sub

' Allows you to add this view by code.
' Parent must be an Activity or a Panel.
Public Sub AddToParent(Parent As View, Left As Int, Top As Int, Width As Int, Height As Int)
	If Not(mBase.IsInitialized) Then
		mBase.Initialize("mBase")
	End If
	
	If Parent Is Activity Then
		Dim Act As Activity = Parent
		Act.AddView(mBase, Left, Top, Width, Height)
	Else ' panel
		Dim pnl As Panel = Parent
		pnl.AddView(mBase, Left, Top, Width, Height)
	End If
	
	mBackTextColor = Colors.Gray
	mTextColor = Colors.Black

	CreateGUI
End Sub

Public Sub GetBase As Panel
	Return mBase
End Sub

#Region + PROPERTIES 

Public Sub setText (Txt As String)
	etForeground.Text = Txt
End Sub
Public Sub getText As String
	Return etForeground.Text
End Sub

' This CV is made of two EditText:
' this property returns the one which is in Foreground.
Public Sub GetForegroundEditText As EditText
	Return etForeground
End Sub

' This CV is made of two EditText:
' this property returns the one which is in Background.
Public Sub GetBackgroundEditText As EditText
	Return etBackground
End Sub

' Gets the DB's directory used by cvAutoCompleteEditText.
Public Sub getDBDir As String
	Return mDBDir
End Sub

' Gets the DB's file name used by cvAutoCompleteEditText.
Public Sub getDBFileName As String
	Return mDBFileName
End Sub

' Gets the words DB
Public Sub getDB As SQL
	Return mDB
End Sub

' Gets/Sets the "suggestion" color.
' It is Gray by default.
Public Sub setBackTextColor(BackTextColor As Int)
	mBackTextColor = BackTextColor
	etBackground.TextColor = mBackTextColor
End Sub
Public Sub getBackTextColor As Int
	Return mBackTextColor
End Sub

Public Sub setTextColor(TextColor As Int)
	mTextColor = TextColor
	etForeground.TextColor = mTextColor
End Sub
Public Sub getTextColor As Int
	Return mTextColor
End Sub

' Gets/Sets the background of the view to be a ColorDrawable with the given color.
' If the current background is of type GradientDrawable or ColorDrawable the round corners will be kept.
Public Sub setColor(Color As Int)
	mColor = Color
	etBackground.Color = Color
End Sub
Public Sub getColor As Int
	Return mColor
End Sub

#Region + CaseSensitive 
' Sets/Gets the type of comparison of the text.
' It is False by default, then not case sensitive.
Public Sub setCaseSensitive(CaseSensitive As Boolean)
	mCaseSensitive = CaseSensitive
	If mCaseSensitive Then
		mDB.ExecNonQuery("PRAGMA case_sensitive_like=ON")
	Else
		mDB.ExecNonQuery("PRAGMA case_sensitive_like=OFF")
	End If
End Sub
Public Sub getCaseSensitive As Boolean
	Return mCaseSensitive
End Sub
#End Region

Public Sub setInDBOnly(InDBOnly As Boolean)
	mInDBOnly = InDBOnly
End Sub
' If True (default), only words found in the DB are allowed.
Public Sub getInDBOnly As Boolean
	Return mInDBOnly
End Sub

' Forces the O.S. to display Done as Action Key in the virtual keyboard.
' It is False by default.
Public Sub setForceDoneButton(ForceDone As Boolean)
	mForceDoneButton = ForceDone
	etForeground.ForceDoneButton = mForceDoneButton
	If mForceDoneButton Then
		Dim JO As JavaObject = etForeground
		JO.RunMethod("setHorizontallyScrolling", Array As Object(False))
		JO.RunMethod("setLines", Array As Object(3))
	End If
End Sub
Public Sub getForceDoneButton As Boolean
	Return mForceDoneButton
End Sub

' Sets/Gets the classic hint of EditText
Public Sub setHintText(Hint As String)
	mHintText = Hint
	etForeground.Hint = mHintText
End Sub
Public Sub getHintText As String
	Return mHintText
End Sub

' Gets/Sets Keyboard's suggestions on/off
Public Sub setKBSuggestions(Enable As Boolean)
	mKBSuggestions = Enable
	If mKBSuggestions Then
		etForeground.InputType = Bit.And(etForeground.InputType, Bit.Not(145))
		etBackground.InputType = Bit.And(etBackground.InputType, Bit.Not(145))
	Else
		etForeground.InputType = Bit.Or(etForeground.InputType, 145)
		etBackground.InputType = Bit.Or(etBackground.InputType, 145)
	End If
End Sub
Public Sub getKBSuggestions As Boolean
	Return mKBSuggestions
End Sub

' Gets/Sets full screen keyboard on/off
Public Sub setKBFullScreen(Enable As Boolean)
	Dim jo As JavaObject = etForeground
	Dim ImeOptions As Int = jo.RunMethod("getImeOptions", Null)
	Dim NewImeOptions, IME_FullScreen As Int
	
	mKBFullScreen = Enable
	
	Dim ph As Phone
	Dim SDKVersion As Int = ph.SdkVersion
	If SDKVersion <11 Then
		IME_FullScreen = IME_FLAG_NO_EXTRACT_UI
	Else
		IME_FullScreen = IME_FLAG_NO_FULLSCREEN
	End If

	If mKBFullScreen Then
		NewImeOptions = Bit.And(ImeOptions, Bit.Not(IME_FullScreen))
	Else
		NewImeOptions = Bit.Or(ImeOptions, IME_FullScreen)
	End If

	jo.RunMethod("setImeOptions", Array As Object(NewImeOptions))
End Sub
Public Sub getKBFullScreen As Boolean
	Return mKBFullScreen
End Sub

#End Region

#Region + METHODS 

#Region + PUBLIC METHODS 

' Initializes the DB used to store the words.
' You must call it always, even if you have added
' this Custom View by Designer.
' Do not forget to call DeleteAllWordsDB, una tantum,
' BEFORE initializing your first Custom View.
Public Sub InitWordsDB
	InitDB
	setCaseSensitive(mCaseSensitive)
End Sub

' Initializes the DB used to store the words.
' This SuggestionsEditText will use the same DB
' of SuggET, which must be already initialized.
Public Sub InitWordsDB2(SuggET As SuggestionsEditText)
	If SuggET.DB.IsInitialized Then
		mDB = SuggET.DB
		setCaseSensitive(mCaseSensitive)
	Else
		LogColor("SuggestionsEditText-InitWordsDB2" & CRLF & TAB & _
			"Error: You must initialize SuggET's DB", Colors.Red) 
	End If
End Sub

' Deletes all old unneded DB files used before.
' Use this method only once, BEFORE INITIALIZING
' THE DB (InitWordsDB method) of your first Custom View.
' Also, with this method you set the DB directory.
' -----
' Dir: Optional - the DB directory
' if you pass an empty string, File.DirInternal will be used.
Public Sub DeleteAllWordsDBs(Dir As String)
	If Dir.Length > 0 Then
		mDBDir = Dir
	Else
		mDBDir = File.DirInternal
	End If
	
	Dim lstAllFiles As List = File.ListFiles(mDBDir)
	Dim f As String
	For i = 0 To lstAllFiles.Size - 1
		f = lstAllFiles.Get(i)
		If f.StartsWith("ACE") _
		And (f.EndsWith(".db") Or f.EndsWith(".db-journal")) Then
			File.Delete(mDBDir, f)
		End If
	Next
End Sub

' Adds a word.
Public Sub Add(Word As String)
	QueryX = "INSERT OR REPLACE INTO Words (Word) VALUES (?)"
	mDB.ExecNonQuery2(QueryX, Array As Object(Word))
End Sub

' Adds a list of words. Will return True if successful.
' Example1:
' cvAutoCompleteEditText1.AddAll(Arry As String("One", "Two", "Three"))
' Example2:
' Dim lst As List
' lst.Initialize
' lst.Add("One")
' lst.Add("Two")
' lst.Add("Three")
' cvAutoCompleteEditText1.AddAll(lst)
Public Sub AddAll(lstWords As List) As Boolean
	Private Result As Boolean = True
	
	QueryX = "INSERT OR REPLACE INTO Words (Word) VALUES (?)"

	' Adds the string list.
	mDB.BeginTransaction
	Try
		For r = 0 To lstWords.Size - 1
			mDB.ExecNonQuery2(QueryX, Array As Object(lstWords.Get(r)))
		Next
		mDB.TransactionSuccessful
	Catch
		LogColor(Application.LabelName & " - AddAll" & CRLF & TAB & LastException.Message, Colors.Red)
		Result = False
	End Try
	mDB.EndTransaction

	Return Result
End Sub

' Same as AddAll but this uses ansync method
Public Sub AddAllAsync(lstWords As List)
	QueryX = "INSERT OR REPLACE INTO Words (Word) VALUES (?)"
	For r = 0 To lstWords.Size - 1
		mDB.AddNonQueryToBatch(QueryX, Array As Object(lstWords.Get(r)))
	Next
	mDB.ExecNonQueryBatch("AddAllAsync")
End Sub

' Removes a word from DB.
Public Sub RemoveWord(Word As String)
	QueryX = "DELETE FROM Words WHERE Word = ?"
	mDB.ExecNonQuery2(QueryX, Array As String(Word))
End Sub

' Removes all words from DB.
Public Sub RemoveAllWords
	QueryX = "DELETE FROM Words"
	mDB.ExecNonQuery(QueryX)
End Sub

' Checks if a word exists.
Public Sub Contains(Word As String) As Boolean
	Dim Result As Boolean
	QueryX = "SELECT Word FROM Words WHERE Word = ?"
	Private Curs As Cursor
	Curs = mDB.ExecQuery2(QueryX, Array As String(Word))
	Result = (Curs.RowCount > 0)
	Curs.Close
	Return Result
End Sub

#End Region

#Region + PRIVATE METHODS 

Private Sub CreateGUI
	mBase.Color = Colors.Transparent
	
	etBackground.Initialize("etBackground")
	mBase.AddView(etBackground, 0, 0, mBase.Width, mBase.Height)
	etBackground.SingleLine = True
	SetFocusable(etBackground, False)
	If mColor <> -1 And mColor <> mFakeBaseColor Then
		etBackground.Color = mColor
		setColor(mColor)
	End If
	setBackTextColor(mBackTextColor)
	etBackground.TextSize = 14 * TextSizeScaleFactor
	
	etForeground.Initialize("etForeground")
	mBase.AddView(etForeground, 0, 0, mBase.Width, mBase.Height)
	etForeground.SingleLine = True
	etForeground.BringToFront
	etForeground.Color = Colors.Transparent
	If mTextColor <> DefaultTextColorConstant Then
		setTextColor(mTextColor)
	End If
	etForeground.TextSize = 14 * TextSizeScaleFactor
	
	
	setKBSuggestions(mKBSuggestions)
	
	setForceDoneButton(mForceDoneButton)
	
	setKBFullScreen(mKBFullScreen)
	
	SetEditTextNoUI(etForeground)
End Sub

Private Sub InitDB
	' Open database or create if it not exists
	mDB.Initialize(mDBDir, mDBFileName, True)

	' Create table
	QueryX = "CREATE TABLE IF NOT EXISTS Words (Word TEXT)"
	
	' Create index
	mDB.ExecNonQuery(QueryX)
	QueryX = "CREATE UNIQUE INDEX IF NOT EXISTS ItemIdx ON Words (Word)"
	mDB.ExecNonQuery(QueryX)
End Sub

Private Sub GetWordFromDB(InitialLetters As String) As String
	Dim FirstWordFound As String
	
	If InitialLetters.Length > 0 Then
		QueryX = "SELECT Word FROM Words WHERE Word LIKE '" & InitialLetters & "%'"
		Private Curs As Cursor
		Curs = mDB.ExecQuery(QueryX)
		
		If Curs.RowCount > 0 Then
			Curs.Position = 0
			FirstWordFound = Curs.GetString("Word")
		End If
		Curs.Close
	End If

	Return FirstWordFound
End Sub

Private Sub ConfirmWord
	' To "restore" the case
	If mLastWordFound.Length > 0 Then
		etForeground.Text = mLastWordFound
	Else
		If mInDBOnly Then
			etForeground.Text = ""
			etForeground.RequestFocus
		End If
	End If
	etBackground.Text = ""
End Sub

Private Sub TextSizeScaleFactor As Float
	Private cRefWidth As Int = 320
	Private cRefHeight As Int = 480
	Private cRefScale As Float = 1
	Private Rate As Float = 0.3
	Private cScaleX As Float
	Dim DeviceScale As Float
	DeviceScale = 100dip / 100
	
	If GetDevicePhysicalSize < 6 Then
		If 100%x > 100%y Then
			' landscape
			cScaleX = 100%x / DeviceScale / (cRefHeight / cRefScale)
		Else
			' portrait
			cScaleX = 100%x / DeviceScale / (cRefWidth / cRefScale)
		End If
	Else
		If 100%x > 100%y Then
			' landscape
			cScaleX = 1 + Rate * (100%x / DeviceScale / (cRefHeight / cRefScale) - 1)
		Else
			' portrait
			cScaleX = 1 + Rate * (100%x / DeviceScale / (cRefWidth / cRefScale) - 1)
		End If
	End If
	
	Return cScaleX * 1.25
End Sub

Private Sub GetDevicePhysicalSize As Float
	Dim lv As LayoutValues
	lv = GetDeviceLayoutValues
	Return Sqrt(Power(lv.Height / lv.Scale / 160, 2) + Power(lv.Width / lv.Scale / 160, 2))
End Sub

Private Sub SetFocusable(Vw As View, Focusable As Boolean)
	Dim obj1 As Reflector
	obj1.Target = Vw
	If Focusable Then
		obj1.RunMethod2("setFocusable", "True", "java.lang.boolean")
	Else
		obj1.RunMethod2("setFocusable", "False", "java.lang.boolean")
	End If
End Sub

'Does not show the software keyboard's text box.
Private Sub SetEditTextNoUI(ET As EditText)
	Dim jo As JavaObject = ET
	jo.RunMethod("setImeOptions", Array As Object(IME_FLAG_NO_FULLSCREEN))
End Sub

#End Region

#End Region

#Region + VIEWS' EVENTS 

Private Sub etForeground_TextChanged (Old As String, New As String)
	etBackground.Text = ""

	If New.Length > 0 Then
		Dim FirstWordFound As String = GetWordFromDB(New)
		If FirstWordFound.Length > 0 Then
			etBackground.Text = New & FirstWordFound.SubString(New.Length)
			mLastWordFound = FirstWordFound
		Else
			mLastWordFound = ""
		End If
	End If
	
	If SubExists(CallBack, EventName & "_TextChanged") Then
		CallSub3(CallBack, EventName & "_TextChanged", Old, New)
	End If
End Sub

Private Sub etForeground_EnterPressed
	ConfirmWord
	If SubExists(CallBack, EventName & "_EnterPressed") Then
		CallSub(CallBack, EventName & "_EnterPressed")
	End If
End Sub

Private Sub etForeground_FocusChanged (HasFocus As Boolean)
	ConfirmWord
	If SubExists(CallBack, EventName & "_FocusChanged") Then
		CallSub2(CallBack, EventName & "_FocusChanged", HasFocus)
	End If
End Sub

#End Region

#Region + OTHER EVENTS 
Private Sub AddAllAsync_NonQueryComplete (Success As Boolean)
	If SubExists(CallBack, EventName & "_AddAllAsyncComplete") Then
		CallSub2(CallBack, EventName & "_AddAllAsyncComplete", Success)
	End If
End Sub
#End Region
