Skip to content

amirisback/keyboard

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Banner Jitpack Io Android CI Scan with Detekt Google Badge Android Arsenal

  • Simple research keyboard for Android
  • Custom Keyboard
  • Emoji Custom Keyboard
  • Call API inside Keyboard
  • Open Form inside Keyboard
  • Support Dark Theme
  • AutoText Feature
  • Setup Toggle Feature

Version Release

This Is Latest Release

$version_release = 1.1.7

What's New??

* Avaiable in dark mode *
* Enhance Performance *
* Easy Change Background Keyboard *
* Setup Theme *

How To Use As Library (Coming Soon)

Step 1. Add the JitPack repository to your build file (build.gradle : Project)

<Option 1> Groovy Gradle

// Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

<Option 2> Kotlin DSL Gradle

// Add it in your root build.gradle.kts at the end of repositories:

allprojects {
    repositories {
        ...
        maven("https://jitpack.io")
    }
}

Step 2. Add the dependency (build.gradle : Module)

<Option 1> Groovy Gradle

dependencies {
    // library frogo-keyboard
    implementation 'com.github.amirisback:keyboard:1.1.7'
}

<Option 2> Kotlin DSL Gradle

dependencies {
    // library frogo-keyboard
    implementation("com.github.amirisback:keyboard:1.1.7")
}

Step 3. Create Layout Keyboard IME

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/keyboard_holder"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/keyboard_bg_root">

    <!--  start of base keyboard-->
    <LinearLayout
        android:id="@+id/container_keyboard_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/keyboard_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/keyboard_bg_root"
            android:minHeight="@dimen/frogo_dimen_64dp" />

        <com.frogobox.libkeyboard.ui.main.MainKeyboard
            android:id="@+id/keyboard_main"
            style="@style/KwKeyboardView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/theme_dark_background_color" />

    </LinearLayout>
    <!--   End of base keyboard-->

    <!--  below is the layout for your header menu on top of your base keyboard -->
    <com.frogobox.appkeyboard.ui.keyboard.autotext.AutoTextKeyboard
        android:id="@+id/keyboard_autotext"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

    <com.frogobox.appkeyboard.ui.keyboard.templatetext.TemplateTextKeyboard
        android:id="@+id/keyboard_template_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

    <com.frogobox.appkeyboard.ui.keyboard.news.NewsKeyboard
        android:id="@+id/keyboard_news"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

    <com.frogobox.appkeyboard.ui.keyboard.movie.MovieKeyboard
        android:id="@+id/keyboard_moview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

    <com.frogobox.appkeyboard.ui.keyboard.webview.WebiewKeyboard
        android:id="@+id/keyboard_webview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

    <com.frogobox.appkeyboard.ui.keyboard.form.FormKeyboard
        android:id="@+id/keyboard_form"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />
    <!--  end of header menu layout -->

    <com.frogobox.libkeyboard.ui.emoji.EmojiKeyboard
        android:id="@+id/keyboard_emoji"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/container_keyboard_main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/container_keyboard_main" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4. Create Service Keyboard IME

Create Class Keyboard IME

class KeyboardIME : BaseKeyboardIME<YourIMELayoutBinding>() {
  
  // set your custom keyboard layout
  override fun setupViewBinding(): YourIMELayoutBinding {
    return YourIMELayoutBinding.inflate(LayoutInflater.from(this), null, false)
  }

  override fun initialSetupKeyboard() {
    binding?.keyboardMain?.setKeyboard(keyboard!!) // your base keyboard
    binding?.mockMeasureHeightKeyboardMain?.setKeyboard(keyboard!!) // this code is for your keyboard header menu 
  }

  override fun setupBinding() {
    initialSetupKeyboard()
    binding?.keyboardMain?.mOnKeyboardActionListener = this
    binding?.keyboardEmoji?.mOnKeyboardActionListener = this
  }

  // redraw keyboard for capslock on/off state
  override fun invalidateAllKeys() {
    binding?.keyboardMain?.invalidateAllKeys()
  }

  // call this function when navigating to your feature
  override fun hideMainKeyboard() {
    binding?.apply {
      keyboardMain.gone()
      keyboardHeader.gone()
      mockMeasureHeightKeyboard.invisible()
    }
  }

  override fun showOnlyKeyboard() {
    binding?.keyboardMain?.visible()
  }

  override fun hideOnlyKeyboard() {
    binding?.keyboardMain?.visible()
  }

  // setup emoji keyboard 
  override fun runEmojiBoard() {
    binding?.keyboardEmoji?.visible()
    hideMainKeyboard()
    binding?.keyboardEmoji?.openEmojiPalette()
  }
}

