├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── divyankit │ │ └── keyboard │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── divyankit │ │ │ └── keyboard │ │ │ ├── ApplicationKeyboard.java │ │ │ ├── activities │ │ │ └── ImePreferences.java │ │ │ ├── android │ │ │ ├── InputMethodSettingsFragment.java │ │ │ ├── InputMethodSettingsImpl.java │ │ │ └── InputMethodSettingsInterface.java │ │ │ └── utils │ │ │ ├── CandidateView.java │ │ │ ├── LatinKeyboard.java │ │ │ ├── LatinKeyboardView.java │ │ │ └── SoftKeyboard.java │ └── res │ │ ├── drawable-hdpi │ │ ├── sym_keyboard_delete.png │ │ ├── sym_keyboard_done.png │ │ ├── sym_keyboard_language_switch.png │ │ ├── sym_keyboard_return.png │ │ ├── sym_keyboard_search.png │ │ ├── sym_keyboard_shift.png │ │ └── sym_keyboard_space.png │ │ ├── drawable-mdpi │ │ ├── sym_keyboard_delete.png │ │ ├── sym_keyboard_done.png │ │ ├── sym_keyboard_language_switch.png │ │ ├── sym_keyboard_return.png │ │ ├── sym_keyboard_search.png │ │ ├── sym_keyboard_shift.png │ │ └── sym_keyboard_space.png │ │ ├── drawable │ │ └── pay.png │ │ ├── layout │ │ └── input.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── ime_preferences.xml │ │ ├── method.xml │ │ ├── qwerty.xml │ │ ├── symbols.xml │ │ └── symbols_shift.xml │ └── test │ └── java │ └── com │ └── divyankit │ └── keyboard │ └── ExampleUnitTest.java ├── art ├── 1.png ├── 2.png ├── 3.png └── 4.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyboard 2 | 3 | This code teach us how we can develop our own custom keyboard. 4 | 5 | ## Some Screenshots 6 | - Here 7 | 8 | ![1](https://github.com/AnkitDroidGit/Android-Custom-Keyboard/blob/master/art/1.png) 9 | ![1](https://github.com/AnkitDroidGit/Android-Custom-Keyboard/blob/master/art/2.png) 10 | ![1](https://github.com/AnkitDroidGit/Android-Custom-Keyboard/blob/master/art/3.png) 11 | ![1](https://github.com/AnkitDroidGit/Android-Custom-Keyboard/blob/master/art/4.png) 12 | 13 | 14 | 15 | ## let's connect to learn together 16 | 17 | - [Twitter](https://twitter.com/KumarAnkitRKE) 18 | - [Github](https://github.com/AnkitDroidGit) 19 | - [LinkedIn](https://www.linkedin.com/in/kumarankitkumar/) 20 | - [Facebook](https://www.facebook.com/freeankit) 21 | - [Slack](https://ankitdroid.slack.com) 22 | - [Stackoverflow](https://stackoverflow.com/users/3282461/android) 23 | - [Android App](https://play.google.com/store/apps/details?id=com.freeankit.ankitprofile) 24 | 25 | ### License 26 | 27 | Copyright 2017 Ankit Kumar 28 | 29 | Licensed under the Apache License, Version 2.0 (the "License"); 30 | you may not use this file except in compliance with the License. 31 | You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software 36 | distributed under the License is distributed on an "AS IS" BASIS, 37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 | See the License for the specific language governing permissions and 39 | limitations under the License. 40 | 41 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "28.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.divyankit.keyboard" 9 | minSdkVersion 19 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testImplementation 'junit:junit:4.12' 25 | implementation 'com.android.support:appcompat-v7:28.0.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/ankit/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/divyankit/keyboard/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/ApplicationKeyboard.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by ankit on 26/4/16. 7 | */ 8 | public class ApplicationKeyboard extends Application { 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/activities/ImePreferences.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.preference.PreferenceActivity; 6 | 7 | import com.divyankit.keyboard.R; 8 | import com.divyankit.keyboard.android.InputMethodSettingsFragment; 9 | 10 | 11 | /** 12 | * Created by ankit on 4/4/16. 13 | */ 14 | public class ImePreferences extends PreferenceActivity { 15 | @Override 16 | public Intent getIntent() { 17 | final Intent modIntent = new Intent(super.getIntent()); 18 | modIntent.putExtra(EXTRA_SHOW_FRAGMENT, Settings.class.getName()); 19 | modIntent.putExtra(EXTRA_NO_HEADERS, true); 20 | return modIntent; 21 | } 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setTitle(R.string.settings_name); 27 | } 28 | 29 | @Override 30 | protected boolean isValidFragment(final String fragmentName) { 31 | return Settings.class.getName().equals(fragmentName); 32 | } 33 | 34 | public static class Settings extends InputMethodSettingsFragment { 35 | @Override 36 | public void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setInputMethodSettingsCategoryTitle(R.string.language_selection_title); 39 | setSubtypeEnablerTitle(R.string.select_language); 40 | // Load the preferences from an XML resource 41 | addPreferencesFromResource(R.xml.ime_preferences); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/android/InputMethodSettingsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This is a part of the inputmethod-common static Java library. 19 | * The original source code can be found at frameworks/opt/inputmethodcommon of Android Open Source 20 | * Project. 21 | */ 22 | 23 | package com.divyankit.keyboard.android; 24 | 25 | import android.content.Context; 26 | import android.graphics.drawable.Drawable; 27 | import android.os.Bundle; 28 | import android.preference.PreferenceFragment; 29 | 30 | /** 31 | * This is a helper class for an IME's settings preference fragment. It's recommended for every 32 | * IME to have its own settings preference fragment which inherits this class. 33 | */ 34 | public abstract class InputMethodSettingsFragment extends PreferenceFragment 35 | implements InputMethodSettingsInterface { 36 | private final InputMethodSettingsImpl mSettings = new InputMethodSettingsImpl(); 37 | 38 | @Override 39 | public void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | final Context context = getActivity(); 42 | setPreferenceScreen(getPreferenceManager().createPreferenceScreen(context)); 43 | mSettings.init(context, getPreferenceScreen()); 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | @Override 50 | public void setInputMethodSettingsCategoryTitle(int resId) { 51 | mSettings.setInputMethodSettingsCategoryTitle(resId); 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | @Override 58 | public void setInputMethodSettingsCategoryTitle(CharSequence title) { 59 | mSettings.setInputMethodSettingsCategoryTitle(title); 60 | } 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | @Override 66 | public void setSubtypeEnablerTitle(int resId) { 67 | mSettings.setSubtypeEnablerTitle(resId); 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | @Override 74 | public void setSubtypeEnablerTitle(CharSequence title) { 75 | mSettings.setSubtypeEnablerTitle(title); 76 | } 77 | 78 | /** 79 | * {@inheritDoc} 80 | */ 81 | @Override 82 | public void setSubtypeEnablerIcon(int resId) { 83 | mSettings.setSubtypeEnablerIcon(resId); 84 | } 85 | 86 | /** 87 | * {@inheritDoc} 88 | */ 89 | @Override 90 | public void setSubtypeEnablerIcon(Drawable drawable) { 91 | mSettings.setSubtypeEnablerIcon(drawable); 92 | } 93 | 94 | /** 95 | * {@inheritDoc} 96 | */ 97 | @Override 98 | public void onResume() { 99 | super.onResume(); 100 | mSettings.updateSubtypeEnabler(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/android/InputMethodSettingsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This is a part of the inputmethod-common static Java library. 19 | * The original source code can be found at frameworks/opt/inputmethodcommon of Android Open Source 20 | * Project. 21 | */ 22 | 23 | package com.divyankit.keyboard.android; 24 | 25 | import android.content.Context; 26 | import android.content.Intent; 27 | import android.graphics.drawable.Drawable; 28 | import android.preference.Preference; 29 | import android.preference.Preference.OnPreferenceClickListener; 30 | import android.preference.PreferenceScreen; 31 | import android.provider.Settings; 32 | import android.text.TextUtils; 33 | import android.view.inputmethod.InputMethodInfo; 34 | import android.view.inputmethod.InputMethodManager; 35 | import android.view.inputmethod.InputMethodSubtype; 36 | 37 | import java.util.List; 38 | 39 | class InputMethodSettingsImpl implements InputMethodSettingsInterface { 40 | private Preference mSubtypeEnablerPreference; 41 | private int mInputMethodSettingsCategoryTitleRes; 42 | private CharSequence mInputMethodSettingsCategoryTitle; 43 | private int mSubtypeEnablerTitleRes; 44 | private CharSequence mSubtypeEnablerTitle; 45 | private int mSubtypeEnablerIconRes; 46 | private Drawable mSubtypeEnablerIcon; 47 | private InputMethodManager mImm; 48 | private InputMethodInfo mImi; 49 | private Context mContext; 50 | 51 | /** 52 | * Initialize internal states of this object. 53 | * 54 | * @param context the context for this application. 55 | * @param prefScreen a PreferenceScreen of PreferenceActivity or PreferenceFragment. 56 | * @return true if this application is an IME and has two or more subtypes, false otherwise. 57 | */ 58 | public boolean init(final Context context, final PreferenceScreen prefScreen) { 59 | mContext = context; 60 | mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); 61 | mImi = getMyImi(context, mImm); 62 | if (mImi == null || mImi.getSubtypeCount() <= 1) { 63 | return false; 64 | } 65 | mSubtypeEnablerPreference = new Preference(context); 66 | mSubtypeEnablerPreference 67 | .setOnPreferenceClickListener(new OnPreferenceClickListener() { 68 | @Override 69 | public boolean onPreferenceClick(Preference preference) { 70 | final CharSequence title = getSubtypeEnablerTitle(context); 71 | final Intent intent = 72 | new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); 73 | intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, mImi.getId()); 74 | if (!TextUtils.isEmpty(title)) { 75 | intent.putExtra(Intent.EXTRA_TITLE, title); 76 | } 77 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 78 | | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 79 | | Intent.FLAG_ACTIVITY_CLEAR_TOP); 80 | context.startActivity(intent); 81 | return true; 82 | } 83 | }); 84 | prefScreen.addPreference(mSubtypeEnablerPreference); 85 | updateSubtypeEnabler(); 86 | return true; 87 | } 88 | 89 | private static InputMethodInfo getMyImi(Context context, InputMethodManager imm) { 90 | final List imis = imm.getInputMethodList(); 91 | for (int i = 0; i < imis.size(); ++i) { 92 | final InputMethodInfo imi = imis.get(i); 93 | if (imis.get(i).getPackageName().equals(context.getPackageName())) { 94 | return imi; 95 | } 96 | } 97 | return null; 98 | } 99 | 100 | private static String getEnabledSubtypesLabel( 101 | Context context, InputMethodManager imm, InputMethodInfo imi) { 102 | if (context == null || imm == null || imi == null) return null; 103 | final List subtypes = imm.getEnabledInputMethodSubtypeList(imi, true); 104 | final StringBuilder sb = new StringBuilder(); 105 | final int N = subtypes.size(); 106 | for (int i = 0; i < N; ++i) { 107 | final InputMethodSubtype subtype = subtypes.get(i); 108 | if (sb.length() > 0) { 109 | sb.append(", "); 110 | } 111 | sb.append(subtype.getDisplayName(context, imi.getPackageName(), 112 | imi.getServiceInfo().applicationInfo)); 113 | } 114 | return sb.toString(); 115 | } 116 | 117 | /** 118 | * {@inheritDoc} 119 | */ 120 | @Override 121 | public void setInputMethodSettingsCategoryTitle(int resId) { 122 | mInputMethodSettingsCategoryTitleRes = resId; 123 | updateSubtypeEnabler(); 124 | } 125 | 126 | /** 127 | * {@inheritDoc} 128 | */ 129 | @Override 130 | public void setInputMethodSettingsCategoryTitle(CharSequence title) { 131 | mInputMethodSettingsCategoryTitleRes = 0; 132 | mInputMethodSettingsCategoryTitle = title; 133 | updateSubtypeEnabler(); 134 | } 135 | 136 | /** 137 | * {@inheritDoc} 138 | */ 139 | @Override 140 | public void setSubtypeEnablerTitle(int resId) { 141 | mSubtypeEnablerTitleRes = resId; 142 | updateSubtypeEnabler(); 143 | } 144 | 145 | /** 146 | * {@inheritDoc} 147 | */ 148 | @Override 149 | public void setSubtypeEnablerTitle(CharSequence title) { 150 | mSubtypeEnablerTitleRes = 0; 151 | mSubtypeEnablerTitle = title; 152 | updateSubtypeEnabler(); 153 | } 154 | 155 | /** 156 | * {@inheritDoc} 157 | */ 158 | @Override 159 | public void setSubtypeEnablerIcon(int resId) { 160 | mSubtypeEnablerIconRes = resId; 161 | updateSubtypeEnabler(); 162 | } 163 | 164 | /** 165 | * {@inheritDoc} 166 | */ 167 | @Override 168 | public void setSubtypeEnablerIcon(Drawable drawable) { 169 | mSubtypeEnablerIconRes = 0; 170 | mSubtypeEnablerIcon = drawable; 171 | updateSubtypeEnabler(); 172 | } 173 | 174 | private CharSequence getSubtypeEnablerTitle(Context context) { 175 | if (mSubtypeEnablerTitleRes != 0) { 176 | return context.getString(mSubtypeEnablerTitleRes); 177 | } else { 178 | return mSubtypeEnablerTitle; 179 | } 180 | } 181 | 182 | public void updateSubtypeEnabler() { 183 | if (mSubtypeEnablerPreference != null) { 184 | if (mSubtypeEnablerTitleRes != 0) { 185 | mSubtypeEnablerPreference.setTitle(mSubtypeEnablerTitleRes); 186 | } else if (!TextUtils.isEmpty(mSubtypeEnablerTitle)) { 187 | mSubtypeEnablerPreference.setTitle(mSubtypeEnablerTitle); 188 | } 189 | final String summary = getEnabledSubtypesLabel(mContext, mImm, mImi); 190 | if (!TextUtils.isEmpty(summary)) { 191 | mSubtypeEnablerPreference.setSummary(summary); 192 | } 193 | if (mSubtypeEnablerIconRes != 0) { 194 | mSubtypeEnablerPreference.setIcon(mSubtypeEnablerIconRes); 195 | } else if (mSubtypeEnablerIcon != null) { 196 | mSubtypeEnablerPreference.setIcon(mSubtypeEnablerIcon); 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/android/InputMethodSettingsInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | 17 | /** 18 | * This is a part of the inputmethod-common static Java library. 19 | * The original source code can be found at frameworks/opt/inputmethodcommon of Android Open Source 20 | * Project. 21 | */ 22 | 23 | package com.divyankit.keyboard.android; 24 | 25 | import android.graphics.drawable.Drawable; 26 | 27 | /** 28 | * InputMethodSettingsInterface is the interface for adding IME related preferences to 29 | * PreferenceActivity or PreferenceFragment. 30 | */ 31 | public interface InputMethodSettingsInterface { 32 | /** 33 | * Sets the title for the input method settings category with a resource ID. 34 | * @param resId The resource ID of the title. 35 | */ 36 | public void setInputMethodSettingsCategoryTitle(int resId); 37 | 38 | /** 39 | * Sets the title for the input method settings category with a CharSequence. 40 | * @param title The title for this preference. 41 | */ 42 | public void setInputMethodSettingsCategoryTitle(CharSequence title); 43 | 44 | /** 45 | * Sets the title for the input method enabler preference for launching subtype enabler with a 46 | * resource ID. 47 | * @param resId The resource ID of the title. 48 | */ 49 | public void setSubtypeEnablerTitle(int resId); 50 | 51 | /** 52 | * Sets the title for the input method enabler preference for launching subtype enabler with a 53 | * CharSequence. 54 | * @param title The title for this preference. 55 | */ 56 | public void setSubtypeEnablerTitle(CharSequence title); 57 | 58 | /** 59 | * Sets the icon for the preference for launching subtype enabler with a resource ID. 60 | * @param resId The resource id of an optional icon for the preference. 61 | */ 62 | public void setSubtypeEnablerIcon(int resId); 63 | 64 | /** 65 | * Sets the icon for the Preference for launching subtype enabler with a Drawable. 66 | * @param drawable The drawable of an optional icon for the preference. 67 | */ 68 | public void setSubtypeEnablerIcon(Drawable drawable); 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/utils/CandidateView.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Drawable; 9 | import android.view.GestureDetector; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | 13 | import com.divyankit.keyboard.R; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by ankit on 4/4/16. 20 | */ 21 | public class CandidateView extends View { 22 | private static final int OUT_OF_BOUNDS = 10; 23 | private SoftKeyboard mService; 24 | private List mSuggestions; 25 | private int mSelectedIndex; 26 | private int mTouchX = OUT_OF_BOUNDS; 27 | private Drawable mSelectionHighlight; 28 | private boolean mTypedWordValid; 29 | 30 | private Rect mBgPadding; 31 | private static final int MAX_SUGGESTIONS = 32; 32 | private static final int SCROLL_PIXELS = 20; 33 | 34 | private int[] mWordWidth = new int[MAX_SUGGESTIONS]; 35 | private int[] mWordX = new int[MAX_SUGGESTIONS]; 36 | private static final int X_GAP = 20; 37 | 38 | private static final List EMPTY_LIST = new ArrayList(); 39 | private int mColorNormal; 40 | private int mColorRecommended; 41 | private int mColorOther; 42 | private int mVerticalPadding; 43 | private Paint mPaint; 44 | private boolean mScrolled; 45 | private int mTargetScrollX; 46 | 47 | private int mTotalWidth; 48 | 49 | private GestureDetector mGestureDetector; 50 | 51 | /** 52 | * Construct a CandidateView for showing suggested words for completion. 53 | * 54 | * @param context 55 | * @param attrs 56 | */ 57 | public CandidateView(Context context) { 58 | super(context); 59 | mSelectionHighlight = context.getResources().getDrawable(android.R.drawable.list_selector_background); 60 | mSelectionHighlight.setState( 61 | new int[]{ 62 | android.R.attr.state_enabled, 63 | android.R.attr.state_focused, 64 | android.R.attr.state_window_focused, 65 | android.R.attr.state_pressed 66 | }); 67 | Resources r = context.getResources(); 68 | 69 | setBackgroundColor(r.getColor(R.color.candidate_background)); 70 | 71 | mColorNormal = r.getColor(R.color.candidate_normal); 72 | mColorRecommended = r.getColor(R.color.candidate_recommended); 73 | mColorOther = r.getColor(R.color.candidate_other); 74 | mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding); 75 | 76 | mPaint = new Paint(); 77 | mPaint.setColor(mColorNormal); 78 | mPaint.setAntiAlias(true); 79 | mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height)); 80 | mPaint.setStrokeWidth(10); 81 | 82 | mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { 83 | @Override 84 | public boolean onScroll(MotionEvent e1, MotionEvent e2, 85 | float distanceX, float distanceY) { 86 | mScrolled = true; 87 | int sx = getScrollX(); 88 | sx += distanceX; 89 | if (sx < 0) { 90 | sx = 0; 91 | } 92 | if (sx + getWidth() > mTotalWidth) { 93 | sx -= distanceX; 94 | } 95 | mTargetScrollX = sx; 96 | scrollTo(sx, getScrollY()); 97 | invalidate(); 98 | return true; 99 | } 100 | }); 101 | setHorizontalFadingEdgeEnabled(true); 102 | setWillNotDraw(false); 103 | setHorizontalScrollBarEnabled(false); 104 | setVerticalScrollBarEnabled(false); 105 | } 106 | 107 | /** 108 | * A connection back to the service to communicate with the text field 109 | * 110 | * @param listener 111 | */ 112 | public void setService(SoftKeyboard listener) { 113 | mService = listener; 114 | } 115 | 116 | @Override 117 | public int computeHorizontalScrollRange() { 118 | return mTotalWidth; 119 | } 120 | 121 | @Override 122 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 123 | int measuredWidth = resolveSize(50, widthMeasureSpec); 124 | 125 | // Get the desired height of the icon menu view (last row of items does 126 | // not have a divider below) 127 | Rect padding = new Rect(); 128 | mSelectionHighlight.getPadding(padding); 129 | final int desiredHeight = ((int) mPaint.getTextSize()) + mVerticalPadding 130 | + padding.top + padding.bottom; 131 | 132 | // Maximum possible width and desired height 133 | setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec)); 134 | } 135 | 136 | /** 137 | * If the canvas is null, then only touch calculations are performed to pick the target 138 | * candidate. 139 | */ 140 | @Override 141 | protected void onDraw(Canvas canvas) { 142 | if (canvas != null) { 143 | super.onDraw(canvas); 144 | } 145 | mTotalWidth = 0; 146 | if (mSuggestions == null) return; 147 | 148 | if (mBgPadding == null) { 149 | mBgPadding = new Rect(0, 0, 0, 0); 150 | if (getBackground() != null) { 151 | getBackground().getPadding(mBgPadding); 152 | } 153 | } 154 | int x = 0; 155 | final int count = mSuggestions.size(); 156 | final int height = getHeight(); 157 | final Rect bgPadding = mBgPadding; 158 | final Paint paint = mPaint; 159 | final int touchX = mTouchX; 160 | final int scrollX = getScrollX(); 161 | final boolean scrolled = mScrolled; 162 | final boolean typedWordValid = mTypedWordValid; 163 | final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent()); 164 | for (int i = 0; i < count; i++) { 165 | String suggestion = mSuggestions.get(i); 166 | float textWidth = paint.measureText(suggestion); 167 | final int wordWidth = (int) textWidth + X_GAP * 2; 168 | mWordX[i] = x; 169 | mWordWidth[i] = wordWidth; 170 | paint.setColor(mColorNormal); 171 | if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) { 172 | if (canvas != null) { 173 | canvas.translate(x, 0); 174 | mSelectionHighlight.setBounds(10, bgPadding.top, wordWidth, height); 175 | mSelectionHighlight.draw(canvas); 176 | canvas.translate(-x, 0); 177 | } 178 | mSelectedIndex = i; 179 | } 180 | if (canvas != null) { 181 | if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) { 182 | paint.setFakeBoldText(true); 183 | paint.setColor(mColorRecommended); 184 | } else if (i != 0) { 185 | paint.setColor(mColorOther); 186 | } 187 | canvas.drawText(suggestion, x + X_GAP, y, paint); 188 | paint.setColor(mColorOther); 189 | canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top, 190 | x + wordWidth + 0.5f, height + 1, paint); 191 | paint.setFakeBoldText(false); 192 | } 193 | x += wordWidth; 194 | } 195 | mTotalWidth = x; 196 | if (mTargetScrollX != getScrollX()) { 197 | scrollToTarget(); 198 | } 199 | } 200 | 201 | private void scrollToTarget() { 202 | int sx = getScrollX(); 203 | if (mTargetScrollX > sx) { 204 | sx += SCROLL_PIXELS; 205 | if (sx >= mTargetScrollX) { 206 | sx = mTargetScrollX; 207 | requestLayout(); 208 | } 209 | } else { 210 | sx -= SCROLL_PIXELS; 211 | if (sx <= mTargetScrollX) { 212 | sx = mTargetScrollX; 213 | requestLayout(); 214 | } 215 | } 216 | scrollTo(sx, getScrollY()); 217 | invalidate(); 218 | } 219 | 220 | public void setSuggestions(List suggestions, boolean completions, boolean typedWordValid) { 221 | clear(); 222 | if (suggestions != null) { 223 | mSuggestions = new ArrayList(suggestions); 224 | } 225 | mTypedWordValid = typedWordValid; 226 | scrollTo(0, 0); 227 | mTargetScrollX = 0; 228 | // Compute the total width 229 | //draw(null); 230 | invalidate(); 231 | requestLayout(); 232 | } 233 | 234 | public void clear() { 235 | mSuggestions = EMPTY_LIST; 236 | mTouchX = OUT_OF_BOUNDS; 237 | mSelectedIndex = -1; 238 | invalidate(); 239 | } 240 | 241 | @Override 242 | public boolean onTouchEvent(MotionEvent me) { 243 | if (mGestureDetector.onTouchEvent(me)) { 244 | return true; 245 | } 246 | int action = me.getAction(); 247 | int x = (int) me.getX(); 248 | int y = (int) me.getY(); 249 | mTouchX = x; 250 | switch (action) { 251 | case MotionEvent.ACTION_DOWN: 252 | mScrolled = false; 253 | invalidate(); 254 | break; 255 | case MotionEvent.ACTION_MOVE: 256 | if (y <= 0) { 257 | // Fling up!? 258 | if (mSelectedIndex >= 0) { 259 | mService.pickSuggestionManually(mSelectedIndex); 260 | mSelectedIndex = -1; 261 | } 262 | } 263 | invalidate(); 264 | break; 265 | case MotionEvent.ACTION_UP: 266 | if (!mScrolled) { 267 | if (mSelectedIndex >= 0) { 268 | mService.pickSuggestionManually(mSelectedIndex); 269 | } 270 | } 271 | mSelectedIndex = -1; 272 | removeHighlight(); 273 | requestLayout(); 274 | break; 275 | } 276 | return true; 277 | } 278 | 279 | /** 280 | * For flick through from keyboard, call this method with the x coordinate of the flick 281 | * gesture. 282 | * 283 | * @param x 284 | */ 285 | public void takeSuggestionAt(float x) { 286 | mTouchX = (int) x; 287 | // To detect candidate 288 | draw(null); 289 | if (mSelectedIndex >= 0) { 290 | mService.pickSuggestionManually(mSelectedIndex); 291 | } 292 | invalidate(); 293 | } 294 | 295 | private void removeHighlight() { 296 | mTouchX = OUT_OF_BOUNDS; 297 | invalidate(); 298 | } 299 | 300 | } 301 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/utils/LatinKeyboard.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.XmlResourceParser; 6 | import android.graphics.drawable.Drawable; 7 | import android.inputmethodservice.Keyboard; 8 | import android.view.inputmethod.EditorInfo; 9 | import android.view.inputmethod.InputMethodManager; 10 | 11 | import com.divyankit.keyboard.R; 12 | 13 | /** 14 | * Created by ankit on 4/4/16. 15 | */ 16 | public class LatinKeyboard extends Keyboard { 17 | private Key mEnterKey; 18 | private Key mSpaceKey; 19 | /** 20 | * Stores the current state of the mode change key. Its width will be dynamically updated to 21 | * match the region of {@link #mModeChangeKey} when {@link #mModeChangeKey} becomes invisible. 22 | */ 23 | private Key mModeChangeKey; 24 | /** 25 | * Stores the current state of the language switch key (a.k.a. globe key). This should be 26 | * visible while {@link InputMethodManager#shouldOfferSwitchingToNextInputMethod(IBinder)} 27 | * returns true. When this key becomes invisible, its width will be shrunk to zero. 28 | */ 29 | private Key mLanguageSwitchKey; 30 | /** 31 | * Stores the size and other information of {@link #mModeChangeKey} when 32 | * {@link #mLanguageSwitchKey} is visible. This should be immutable and will be used only as a 33 | * reference size when the visibility of {@link #mLanguageSwitchKey} is changed. 34 | */ 35 | private Key mSavedModeChangeKey; 36 | /** 37 | * Stores the size and other information of {@link #mLanguageSwitchKey} when it is visible. 38 | * This should be immutable and will be used only as a reference size when the visibility of 39 | * {@link #mLanguageSwitchKey} is changed. 40 | */ 41 | private Key mSavedLanguageSwitchKey; 42 | 43 | public LatinKeyboard(Context context, int xmlLayoutResId) { 44 | super(context, xmlLayoutResId); 45 | } 46 | 47 | public LatinKeyboard(Context context, int layoutTemplateResId, 48 | CharSequence characters, int columns, int horizontalPadding) { 49 | super(context, layoutTemplateResId, characters, columns, horizontalPadding); 50 | } 51 | 52 | @Override 53 | protected Key createKeyFromXml(Resources res, Row parent, int x, int y, XmlResourceParser parser) { 54 | Key key = new LatinKey(res, parent, x, y, parser); 55 | if (key.codes[0] == 10) { 56 | mEnterKey = key; 57 | } else if (key.codes[0] == ' ') { 58 | mSpaceKey = key; 59 | } else if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) { 60 | mModeChangeKey = key; 61 | mSavedModeChangeKey = new LatinKey(res, parent, x, y, parser); 62 | } else if (key.codes[0] == LatinKeyboardView.KEYCODE_LANGUAGE_SWITCH) { 63 | mLanguageSwitchKey = key; 64 | mSavedLanguageSwitchKey = new LatinKey(res, parent, x, y, parser); 65 | } 66 | return key; 67 | } 68 | 69 | /** 70 | * Dynamically change the visibility of the language switch key (a.k.a. globe key). 71 | * 72 | * @param visible True if the language switch key should be visible. 73 | */ 74 | void setLanguageSwitchKeyVisibility(boolean visible) { 75 | if (visible) { 76 | // The language switch key should be visible. Restore the size of the mode change key 77 | // and language switch key using the saved layout. 78 | mModeChangeKey.width = mSavedModeChangeKey.width; 79 | mModeChangeKey.x = mSavedModeChangeKey.x; 80 | mLanguageSwitchKey.width = mSavedLanguageSwitchKey.width; 81 | mLanguageSwitchKey.icon = mSavedLanguageSwitchKey.icon; 82 | mLanguageSwitchKey.iconPreview = mSavedLanguageSwitchKey.iconPreview; 83 | } else { 84 | // The language switch key should be hidden. Change the width of the mode change key 85 | // to fill the space of the language key so that the user will not see any strange gap. 86 | mModeChangeKey.width = mSavedModeChangeKey.width + mSavedLanguageSwitchKey.width; 87 | mLanguageSwitchKey.width = 0; 88 | mLanguageSwitchKey.icon = null; 89 | mLanguageSwitchKey.iconPreview = null; 90 | } 91 | } 92 | 93 | /** 94 | * This looks at the ime options given by the current editor, to set the 95 | * appropriate label on the keyboard's enter key (if it has one). 96 | */ 97 | void setImeOptions(Resources res, int options) { 98 | if (mEnterKey == null) { 99 | return; 100 | } 101 | switch (options & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION)) { 102 | case EditorInfo.IME_ACTION_GO: 103 | mEnterKey.iconPreview = null; 104 | mEnterKey.icon = null; 105 | mEnterKey.label = res.getText(R.string.label_go_key); 106 | break; 107 | case EditorInfo.IME_ACTION_NEXT: 108 | mEnterKey.iconPreview = null; 109 | mEnterKey.icon = null; 110 | mEnterKey.label = res.getText(R.string.label_next_key); 111 | break; 112 | case EditorInfo.IME_ACTION_SEARCH: 113 | mEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_search); 114 | mEnterKey.label = null; 115 | break; 116 | case EditorInfo.IME_ACTION_SEND: 117 | mEnterKey.iconPreview = null; 118 | mEnterKey.icon = null; 119 | mEnterKey.label = res.getText(R.string.label_send_key); 120 | break; 121 | default: 122 | mEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_return); 123 | mEnterKey.label = null; 124 | break; 125 | } 126 | } 127 | 128 | void setSpaceIcon(final Drawable icon) { 129 | if (mSpaceKey != null) { 130 | mSpaceKey.icon = icon; 131 | } 132 | } 133 | 134 | static class LatinKey extends Key { 135 | 136 | public LatinKey(Resources res, Row parent, int x, int y, 137 | XmlResourceParser parser) { 138 | super(res, parent, x, y, parser); 139 | } 140 | 141 | /** 142 | * Overriding this method so that we can reduce the target area for the key that 143 | * closes the keyboard. 144 | */ 145 | @Override 146 | public boolean isInside(int x, int y) { 147 | return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y); 148 | } 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/utils/LatinKeyboardView.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard.utils; 2 | 3 | import android.content.Context; 4 | import android.inputmethodservice.Keyboard; 5 | import android.inputmethodservice.Keyboard.Key; 6 | import android.inputmethodservice.KeyboardView; 7 | import android.util.AttributeSet; 8 | import android.view.inputmethod.InputMethodSubtype; 9 | 10 | /** 11 | * Created by ankit on 4/4/16. 12 | */ 13 | public class LatinKeyboardView extends KeyboardView { 14 | static final int KEYCODE_OPTIONS = -100; 15 | static final int KEYCODE_LANGUAGE_SWITCH = -101; 16 | static final int KEYCODE_API = -600; 17 | 18 | public LatinKeyboardView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { 23 | super(context, attrs, defStyle); 24 | } 25 | 26 | @Override 27 | protected boolean onLongPress(Key key) { 28 | if (key.codes[0] == Keyboard.KEYCODE_CANCEL) { 29 | getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null); 30 | return true; 31 | } else { 32 | return super.onLongPress(key); 33 | } 34 | } 35 | 36 | void setSubtypeOnSpaceKey(final InputMethodSubtype subtype) { 37 | final LatinKeyboard keyboard = (LatinKeyboard) getKeyboard(); 38 | // keyboard.setSpaceIcon(getResources().getDrawable(subtype.getIconResId())); 39 | // keyboard.setShifted(true); 40 | invalidateAllKeys(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/divyankit/keyboard/utils/SoftKeyboard.java: -------------------------------------------------------------------------------- 1 | package com.divyankit.keyboard.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Point; 8 | import android.inputmethodservice.InputMethodService; 9 | import android.inputmethodservice.Keyboard; 10 | import android.inputmethodservice.KeyboardView; 11 | import android.os.Build; 12 | import android.os.Environment; 13 | import android.os.IBinder; 14 | import android.text.InputType; 15 | import android.text.method.MetaKeyKeyListener; 16 | import android.util.Log; 17 | import android.view.Display; 18 | import android.view.KeyCharacterMap; 19 | import android.view.KeyEvent; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.view.Window; 23 | import android.view.WindowManager; 24 | import android.view.inputmethod.CompletionInfo; 25 | import android.view.inputmethod.EditorInfo; 26 | import android.view.inputmethod.InputConnection; 27 | import android.view.inputmethod.InputMethodManager; 28 | import android.view.inputmethod.InputMethodSubtype; 29 | import android.widget.LinearLayout; 30 | 31 | import com.divyankit.keyboard.R; 32 | 33 | import java.io.File; 34 | import java.io.FileOutputStream; 35 | import java.util.ArrayList; 36 | import java.util.Date; 37 | import java.util.List; 38 | 39 | 40 | /** 41 | * Created by ankit on 4/4/16. 42 | */ 43 | public class SoftKeyboard extends InputMethodService 44 | implements KeyboardView.OnKeyboardActionListener { 45 | static final boolean DEBUG = false; 46 | LinearLayout view; 47 | 48 | /** 49 | * This boolean indicates the optional example code for performing 50 | * processing of hard keys in addition to regular text generation 51 | * from on-screen interaction. It would be used for input methods that 52 | * perform language translations (such as converting text entered on 53 | * a QWERTY keyboard to Chinese), but may not be used for input methods 54 | * that are primarily intended to be used for on-screen text entry. 55 | */ 56 | static final boolean PROCESS_HARD_KEYS = true; 57 | private InputMethodManager mInputMethodManager; 58 | private LatinKeyboardView mInputView; 59 | private CandidateView mCandidateView; 60 | private CompletionInfo[] mCompletions; 61 | 62 | private StringBuilder mComposing = new StringBuilder(); 63 | private boolean mPredictionOn; 64 | private boolean mCompletionOn; 65 | private int mLastDisplayWidth; 66 | private boolean mCapsLock; 67 | private long mLastShiftTime; 68 | private long mMetaState; 69 | 70 | private LatinKeyboard mSymbolsKeyboard; 71 | private LatinKeyboard mSymbolsShiftedKeyboard; 72 | private LatinKeyboard mQwertyKeyboard; 73 | 74 | private LatinKeyboard mCurKeyboard; 75 | 76 | private String mWordSeparators; 77 | 78 | 79 | //Added for image upload 80 | private String UPLOAD_URL = "http://simplifiedcoding.16mb.com/VolleyUpload/upload.php"; 81 | private static final String KEY_IMAGE = "key_image"; 82 | private static final String KEY_NAME = "key_name"; 83 | private Bitmap bitmap; 84 | 85 | // 86 | 87 | /** 88 | * Main initialization of the input method component. Be sure to call 89 | * to super class. 90 | */ 91 | @Override 92 | public void onCreate() { 93 | super.onCreate(); 94 | mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); 95 | mWordSeparators = getResources().getString(R.string.word_separators); 96 | } 97 | 98 | /** 99 | * This is the point where you can do all of your UI initialization. It 100 | * is called after creation and any configuration change. 101 | */ 102 | @Override 103 | public void onInitializeInterface() { 104 | if (mQwertyKeyboard != null) { 105 | // Configuration changes can happen after the keyboard gets recreated, 106 | // so we need to be able to re-build the keyboards if the available 107 | // space has changed. 108 | int displayWidth = getMaxWidth(); 109 | if (displayWidth == mLastDisplayWidth) return; 110 | mLastDisplayWidth = displayWidth; 111 | } 112 | mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty); 113 | mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols); 114 | mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift); 115 | } 116 | 117 | /** 118 | * Called by the framework when your view for creating input needs to 119 | * be generated. This will be called the first time your input method 120 | * is displayed, and every time it needs to be re-created such as due to 121 | * a configuration change. 122 | */ 123 | @Override 124 | public View onCreateInputView() { 125 | mInputView = (LatinKeyboardView) getLayoutInflater().inflate(R.layout.input, null); 126 | mInputView.setOnKeyboardActionListener(this); 127 | setLatinKeyboard(mQwertyKeyboard); 128 | return mInputView; 129 | } 130 | 131 | @TargetApi(Build.VERSION_CODES.KITKAT) 132 | private void setLatinKeyboard(LatinKeyboard nextKeyboard) { 133 | final boolean shouldSupportLanguageSwitchKey = 134 | mInputMethodManager.shouldOfferSwitchingToNextInputMethod(getToken()); 135 | nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey); 136 | mInputView.setKeyboard(nextKeyboard); 137 | } 138 | 139 | 140 | /** 141 | * Called by the framework when your view for showing candidates needs to 142 | * be generated, like {@link #onCreateInputView}. 143 | */ 144 | @Override 145 | public View onCreateCandidatesView() { 146 | mCandidateView = new CandidateView(this); 147 | mCandidateView.setService(this); 148 | return mCandidateView; 149 | } 150 | 151 | /** 152 | * This is the main point where we do our initialization of the input method 153 | * to begin operating on an application. At this point we have been 154 | * bound to the client, and are now receiving all of the detailed information 155 | * about the target of our edits. 156 | */ 157 | @Override 158 | public void onStartInput(EditorInfo attribute, boolean restarting) { 159 | super.onStartInput(attribute, restarting); 160 | 161 | // Reset our state. We want to do this even if restarting, because 162 | // the underlying state of the text editor could have changed in any way. 163 | mComposing.setLength(0); 164 | updateCandidates(); 165 | 166 | if (!restarting) { 167 | // Clear shift states. 168 | mMetaState = 0; 169 | } 170 | 171 | mPredictionOn = false; 172 | mCompletionOn = false; 173 | mCompletions = null; 174 | 175 | // We are now going to initialize our state based on the type of 176 | // text being edited. 177 | switch (attribute.inputType & InputType.TYPE_MASK_CLASS) { 178 | case InputType.TYPE_CLASS_NUMBER: 179 | case InputType.TYPE_CLASS_DATETIME: 180 | // Numbers and dates default to the symbols keyboard, with 181 | // no extra features. 182 | mCurKeyboard = mSymbolsKeyboard; 183 | break; 184 | 185 | case InputType.TYPE_CLASS_PHONE: 186 | // Phones will also default to the symbols keyboard, though 187 | // often you will want to have a dedicated phone keyboard. 188 | mCurKeyboard = mSymbolsKeyboard; 189 | break; 190 | 191 | case InputType.TYPE_CLASS_TEXT: 192 | // This is general text editing. We will default to the 193 | // normal alphabetic keyboard, and assume that we should 194 | // be doing predictive text (showing candidates as the 195 | // user types). 196 | mCurKeyboard = mQwertyKeyboard; 197 | mPredictionOn = true; 198 | 199 | // We now look for a few special variations of text that will 200 | // modify our behavior. 201 | int variation = attribute.inputType & InputType.TYPE_MASK_VARIATION; 202 | if (variation == InputType.TYPE_TEXT_VARIATION_PASSWORD || 203 | variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { 204 | // Do not display predictions / what the user is typing 205 | // when they are entering a password. 206 | mPredictionOn = false; 207 | } 208 | 209 | if (variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 210 | || variation == InputType.TYPE_TEXT_VARIATION_URI 211 | || variation == InputType.TYPE_TEXT_VARIATION_FILTER) { 212 | // Our predictions are not useful for e-mail addresses 213 | // or URIs. 214 | mPredictionOn = false; 215 | } 216 | 217 | if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { 218 | // If this is an auto-complete text view, then our predictions 219 | // will not be shown and instead we will allow the editor 220 | // to supply their own. We only show the editor's 221 | // candidates when in fullscreen mode, otherwise relying 222 | // own it displaying its own UI. 223 | mPredictionOn = false; 224 | mCompletionOn = isFullscreenMode(); 225 | } 226 | 227 | // We also want to look at the current state of the editor 228 | // to decide whether our alphabetic keyboard should start out 229 | // shifted. 230 | updateShiftKeyState(attribute); 231 | break; 232 | 233 | default: 234 | // For all unknown input types, default to the alphabetic 235 | // keyboard with no special features. 236 | mCurKeyboard = mQwertyKeyboard; 237 | updateShiftKeyState(attribute); 238 | } 239 | 240 | // Update the label on the enter key, depending on what the application 241 | // says it will do. 242 | mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions); 243 | } 244 | 245 | /** 246 | * This is called when the user is done editing a field. We can use 247 | * this to reset our state. 248 | */ 249 | @Override 250 | public void onFinishInput() { 251 | super.onFinishInput(); 252 | 253 | // Clear current composing text and candidates. 254 | mComposing.setLength(0); 255 | updateCandidates(); 256 | 257 | // We only hide the candidates window when finishing input on 258 | // a particular editor, to avoid popping the underlying application 259 | // up and down if the user is entering text into the bottom of 260 | // its window. 261 | setCandidatesViewShown(false); 262 | 263 | mCurKeyboard = mQwertyKeyboard; 264 | if (mInputView != null) { 265 | mInputView.closing(); 266 | } 267 | } 268 | 269 | @Override 270 | public void onStartInputView(EditorInfo attribute, boolean restarting) { 271 | super.onStartInputView(attribute, restarting); 272 | // Apply the selected keyboard to the input view. 273 | setLatinKeyboard(mCurKeyboard); 274 | mInputView.closing(); 275 | final InputMethodSubtype subtype = mInputMethodManager.getCurrentInputMethodSubtype(); 276 | mInputView.setSubtypeOnSpaceKey(subtype); 277 | } 278 | 279 | @Override 280 | public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) { 281 | mInputView.setSubtypeOnSpaceKey(subtype); 282 | } 283 | 284 | /** 285 | * Deal with the editor reporting movement of its cursor. 286 | */ 287 | @Override 288 | public void onUpdateSelection(int oldSelStart, int oldSelEnd, 289 | int newSelStart, int newSelEnd, 290 | int candidatesStart, int candidatesEnd) { 291 | super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, 292 | candidatesStart, candidatesEnd); 293 | 294 | // If the current selection in the text view changes, we should 295 | // clear whatever candidate text we have. 296 | if (mComposing.length() > 0 && (newSelStart != candidatesEnd 297 | || newSelEnd != candidatesEnd)) { 298 | mComposing.setLength(0); 299 | updateCandidates(); 300 | InputConnection ic = getCurrentInputConnection(); 301 | if (ic != null) { 302 | ic.finishComposingText(); 303 | } 304 | } 305 | } 306 | 307 | /** 308 | * This tells us about completions that the editor has determined based 309 | * on the current text in it. We want to use this in fullscreen mode 310 | * to show the completions ourself, since the editor can not be seen 311 | * in that situation. 312 | */ 313 | @Override 314 | public void onDisplayCompletions(CompletionInfo[] completions) { 315 | if (mCompletionOn) { 316 | mCompletions = completions; 317 | if (completions == null) { 318 | setSuggestions(null, false, false); 319 | return; 320 | } 321 | 322 | List stringList = new ArrayList(); 323 | for (int i = 0; i < completions.length; i++) { 324 | CompletionInfo ci = completions[i]; 325 | if (ci != null) stringList.add(ci.getText().toString()); 326 | } 327 | setSuggestions(stringList, true, true); 328 | } 329 | } 330 | 331 | /** 332 | * This translates incoming hard key events in to edit operations on an 333 | * InputConnection. It is only needed when using the 334 | * PROCESS_HARD_KEYS option. 335 | */ 336 | private boolean translateKeyDown(int keyCode, KeyEvent event) { 337 | mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState, 338 | keyCode, event); 339 | int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState)); 340 | mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState); 341 | InputConnection ic = getCurrentInputConnection(); 342 | if (c == 0 || ic == null) { 343 | return false; 344 | } 345 | 346 | boolean dead = false; 347 | if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { 348 | dead = true; 349 | c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; 350 | } 351 | 352 | if (mComposing.length() > 0) { 353 | char accent = mComposing.charAt(mComposing.length() - 1); 354 | int composed = KeyEvent.getDeadChar(accent, c); 355 | if (composed != 0) { 356 | c = composed; 357 | mComposing.setLength(mComposing.length() - 1); 358 | } 359 | } 360 | 361 | onKey(c, null); 362 | 363 | return true; 364 | } 365 | 366 | /** 367 | * Use this to monitor key events being delivered to the application. 368 | * We get first crack at them, and can either resume them or let them 369 | * continue to the app. 370 | */ 371 | @Override 372 | public boolean onKeyDown(int keyCode, KeyEvent event) { 373 | switch (keyCode) { 374 | case KeyEvent.KEYCODE_BACK: 375 | // The InputMethodService already takes care of the back 376 | // key for us, to dismiss the input method if it is shown. 377 | // However, our keyboard could be showing a pop-up window 378 | // that back should dismiss, so we first allow it to do that. 379 | if (event.getRepeatCount() == 0 && mInputView != null) { 380 | if (mInputView.handleBack()) { 381 | return true; 382 | } 383 | } 384 | break; 385 | 386 | case KeyEvent.KEYCODE_DEL: 387 | // Special handling of the delete key: if we currently are 388 | // composing text for the user, we want to modify that instead 389 | // of let the application to the delete itself. 390 | if (mComposing.length() > 0) { 391 | onKey(Keyboard.KEYCODE_DELETE, null); 392 | return true; 393 | } 394 | break; 395 | 396 | case KeyEvent.KEYCODE_ENTER: 397 | // Let the underlying text editor always handle these. 398 | return false; 399 | 400 | default: 401 | // For all other keys, if we want to do transformations on 402 | // text being entered with a hard keyboard, we need to process 403 | // it and do the appropriate action. 404 | if (PROCESS_HARD_KEYS) { 405 | if (keyCode == KeyEvent.KEYCODE_SPACE 406 | && (event.getMetaState() & KeyEvent.META_ALT_ON) != 0) { 407 | // A silly example: in our input method, Alt+Space 408 | // is a shortcut for 'android' in lower case. 409 | InputConnection ic = getCurrentInputConnection(); 410 | if (ic != null) { 411 | // First, tell the editor that it is no longer in the 412 | // shift state, since we are consuming this. 413 | ic.clearMetaKeyStates(KeyEvent.META_ALT_ON); 414 | keyDownUp(KeyEvent.KEYCODE_A); 415 | keyDownUp(KeyEvent.KEYCODE_N); 416 | keyDownUp(KeyEvent.KEYCODE_D); 417 | keyDownUp(KeyEvent.KEYCODE_R); 418 | keyDownUp(KeyEvent.KEYCODE_O); 419 | keyDownUp(KeyEvent.KEYCODE_I); 420 | keyDownUp(KeyEvent.KEYCODE_D); 421 | // And we consume this event. 422 | return true; 423 | } 424 | } 425 | if (mPredictionOn && translateKeyDown(keyCode, event)) { 426 | return true; 427 | } 428 | } 429 | } 430 | 431 | return super.onKeyDown(keyCode, event); 432 | } 433 | 434 | /** 435 | * Use this to monitor key events being delivered to the application. 436 | * We get first crack at them, and can either resume them or let them 437 | * continue to the app. 438 | */ 439 | @Override 440 | public boolean onKeyUp(int keyCode, KeyEvent event) { 441 | // If we want to do transformations on text being entered with a hard 442 | // keyboard, we need to process the up events to update the meta key 443 | // state we are tracking. 444 | if (PROCESS_HARD_KEYS) { 445 | if (mPredictionOn) { 446 | mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState, 447 | keyCode, event); 448 | } 449 | } 450 | 451 | return super.onKeyUp(keyCode, event); 452 | } 453 | 454 | /** 455 | * Helper function to commit any text being composed in to the editor. 456 | */ 457 | private void commitTyped(InputConnection inputConnection) { 458 | if (mComposing.length() > 0) { 459 | inputConnection.commitText(mComposing, mComposing.length()); 460 | mComposing.setLength(0); 461 | updateCandidates(); 462 | } 463 | } 464 | 465 | /** 466 | * Helper to update the shift state of our keyboard based on the initial 467 | * editor state. 468 | */ 469 | private void updateShiftKeyState(EditorInfo attr) { 470 | if (attr != null 471 | && mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) { 472 | int caps = 0; 473 | EditorInfo ei = getCurrentInputEditorInfo(); 474 | if (ei != null && ei.inputType != InputType.TYPE_NULL) { 475 | caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType); 476 | } 477 | mInputView.setShifted(mCapsLock || caps != 0); 478 | } 479 | } 480 | 481 | /** 482 | * Helper to determine if a given character code is alphabetic. 483 | */ 484 | private boolean isAlphabet(int code) { 485 | if (Character.isLetter(code)) { 486 | return true; 487 | } else { 488 | return false; 489 | } 490 | } 491 | 492 | /** 493 | * Helper to send a key down / key up pair to the current editor. 494 | */ 495 | private void keyDownUp(int keyEventCode) { 496 | getCurrentInputConnection().sendKeyEvent( 497 | new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); 498 | getCurrentInputConnection().sendKeyEvent( 499 | new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); 500 | } 501 | 502 | /** 503 | * Helper to send a character to the editor as raw key events. 504 | */ 505 | private void sendKey(int keyCode) { 506 | switch (keyCode) { 507 | case '\n': 508 | keyDownUp(KeyEvent.KEYCODE_ENTER); 509 | break; 510 | default: 511 | if (keyCode >= '0' && keyCode <= '9') { 512 | keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0); 513 | } else { 514 | getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1); 515 | } 516 | break; 517 | } 518 | } 519 | 520 | // Implementation of KeyboardViewListener 521 | public void onKey(int primaryCode, int[] keyCodes) { 522 | if (isWordSeparator(primaryCode)) { 523 | // Handle separator 524 | if (mComposing.length() > 0) { 525 | commitTyped(getCurrentInputConnection()); 526 | } 527 | sendKey(primaryCode); 528 | updateShiftKeyState(getCurrentInputEditorInfo()); 529 | } else if (primaryCode == Keyboard.KEYCODE_DELETE) { 530 | handleBackspace(); 531 | } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { 532 | handleShift(); 533 | } else if (primaryCode == Keyboard.KEYCODE_CANCEL) { 534 | handleClose(); 535 | return; 536 | } else if (primaryCode == LatinKeyboardView.KEYCODE_LANGUAGE_SWITCH) { 537 | handleLanguageSwitch(); 538 | return; 539 | } else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) { 540 | // Show a menu or somethin' 541 | } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mInputView != null) { 542 | Keyboard current = mInputView.getKeyboard(); 543 | if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) { 544 | setLatinKeyboard(mQwertyKeyboard); 545 | } else { 546 | setLatinKeyboard(mSymbolsKeyboard); 547 | mSymbolsKeyboard.setShifted(false); 548 | } 549 | } else { 550 | handleCharacter(primaryCode, keyCodes); 551 | } 552 | } 553 | 554 | public void onText(CharSequence text) { 555 | InputConnection ic = getCurrentInputConnection(); 556 | if (ic == null) return; 557 | ic.beginBatchEdit(); 558 | if (mComposing.length() > 0) { 559 | commitTyped(ic); 560 | } 561 | ic.commitText(text, 0); 562 | ic.endBatchEdit(); 563 | updateShiftKeyState(getCurrentInputEditorInfo()); 564 | } 565 | 566 | /** 567 | * Update the list of available candidates from the current composing 568 | * text. This will need to be filled in by however you are determining 569 | * candidates. 570 | */ 571 | private void updateCandidates() { 572 | if (!mCompletionOn) { 573 | if (mComposing.length() > 0) { 574 | ArrayList list = new ArrayList(); 575 | list.add(mComposing.toString()); 576 | setSuggestions(list, true, true); 577 | } else { 578 | setSuggestions(null, false, false); 579 | } 580 | } 581 | } 582 | 583 | public void setSuggestions(List suggestions, boolean completions, 584 | boolean typedWordValid) { 585 | if (suggestions != null && suggestions.size() > 0) { 586 | setCandidatesViewShown(true); 587 | } else if (isExtractViewShown()) { 588 | setCandidatesViewShown(true); 589 | } 590 | if (mCandidateView != null) { 591 | mCandidateView.setSuggestions(suggestions, completions, typedWordValid); 592 | } 593 | } 594 | 595 | private void handleBackspace() { 596 | final int length = mComposing.length(); 597 | if (length > 1) { 598 | mComposing.delete(length - 1, length); 599 | getCurrentInputConnection().setComposingText(mComposing, 1); 600 | updateCandidates(); 601 | } else if (length > 0) { 602 | mComposing.setLength(0); 603 | getCurrentInputConnection().commitText("", 0); 604 | updateCandidates(); 605 | } else { 606 | keyDownUp(KeyEvent.KEYCODE_DEL); 607 | } 608 | updateShiftKeyState(getCurrentInputEditorInfo()); 609 | } 610 | 611 | private void handleShift() { 612 | if (mInputView == null) { 613 | return; 614 | } 615 | 616 | Keyboard currentKeyboard = mInputView.getKeyboard(); 617 | if (mQwertyKeyboard == currentKeyboard) { 618 | // Alphabet keyboard 619 | checkToggleCapsLock(); 620 | mInputView.setShifted(mCapsLock || !mInputView.isShifted()); 621 | } else if (currentKeyboard == mSymbolsKeyboard) { 622 | mSymbolsKeyboard.setShifted(true); 623 | setLatinKeyboard(mSymbolsShiftedKeyboard); 624 | mSymbolsShiftedKeyboard.setShifted(true); 625 | } else if (currentKeyboard == mSymbolsShiftedKeyboard) { 626 | mSymbolsShiftedKeyboard.setShifted(false); 627 | setLatinKeyboard(mSymbolsKeyboard); 628 | mSymbolsKeyboard.setShifted(false); 629 | } 630 | } 631 | 632 | private void handleCharacter(int primaryCode, int[] keyCodes) { 633 | if (isInputViewShown()) { 634 | if (mInputView.isShifted()) { 635 | primaryCode = Character.toUpperCase(primaryCode); 636 | } 637 | } 638 | if (isAlphabet(primaryCode) && mPredictionOn) { 639 | mComposing.append((char) primaryCode); 640 | getCurrentInputConnection().setComposingText(mComposing, 1); 641 | updateShiftKeyState(getCurrentInputEditorInfo()); 642 | updateCandidates(); 643 | } else { 644 | getCurrentInputConnection().commitText( 645 | String.valueOf((char) primaryCode), 1); 646 | } 647 | } 648 | 649 | private void handleClose() { 650 | commitTyped(getCurrentInputConnection()); 651 | requestHideSelf(0); 652 | mInputView.closing(); 653 | } 654 | 655 | private IBinder getToken() { 656 | final Dialog dialog = getWindow(); 657 | if (dialog == null) { 658 | return null; 659 | } 660 | final Window window = dialog.getWindow(); 661 | if (window == null) { 662 | return null; 663 | } 664 | return window.getAttributes().token; 665 | } 666 | 667 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 668 | private void handleLanguageSwitch() { 669 | mInputMethodManager.switchToNextInputMethod(getToken(), false /* onlyCurrentIme */); 670 | } 671 | 672 | private void checkToggleCapsLock() { 673 | long now = System.currentTimeMillis(); 674 | if (mLastShiftTime + 800 > now) { 675 | mCapsLock = !mCapsLock; 676 | mLastShiftTime = 0; 677 | } else { 678 | mLastShiftTime = now; 679 | } 680 | } 681 | 682 | private String getWordSeparators() { 683 | return mWordSeparators; 684 | } 685 | 686 | public boolean isWordSeparator(int code) { 687 | String separators = getWordSeparators(); 688 | return separators.contains(String.valueOf((char) code)); 689 | } 690 | 691 | public void pickDefaultCandidate() { 692 | pickSuggestionManually(0); 693 | } 694 | 695 | public void pickSuggestionManually(int index) { 696 | if (mCompletionOn && mCompletions != null && index >= 0 697 | && index < mCompletions.length) { 698 | CompletionInfo ci = mCompletions[index]; 699 | getCurrentInputConnection().commitCompletion(ci); 700 | if (mCandidateView != null) { 701 | mCandidateView.clear(); 702 | } 703 | updateShiftKeyState(getCurrentInputEditorInfo()); 704 | } else if (mComposing.length() > 0) { 705 | // If we were generating candidate suggestions for the current 706 | // text, we would commit one of them here. But for this sample, 707 | // we will just commit the current text. 708 | commitTyped(getCurrentInputConnection()); 709 | } 710 | } 711 | 712 | public void swipeRight() { 713 | if (mCompletionOn) { 714 | pickDefaultCandidate(); 715 | } 716 | } 717 | 718 | public void swipeLeft() { 719 | handleBackspace(); 720 | } 721 | 722 | public void swipeDown() { 723 | handleClose(); 724 | } 725 | 726 | public void swipeUp() { 727 | } 728 | 729 | public void onPress(int primaryCode) { 730 | } 731 | 732 | public void onRelease(int primaryCode) { 733 | } 734 | 735 | @Override 736 | public void onDestroy() { 737 | super.onDestroy(); 738 | } 739 | 740 | private void takeScreenshot() { 741 | Date now = new Date(); 742 | android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now); 743 | 744 | try { 745 | // image naming and path to include sd card appending name you choose for file 746 | String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg"; 747 | 748 | view = new LinearLayout(getApplicationContext()); 749 | WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 750 | Display display = wm.getDefaultDisplay(); 751 | Point size = new Point(); 752 | display.getSize(size); 753 | int width = size.x; 754 | int height = size.y; 755 | view.setLayoutParams(new ViewGroup.LayoutParams(width, height)); 756 | // create bitmap screen capture 757 | View v1 = view.getRootView(); 758 | v1.setDrawingCacheEnabled(true); 759 | Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache()); 760 | v1.setDrawingCacheEnabled(false); 761 | 762 | File imageFile = new File(mPath); 763 | 764 | FileOutputStream outputStream = new FileOutputStream(imageFile); 765 | int quality = 100; 766 | bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream); 767 | outputStream.flush(); 768 | outputStream.close(); 769 | Log.d("Screen", "taken"); 770 | //openScreenshot(imageFile); 771 | } catch (Throwable e) { 772 | // Several error may come out with file handling or OOM 773 | e.printStackTrace(); 774 | } 775 | } 776 | } 777 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_language_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_language_switch.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_return.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_shift.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sym_keyboard_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-hdpi/sym_keyboard_space.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_language_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_language_switch.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_return.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_shift.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sym_keyboard_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable-mdpi/sym_keyboard_space.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/drawable/pay.png -------------------------------------------------------------------------------- /app/src/main/res/layout/input.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/Android-Custom-Keyboard/909c7094c10805fea7957b1b7101b055c0ce5133/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 46dip 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | > 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | #3F51B5 21 | #303F9F 22 | #FF4081 23 | #FF000000 24 | #FFE35900 25 | #ff808080 26 | #bbffffff 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 50dip 23 | 16sp 24 | 6sp 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | KeyBoard 22 | 23 | 24 | \u0020.,;:!?\n()[]*&@{}/<>_+=|" 25 | 26 | 27 | Go 28 | Next 29 | Send 30 | 31 | 32 | %s 33 | English (GB) 34 | 35 | 36 | Nest Keys 37 | Input languages 38 | Select input languages 39 | General 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 |