Repository: VladThodo/behe-keyboard Branch: master Commit: 2ff8f030643b Files: 84 Total size: 235.8 KB Directory structure: gitextract_6wnfhx6g/ ├── .github/ │ └── workflows/ │ ├── greetings.yml │ ├── label.yml │ └── labeler.yaml ├── .gitignore ├── .idea/ │ ├── codeStyleSettings.xml │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── vlath/ │ │ └── keyboard/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── vlath/ │ │ │ └── keyboard/ │ │ │ ├── CandidateView.java │ │ │ ├── CustomKeyboard.java │ │ │ ├── LatinKeyboard.java │ │ │ ├── Main.java │ │ │ ├── PCKeyboard.java │ │ │ ├── Preference.java │ │ │ ├── PreferenceFragment.java │ │ │ ├── SeekPreference.java │ │ │ └── Variables.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── key_background.xml │ │ │ ├── key_background_back.xml │ │ │ ├── normal.xml │ │ │ ├── preview_background.xml │ │ │ └── round_corners.xml │ │ ├── layout/ │ │ │ ├── activate.xml │ │ │ ├── keyboard.xml │ │ │ ├── keyboard_key_back.xml │ │ │ ├── main.xml │ │ │ ├── popup.xml │ │ │ ├── pref.xml │ │ │ ├── preview.xml │ │ │ ├── seek_dialog.xml │ │ │ ├── slide1.xml │ │ │ ├── slide2.xml │ │ │ ├── slide3.xml │ │ │ └── slide4.xml │ │ ├── values/ │ │ │ ├── attr.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-pt-rBR/ │ │ │ └── strings.xml │ │ └── xml/ │ │ ├── arrow_keys.xml │ │ ├── azerty.xml │ │ ├── azerty_arrow_numbers.xml │ │ ├── azerty_arrows.xml │ │ ├── azerty_numbers.xml │ │ ├── dvorak.xml │ │ ├── emoji.xml │ │ ├── emoji2.xml │ │ ├── ime_preferences.xml │ │ ├── method.xml │ │ ├── numbers.xml │ │ ├── popup_template.xml │ │ ├── programming.xml │ │ ├── qwerty.xml │ │ ├── qwerty_arrow_numbers.xml │ │ ├── qwerty_arrows.xml │ │ ├── qwerty_numbers.xml │ │ ├── qwertz.xml │ │ ├── qwertz_arrow_numbers.xml │ │ ├── qwertz_arrows.xml │ │ ├── qwertz_numbers.xml │ │ ├── symbols.xml │ │ └── symbols2.xml │ └── test/ │ └── java/ │ └── com/ │ └── vlath/ │ └── keyboard/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/greetings.yml ================================================ name: Greetings on: [pull_request, issues] jobs: greeting: runs-on: ubuntu-latest steps: - uses: actions/first-interaction@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} issue-message: 'Hi there and thanks for reporting the issue! I''ll analize it as soon as I can and try to fix it. This is an automated message, I always read all of the issues, though I may not respond all the times. Thanks for using my app!' pr-message: 'Hi there! Thanks for your fisrt contribution on my project. If everything''s alright, I''ll merge it as soon as I can.' ================================================ FILE: .github/workflows/label.yml ================================================ name: "Pull Request Labeler" on: - pull_request jobs: triage: runs-on: ubuntu-latest steps: - uses: actions/labeler@v2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" ================================================ FILE: .github/workflows/labeler.yaml ================================================ translation: - app/src/main/res/values/strings.xml ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/codeStyleSettings.xml ================================================ ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: .scrutinizer.yml ================================================ checks: java: true ================================================ FILE: .travis.yml ================================================ language: android sudo: required dist: trusty group: deprecated-2017Q4 android: components: - tools - build-tools-26.0.0 - android-26 - extra-android-support - extra-android-m2repository licenses: - 'android-sdk-license-.+' - '.*intel.+' addons: apt: packages: - oracle-java8-installer before_install: - chmod +x gradlew - git submodule update --init --recursive script: - ./gradlew build check branches: only: - master - dev ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [![Travis](https://img.shields.io/travis/VladThodo/behe-keyboard/master?label=master&style=flat-square)]() [![Travis](https://img.shields.io/travis/VladThodo/behe-keyboard/dev?label=dev&style=flat-square)]() [![Quality Score](https://img.shields.io/scrutinizer/g/VladThodo/behe-keyboard.svg?style=flat-square)]() [![license](https://img.shields.io/github/license/VladThodo/behe-keyboard.svg?style=flat-square)]() [![GitHub release](https://img.shields.io/github/release/VladThodo/behe-keyboard.svg?style=flat-square)]() [![GitHub issues](https://img.shields.io/github/issues/VladThodo/behe-keyboard.svg?style=flat-square)]()
# BeHe Keyboard A lightweight hacking & programming keyboard with material design. BeHe Keyboard aims to provide a smooth and simple keyboard experience, while also providing special keys and interfaces, all with material design. Also, BeHe Keyboard does not spy on you and is easy to use. Check it out! Get it on F-Droid Get it on Google Play ## License Copyright 2017 - 2019 Vlad Todosin Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { lintOptions { abortOnError false } compileSdkVersion 26 buildToolsVersion "26.0.0" defaultConfig { applicationId "com.vlath.keyboard" minSdkVersion 18 targetSdkVersion 26 versionCode 112 versionName "1.1.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.3.1' testCompile 'junit:junit:4.12' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in C:\Users\Vlad\Desktop\SDK/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/vlath/keyboard/ExampleInstrumentedTest.java ================================================ package com.vlath.keyboard; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.vlath.keyboard", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/vlath/keyboard/CandidateView.java ================================================ package com.vlath.keyboard; /** * Created by todo on 02.08.2017. */ /* * Copyright (C) 2008-2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import android.view.MotionEvent; import android.view.View; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; import java.util.List; public class CandidateView extends View { private static final int OUT_OF_BOUNDS = -1; private PCKeyboard mService; private List mSuggestions; private int mSelectedIndex; private int mTouchX = OUT_OF_BOUNDS; private Drawable mSelectionHighlight; private boolean mTypedWordValid; private Rect mBgPadding; private static final int MAX_SUGGESTIONS = 32; private static final int SCROLL_PIXELS = 20; private int[] mWordWidth = new int[MAX_SUGGESTIONS]; private int[] mWordX = new int[MAX_SUGGESTIONS]; private static final int X_GAP = 60; private static final List EMPTY_LIST = new ArrayList(); private int mColorNormal; private int mColorRecommended; private int mColorOther; private int mVerticalPadding; private Paint mPaint; private boolean mScrolled; private int mTargetScrollX; private int mTotalWidth; private GestureDetector mGestureDetector; public CandidateView(Context context) { super(context); mSelectionHighlight = context.getResources().getDrawable( android.R.drawable.list_selector_background); mSelectionHighlight.setState(new int[] { android.R.attr.state_enabled, android.R.attr.state_focused, android.R.attr.state_window_focused, android.R.attr.state_pressed }); Resources r = context.getResources(); setBackgroundColor(r.getColor(R.color.gray)); mColorNormal = r.getColor(R.color.candidate_normal); mColorRecommended = r.getColor(R.color.candidate_recommended); mColorOther = r.getColor(R.color.candidate_other); mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding); mPaint = new Paint(); mPaint.setColor(mColorNormal); mPaint.setAntiAlias(true); mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height)); mPaint.setStrokeWidth(5); mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mScrolled = true; int sx = getScrollX(); sx += distanceX; if (sx < 0) { sx = 0; } if (sx + getWidth() > mTotalWidth) { sx -= distanceX; } mTargetScrollX = sx; scrollTo(sx, getScrollY()); invalidate(); return true; } }); setHorizontalFadingEdgeEnabled(true); setWillNotDraw(false); setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); } /** * A connection back to the service to communicate with the text field * @param listener */ public void setService(PCKeyboard listener) { mService = listener; } @Override public int computeHorizontalScrollRange() { return mTotalWidth; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredWidth = resolveSize(50, widthMeasureSpec); // Get the desired height of the icon menu view (last row of items does // not have a divider below) Rect padding = new Rect(); mSelectionHighlight.getPadding(padding); final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding + padding.top + padding.bottom; // Maximum possible width and desired height setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec)); } /** * If the canvas is null, then only touch calculations are performed to pick the target * candidate. */ @Override protected void onDraw(Canvas canvas) { if (canvas != null) { super.onDraw(canvas); } mTotalWidth = 0; if (mSuggestions == null) return; if (mBgPadding == null) { mBgPadding = new Rect(0, 0, 0, 0); if (getBackground() != null) { getBackground().getPadding(mBgPadding); } } int x = 0; final int count = mSuggestions.size(); final int height = getHeight(); final Rect bgPadding = mBgPadding; final Paint paint = mPaint; final int touchX = mTouchX; final int scrollX = getScrollX(); final boolean scrolled = mScrolled; final boolean typedWordValid = mTypedWordValid; final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent()); for (int i = 0; i < count; i++) { String suggestion = mSuggestions.get(i); float textWidth = paint.measureText(suggestion); final int wordWidth = (int) textWidth + X_GAP * 2; mWordX[i] = x; mWordWidth[i] = wordWidth; paint.setColor(mColorNormal); if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled) { if (canvas != null) { canvas.translate(x, 0); mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height); mSelectionHighlight.draw(canvas); canvas.translate(-x, 0); } mSelectedIndex = i; } if (canvas != null) { if ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid)) { paint.setFakeBoldText(true); paint.setColor(mColorRecommended); } else if (i != 0) { paint.setColor(mColorOther); } canvas.drawText(suggestion, x + X_GAP, y, paint); paint.setColor(mColorOther); canvas.drawLine(x + wordWidth + 0.5f, bgPadding.top, x + wordWidth + 0.5f, height + 1, paint); paint.setFakeBoldText(false); } x += wordWidth; } mTotalWidth = x; if (mTargetScrollX != getScrollX()) { scrollToTarget(); } } private void scrollToTarget() { int sx = getScrollX(); if (mTargetScrollX > sx) { sx += SCROLL_PIXELS; if (sx >= mTargetScrollX) { sx = mTargetScrollX; requestLayout(); } } else { sx -= SCROLL_PIXELS; if (sx <= mTargetScrollX) { sx = mTargetScrollX; requestLayout(); } } scrollTo(sx, getScrollY()); invalidate(); } public void setSuggestions(List suggestions, boolean completions, boolean typedWordValid) { clear(); if (suggestions != null) { mSuggestions = new ArrayList(suggestions); } mTypedWordValid = typedWordValid; scrollTo(0, 0); mTargetScrollX = 0; // Compute the total width draw(new Canvas()); invalidate(); requestLayout(); } public void clear() { mSuggestions = EMPTY_LIST; mTouchX = OUT_OF_BOUNDS; mSelectedIndex = -1; invalidate(); } @Override public boolean onTouchEvent(MotionEvent me) { if (mGestureDetector.onTouchEvent(me)) { return true; } int action = me.getAction(); int x = (int) me.getX(); int y = (int) me.getY(); mTouchX = x; switch (action) { case MotionEvent.ACTION_DOWN: mScrolled = false; invalidate(); break; case MotionEvent.ACTION_MOVE: if (y <= 0) { // Fling up!? if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex); mSelectedIndex = -1; } } invalidate(); break; case MotionEvent.ACTION_UP: if (!mScrolled) { if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex); } } mSelectedIndex = -1; removeHighlight(); requestLayout(); break; } return true; } /** * For flick through from keyboard, call this method with the x coordinate of the flick * gesture. * @param x */ public void takeSuggestionAt(float x) { mTouchX = (int) x; // To detect candidate draw(null); if (mSelectedIndex >= 0) { mService.pickSuggestionManually(mSelectedIndex); } invalidate(); } private void removeHighlight() { mTouchX = OUT_OF_BOUNDS; invalidate(); } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/CustomKeyboard.java ================================================ package com.vlath.keyboard; /** * Created by Vlad on 6/14/2017. */ import android.graphics.Bitmap; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; import android.inputmethodservice.KeyboardView; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.inputmethodservice.Keyboard; import android.inputmethodservice.Keyboard.Key; import android.inputmethodservice.KeyboardView; import android.os.Vibrator; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.InputMethodSubtype; import android.widget.PopupWindow; import java.lang.reflect.Field; import java.util.List; public class CustomKeyboard extends KeyboardView { Drawable mTransparent = new ColorDrawable(Color.TRANSPARENT); NinePatchDrawable mSpaceBackground = (NinePatchDrawable) getContext().getResources().getDrawable(R.drawable.space); NinePatchDrawable mPressedBackground = (NinePatchDrawable) getContext().getResources().getDrawable(R.drawable.press); Paint mPaint = new Paint(); public CustomKeyboard(Context context, AttributeSet attrs) { super(context, attrs); } public CustomKeyboard(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public LatinKeyboard getLatinKeyboard(){ return (LatinKeyboard)getKeyboard(); } @Override protected boolean onLongPress(Key key) { if (key.codes[0] == Keyboard.KEYCODE_CANCEL) { getOnKeyboardActionListener().onKey(LatinKeyboard.KEYCODE_OPTIONS, null); return true; } return super.onLongPress(key); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setTextSize(28); mPaint.setColor(Color.parseColor("#a5a7aa")); List keys = getKeyboard().getKeys(); for(Key key: keys) { if(key.label != null) { if (key.codes[0] == 32) { mSpaceBackground.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); mSpaceBackground.draw(canvas); } if (Variables.isAnyOn()) { if (Variables.isCtrl()) { if (key.codes[0] == -113) { mPressedBackground.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); mPressedBackground.draw(canvas); } } if (Variables.isAlt()){ if (key.codes[0] == -114) { mPressedBackground.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); mPressedBackground.draw(canvas); } } } else{ if(key.codes[0] == -113) { mTransparent.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); mTransparent.draw(canvas); } if(key.codes[0] == -114) { mTransparent.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); mTransparent.draw(canvas); } } } } } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/LatinKeyboard.java ================================================ package com.vlath.keyboard; /** * Created by Vlad on 6/14/2017. */ import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; public class LatinKeyboard extends Keyboard { static final int KEYCODE_OPTIONS = -100; static final int KEYCODE_LAYUOUT_SWITCH = -101; static final int KEYCODE_DPAD_R = -111; static final int KEYCODE_DPAD_L = -108; static final int KEYCODE_DPAD_U = -107; static final int KEYCODE_DPAD_DO = -109; static final int KEYCODE_ESCAPE = -112; static final int KEYCODE_CTRL = -113; static final int KEYCODE_ALT = -114; static final int KEYCODE_STANDARD_SWITCH = -117; static final int KEYCODE_DELL_PROCESS = -121; static final int KEYCODE_I_DONT_KNOW_WHY_I_PUT_THAT_HERE = -122; private Key mEnterKey; private Key mSpaceKey; private static short rowNumber = 4; /** * Stores the current state of the mode change key. Its width will be dynamically updated to * match the region of {@link #mModeChangeKey} when {@link #mModeChangeKey} becomes invisible. */ private Key mModeChangeKey; /** * Stores the current state of the language switch key (a.k.a. globe key). This should be * returns true. When this key becomes invisible, its width will be shrunk to zero. */ private Key mLanguageSwitchKey; /** * Stores the size and other information of {@link #mModeChangeKey} when * {@link #mLanguageSwitchKey} is visible. This should be immutable and will be used only as a * reference size when the visibility of {@link #mLanguageSwitchKey} is changed. */ private Key mSavedModeChangeKey; /** * Stores the size and other information of {@link #mLanguageSwitchKey} when it is visible. * This should be immutable and will be used only as a reference size when the visibility of * {@link #mLanguageSwitchKey} is changed. */ private Key mSavedLanguageSwitchKey; public LatinKeyboard(Context context, int xmlLayoutResId) { super(context, xmlLayoutResId); } public LatinKeyboard(Context context, int layoutTemplateResId, CharSequence characters, int columns, int horizontalPadding) { super(context, layoutTemplateResId, characters, columns, horizontalPadding); } @Override protected Key createKeyFromXml(Resources res, Row parent, int x, int y, XmlResourceParser parser) { Key key = new LatinKey(res, parent, x, y, parser); if (key.codes[0] == 10) { mEnterKey = key; } else if (key.codes[0] == ' ') { mSpaceKey = key; } else if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) { mModeChangeKey = key; mSavedModeChangeKey = new LatinKey(res, parent, x, y, parser); } else if (key.codes[0] == LatinKeyboard.KEYCODE_LAYUOUT_SWITCH) { mLanguageSwitchKey = key; mSavedLanguageSwitchKey = new LatinKey(res, parent, x, y, parser); } return key; } /** * Dynamically change the visibility of the language switch key (a.k.a. globe key). * @param visible True if the language switch key should be visible. */ void setLanguageSwitchKeyVisibility(boolean visible) { if (visible) { // The language switch key should be visible. Restore the size of the mode change key // and language switch key using the saved layout. mModeChangeKey.width = mSavedModeChangeKey.width; mModeChangeKey.x = mSavedModeChangeKey.x; mLanguageSwitchKey.width = mSavedLanguageSwitchKey.width; mLanguageSwitchKey.icon = mSavedLanguageSwitchKey.icon; mLanguageSwitchKey.iconPreview = mSavedLanguageSwitchKey.iconPreview; } else { // The language switch key should be hidden. Change the width of the mode change key // to fill the space of the language key so that the user will not see any strange gap. mModeChangeKey.width = mSavedModeChangeKey.width + mSavedLanguageSwitchKey.width; mLanguageSwitchKey.width = 0; mLanguageSwitchKey.icon = null; mLanguageSwitchKey.iconPreview = null; } } /** * This looks at the ime options given by the current editor, to set the * appropriate label on the keyboard's enter key (if it has one). */ void setImeOptions(Resources res, int options) { if (mEnterKey == null) { return; } switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) { case EditorInfo.IME_ACTION_GO: mEnterKey.iconPreview = null; mEnterKey.icon = null; mEnterKey.label = "ENT"; break; case EditorInfo.IME_ACTION_NEXT: mEnterKey.iconPreview = null; mEnterKey.icon = null; mEnterKey.label = "N"; break; case EditorInfo.IME_ACTION_SEARCH: // mEnterKey.icon = "K"; mEnterKey.label = null; break; case EditorInfo.IME_ACTION_SEND: mEnterKey.iconPreview = null; mEnterKey.icon = null; mEnterKey.label = "HH"; break; default: // mEnterKey.icon = "U"; mEnterKey.label = null; break; } } public void setRowNumber(short number){ rowNumber = number; } void setSpaceIcon(final Drawable icon) { if (mSpaceKey != null) { mSpaceKey.icon = icon; } } public void changeKeyHeight(double height_modifier){ int height = 0; for(Keyboard.Key key : getKeys()) { key.height *= height_modifier; key.y *= height_modifier; height = key.height; } setKeyHeight(height); getNearestKeys(0, 0); //somehow adding this fixed a weird bug where bottom row keys could not be pressed if keyboard height is too tall.. from the Keyboard source code seems like calling this will recalculate some values used in keypress detection calculation } /** This piece of code is intended to help us to resize the keyboard at runtime, * thus giving us the opportunity to customize its height. It's a bit tricky though. * And StackOverflow inspired me to be honest. * **/ @Override public int getHeight(){ return getKeyHeight() * rowNumber; } public void setKeyHeight(int height) { super.setKeyHeight(height); } static class LatinKey extends Keyboard.Key { public LatinKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) { super(res, parent, x, y, parser); } /** * Overriding this method so that we can reduce the target area for the key that * closes the keyboard. */ @Override public boolean isInside(int x, int y) { return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y); } } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/Main.java ================================================ package com.vlath.keyboard; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.Settings; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class Main extends AppCompatActivity { private ViewPager viewPager; private MyViewPagerAdapter myViewPagerAdapter; private LinearLayout dotsLayout; private TextView[] dots; private int[] layouts; private Button btnSkip, btnNext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(!getPresentationShown()) { // Checking for first time launch - before calling setContentView() // Making notification bar transparent if (Build.VERSION.SDK_INT >= 21) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } setContentView(R.layout.main); getSupportActionBar().hide(); viewPager = (ViewPager) findViewById(R.id.view_pager); dotsLayout = (LinearLayout) findViewById(R.id.layoutDots); btnSkip = (Button) findViewById(R.id.btn_skip); btnNext = (Button) findViewById(R.id.btn_next); // layouts of all welcome sliders // add few more layouts if you want layouts = new int[]{ R.layout.slide1, R.layout.slide2, R.layout.slide3, R.layout.slide4 }; // adding bottom dots addBottomDots(0); // making notification bar transparent changeStatusBarColor(); myViewPagerAdapter = new MyViewPagerAdapter(); viewPager.setAdapter(myViewPagerAdapter); viewPager.addOnPageChangeListener(viewPagerPageChangeListener); btnSkip.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setPresentationShown(); launchHomeScreen(); } }); btnNext.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // checking for last page // if last page home screen will be launched int current = getItem(+1); if (current < layouts.length) { // move to next screen viewPager.setCurrentItem(current); } else { setPresentationShown(); launchHomeScreen(); } } }); } else{ setContentView(R.layout.activate); TextView t2 = (TextView) findViewById(R.id.textView2); t2.setMovementMethod(LinkMovementMethod.getInstance()); showTwitterDialog(); } } private void addBottomDots(int currentPage) { dots = new TextView[layouts.length]; int[] colorsActive = getResources().getIntArray(R.array.array_dot_active); int[] colorsInactive = getResources().getIntArray(R.array.array_dot_inactive); dotsLayout.removeAllViews(); for (int i = 0; i < dots.length; i++) { dots[i] = new TextView(this); dots[i].setText(Html.fromHtml("•")); dots[i].setTextSize(35); dots[i].setTextColor(colorsInactive[currentPage]); dotsLayout.addView(dots[i]); } if (dots.length > 0) dots[currentPage].setTextColor(colorsActive[currentPage]); } private int getItem(int i) { return viewPager.getCurrentItem() + i; } private void launchHomeScreen() { Intent intent = getIntent(); finish(); startActivity(intent); overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right); } // viewpager change listener ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { addBottomDots(position); // changing the next button text 'NEXT' / 'GOT IT' if (position == layouts.length - 1) { // last page. make button text to GOT IT btnNext.setText("GOT IT"); btnSkip.setVisibility(View.GONE); } else { // still pages are left btnNext.setText("NEXT"); btnSkip.setVisibility(View.VISIBLE); } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; /** * Making notification bar transparent */ private void changeStatusBarColor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); } } private void normalStatusBar(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark)); } } /** * View pager adapter */ public class MyViewPagerAdapter extends PagerAdapter { private LayoutInflater layoutInflater; public MyViewPagerAdapter() { } @Override public Object instantiateItem(ViewGroup container, int position) { layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(layouts[position], container, false); container.addView(view); return view; } @Override public int getCount() { return layouts.length; } @Override public boolean isViewFromObject(View view, Object obj) { return view == obj; } @Override public void destroyItem(ViewGroup container, int position, Object object) { View view = (View) object; container.removeView(view); } } public boolean getPresentationShown(){ try { return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("presentation", false); } catch (Exception e){ return false; } } public void setPresentationShown(){ PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("presentation", true).commit(); } public void showTwitterDialog(){ if(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown",false)) {} else { AlertDialog alertDialog = new AlertDialog.Builder(this) .setTitle(getString(R.string.up)) .setMessage(getString(R.string.follow)) .setPositiveButton("Follow", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/VlathXDA")); startActivity(intent); } }) .setNegativeButton("No thanks", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }) .show(); PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("shown",true).apply(); } } public void settings(View v){ Intent intent = new Intent(this, Preference.class); startActivity(intent); } public void enable(View v){ this.startActivity(new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)); } public void select(View v){ InputMethodManager imeManager = (InputMethodManager) getApplicationContext().getSystemService(INPUT_METHOD_SERVICE); if (imeManager != null) { imeManager.showInputMethodPicker(); } else { Toast.makeText(this, "Not possible" , Toast.LENGTH_LONG).show(); } } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/PCKeyboard.java ================================================ package com.vlath.keyboard; /* * Copyright (C) 2008-2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import android.app.Dialog; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.inputmethodservice.InputMethodService; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.os.IBinder; import android.os.Vibrator; import android.preference.*; import android.text.InputType; import android.text.method.MetaKeyKeyListener; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SpellCheckerSession; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import android.view.textservice.TextServicesManager; import android.widget.PopupWindow; import java.util.ArrayList; import java.util.List; /** Main class of the keyboard, extending InputMethodService. Here we handle all the user interaction with the keyboard itself. */ public class PCKeyboard extends InputMethodService implements KeyboardView.OnKeyboardActionListener, SpellCheckerSession.SpellCheckerSessionListener { /** * As we don't want to bother changing the app theme, we use filters to theme the keyboard. * Each filter needs an array of colors. The arrays are declared below. */ // TODO: Add the arrays in a separate, static class, so they are eay to access and modify private static final float[] sNoneColorArray = { 1.0f, 0, 0, 0, 0, // red 0, 1.0f, 0, 0, 0, // green 0, 0, 1.0f, 0, 0, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sNegativeColorArray = { -1.0f, 0, 0, 0, 255, // red 0, -1.0f, 0, 0, 255, // green 0, 0, -1.0f, 0, 255, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sBlueBlackColorArray = { -0.6f, 0, 0, 0, 41, // red 0, -0.6f, 0, 0, 128, // green 0, 0, -0.6f, 0, 185, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sBlueWhiteColorArray = { 1.0f, 0, 0, 0, 41, // red 0, 1.0f, 0, 0, 128, // green 0, 0, 1.0f, 0, 185, // blue 0, 0, 0, 1.0f, 1 // alpha }; private static final float[] sRedWhiteColorArray = { 1.0f, 0, 0, 0, 192, // red 0, 1.0f, 0, 0, 57, // green 0, 0, 1.0f, 0, 43, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sRedBlackColorArray = { -0.6f, 0, 0, 0, 192, // red 0, -0.6f, 0, 0, 57, // green 0, 0, -0.6f, 0, 43, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sOrangeBlackColorArray = { 1.0f, 0, 0, 0, 230, // red 0, 1.0f, 0, 0, 126, // green 0, 0, 1.0f, 0, 34, // blue 0, 0, 0, 1.0f, 0 // alpha }; private static final float[] sMaterialDarkColorArray = { 1.0f, 0, 0, 0, 55, // red 0, 1.0f, 0, 0, 71, // green 0, 0, 1.0f, 0, 79, // blue 0, 0, 0, 1.0f, 1 // alpha }; static final boolean PROCESS_HARD_KEYS = true; private InputMethodManager mInputMethodManager; private CustomKeyboard mInputView; private CandidateView mCandidateView; private CompletionInfo[] mCompletions; private StringBuilder mComposing = new StringBuilder(); private boolean mPredictionOn; private boolean mCompletionOn; private int mLastDisplayWidth; private boolean mCapsLock; private long mLastShiftTime; private long mMetaState; private LatinKeyboard mSymbolsKeyboard; private LatinKeyboard mSymbolsShiftedKeyboard; private LatinKeyboard mStandardKeyboard; private String mWordSeparators; private SpellCheckerSession mScs; private List mSuggestions; private boolean firstCaps = false; private boolean isSysmbols = false; private boolean shiftSim = false; private boolean isDpad = false; private boolean isProgramming = false; private InputMethodManager mServ; private float[] mDefaultFilter; long shift_pressed=0; private short rowNumber = 4; private CustomKeyboard kv; private LatinKeyboard currentKeyboard; private LatinKeyboard mCurKeyboard; private LatinKeyboard standardKeyboard; private int standardKeyboardID = R.xml.qwerty; /** * Main initialization of the input method component. Be sure to call * to super class. */ @Override public void onCreate() { super.onCreate(); mInputMethodManager = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mWordSeparators = getResources().getString(R.string.word_separators); final TextServicesManager tsm = (TextServicesManager) getSystemService( Context.TEXT_SERVICES_MANAGER_SERVICE); mScs = tsm.newSpellCheckerSession(null, null, this, true); } /** * This is the point where you can do all of your UI initialization. It * is called after creation and any configuration change. */ @Override public void onInitializeInterface() { if (mStandardKeyboard != null) { // Configuration changes can happen after the keyboard gets recreated, // so we need to be able to re-build the keyboards if the available // space has changed. int displayWidth = getMaxWidth(); if (displayWidth == mLastDisplayWidth) return; mLastDisplayWidth = displayWidth; } mStandardKeyboard = new LatinKeyboard(this, standardKeyboardID); mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols); mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols2); } /** * Called by the framework when your view for creating input needs to * be generated. This will be called the first time your input method * is displayed, and every time it needs to be re-created such as due to * a configuration change. * * We also s */ @Override public View onCreateInputView() { mInputView = (CustomKeyboard) getLayoutInflater().inflate( R.layout.keyboard, null); mInputView.setOnKeyboardActionListener(this); mInputView.setPreviewEnabled(false); setLatinKeyboard(mStandardKeyboard); return mInputView; } private void setLatinKeyboard(LatinKeyboard nextKeyboard) { mInputView.setKeyboard(nextKeyboard); } /** * Called by the framework when your view for showing candidates needs to * be generated, like {@link #onCreateInputView}. */ @Override public View onCreateCandidatesView() { mCandidateView = new CandidateView(this); mCandidateView.setService(this); setTheme(); Paint mPaint = new Paint(); ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter(mDefaultFilter); mPaint.setColorFilter(filterInvert); mCandidateView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); return mCandidateView; } /** * This is the main point where we do our initialization of the input method * to begin operating on an application. At this point we have been * bound to the client, and are now receiving all of the detailed information * about the target of our edits. * * * And we have to reinitialize all we've one to make sure the keyboard aspect matches * The one selected in settings. */ @Override public void onStartInput(EditorInfo attribute, boolean restarting) { super.onStartInput(attribute, restarting); setTheme(); mComposing.setLength(0); updateCandidates(); /** * Some code on here is based on the SoftKeyboard Sample. I don't fully understand it. * I need to look it up and delete any unnecessary stuff. * */ if (!restarting) { // Clear shift states. mMetaState = 0; } mCompletions = null; if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("bord",false)){ kv = (CustomKeyboard) getLayoutInflater().inflate(R.layout.keyboard_key_back, null); } else { kv = (CustomKeyboard) getLayoutInflater().inflate(R.layout.keyboard, null); } setStandardKeyboard(); setInputType(); Paint mPaint = new Paint(); ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter(mDefaultFilter); mPaint.setColorFilter(filterInvert); mCandidateView = new CandidateView(this); mCandidateView.setService(this); kv.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); currentKeyboard.setRowNumber(getRowNumber()); kv.setKeyboard(currentKeyboard); capsOnFirst(); kv.setOnKeyboardActionListener(this); mPredictionOn = PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("pred", false); mCompletionOn = false; mCandidateView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); setInputView(kv); kv.getLatinKeyboard().changeKeyHeight(getHeightKeyModifier()); setCandidatesView(mCandidateView); } /** * This is called when the user is done editing a field. We can use * this to reset our state. */ @Override public void onFinishInput() { super.onFinishInput(); // Clear current composing text and candidates. mComposing.setLength(0); updateCandidates(); // We only hide the candidates window when finishing input on // a particular editor, to avoid popping the underlying application // up and down if the user is entering text into the bottom of // its window. setCandidatesViewShown(false); mCurKeyboard = mStandardKeyboard; if (mInputView != null) { mInputView.closing(); } } /** * Deal with the editor reporting movement of its cursor. */ @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd); // If the current selection in the text view changes, we should // clear whatever candidate text we have. if (mComposing.length() > 0 && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd)) { mComposing.setLength(0); updateCandidates(); InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.finishComposingText(); } } } /** * This tells us about completions that the editor has determined based * on the current text in it. We want to use this in fullscreen mode * to show the completions ourself, since the editor can not be seen * in that situation. */ @Override public void onDisplayCompletions(CompletionInfo[] completions) { if (mCompletionOn) { mCompletions = completions; if (completions == null) { setSuggestions(null, false, false); return; } List stringList = new ArrayList(); for (int i = 0; i < completions.length; i++) { CompletionInfo ci = completions[i]; if (ci != null) stringList.add(ci.getText().toString()); } setSuggestions(stringList, true, true); } } /** * This translates incoming hard key events in to edit operations on an * InputConnection. It is only needed when using the * PROCESS_HARD_KEYS option. */ private boolean translateKeyDown(int keyCode, KeyEvent event) { mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState, keyCode, event); int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState)); mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState); InputConnection ic = getCurrentInputConnection(); if (c == 0 || ic == null) { return false; } boolean dead = false; if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { dead = true; c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; } if (mComposing.length() > 0) { char accent = mComposing.charAt(mComposing.length() -1 ); int composed = KeyEvent.getDeadChar(accent, c); if (composed != 0) { c = composed; mComposing.setLength(mComposing.length()-1); } } onKey(c, null); return true; } /** * Use this to monitor key events being delivered to the application. * We get first crack at them, and can either resume them or let them * continue to the app. */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return super.onKeyUp(keyCode, event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return super.onKeyDown(keyCode, event); } /** * Helper function to commit any text being composed in to the editor. */ private void commitTyped(InputConnection inputConnection) { if (mComposing.length() > 0) { inputConnection.commitText(mComposing, mComposing.length()); mComposing.setLength(0); updateCandidates(); } } /** * Helper to update the shift state of our keyboard based on the initial * editor state. */ private void updateShiftKeyState(EditorInfo attr) { if (attr != null && mInputView != null && mStandardKeyboard == mInputView.getKeyboard()) { int caps = 0; EditorInfo ei = getCurrentInputEditorInfo(); if (ei != null && ei.inputType != InputType.TYPE_NULL) { caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType); } mInputView.setShifted(mCapsLock || caps != 0); } } /** * Helper to determine if a given character code is alphabetic. */ private boolean isAlphabet(int code) { if (Character.isLetter(code)) { return true; } else { return false; } } /** * Helper to send a key down / key up pair to the current editor. */ private void keyDownUp(int keyEventCode) { getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); } /** * Helper to send a character to the editor as raw key events. */ private void sendKey(int keyCode) { switch (keyCode) { case '\n': keyDownUp(KeyEvent.KEYCODE_ENTER); break; default: if (keyCode >= '0' && keyCode <= '9') { keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0); } else { getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1); } break; } } public void onText(CharSequence text) { InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ic.beginBatchEdit(); if (mComposing.length() > 0) { commitTyped(ic); } ic.commitText(text, 0); ic.endBatchEdit(); updateShiftKeyState(getCurrentInputEditorInfo()); } @Override public void swipeLeft() { } @Override public void swipeRight() { } @Override public void swipeDown() { } @Override public void swipeUp() { } /** * Update the list of available candidates from the current composing * text. This will need to be filled in by however you are determining * candidates. */ private void updateCandidates() { if (!mCompletionOn) { if (mComposing.length() > 0) { ArrayList list = new ArrayList(); list.add(mComposing.toString()); mScs.getSentenceSuggestions(new TextInfo[] {new TextInfo(mComposing.toString())}, 5); setSuggestions(list, true, true); } else { setSuggestions(null, false, false); } } } public void setSuggestions(List suggestions, boolean completions, boolean typedWordValid) { if (suggestions != null && suggestions.size() > 0) { setCandidatesViewShown(true); } else if (isExtractViewShown()) { setCandidatesViewShown(true); } mSuggestions = suggestions; if (mCandidateView != null) { mCandidateView.setSuggestions(suggestions, completions, typedWordValid); } } private void handleBackspace() { final int length = mComposing.length(); if (length > 1) { mComposing.delete(length - 1, length); getCurrentInputConnection().setComposingText(mComposing, 1); updateCandidates(); } else if (length > 0) { mComposing.setLength(0); getCurrentInputConnection().commitText("", 0); updateCandidates(); } else { keyDownUp(KeyEvent.KEYCODE_DEL); } updateShiftKeyState(getCurrentInputEditorInfo()); } private void handleCharacter(int primaryCode, int[] keyCodes) { if (isInputViewShown()) { if (kv.isShifted()) { primaryCode = Character.toUpperCase(primaryCode); } } if (mPredictionOn && !mWordSeparators.contains(String.valueOf((char)primaryCode))) { mComposing.append((char) primaryCode); getCurrentInputConnection().setComposingText(mComposing, 1); updateShiftKeyState(getCurrentInputEditorInfo()); updateCandidates(); } if(mPredictionOn && mWordSeparators.contains(String.valueOf((char)primaryCode))){ char code = (char) primaryCode; if (Character.isLetter(code) && firstCaps || Character.isLetter(code) && Variables.isShift()) { code = Character.toUpperCase(code); } getCurrentInputConnection().setComposingRegion(0,0); getCurrentInputConnection().commitText(String.valueOf(code), 1); firstCaps = false; setCapsOn(false); } if(!mPredictionOn){ char code = (char) primaryCode; if (Character.isLetter(code) && firstCaps || Character.isLetter(code) && Variables.isShift()) { code = Character.toUpperCase(code); } getCurrentInputConnection().setComposingRegion(0,0); getCurrentInputConnection().commitText(String.valueOf(code), 1); firstCaps = false; setCapsOn(false); } } private void handleClose() { commitTyped(getCurrentInputConnection()); requestHideSelf(0); mInputView.closing(); } private IBinder getToken() { final Dialog dialog = getWindow(); if (dialog == null) { return null; } final Window window = dialog.getWindow(); if (window == null) { return null; } return window.getAttributes().token; } private void handleLanguageSwitch() { mInputMethodManager.switchToNextInputMethod(getToken(), false /* onlyCurrentIme */); } private void checkToggleCapsLock() { long now = System.currentTimeMillis(); if (mLastShiftTime + 800 > now) { mCapsLock = !mCapsLock; mLastShiftTime = 0; } else { mLastShiftTime = now; } } private String getWordSeparators() { return mWordSeparators; } public boolean isWordSeparator(String s) { if(s.contains(". ") || s.contains("? ") || s.contains("! ")){ return true; } return false; } public void pickDefaultCandidate() { pickSuggestionManually(0); } public void pickSuggestionManually(int index) { if (mCompletionOn && mCompletions != null && index >= 0 && index < mCompletions.length) { CompletionInfo ci = mCompletions[index]; getCurrentInputConnection().commitCompletion(ci); if (mCandidateView != null) { mCandidateView.clear(); } updateShiftKeyState(getCurrentInputEditorInfo()); } else if (mComposing.length() > 0) { if (mPredictionOn && mSuggestions != null && index >= 0) { mComposing.replace(0, mComposing.length(), mSuggestions.get(index)); } commitTyped(getCurrentInputConnection()); } } public void onPress(int primaryCode) { if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("vib", false)) { Vibrator v = (Vibrator) getBaseContext().getSystemService(Context.VIBRATOR_SERVICE); v.vibrate(40); } } public void onRelease(int primaryCode) { } /** * http://www.tutorialspoint.com/android/android_spelling_checker.htm * Sort of copy-paste, huh. * * I need to find time to refine this code * * * @param results results */ @Override public void onGetSuggestions(SuggestionsInfo[] results) { final StringBuilder sb = new StringBuilder(); for (SuggestionsInfo result : results) { // Returned suggestions are contained in SuggestionsInfo final int len = result.getSuggestionsCount(); sb.append('\n'); for (int j = 0; j < len; ++j) { sb.append(",").append(result.getSuggestionAt(j)); } sb.append(" (").append(len).append(")"); } } private void dumpSuggestionsInfoInternal( final List sb, final SuggestionsInfo si, final int length, final int offset) { // Returned suggestions are contained in SuggestionsInfo final int len = si.getSuggestionsCount(); for (int j = 0; j < len; ++j) { sb.add(si.getSuggestionAt(j)); } } @Override public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { try { final List sb = new ArrayList<>(); for (final SentenceSuggestionsInfo ssi : results) { for (int j = 0; j < ssi.getSuggestionsCount(); ++j) { dumpSuggestionsInfoInternal( sb, ssi.getSuggestionsInfoAt(j), ssi.getOffsetAt(j), ssi.getLengthAt(j)); } } setSuggestions(sb, true, true); } catch(Exception ignored){} } private void setCapsOn(boolean on) { /** Simple function that enables us to rapidly set the keyboard shifted or not. * */ if(Variables.isShift()){ kv.getKeyboard().setShifted(true); kv.invalidateAllKeys(); } else { kv.getKeyboard().setShifted(on); kv.invalidateAllKeys(); } } private void processKeyCombo(int keycode) { /** Ass the function name says, we process key combinations here*/ if (Variables.isAnyOn()) { if (Variables.isCtrl() && Variables.isAlt()) { getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, getHardKeyCode(keycode), 0, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_UP, getHardKeyCode(keycode), 0, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)); } else { if (Variables.isCtrl()) { getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, getHardKeyCode(keycode), 0, KeyEvent.META_CTRL_ON)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_UP, getHardKeyCode(keycode), 0, KeyEvent.META_CTRL_ON)); } if (Variables.isAlt()) { getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, getHardKeyCode(keycode), 0, KeyEvent.META_ALT_ON)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_UP, getHardKeyCode(keycode), 0, KeyEvent.META_ALT_ON)); } } } } private int getHardKeyCode(int keycode) { /** Seems like the actual soft key code doesn't match the hard key code*/ PopupWindow p = new PopupWindow(); char code = (char) keycode; switch (String.valueOf(code)) { case "a": return KeyEvent.KEYCODE_A; case "b": return KeyEvent.KEYCODE_B; case "c": return KeyEvent.KEYCODE_C; case "d": return KeyEvent.KEYCODE_D; case "e": return KeyEvent.KEYCODE_E; case "f": return KeyEvent.KEYCODE_F; case "g": return KeyEvent.KEYCODE_G; case "h": return KeyEvent.KEYCODE_H; case "i": return KeyEvent.KEYCODE_I; case "j": return KeyEvent.KEYCODE_J; case "k": return KeyEvent.KEYCODE_K; case "l": return KeyEvent.KEYCODE_L; case "m": return KeyEvent.KEYCODE_M; case "n": return KeyEvent.KEYCODE_N; case "o": return KeyEvent.KEYCODE_O; case "p": return KeyEvent.KEYCODE_P; case "q": return KeyEvent.KEYCODE_Q; case "r": return KeyEvent.KEYCODE_R; case "s": return KeyEvent.KEYCODE_S; case "t": return KeyEvent.KEYCODE_T; case "u": return KeyEvent.KEYCODE_U; case "v": return KeyEvent.KEYCODE_V; case "w": return KeyEvent.KEYCODE_W; case "x": return KeyEvent.KEYCODE_X; case "y": return KeyEvent.KEYCODE_Y; case "z": return KeyEvent.KEYCODE_Z; default: return keycode; } } private void handleAction() { EditorInfo curEditor = getCurrentInputEditorInfo(); switch (curEditor.imeOptions & EditorInfo.IME_MASK_ACTION) { case EditorInfo.IME_ACTION_DONE: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_DONE); break; case EditorInfo.IME_ACTION_GO: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_GO); break; case EditorInfo.IME_ACTION_NEXT: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_NEXT); break; case EditorInfo.IME_ACTION_SEARCH: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_SEARCH); break; case EditorInfo.IME_ACTION_SEND: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_SEND); break; default: break; } } public void setTheme() { switch (PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getString("theme", "2")) { case "1": mDefaultFilter = sNoneColorArray; break; case "2": mDefaultFilter = sNegativeColorArray; break; case "3": mDefaultFilter = sBlueWhiteColorArray; break; case "4": mDefaultFilter = sBlueBlackColorArray; break; case "5": mDefaultFilter = sRedWhiteColorArray; break; case "6": mDefaultFilter = sRedBlackColorArray; break; case "7": mDefaultFilter = sOrangeBlackColorArray; break; case "8": mDefaultFilter = sMaterialDarkColorArray; break; } } private void setInputType() { /** Checks the preferences for the default keyboard layout. * If standard, we start out whether in standard or numbers, depending on the input type. * */ EditorInfo attribute = getCurrentInputEditorInfo(); if (PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getString("start", "1").equals("1")) { switch (attribute.inputType & InputType.TYPE_MASK_CLASS) { case InputType.TYPE_CLASS_NUMBER: case InputType.TYPE_CLASS_DATETIME: case InputType.TYPE_CLASS_PHONE: currentKeyboard = new LatinKeyboard(this, R.xml.numbers); break; case InputType.TYPE_CLASS_TEXT: int webInputType = attribute.inputType & InputType.TYPE_MASK_VARIATION; if (webInputType == InputType.TYPE_TEXT_VARIATION_URI || webInputType == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT || webInputType == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || webInputType == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS) { currentKeyboard = new LatinKeyboard(this, standardKeyboardID); } else { currentKeyboard = new LatinKeyboard(this, standardKeyboardID); } break; default: currentKeyboard = new LatinKeyboard(this, standardKeyboardID); break; } } else { setDefaultKeyboard(); } if (kv != null) { kv.setKeyboard(currentKeyboard); } } public void setDefaultKeyboard() { switch (PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getString("start", "1")) { case "1": currentKeyboard = standardKeyboard; break; case "2": currentKeyboard = new LatinKeyboard(this, R.xml.arrow_keys); setRowNumber(4); currentKeyboard.setRowNumber(getRowNumber()); break; case "3": currentKeyboard = new LatinKeyboard(this, R.xml.programming); setRowNumber(5); currentKeyboard.setRowNumber(getRowNumber()); break; } } private void capsOnFirst() { /** Huh, a method that calls getCursorCapsMode() and performs a check. * Accordingly to the official android documentation, if the caps mode is not equal to 0, * We should start in caps mode. Although, tests have proven that additionally checks are needed. * I'll see what I can do on this. * */ if(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("caps",true)){ if (getCursorCapsMode(getCurrentInputConnection(), getCurrentInputEditorInfo()) != 0) { firstCaps = true; setCapsOn(true); } } else { firstCaps = false; setCapsOn(false); } } private int getCursorCapsMode(InputConnection ic, EditorInfo attr) { /** A rudimentary method to find out whether we should start with caps on or not. * */ // TODO: Perform additional checks. int caps = 0; EditorInfo ei = getCurrentInputEditorInfo(); if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) { caps = ic.getCursorCapsMode(attr.inputType); } return caps; } @Override public void onKey(int primaryCode, int[] keyCodes) { InputConnection ic = getCurrentInputConnection(); /** Here we handle the key events. */ switch (primaryCode) { case Keyboard.KEYCODE_DELETE: handleBackspace(); break; case Keyboard.KEYCODE_SHIFT: /** We need to check whether we are on symbols layout or not. * Then, perform the operation accordingly. * Also, we check for double tab on the shift, and, if detected * We set a global variable that tells us that the Shift is in the lock position. * */ if (isSysmbols) { if (!shiftSim) { currentKeyboard = new LatinKeyboard(this, R.xml.symbols2); kv.setKeyboard(currentKeyboard); shiftSim = true; } else { currentKeyboard = new LatinKeyboard(this, R.xml.symbols); kv.setKeyboard(currentKeyboard); shiftSim = false; } } else { if (shift_pressed + 200 > System.currentTimeMillis()){ Variables.setShiftOn(); setCapsOn(true); kv.draw(new Canvas()); } else{ if(Variables.isShift()){ Variables.setShiftOff(); firstCaps = false; setCapsOn(firstCaps); shift_pressed = System.currentTimeMillis(); } else{ firstCaps = !firstCaps; setCapsOn(firstCaps); shift_pressed = System.currentTimeMillis(); } } } break; case 10: /** Handle the 'done' action accordingly to the IME Options. */ EditorInfo curEditor = getCurrentInputEditorInfo(); switch (curEditor.imeOptions & EditorInfo.IME_MASK_ACTION) { case EditorInfo.IME_ACTION_DONE: keyDownUp(66); break; case EditorInfo.IME_ACTION_GO: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_GO); break; case EditorInfo.IME_ACTION_NEXT: keyDownUp(66); break; case EditorInfo.IME_ACTION_SEARCH: getCurrentInputConnection().performEditorAction(EditorInfo.IME_ACTION_SEARCH); break; case EditorInfo.IME_ACTION_SEND: keyDownUp(66); break; default: keyDownUp(66); break; } break; case Keyboard.KEYCODE_MODE_CHANGE: /** Switch between standard/symbols layout. */ if (!isSysmbols) { isSysmbols = !isSysmbols; currentKeyboard = new LatinKeyboard(this, R.xml.symbols); kv.setKeyboard(currentKeyboard); } else { isSysmbols = false; currentKeyboard = new LatinKeyboard(this, standardKeyboardID); kv.setKeyboard(currentKeyboard); } kv.getLatinKeyboard().changeKeyHeight(getHeightKeyModifier()); break; case LatinKeyboard.KEYCODE_LAYUOUT_SWITCH: /** Language Switch is a custom value defined in the LatinKeyboard class. * We use it to switch between standard/arrow keys/programming layouts. */ if (isDpad || isProgramming) { if (isProgramming) { currentKeyboard = new LatinKeyboard(this, standardKeyboardID); kv.invalidateAllKeys(); currentKeyboard.setRowNumber(getStandardRowNumber()); kv.setKeyboard(currentKeyboard); isProgramming = false; isDpad = false; } if (isDpad) { currentKeyboard = new LatinKeyboard(this, R.xml.programming); kv.invalidateAllKeys(); setRowNumber(5); currentKeyboard.setRowNumber(getRowNumber()); kv.setKeyboard(currentKeyboard); isDpad = false; isProgramming = true; } } else { currentKeyboard = new LatinKeyboard(this, R.xml.arrow_keys); kv.invalidateAllKeys(); setRowNumber(4); currentKeyboard.setRowNumber(getRowNumber()); kv.setKeyboard(currentKeyboard); isDpad = true; } kv.getLatinKeyboard().changeKeyHeight(getHeightKeyModifier()); break; case LatinKeyboard.KEYCODE_DPAD_L: /** Another custom keycode. */ getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_LEFT)); break; case LatinKeyboard.KEYCODE_DPAD_R: getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT)); break; case LatinKeyboard.KEYCODE_DPAD_U: getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_UP)); break; case LatinKeyboard.KEYCODE_DPAD_DO: getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_DOWN)); break; case LatinKeyboard.KEYCODE_ESCAPE: getCurrentInputConnection().sendKeyEvent( new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ESCAPE, 0)); getCurrentInputConnection().sendKeyEvent( new KeyEvent(100, 100, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ESCAPE, 0)); break; case LatinKeyboard.KEYCODE_CTRL: if (Variables.isCtrl()) { Variables.setCtrlOff(); kv.draw(new Canvas()); } else { Variables.setCtrlOn(); kv.draw(new Canvas()); } break; case LatinKeyboard.KEYCODE_ALT: if (Variables.isAlt()) { Variables.setAltOff(); kv.draw(new Canvas()); } else { Variables.setAltOn(); kv.draw(new Canvas()); } break; case LatinKeyboard.KEYCODE_STANDARD_SWITCH: /** This key enables the user to switch rapidly between standard/arrow keys layouts.*/ currentKeyboard = new LatinKeyboard(getBaseContext(), standardKeyboardID); currentKeyboard.setRowNumber(getStandardRowNumber()); kv.setKeyboard(currentKeyboard); kv.getLatinKeyboard().changeKeyHeight(getHeightKeyModifier()); isDpad = false; break; case LatinKeyboard.KEYCODE_DELL_PROCESS: /** Procces DEL key*/ if(Variables.isAnyOn()){ if(Variables.isCtrl() && Variables.isAlt()) { getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)); } if(Variables.isAlt()){ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, KeyEvent.META_ALT_ON)); } if(Variables.isCtrl()){ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, KeyEvent.META_CTRL_ON)); } } else{ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL , 0)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL, 0)); } break; case LatinKeyboard.KEYCODE_I_DONT_KNOW_WHY_I_PUT_THAT_HERE: if(Variables.isAnyOn()){ if(Variables.isCtrl() && Variables.isAlt()) { getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB, 0, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)); } if(Variables.isAlt()){ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB, 0, KeyEvent.META_ALT_ON)); } if(Variables.isCtrl()){ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB, 0, KeyEvent.META_CTRL_ON)); } } else{ getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB , 0)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(100, 100, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TAB, 0)); } break; default: if (Variables.isAnyOn()) { processKeyCombo(primaryCode); } else { handleCharacter(primaryCode, keyCodes); } } try { /** Some text processing. Helps some guys improve their writing skills, huh*/ if(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("caps",true)) { if (isWordSeparator(ic.getTextBeforeCursor(2, 0).toString())) { setCapsOn(true); firstCaps = true; } } } catch (Exception e) { } } public short getRowNumber(){ return rowNumber; } public void setRowNumber(int number){ rowNumber = (short) number; } public short getStandardRowNumber(){ if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("arr_qrt", false) && PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("nbr_qrt", false)){ return 5; } else{ if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("arr_qrt", false)){ return 4; } else if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("nbr_qrt", false)){ return 5; } else { return 4; } } } public void setStandardKeyboard(){ int layout = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getString("layout", "1")); if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("arr_qrt", false) && PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("nbr_qrt", false)){ switch (layout) { case 2: standardKeyboardID = R.xml.azerty_arrow_numbers; break; case 3: standardKeyboardID = R.xml.qwertz_arrow_numbers; break; default: standardKeyboardID = R.xml.qwerty_arrow_numbers; } setRowNumber(5); } else{ if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("arr_qrt", false)){ switch (layout) { case 2: standardKeyboardID = R.xml.azerty_arrows; break; case 3: standardKeyboardID = R.xml.qwertz_arrows; break; default: standardKeyboardID = R.xml.qwerty_arrows; } setRowNumber(4); } else if(PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getBoolean("nbr_qrt", false)){ switch (layout) { case 2: standardKeyboardID = R.xml.azerty_numbers; break; case 3: standardKeyboardID = R.xml.qwertz_numbers; break; default: standardKeyboardID = R.xml.qwerty_numbers; } setRowNumber(5); } else { switch (layout) { case 2: standardKeyboardID = R.xml.azerty; break; case 3: standardKeyboardID = R.xml.qwertz; break; default: standardKeyboardID = R.xml.qwerty; } setRowNumber(4); } } } public double getHeightKeyModifier() { return (double)PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("height", 50) / (double)50; } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/Preference.java ================================================ package com.vlath.keyboard; import android.app.FragmentManager; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; /** * Created by Vlad on 6/20/2017. */ public class Preference extends ActionBarActivity { @Override public void onCreate(Bundle h){ super.onCreate(h); setContentView(R.layout.pref); getFragmentManager().beginTransaction().replace(R.id.main, new PreferenceFragment()).commit(); } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/PreferenceFragment.java ================================================ package com.vlath.keyboard; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.*; import android.preference.Preference; /** * Created by todo on 30.06.2017. */ public class PreferenceFragment extends android.preference.PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { ListPreference listTheme; ListPreference listStart; ListPreference listLayout; @Override public void onCreate(Bundle s){ super.onCreate(s); addPreferencesFromResource(R.xml.ime_preferences); listTheme = (ListPreference) findPreference("theme"); listStart = (ListPreference) findPreference("start"); listLayout = (ListPreference) findPreference("layout"); listTheme.setSummary(listTheme.getEntry()); listStart.setSummary(listStart.getEntry()); listLayout.setSummary(listLayout.getEntry()); PreferenceManager.getDefaultSharedPreferences(getActivity().getBaseContext()).registerOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { listTheme.setSummary(listTheme.getEntry()); listStart.setSummary(listStart.getEntry()); listLayout.setSummary(listLayout.getEntry()); } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/SeekPreference.java ================================================ package com.vlath.keyboard; /** * Created by todo on 01.12.2017. */ import android.content.Context; import android.content.res.TypedArray; import android.preference.Preference; import android.util.AttributeSet; import android.view.View; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; public class SeekPreference extends Preference implements OnSeekBarChangeListener { private SeekBar mSeekBar; private int mProgress; public SeekPreference(Context context) { this(context, null, 0); } public SeekPreference(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SeekPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setLayoutResource(R.layout.seek_dialog); } @Override protected void onBindView(View view) { super.onBindView(view); mSeekBar = (SeekBar) view.findViewById(R.id.seekbar); mSeekBar.setProgress(mProgress); mSeekBar.setOnSeekBarChangeListener(this); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (!fromUser) return; setValue(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // not used } @Override public void onStopTrackingTouch(SeekBar seekBar) { // not used } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { setValue(restoreValue ? getPersistedInt(mProgress) : (Integer) defaultValue); } public void setValue(int value) { if (shouldPersist()) { persistInt(value); } if (value != mProgress) { mProgress = value; notifyChanged(); } } @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getInt(index, 0); } } ================================================ FILE: app/src/main/java/com/vlath/keyboard/Variables.java ================================================ package com.vlath.keyboard; /** * Created by Vlad on 6/22/2017. */ public class Variables { /** * Here we handle global variables */ private static boolean IS_CTRL = false; private static boolean IS_ALT = false; private static boolean IS_SHIFT = false; public static boolean isAnyOn() { return IS_CTRL || IS_ALT; } public static boolean isCtrl() { return IS_CTRL; } public static boolean isAlt() { return IS_ALT; } public static void setIsCtrl(boolean on) { IS_CTRL = on; } public static void setIsAlt(boolean on) { IS_ALT = on; } public static void setAltOn() { IS_ALT = true; } public static void setAltOff() { IS_ALT = false; } public static void setCtrlOn() { IS_CTRL = true; } public static void setCtrlOff() { IS_CTRL = false; } public static void setShiftOn() { IS_SHIFT = true; } public static void setShiftOff() { IS_SHIFT = false; } public static boolean isShift() { return IS_SHIFT; } } ================================================ FILE: app/src/main/res/drawable/key_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/key_background_back.xml ================================================ ================================================ FILE: app/src/main/res/drawable/normal.xml ================================================ ================================================ FILE: app/src/main/res/drawable/preview_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/round_corners.xml ================================================ ================================================ FILE: app/src/main/res/layout/activate.xml ================================================