For Emoji Keyboard, dont forget to implement Dependency Injection to load emoji asset manager

@HiltAndroidApp
class App: Application() {
  override fun onCreate() {
    super.onCreate()
    setupEmojiCompat()
  }

  private fun setupEmojiCompat() {
    val config = BundledEmojiCompatConfig(this)
    EmojiCompat.init(config)
  }
}

Step 5. Add keyboard header menu

setup keyboard header icon & menu name

class KeyboardUtil {

    private val pref: PreferenceDelegatesImpl by inject(PreferenceDelegatesImpl::class.java)

    fun menuToggle(): List<KeyboardFeature> {
        return listOf(
            KeyboardFeature(
                KeyboardFeatureType.AUTO_TEXT.id,
                KeyboardFeatureType.AUTO_TEXT,
                R.drawable.ic_menu_auto_text,
                pref.getPrefBoolean(KeyboardFeatureType.AUTO_TEXT.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.TEMPLATE_TEXT_APP.id,
                KeyboardFeatureType.TEMPLATE_TEXT_APP,
                R.drawable.ic_menu_ps_app,
                pref.getPrefBoolean(KeyboardFeatureType.TEMPLATE_TEXT_APP.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.TEMPLATE_TEXT_GAME.id,
                KeyboardFeatureType.TEMPLATE_TEXT_GAME,
                R.drawable.ic_menu_ps_game,
                pref.getPrefBoolean(KeyboardFeatureType.TEMPLATE_TEXT_GAME.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.TEMPLATE_TEXT_LOVE.id,
                KeyboardFeatureType.TEMPLATE_TEXT_LOVE,
                R.drawable.ic_menu_ps_love,
                pref.getPrefBoolean(KeyboardFeatureType.TEMPLATE_TEXT_LOVE.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.TEMPLATE_TEXT_GREETING.id,
                KeyboardFeatureType.TEMPLATE_TEXT_GREETING,
                R.drawable.ic_menu_ps_greeting,
                pref.getPrefBoolean(KeyboardFeatureType.TEMPLATE_TEXT_GREETING.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.TEMPLATE_TEXT_SALE.id,
                KeyboardFeatureType.TEMPLATE_TEXT_SALE,
                R.drawable.ic_menu_ps_sale,
                pref.getPrefBoolean(KeyboardFeatureType.TEMPLATE_TEXT_SALE.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.NEWS.id,
                KeyboardFeatureType.NEWS,
                R.drawable.ic_menu_news,
                pref.getPrefBoolean(KeyboardFeatureType.NEWS.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.MOVIE.id,
                KeyboardFeatureType.MOVIE,
                R.drawable.ic_menu_movie,
                pref.getPrefBoolean(KeyboardFeatureType.MOVIE.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.WEB.id,
                KeyboardFeatureType.WEB,
                R.drawable.ic_menu_website,
                pref.getPrefBoolean(KeyboardFeatureType.WEB.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.FORM.id,
                KeyboardFeatureType.FORM,
                R.drawable.ic_menu_form,
                pref.getPrefBoolean(KeyboardFeatureType.FORM.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.CHANGE_KEYBOARD.id,
                KeyboardFeatureType.CHANGE_KEYBOARD,
                R.drawable.ic_menu_keyboard,
                pref.getPrefBoolean(KeyboardFeatureType.CHANGE_KEYBOARD.id, true)
            ),
            KeyboardFeature(
                KeyboardFeatureType.SETTING.id,
                KeyboardFeatureType.SETTING,
                R.drawable.ic_menu_setting,
                pref.getPrefBoolean(KeyboardFeatureType.SETTING.id, true)
            )
        ).sortedBy { it.state }
    }

    fun menuKeyboard(): List<KeyboardFeature> {
        val listFeature = mutableListOf<KeyboardFeature>()
        menuToggle().forEach { data ->
            if (data.state) {
                listFeature.add(data)
            }
        }
        return listFeature
    }

}

setup keyboard header feature on KeyboardIME class

class KeyboardIME : BaseKeyboardIME<YourIMELayoutBinding>() {
    // ...

  override fun setupFeatureKeyboard() {
    val maxMenu = 4
    val gridSize = if (KeyboardUtil().menuKeyboard().size <= maxMenu) {
      KeyboardUtil().menuKeyboard().size
    } else if (KeyboardUtil().menuKeyboard().size.mod(maxMenu) == 0) {
      maxMenu
    } else {
      maxMenu + 1
    }

    binding?.apply {
      if (KeyboardUtil().menuKeyboard().isEmpty()) {
        keyboardHeader.gone()
        mockKeyboardHeader.gone()
      } else {
        keyboardHeader.visible()
        mockKeyboardHeader.visible()
        keyboardHeader.injectorBinding<KeyboardFeature, ItemKeyboardHeaderBinding>()
          .addData(KeyboardUtil().menuKeyboard())
          .addCallback(object :
            IFrogoBindingAdapter<KeyboardFeature, ItemKeyboardHeaderBinding> {

            override fun setViewBinding(parent: ViewGroup): ItemKeyboardHeaderBinding {
              return ItemKeyboardHeaderBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
              )
            }

            override fun setupInitComponent(
              binding: ItemKeyboardHeaderBinding,
              data: KeyboardFeature,
              position: Int,
              notifyListener: FrogoRecyclerNotifyListener<KeyboardFeature>,
            ) {
              binding.ivIcon.setImageResource(data.icon)
              binding.tvTitle.text = data.type.title

              if (data.state) {
                binding.root.visible()
              } else {
                binding.root.gone()
              }

            }

            override fun onItemClicked(
              binding: ItemKeyboardHeaderBinding,
              data: KeyboardFeature,
              position: Int,
              notifyListener: FrogoRecyclerNotifyListener<KeyboardFeature>,
            ) {

              when (data.type) {
                KeyboardFeatureType.NEWS -> {
                  hideMainKeyboard()
                  keyboardNews.visible()
                }

                KeyboardFeatureType.MOVIE -> {
                  hideMainKeyboard()
                  keyboardMoview.visible()
                }

                KeyboardFeatureType.WEB -> {
                  mockMeasureHeightKeyboard.invisible()
                  keyboardHeader.gone()
                  keyboardWebview.visible()
                }

                KeyboardFeatureType.FORM -> {
                  hideMainKeyboard()

                  keyboardForm.visible()
                  keyboardForm.binding?.etText?.showKeyboardExt()
                  keyboardForm.binding?.etText2?.showKeyboardExt()
                  keyboardForm.binding?.etText3?.showKeyboardExt()

                  keyboardForm.setOnClickListener {
                    hideOnlyKeyboard()
                  }
                }

                KeyboardFeatureType.AUTO_TEXT -> {
                  hideMainKeyboard()
                  keyboardAutotext.visible()
                }

                KeyboardFeatureType.TEMPLATE_TEXT_GAME -> {
                  hideMainKeyboard()
                  keyboardTemplateText.setupTemplateTextType(KeyboardFeatureType.TEMPLATE_TEXT_GAME)
                  keyboardTemplateText.visible()
                }

                KeyboardFeatureType.TEMPLATE_TEXT_APP -> {
                  hideMainKeyboard()
                  keyboardTemplateText.setupTemplateTextType(KeyboardFeatureType.TEMPLATE_TEXT_APP)
                  keyboardTemplateText.visible()
                }

                KeyboardFeatureType.TEMPLATE_TEXT_SALE -> {
                  hideMainKeyboard()
                  keyboardTemplateText.setupTemplateTextType(KeyboardFeatureType.TEMPLATE_TEXT_SALE)
                  keyboardTemplateText.visible()
                }

                KeyboardFeatureType.TEMPLATE_TEXT_LOVE -> {
                  hideMainKeyboard()
                  keyboardTemplateText.setupTemplateTextType(KeyboardFeatureType.TEMPLATE_TEXT_LOVE)
                  keyboardTemplateText.visible()
                }

                KeyboardFeatureType.TEMPLATE_TEXT_GREETING -> {
                  hideMainKeyboard()
                  keyboardTemplateText.setupTemplateTextType(KeyboardFeatureType.TEMPLATE_TEXT_GREETING)
                  keyboardTemplateText.visible()
                }

                KeyboardFeatureType.CHANGE_KEYBOARD -> {
                  (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager).showInputMethodPicker()
                }

                KeyboardFeatureType.SETTING -> {
                  binding.root.context.startActivity(
                    Intent(binding.root.context, MainActivity::class.java).apply {
                      addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    })
                }

              }

            }

            override fun onItemLongClicked(
              binding: ItemKeyboardHeaderBinding,
              data: KeyboardFeature,
              position: Int,
              notifyListener: FrogoRecyclerNotifyListener<KeyboardFeature>,
            ) {
            }


          })
          .createLayoutGrid(gridSize)
          .build()
      }
    }
  }
    
    // ...
}

if your feature is using a textfield, add below code to your KeyboardIME class

@RequiresApi(Build.VERSION_CODES.M)
    override fun onKey(code: Int) {
        val formView = binding?.keyboardForm
        var inputConnection = currentInputConnection

        if (formView?.visibility == View.VISIBLE) {
            val et1 = formView.binding?.etText
            val et1Connection = et1?.onCreateInputConnection(EditorInfo())

            val et2 = formView.binding?.etText2
            val et2Connection = et2?.onCreateInputConnection(EditorInfo())

            val et3 = formView.binding?.etText3
            val et3Connection = et3?.onCreateInputConnection(EditorInfo())

            if (et1?.isFocused == true) {
                inputConnection = et1Connection
            } else if (et2?.isFocused == true) {
                inputConnection = et2Connection
            } else if (et3?.isFocused == true) {
                inputConnection = et3Connection
            }

        } else if (binding?.keyboardWebview?.visibility == View.VISIBLE) {
            inputConnection =
                binding?.keyboardWebview?.binding?.webview?.onCreateInputConnection(EditorInfo())
        } else {
            inputConnection = currentInputConnection
        }
        onKeyExt(code, inputConnection)
    }

Step 6. Create keys_config.xml inside xml folder

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
    android:icon="@drawable/ic_frogobox"
    android:settingsActivity="com.frogobox.appkeyboard.ui.main.MainActivity">

    <subtype android:imeSubtypeMode="Keyboard" />

</input-method>

Step 7. Create Keyboard Service In Manifest inside application tag

<service
    android:name=".services.KeyboardIME"
    android:exported="true"
    android:label="@string/app_name"
    android:permission="android.permission.BIND_INPUT_METHOD">
    <meta-data
        android:name="android.view.im"
        android:resource="@xml/keys_config" />
    <intent-filter>
        <action android:name="android.view.InputMethod" />
    </intent-filter>
</service>

Video Play

video-play.mp4

Screen Shoot

How To Activated

Activated Keyboard

Welcome Page (Light) Activated Keyboard (Light) After Activated (Light)
Welcome Page (Dark) Activated Keyboard (Dark) After Activated (Dark)

Change Keyboard

Before Change Keyboard (Light) Change Keyboard (Light) After Change Keyboard (Light)
Before Change Keyboard (Dark) Change Keyboard (Dark) After Change Keyboard (Dark)

Normal Keyboard State

Alphabet Keyboard (Light) Numeric Keyboard (Light)
Alphabet Keyboard (Dark) Numeric Keyboard (Dark)

Feature Keyboard

Using API

News API (Light) Movie API (Light)
News API (Dark) Movie API (Dark)

Webview

Show Webview Input Webview

Form

Show Form (Light) Input Form (Light) Hide Keyboard (Light)
Show Form (Dark) Input Form (Dark) Hide Keyboard (Dark)

Emojis

Emojis (Light) Emojis (Light)
Emojis (Dark) Emojis (Dark)

Auto Text

Menu Auto Text (Light) Auto Text Inputed (Light) Auto Text Dashboard (Light)
Empty View Auto Text (Light) Auto Text Editor (Light) Auto Text Detail (Light)
Menu Auto Text (Dark) Auto Text Inputed (Dark) Auto Text Dashboard (Dark)
Empty View Auto Text (Dark) Auto Text Editor (Dark) Auto Text Detail (Dark)

Open To Other App

Google Search (Light) Google Message (Light) Sign In Google (Light)
Google Search (Dark) Google Message (Dark) Sign In Google (Dark)

Toggle Feature

All Toggle Off (Light) Keyboard No Feature (Light) Some Toggle On (Light) Keyboard With Feature (Light)
All Toggle Off (Dark) Keyboard No Feature (Dark) Some Toggle On (Dark) Keyboard With Feature (Dark)

Multi Language Support

Menu Multi Language (Light) Change Language (Light) Menu Multi Language (Dark) Change Language (Dark)

Multi Theme Suppported

Setup Theme (Light) Theme Applied (Light) Setup Theme (Dark) Theme Applied (Dark)

Documentation

Frogo Library

No. Github Name / Organization Github Project
1. Muhammad Faisal Amir frogo-admob
2. Muhammad Faisal Amir frogo-recycler-view
3. Frogobox frogo-sdk
4. Frogobox frogo-ui
5. Frogobox frogo-consume-api

Colaborator

Very open to anyone, I'll write your name under this, please contribute by sending an email to me

  • Mail To [email protected]
  • Subject : Github _ [Github-Username-Account] _ [Language] _ [Repository-Name]
  • Example : Github_amirisback_kotlin_admob-helper-implementation

Name Of Contribute

  • Muhammad Faisal Amir
  • Waiting List
  • Waiting List

Waiting for your contribute

Attention !!!

  • Please enjoy and don't forget fork and give a star
  • Don't Forget Follow My Github Account

ScreenShoot Apps