commit d1a242fdc1aeed92c214787e1c05f3740965b7d7 Author: litingting Date: Tue Apr 16 17:37:02 2024 +0800 V1.0.1(2) Ad版本 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c4cd1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +.idea/ +.safedk/ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..4a9493c --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,70 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("applovin-quality-service") + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") +} +applovin{ + apiKey = "10baBtdCVFTrdqwoQ7FR4KVx3t_Ik1LAkFM1K8kzJN5R9WEa1arWsJrInJTdTR2X8sXvG_dbl13EIvMMsmCkpq" +} + +android { + namespace = "com.input.board.appskey.keyboard" + compileSdk = 34 + + defaultConfig { + applicationId = "com.input.board.appskey.keyboard" + minSdk = 23 + targetSdk = 34 + versionCode = 2 + versionName = "1.0.1" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.10.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + + + implementation ("com.squareup.okhttp3:okhttp:4.11.0") + implementation ("com.github.bumptech.glide:glide:4.16.0") + //Glide支持webp动图的库 + implementation ("com.github.zjupure:webpdecoder:2.0.4.12.0") + implementation("com.github.omicronapps:7-Zip-JBinding-4Android:Release-16.02-2.02") + + implementation("com.applovin:applovin-sdk:+") + implementation("com.applovin.mediation:vungle-adapter:+") + implementation("com.applovin.mediation:bytedance-adapter:+") + implementation(platform("com.google.firebase:firebase-bom:32.3.1")) + implementation("com.google.firebase:firebase-analytics-ktx") + implementation ("com.google.firebase:firebase-crashlytics-ktx") + + + +} \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..afca4cd --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "326384418328", + "project_id": "nova-keyboard-fada5", + "storage_bucket": "nova-keyboard-fada5.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:326384418328:android:fe2196479b76d028c1cd6d", + "android_client_info": { + "package_name": "com.nova.keyboard" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCMRZX1oeheml8W9SkvL5qjh5HDUO5UY44" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/novakeyboard.jks b/app/novakeyboard.jks new file mode 100644 index 0000000..0bd00a1 Binary files /dev/null and b/app/novakeyboard.jks differ diff --git a/app/novakeynewboard.jks b/app/novakeynewboard.jks new file mode 100644 index 0000000..19149ce Binary files /dev/null and b/app/novakeynewboard.jks differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..c02d417 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,26 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 +-keep class com.omicronapplications.** { *; } +-keep class net.sf.sevenzipjbinding.** { *; } + +# pangel +-keep class com.bytedance.sdk.** { *; } \ No newline at end of file diff --git a/app/src/androidTest/java/com/input/board/appskey/keyboard/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/input/board/appskey/keyboard/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..50bdd1f --- /dev/null +++ b/app/src/androidTest/java/com/input/board/appskey/keyboard/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.input.board.appskey.keyboard + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.ba.ali.apps.keyboard", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9e22dab --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/encrypt_keyboard.json b/app/src/main/assets/encrypt_keyboard.json new file mode 100644 index 0000000..77100d9 Binary files /dev/null and b/app/src/main/assets/encrypt_keyboard.json differ diff --git a/app/src/main/java/com/input/board/appskey/keyboard/KeyBoardApp.kt b/app/src/main/java/com/input/board/appskey/keyboard/KeyBoardApp.kt new file mode 100644 index 0000000..d5a0f1a --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/KeyBoardApp.kt @@ -0,0 +1,97 @@ +package com.input.board.appskey.keyboard + +import android.app.Application +import com.applovin.sdk.AppLovinSdk +import com.input.board.appskey.keyboard.fileTools.DesFile +import com.input.board.appskey.keyboard.fileTools.DesFile.deFile +import com.input.board.appskey.keyboard.jsonBean.ChildBean +import com.input.board.appskey.keyboard.jsonBean.MainBean +import org.json.JSONArray + +class KeyBoardApp : Application() { + companion object { + lateinit var appInstance: KeyBoardApp + + lateinit var list:MutableList + } + + override fun onCreate() { + super.onCreate() + appInstance = this + initializeAppLovin() + dealFile() + } + + private fun dealFile() { + val openFile = appInstance.assets.open("encrypt_keyboard.json") + val deFilePath = deFile(openFile) + val jsonString = DesFile.getJsonString(deFilePath) + resolveJsonString(jsonString) + } + + + private fun resolveJsonString(string: String) { + val jsonData = JSONArray(string) + var dataList:MutableList = mutableListOf() + for (i in 0 until jsonData.length()) { + jsonData.getJSONObject(i).run { + val pName = getString("parent_name") + val listArray = getJSONArray("keyboard_list") + var childBeanList:MutableList = mutableListOf() + for (listIndex in 0 until listArray.length()){ + listArray.getJSONObject(listIndex).also { + val title = it.getString("title") + val thUrl = it.getString("thumbUrl") + val thGif = it.getString("thumbUrlGif") + var zipPath ="" + var imgPath ="" + var imgGif ="" + var imgPreviewGif = "" + if(it.has("detail")){ + val contentObject = it.getJSONObject("detail").getJSONObject("themeContent") + zipPath = contentObject.getString("androidRawZipUrl") + imgPath = contentObject.getString("img") + imgGif = contentObject.getString("imgGif") + imgPreviewGif = contentObject.getString("imgPreviewGif") + }else{ + + } + childBeanList.add(ChildBean().apply { + setImgPath(imgPath) + setZipPath(zipPath) + setTitleName(title) + setImgGif(imgGif) + thumbUrl = thUrl + thumbGif = thGif + }) + + } + + } + + val mainBean = MainBean().apply { + parentName = pName + keyboardList = childBeanList + } + dataList.add(mainBean) + + } + } + + updateDataList(dataList) + + } + + private fun updateDataList(mainList:MutableList){ + list = mainList + + } + + private fun initializeAppLovin(){ + AppLovinSdk.getInstance(this).mediationProvider = "max" + AppLovinSdk.getInstance(this).initializeSdk { + + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputService.java b/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputService.java new file mode 100644 index 0000000..9e7a275 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputService.java @@ -0,0 +1,220 @@ +package com.input.board.appskey.keyboard.customer; + +import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; + +import android.media.MediaPlayer; +import android.view.SurfaceView; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +import com.input.board.appskey.keyboard.R; +import com.input.board.appskey.keyboard.fileTools.SpSkin; + + +import kotlin.Unit; +import kotlin.jvm.functions.Function2; + +public class CustomInputService extends InputMethodService implements MyKeyBoardView.OnKeyboardActionListener { + + + private CustomInputView customInputView; + private MyKeyBoard mKeyBoard; + private View parentView; + private SurfaceView surfaceView; + private MediaPlayer curMediaPlayer; + + private int a = R.xml.a; + private int b= R.xml.b; + private int c = R.xml.c; + + @SuppressLint("InflateParams") + @Override + public View onCreateInputView() { + parentView = getLayoutInflater().inflate(R.layout.custome_input_view, null); + findView(); + return parentView; + } + + private void findView() { + surfaceView = parentView.findViewById(R.id.surfaceView); + mKeyBoard = new MyKeyBoard(this, a); + customInputView = parentView.findViewById(R.id.custom_input_view); + customInputView.setEnabled(true); + customInputView.setPreviewEnabled(false); + customInputView.setKeyboard(mKeyBoard); + customInputView.setOnKeyboardActionListener(this); + } + + @Override + public void onWindowShown() { + super.onWindowShown(); + String skinPath = SpSkin.INSTANCE.getSkinPath(); + if(skinPath == null || skinPath.isEmpty()){ + customInputView.updateUi(surfaceView); + }else { + KeyFunction.INSTANCE.readBgOrVideo(this, surfaceView, new Function2() { + @Override + public Unit invoke(MediaPlayer mediaPlayer, Drawable drawable) { + if (mediaPlayer != null) { + curMediaPlayer = mediaPlayer; + curMediaPlayer.start(); + } else { + customInputView.setBackground(drawable); + } + customInputView.updateUi(surfaceView); + return null; + } + }); + } + + + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (curMediaPlayer != null) { + curMediaPlayer.stop(); + curMediaPlayer.release(); + curMediaPlayer = null; + } + } + + @Override + public void onPress(int primaryCode) { + + } + + @Override + public void onRelease(int primaryCode) { + + } + + @Override + public void onKey(int primaryCode, int[] keyCodes) { + + InputConnection curInputConnect = getCurrentInputConnection(); + switch (primaryCode) { + case KeyCodeConst.KEY_CODE_DELETE: + curInputConnect.deleteSurroundingText(1, 0); + break; + case KeyCodeConst.KEY_CODE_SHIFT: + switchShift(); + break; + case KeyCodeConst.KEY_CODE_NUMBER_SHIFT: + case KeyCodeConst.KEY_CODE_SYMBOL_SHIFT: + switchMoreOrNumber(); + break; + case KeyCodeConst.KEY_CODE_CHANGE_NUMBER: + case KeyCodeConst.KEY_CODE_BACK: + switchNormalOrNumber(); + break; + + case KeyCodeConst.KEY_CODE_COMPLETE: + case KeyCodeConst.KEY_CODE_CANCEL: + curInputConnect.performEditorAction(EditorInfo.IME_ACTION_DONE); + break; + default: + String codeToChar = KeyFunction.INSTANCE.primaryCodeToChar(primaryCode); + curInputConnect.commitText(codeToChar, 1); + if (customInputView.isLowerCase() == 1) { + //自动转小写 + customInputView.setLowerCase(0); + KeyFunction.INSTANCE.keyToLowerCase(mKeyBoard); + customInputView.setKeyboard(mKeyBoard); + } + break; + + } + + + } + + private void switchMoreOrNumber() { + int mode = customInputView.getMode(); + switch (mode) { + case 1: + mKeyBoard = new MyKeyBoard(this, c); + customInputView.setMode(2); + customInputView.setKeyboard(mKeyBoard); + break; + case 2: + mKeyBoard = new MyKeyBoard(this, b); + customInputView.setMode(1); + customInputView.setKeyboard(mKeyBoard); + break; + } + + } + + private void switchNormalOrNumber() { + int mode = customInputView.getMode(); + switch (mode) { + case 0: + mKeyBoard = new MyKeyBoard(this, b); + customInputView.setMode(1); + customInputView.setKeyboard(mKeyBoard); + break; + case 1: + case 2: + mKeyBoard = new MyKeyBoard(this, a); + customInputView.setMode(0); + customInputView.setKeyboard(mKeyBoard); + break; + } + + } + + private void switchShift() { + int lowerCase = customInputView.isLowerCase(); + switch (lowerCase) { + case 0: + //当前小写转大写 + customInputView.setLowerCase(1); + KeyFunction.INSTANCE.keyToUpper(mKeyBoard); + customInputView.setKeyboard(mKeyBoard); + break; + case 1: + //当前大写转锁定大写 + customInputView.setLowerCase(2); + break; + case 2: + //当前锁定大写转小写 + customInputView.setLowerCase(0); + KeyFunction.INSTANCE.keyToLowerCase(mKeyBoard); + customInputView.setKeyboard(mKeyBoard); + break; + } + } + + + @Override + public void onText(CharSequence text) { + + } + + @Override + public void swipeLeft() { + + } + + @Override + public void swipeRight() { + + } + + @Override + public void swipeDown() { + + } + + @Override + public void swipeUp() { + + } + + +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputView.java b/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputView.java new file mode 100644 index 0000000..56812e5 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/CustomInputView.java @@ -0,0 +1,238 @@ +package com.input.board.appskey.keyboard.customer; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.SurfaceView; + +import androidx.core.graphics.drawable.DrawableCompat; + +import com.input.board.appskey.keyboard.R; + + + +public class CustomInputView extends MyKeyBoardView { + + private Paint mPaint; + private Context mContext; + + private float mRation = 0.5f; + + + //0 小写 1 大写 2 大写锁定 + private int isLowerCase = 0; + //0 默认键盘 1 字母键盘 2 符号键盘 + private int mMode = 0; + + private KeyBoardSkin keyBoardSkin; + + public CustomInputView(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + setAttribute(attrs, context); + } + + public CustomInputView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mContext = context; + setAttribute(attrs, context); + } + + public CustomInputView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mContext = context; + setAttribute(attrs, context); + } + + + public void setMode(int mType) { + this.mMode = mType; + } + + public int getMode() { + return mMode; + } + + public int isLowerCase() { + return isLowerCase; + } + + public void setLowerCase(int lowerCase) { + isLowerCase = lowerCase; + } + + public void updateUi(SurfaceView surfaceView) { + keyBoardSkin.updateSkinConfig(surfaceView); + invalidate(); + } + + private void initPaint() { + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setTextAlign(Paint.Align.CENTER); + + } + + private void setAttribute(AttributeSet attrs, Context con) { + keyBoardSkin = new KeyBoardSkin(con); + initPaint(); + TypedArray mTypedArray = con.obtainStyledAttributes(attrs, R.styleable.CustomInputView); + + int color = mTypedArray.getColor(R.styleable.CustomInputView_text_color_done, 1); + + Drawable drawable = mTypedArray.getDrawable(R.styleable.CustomInputView_drawable_cancel); + + int textSize = mTypedArray.getInt(R.styleable.CustomInputView_text_size_key, 12); + + mTypedArray.recycle(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + for (MyKeyBoard.Key curKey : getKeyboard().getKeys()) { + int code = curKey.codes[0]; + realDraw(curKey, canvas, code); + + } + + } + private void realDraw(MyKeyBoard.Key curKey, Canvas canvas, int code) { + switch (code) { + case KeyCodeConst.KEY_CODE_SHIFT: + drawAllShift(curKey, canvas); + break; + case KeyCodeConst.KEY_CODE_NUMBER_SHIFT: + onDrawCurKey(curKey, canvas, "More", keyBoardSkin.getFunctionDraw(), null); + break; + case KeyCodeConst.KEY_CODE_DELETE: + onDrawCurKey(curKey, canvas, "Delete", keyBoardSkin.getFunctionDraw(),null); + break; + case KeyCodeConst.KEY_CODE_SYMBOL_SHIFT: + onDrawCurKey(curKey, canvas, "123", keyBoardSkin.getFunctionDraw(), null); + break; + case KeyCodeConst.KEY_CODE_CHANGE_NUMBER: + onDrawCurKey(curKey, canvas, null, keyBoardSkin.getToDraw(), null); + break; + case KeyCodeConst.KEY_CODE_BACK: + onDrawCurKey(curKey, canvas, "Back", keyBoardSkin.getToDraw(), null); + break; + case KeyCodeConst.KEY_CODE_SPACE: + onDrawCurKey(curKey, canvas, null, keyBoardSkin.getSpaceDraw(), null); + break; + case KeyCodeConst.KEY_CODE_COMPLETE: + onDrawCurKey(curKey, canvas, "Done", keyBoardSkin.getFunctionDraw(), null); + break; + case KeyCodeConst.KEY_CODE_CANCEL: + onDrawCurKey(curKey, canvas, "Done", keyBoardSkin.getFunctionDraw(), null); + break; + default: + + onDrawCurKey(curKey, canvas, null, keyBoardSkin.getGeneralDraw(), null); + break; + + + } + } + + private void drawAllShift(MyKeyBoard.Key curKey, Canvas canvas) { + if (isLowerCase == 0) { + onDrawCurKey(curKey, canvas, "Shift", keyBoardSkin.getFunctionDraw(), null); + } else if (isLowerCase == 1) { + onDrawCurKey(curKey, canvas, "Shift", keyBoardSkin.getFunctionDraw(),null); + } else if(isLowerCase == 2){ + onDrawCurKey(curKey, canvas, "Shift", keyBoardSkin.getFunctionDraw(), null); + } + } + + private void onDrawCurKey(MyKeyBoard.Key curKey, Canvas curCanvas, String label, Drawable bgDrawable, Drawable iconDraw) { + + if (bgDrawable != null) { + onDrawKeyBackground(curKey, curCanvas, bgDrawable); + } + if (iconDraw != null) { + onDrawKeyIcon(curKey, curCanvas, iconDraw); + } + onDrawKeyText(curKey, curCanvas, label); + } + + private void onDrawKeyText(MyKeyBoard.Key curKey, Canvas curCanvas, String label) { + mPaint.setColor(keyBoardSkin.getKeyTextColor()); + mPaint.setTextSize(mContext.getResources().getDimension(R.dimen.text_size)); + float v = curKey.width / 2f; + float v1 = curKey.height / 2f; + float v2 = (mPaint.getTextSize() - mPaint.descent()) / 2f; + + + if (curKey.label != null) { + curCanvas.drawText((String) curKey.label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint); + } else if (label != null) { + curCanvas.drawText(label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint); + } + + + } + + private void onDrawKeyBackground(MyKeyBoard.Key curKey, Canvas curCanvas, Drawable curDrawable) { + if (curKey.codes[0] != 0) { + curDrawable.setState(curKey.getCurrentDrawableState()); + } + Rect rect = new Rect((curKey.x + this.getPaddingLeft()), (curKey.y + this.getPaddingTop()), (curKey.x + this.getPaddingLeft() + curKey.width), (curKey.y + this.getPaddingTop() + curKey.height)); + curDrawable.setBounds(rect); + + curDrawable.draw(curCanvas); + + + } + + private void onDrawKeyIcon(MyKeyBoard.Key curKey, Canvas curCanvas, Drawable curDrawable) { + + Drawable wrap = DrawableCompat.wrap(curDrawable); + + curKey.icon = curDrawable; + float iconW = (float) curKey.icon.getIntrinsicWidth(); + float iconH = (float) curKey.icon.getIntrinsicHeight(); + + float wDivRation = iconW / curKey.width; + float hDivRation = iconH / curKey.height; + + curKey.icon.draw(curCanvas); + + if (wDivRation > hDivRation) { + float minRatio = 0; + if (wDivRation <= mRation) { + minRatio = wDivRation; + } else { + minRatio = mRation; + } + iconH = (iconH / wDivRation) * minRatio; + iconW = (iconW / wDivRation) * minRatio; + } else { + float minRatio = 0; + if (hDivRation <= mRation) { + minRatio = hDivRation; + } else { + minRatio = mRation; + } + iconH = (iconH / hDivRation) * minRatio; + iconW = (iconW / hDivRation) * minRatio; + } + float subW = (curKey.width - iconW) / 2f; + float subH = (curKey.height - iconH) / 2f; + int xLeft = (int) (curKey.x + getPaddingLeft() + subW); + int yTop = (int) (curKey.y + getPaddingTop() + subH); + int xRight = (int) (xLeft + iconW); + int yBottom = (int) (yTop + iconH); + curKey.icon.setBounds(xLeft, yTop, xRight, yBottom); + curKey.icon.draw(curCanvas); + + + } + +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/IntersitialAd.java b/app/src/main/java/com/input/board/appskey/keyboard/customer/IntersitialAd.java new file mode 100644 index 0000000..5d45dcd --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/IntersitialAd.java @@ -0,0 +1,92 @@ +package com.input.board.appskey.keyboard.customer; + +import android.app.Activity; + +import androidx.annotation.NonNull; + +import com.applovin.mediation.MaxAd; +import com.applovin.mediation.MaxAdListener; +import com.applovin.mediation.MaxError; +import com.applovin.mediation.ads.MaxInterstitialAd; +import com.input.board.appskey.keyboard.interf.onAdShowCallback; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class IntersitialAd { + + private static String unitAd1 = "06bd48bfaba9b6ec"; + private static String unitAd2 = "66a8f5e488c3adac"; + private static String unitAd3 = "16413a54ebedc530"; + + + + private static MaxInterstitialAd createAd(String unitAd, Activity act) { + MaxInterstitialAd maxInterAd = new MaxInterstitialAd(unitAd, act); + if (!maxInterAd.isReady()) { + maxInterAd.loadAd(); + }else { + + } + return maxInterAd; + } + + public static List loadInterAd(Activity act) { + MaxInterstitialAd ad1 = createAd(unitAd1, act); + MaxInterstitialAd ad2 = createAd(unitAd2, act); + MaxInterstitialAd ad3 = createAd(unitAd3, act); + List list = new ArrayList<>(); + list.add(ad1); + list.add(ad2); + list.add(ad3); + Collections.shuffle(list); + return list; + } + + public static MaxInterstitialAd showInterAd(List ads) { + Collections.shuffle(ads); + for (int index = 0; index < ads.size(); index++) { + MaxInterstitialAd interstitialAd = ads.get(index); + if (interstitialAd.isReady()) { + return interstitialAd; + } + } + return null; + } + + public static void setAdCallback(MaxInterstitialAd ad, onAdShowCallback showCallback) { + ad.setListener(new MaxAdListener() { + @Override + public void onAdLoaded(@NonNull MaxAd maxAd) { + + } + + @Override + public void onAdDisplayed(@NonNull MaxAd maxAd) { + showCallback.displayed(); + } + + @Override + public void onAdHidden(@NonNull MaxAd maxAd) { + showCallback.hidden(); + } + + @Override + public void onAdClicked(@NonNull MaxAd maxAd) { + + } + + @Override + public void onAdLoadFailed(@NonNull String s, @NonNull MaxError maxError) { + + } + + @Override + public void onAdDisplayFailed(@NonNull MaxAd maxAd, @NonNull MaxError maxError) { + showCallback.displayFail(); + } + }); + + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyBoardSkin.kt b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyBoardSkin.kt new file mode 100644 index 0000000..ce0f305 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyBoardSkin.kt @@ -0,0 +1,152 @@ +package com.input.board.appskey.keyboard.customer + +import android.content.Context +import android.graphics.BitmapFactory +import android.graphics.Color +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.StateListDrawable +import android.util.Xml +import android.view.SurfaceView +import androidx.core.content.ContextCompat +import com.input.board.appskey.keyboard.KeyBoardApp +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.fileTools.SpSkin +import org.xmlpull.v1.XmlPullParser +import java.io.File +import java.io.StringReader + +class KeyBoardSkin(var context: Context) { + + + private var textSize = 13f + + var functionDraw: Drawable = getDefaultDrawList(R.mipmap.key_normal,R.mipmap.key_pressed_on) + var generalDraw: Drawable = getDefaultDrawList(R.mipmap.key_normal,R.mipmap.key_pressed_on) + + var toDraw: Drawable = getDefaultDrawList(R.mipmap.key_normal,R.mipmap.key_pressed_on) + var spaceDraw: Drawable = getDefaultDrawList(R.mipmap.key_normal,R.mipmap.key_pressed_on) + + var switchDraw: Drawable? = null + var deleteDraw: Drawable? = null + var backDraw: Drawable? = null + var searchDraw: Drawable? = null + + var shiftDraw: Drawable? = null + var shiftLockDraw: Drawable? = null + + var keyTextColor: Int = ContextCompat.getColor(context, R.color.black) + var keyTextColorFunction: Int = ContextCompat.getColor(context, R.color.black) + fun updateSkinConfig(surfaceView: SurfaceView) { + SpSkin.getSkinPath()?.let { resPath -> + val pPath = "${resPath}res/drawable-xhdpi-v4/" + pPath.let { + readColors(resPath) { + for ((name, value) in it) { + if (name == KeyCodeConst.keyTextColorName) { + keyTextColor = value + } + if (name == KeyCodeConst.keyTextColorFunctionName) { + keyTextColorFunction = value + } + } + + } + functionDraw = getDrawList( + it + KeyCodeConst.functionNormalName, + it + KeyCodeConst.functionPressName + ) + generalDraw = getDrawList(it + KeyCodeConst.normalName, it + KeyCodeConst.pressName) + toDraw = getDrawList(it + KeyCodeConst.toNormalName, it + KeyCodeConst.toPressName) + spaceDraw = + getDrawList(it + KeyCodeConst.spaceNormalName, it + KeyCodeConst.spacePressName) + switchDraw = + getDrawList(it + KeyCodeConst.imeSwitchName, it + KeyCodeConst.imeSwitchName) + deleteDraw = getDrawList( + it + KeyCodeConst.deleteNormalName, + it + KeyCodeConst.deletePressName + ) + backDraw = getDrawList(it + KeyCodeConst.backName, it + KeyCodeConst.backName) + searchDraw = getDrawList(it + KeyCodeConst.searchName, it + KeyCodeConst.searchName) + shiftDraw = getDrawList( + it + KeyCodeConst.shiftNormalName, + it + KeyCodeConst.shiftNormalName + ) + shiftLockDraw = + getDrawList(it + KeyCodeConst.shiftLockName, it + KeyCodeConst.shiftLockName) + } + + } + } + + + private fun getDefaultDrawList(normalDrawId: Int, pressDrawId: Int): StateListDrawable { + val normalDraw = ContextCompat.getDrawable(KeyBoardApp.appInstance,normalDrawId ) + val pressDraw = ContextCompat.getDrawable(KeyBoardApp.appInstance,pressDrawId ) + val stateListDrawable = StateListDrawable().apply { + addState( + intArrayOf(android.R.attr.state_pressed), + pressDraw + ) + addState(intArrayOf(), normalDraw) + } + + return stateListDrawable + + + } + + + private fun getDrawList(normalPath: String, pressPath: String): StateListDrawable { + val pressDraw = BitmapFactory.decodeFile(pressPath) + val normalDraw = BitmapFactory.decodeFile(normalPath) + val stateListDrawable = StateListDrawable().apply { + addState( + intArrayOf(android.R.attr.state_pressed), + BitmapDrawable(context.resources, pressDraw) + ) + addState(intArrayOf(), BitmapDrawable(context.resources, normalDraw)) + } + + return stateListDrawable + + + } + + private fun readColors(resPath: String, callBack: (Map) -> Unit) { + val resMaps = mutableMapOf() + + val pPath = "${resPath}res/colors.xml" + val file = File(pPath) + if (file.exists()) { + val xmlPullParser = Xml.newPullParser().apply { + setInput(StringReader(file.readText())) + setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) + + } + var curType = xmlPullParser.eventType + while (curType != XmlPullParser.END_DOCUMENT) { + val b = curType == XmlPullParser.START_TAG + val b1 = xmlPullParser.name == "color" + val b2 = xmlPullParser.name == "item" + if (b && (b1 || b2)) { + val attributeName = xmlPullParser.getAttributeValue(null, "name") + val nextTextValue = xmlPullParser.nextText() + val b3 = attributeName == KeyCodeConst.keyTextColorName + val b4 = attributeName == KeyCodeConst.keyTextColorFunctionName + if (b3 || b4) { + resMaps[attributeName] = Color.parseColor(nextTextValue) + } + } + curType = xmlPullParser.next() + } + + } + + callBack.invoke(resMaps) + + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyCodeConst.kt b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyCodeConst.kt new file mode 100644 index 0000000..b8c2a91 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyCodeConst.kt @@ -0,0 +1,55 @@ +package com.input.board.appskey.keyboard.customer + +object KeyCodeConst { + + + + const val KEY_CODE_DELETE = -5 + + + //同一个按键 + const val KEY_CODE_SHIFT = -1 + const val KEY_CODE_NUMBER_SHIFT = -103 + const val KEY_CODE_SYMBOL_SHIFT = -101 + + //同一个按键 + const val KEY_CODE_CHANGE_NUMBER = -2 + const val KEY_CODE_BACK = -102 + + + const val KEY_CODE_COMPLETE = -4 + const val KEY_CODE_CANCEL = -3 + + const val KEY_CODE_SPACE = 32 + + + const val functionNormalName = "btn_keyboard_key_functional_normal.9.png" + const val functionPressName = "btn_keyboard_key_functional_pressed.9.png" + + const val normalName = "btn_keyboard_key_normal_normal.9.png" + const val pressName = "btn_keyboard_key_normal_pressed.9.png" + + const val toNormalName="btn_keyboard_key_toggle_normal_on.9.png" + const val toPressName="btn_keyboard_key_toggle_pressed_on.9.png" + + const val spaceNormalName = "btn_keyboard_spacekey_normal_normal.9.png" + const val spacePressName = "btn_keyboard_spacekey_normal_pressed.9.png" + + const val imeSwitchName ="ic_ime_switcher.png" + + const val deleteNormalName = "sym_keyboard_delete_normal.png" + const val deletePressName = "sym_keyboard_delete_pressed.png" + + const val backName ="sym_keyboard_return_normal.png" + + const val searchName ="sym_keyboard_search.png" + + const val shiftNormalName ="sym_keyboard_shift.png" + const val shiftLockName ="sym_keyboard_shift_locked.png" + + const val keyTextColorName ="key_text_color_normal" + const val keyTextColorFunctionName ="key_text_color_functional" + + const val videoName ="keyboard_background_video.mp4" + const val bgName ="keyboard_background.jpg" +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyFunction.kt b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyFunction.kt new file mode 100644 index 0000000..198c69a --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/KeyFunction.kt @@ -0,0 +1,100 @@ +package com.input.board.appskey.keyboard.customer + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +//import android.inputmethodservice.Keyboard +import android.media.MediaPlayer +import android.view.SurfaceHolder +import android.view.SurfaceView +import com.input.board.appskey.keyboard.fileTools.SpSkin +import java.io.File + +object KeyFunction { + + fun keyToUpper(mKeyBoard: MyKeyBoard) { + for (key in mKeyBoard.keys) { + if (key.label != null) { + if (key.label.length == 1) { + val charLabel = key.label.toString()[0] + val toUpperCase = charLabel.toUpperCase() + key.codes[0] = toUpperCase.toInt() + key.label = toUpperCase.toString() + } + + } + } + } + + + fun keyToLowerCase(mKeyBoard: MyKeyBoard) { + for (key in mKeyBoard.keys) { + if (key.label != null) { + if (key.label.length == 1) { + val charLabel = key.label.toString()[0] + val toLowerCase = charLabel.toLowerCase() + key.codes[0] = toLowerCase.toInt() + key.label = toLowerCase.toString() + } + + } + } + } + + fun primaryCodeToChar(primCode: Int): String { + val toString = primCode.toChar().toString() + + return toString + } + + @SuppressLint("SuspiciousIndentation") + fun readBgOrVideo( + context: Context, + surfaceView: SurfaceView, + playAction: (mediaPlayer: MediaPlayer?,bgDraw:Drawable?) -> Unit + ) { + + SpSkin.getSkinPath()?.let { resPath -> + val videoPath = "${resPath}res/raw/${KeyCodeConst.videoName}" + val backgroundPath = "${resPath}res/drawable-xxhdpi-v4/${KeyCodeConst.bgName}" + val file = File(videoPath) + if (file.exists()) { + val mediaPlayer = MediaPlayer().apply { + setDataSource(videoPath) + prepareAsync() + } + surfaceView.holder.addCallback(object : SurfaceHolder.Callback { + override fun surfaceCreated(holder: SurfaceHolder) { + mediaPlayer.setDisplay(holder) + } + + override fun surfaceChanged( + holder: SurfaceHolder, + format: Int, + width: Int, + height: Int + ) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + + } + + }) + playAction.invoke(mediaPlayer,null); + }else{ + + val bitmapDrawable = BitmapDrawable(context.resources, BitmapFactory.decodeFile(backgroundPath)) + + playAction.invoke(null,bitmapDrawable) + + } + + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoard.java b/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoard.java new file mode 100644 index 0000000..64ac40a --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoard.java @@ -0,0 +1,859 @@ +package com.input.board.appskey.keyboard.customer; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; + +import androidx.annotation.XmlRes; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + + +import com.input.board.appskey.keyboard.R; + +public class MyKeyBoard { + + static final String TAG = "Keyboard"; + + // Keyboard XML Tags + private static final String TAG_KEYBOARD = "Keyboard"; + private static final String TAG_ROW = "Row"; + private static final String TAG_KEY = "Key"; + + public static final int EDGE_LEFT = 0x01; + public static final int EDGE_RIGHT = 0x02; + public static final int EDGE_TOP = 0x04; + public static final int EDGE_BOTTOM = 0x08; + + public static final int KEYCODE_SHIFT = -1; + public static final int KEYCODE_MODE_CHANGE = -2; + public static final int KEYCODE_CANCEL = -3; + public static final int KEYCODE_DONE = -4; + public static final int KEYCODE_DELETE = -5; + public static final int KEYCODE_ALT = -6; + + /** Keyboard label **/ + private CharSequence mLabel; + + /** Horizontal gap default for all rows */ + private int mDefaultHorizontalGap; + + /** Default key width */ + private int mDefaultWidth; + + /** Default key height */ + private int mDefaultHeight; + + /** Default gap between rows */ + private int mDefaultVerticalGap; + + /** Is the keyboard in the shifted state */ + private boolean mShifted; + + /** Key instance for the shift key, if present */ + private MyKeyBoard.Key[] mShiftKeys = { null, null }; + + /** Key index for the shift key, if present */ + private int[] mShiftKeyIndices = {-1, -1}; + + /** Current key width, while loading the keyboard */ + private int mKeyWidth; + + /** Current key height, while loading the keyboard */ + private int mKeyHeight; + + /** Total height of the keyboard, including the padding and keys */ + private int mTotalHeight; + + /** + * Total width of the keyboard, including left side gaps and keys, but not any gaps on the + * right side. + */ + private int mTotalWidth; + + /** List of keys in this keyboard */ + private List mKeys; + + /** List of modifier keys such as Shift & Alt, if any */ + private List mModifierKeys; + + /** Width of the screen available to fit the keyboard */ + private int mDisplayWidth; + + /** Height of the screen */ + private int mDisplayHeight; + + /** Keyboard mode, or zero, if none. */ + private int mKeyboardMode; + + // Variables for pre-computing nearest keys. + + private static final int GRID_WIDTH = 10; + private static final int GRID_HEIGHT = 5; + private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT; + private int mCellWidth; + private int mCellHeight; + private int[][] mGridNeighbors; + private int mProximityThreshold; + /** Number of key widths from current touch point to search for nearest keys. */ + private static float SEARCH_DISTANCE = 1.8f; + + private ArrayList rows = new ArrayList<>(); + + /** + * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate. + * Some of the key size defaults can be overridden per row from what the {@link MyKeyBoard} + * defines. + * @attr ref android.R.styleable#King_Keyboard_keyWidth + * @attr ref android.R.styleable#King_Keyboard_keyHeight + * @attr ref android.R.styleable#King_Keyboard_horizontalGap + * @attr ref android.R.styleable#King_Keyboard_verticalGap + * @attr ref android.R.styleable#King_Keyboard_Row_rowEdgeFlags + * @attr ref android.R.styleable#King_Keyboard_Row_keyboardMode + */ + public static class Row { + /** Default width of a key in this row. */ + public int defaultWidth; + /** Default height of a key in this row. */ + public int defaultHeight; + /** Default horizontal gap between keys in this row. */ + public int defaultHorizontalGap; + /** Vertical gap following this row. */ + public int verticalGap; + + ArrayList mKeys = new ArrayList<>(); + + /** + * Edge flags for this row of keys. Possible values that can be assigned are + * {@link MyKeyBoard#EDGE_TOP EDGE_TOP} and {@link MyKeyBoard#EDGE_BOTTOM EDGE_BOTTOM} + */ + public int rowEdgeFlags; + + /** The keyboard mode for this row */ + public int mode; + + private MyKeyBoard parent; + + public Row(MyKeyBoard parent) { + this.parent = parent; + } + + public Row(Resources res, MyKeyBoard parent, XmlResourceParser parser) { + this.parent = parent; + TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.My_Keyboard_view); + defaultWidth = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyWidth, + parent.mDisplayWidth, parent.mDefaultWidth); + defaultHeight = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyHeight, + parent.mDisplayHeight, parent.mDefaultHeight); + defaultHorizontalGap = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_horizontalGap, + parent.mDisplayWidth, parent.mDefaultHorizontalGap); + verticalGap = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_verticalGap, + parent.mDisplayHeight, parent.mDefaultVerticalGap); + a.recycle(); + a = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Kil_Keyboard_Row); + rowEdgeFlags = a.getInt(R.styleable.Kil_Keyboard_Row_android_rowEdgeFlags, 0); + mode = a.getResourceId(R.styleable.Kil_Keyboard_Row_android_keyboardMode, + 0); + } + } + + /** + * Class for describing the position and characteristics of a single key in the keyboard. + * + * @attr ref android.R.styleable#King_Keyboard_keyWidth + * @attr ref android.R.styleable#King_Keyboard_keyHeight + * @attr ref android.R.styleable#King_Keyboard_horizontalGap + * @attr ref android.R.styleable#King_Keyboard_Key_codes + * @attr ref android.R.styleable#King_Keyboard_Key_keyIcon + * @attr ref android.R.styleable#King_Keyboard_Key_keyLabel + * @attr ref android.R.styleable#King_Keyboard_Key_iconPreview + * @attr ref android.R.styleable#King_Keyboard_Key_isSticky + * @attr ref android.R.styleable#King_Keyboard_Key_isRepeatable + * @attr ref android.R.styleable#King_Keyboard_Key_isModifier + * @attr ref android.R.styleable#King_Keyboard_Key_popupKeyboard + * @attr ref android.R.styleable#King_Keyboard_Key_popupCharacters + * @attr ref android.R.styleable#King_Keyboard_Key_keyOutputText + * @attr ref android.R.styleable#King_Keyboard_Key_keyEdgeFlags + */ + public static class Key { + /** + * All the key codes (unicode or custom code) that this key could generate, zero'th + * being the most important. + */ + public int[] codes; + + /** Label to display */ + public CharSequence label; + + /** Icon to display instead of a label. Icon takes precedence over a label */ + public Drawable icon; + /** Preview version of the icon, for the preview popup */ + public Drawable iconPreview; + /** Width of the key, not including the gap */ + public int width; + /** Height of the key, not including the gap */ + public int height; + /** The horizontal gap before this key */ + public int gap; + /** Whether this key is sticky, i.e., a toggle key */ + public boolean sticky; + /** X coordinate of the key in the keyboard layout */ + public int x; + /** Y coordinate of the key in the keyboard layout */ + public int y; + /** The current pressed state of this key */ + public boolean pressed; + /** If this is a sticky key, is it on? */ + public boolean on; + /** Text to output when pressed. This can be multiple characters, like ".com" */ + public CharSequence text; + /** Popup characters */ + public CharSequence popupCharacters; + + /** + * Flags that specify the anchoring to edges of the keyboard for detecting touch events + * that are just out of the boundary of the key. This is a bit mask of + * {@link MyKeyBoard#EDGE_LEFT}, {@link MyKeyBoard#EDGE_RIGHT}, {@link MyKeyBoard#EDGE_TOP} and + * {@link MyKeyBoard#EDGE_BOTTOM}. + */ + public int edgeFlags; + /** Whether this is a modifier key, such as Shift or Alt */ + public boolean modifier; + /** The keyboard that this key belongs to */ + private MyKeyBoard keyboard; + /** + * If this key pops up a mini keyboard, this is the resource id for the XML layout for that + * keyboard. + */ + public int popupResId; + /** Whether this key repeats itself when held down */ + public boolean repeatable; + + + private final static int[] KEY_STATE_NORMAL_ON = { + android.R.attr.state_checkable, + android.R.attr.state_checked + }; + + private final static int[] KEY_STATE_PRESSED_ON = { + android.R.attr.state_pressed, + android.R.attr.state_checkable, + android.R.attr.state_checked + }; + + private final static int[] KEY_STATE_NORMAL_OFF = { + android.R.attr.state_checkable + }; + + private final static int[] KEY_STATE_PRESSED_OFF = { + android.R.attr.state_pressed, + android.R.attr.state_checkable + }; + + private final static int[] KEY_STATE_NORMAL = { + }; + + private final static int[] KEY_STATE_PRESSED = { + android.R.attr.state_pressed + }; + + /** Create an empty key with no attributes. */ + public Key(MyKeyBoard.Row parent) { + keyboard = parent.parent; + height = parent.defaultHeight; + width = parent.defaultWidth; + gap = parent.defaultHorizontalGap; + edgeFlags = parent.rowEdgeFlags; + } + + /** Create a key with the given top-left coordinate and extract its attributes from + * the XML parser. + * @param res resources associated with the caller's context + * @param parent the row that this key belongs to. The row must already be attached to + * a {@link MyKeyBoard}. + * @param x the x coordinate of the top-left + * @param y the y coordinate of the top-left + * @param parser the XML parser containing the attributes for this key + */ + public Key(Resources res, MyKeyBoard.Row parent, int x, int y, XmlResourceParser parser) { + this(parent); + + this.x = x; + this.y = y; + + TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.My_Keyboard_view); + + width = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyWidth, + keyboard.mDisplayWidth, parent.defaultWidth); + height = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyHeight, + keyboard.mDisplayHeight, parent.defaultHeight); + gap = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_horizontalGap, + keyboard.mDisplayWidth, parent.defaultHorizontalGap); + a.recycle(); + a = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.K_Keyboard_Key); + this.x += gap; + TypedValue codesValue = new TypedValue(); + a.getValue(R.styleable.K_Keyboard_Key_android_codes, + codesValue); + if (codesValue.type == TypedValue.TYPE_INT_DEC + || codesValue.type == TypedValue.TYPE_INT_HEX) { + codes = new int[] { codesValue.data }; + } else if (codesValue.type == TypedValue.TYPE_STRING) { + codes = parseCSV(codesValue.string.toString()); + } + + iconPreview = a.getDrawable(R.styleable.K_Keyboard_Key_android_iconPreview); + if (iconPreview != null) { + iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(), + iconPreview.getIntrinsicHeight()); + } + popupCharacters = a.getText( + R.styleable.K_Keyboard_Key_android_popupCharacters); + popupResId = a.getResourceId( + R.styleable.K_Keyboard_Key_android_popupKeyboard, 0); + repeatable = a.getBoolean( + R.styleable.K_Keyboard_Key_android_isRepeatable, false); + modifier = a.getBoolean( + R.styleable.K_Keyboard_Key_android_isModifier, false); + sticky = a.getBoolean( + R.styleable.K_Keyboard_Key_android_isSticky, false); + edgeFlags = a.getInt(R.styleable.K_Keyboard_Key_android_keyEdgeFlags, 0); + edgeFlags |= parent.rowEdgeFlags; + + icon = a.getDrawable( + R.styleable.K_Keyboard_Key_android_keyIcon); + if (icon != null) { + icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); + } + label = a.getText(R.styleable.K_Keyboard_Key_android_keyLabel); + text = a.getText(R.styleable.K_Keyboard_Key_android_keyOutputText); + + if (codes == null && !TextUtils.isEmpty(label)) { + codes = new int[] { label.charAt(0) }; + } + a.recycle(); + } + + /** + * Informs the key that it has been pressed, in case it needs to change its appearance or + * state. + * @see #onReleased(boolean) + */ + public void onPressed() { + pressed = !pressed; + } + + /** + * Changes the pressed state of the key. + * + *

Toggled state of the key will be flipped when all the following conditions are + * fulfilled:

+ * + *
    + *
  • This is a sticky key, that is, {@link #sticky} is {@code true}. + *
  • The parameter {@code inside} is {@code true}. + *
  • {@link android.os.Build.VERSION#SDK_INT} is greater than + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. + *
+ * + * @param inside whether the finger was released inside the key. Works only on Android M and + * later. See the method document for details. + * @see #onPressed() + */ + public void onReleased(boolean inside) { + pressed = !pressed; + if (sticky && inside) { + on = !on; + } + } + + int[] parseCSV(String value) { + int count = 0; + int lastIndex = 0; + if (value.length() > 0) { + count++; + while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) { + count++; + } + } + int[] values = new int[count]; + count = 0; + StringTokenizer st = new StringTokenizer(value, ","); + while (st.hasMoreTokens()) { + try { + values[count++] = Integer.parseInt(st.nextToken()); + } catch (NumberFormatException nfe) { + Log.e(TAG, "Error parsing keycodes " + value); + } + } + return values; + } + + /** + * Detects if a point falls inside this key. + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + * @return whether or not the point falls inside the key. If the key is attached to an edge, + * it will assume that all points between the key and the edge are considered to be inside + * the key. + */ + public boolean isInside(int x, int y) { + boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0; + boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0; + boolean topEdge = (edgeFlags & EDGE_TOP) > 0; + boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0; + if ((x >= this.x || (leftEdge && x <= this.x + this.width)) + && (x < this.x + this.width || (rightEdge && x >= this.x)) + && (y >= this.y || (topEdge && y <= this.y + this.height)) + && (y < this.y + this.height || (bottomEdge && y >= this.y))) { + return true; + } else { + return false; + } + } + + /** + * Returns the square of the distance between the center of the key and the given point. + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + * @return the square of the distance of the point from the center of the key + */ + public int squaredDistanceFrom(int x, int y) { + int xDist = this.x + width / 2 - x; + int yDist = this.y + height / 2 - y; + return xDist * xDist + yDist * yDist; + } + + /** + * Returns the drawable state for the key, based on the current state and type of the key. + * @return the drawable state of the key. + * @see android.graphics.drawable.StateListDrawable#setState(int[]) + */ + public int[] getCurrentDrawableState() { + int[] states = KEY_STATE_NORMAL; + + if (on) { + if (pressed) { + states = KEY_STATE_PRESSED_ON; + } else { + states = KEY_STATE_NORMAL_ON; + } + } else { + if (sticky) { + if (pressed) { + states = KEY_STATE_PRESSED_OFF; + } else { + states = KEY_STATE_NORMAL_OFF; + } + } else { + if (pressed) { + states = KEY_STATE_PRESSED; + } + } + } + return states; + } + } + + /** + * Creates a keyboard from the given xml key layout file. + * @param context the application or service context + * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. + */ + public MyKeyBoard(Context context, int xmlLayoutResId) { + this(context, xmlLayoutResId, 0); + } + + /** + * Creates a keyboard from the given xml key layout file. Weeds out rows + * that have a keyboard mode defined but don't match the specified mode. + * @param context the application or service context + * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. + * @param modeId keyboard mode identifier + * @param width sets width of keyboard + * @param height sets height of keyboard + */ + public MyKeyBoard(Context context, @XmlRes int xmlLayoutResId, int modeId, int width, + int height) { + mDisplayWidth = width; + mDisplayHeight = height; + + mDefaultHorizontalGap = 0; + mDefaultWidth = mDisplayWidth / 10; + mDefaultVerticalGap = 0; + mDefaultHeight = mDefaultWidth; + mKeys = new ArrayList<>(); + mModifierKeys = new ArrayList<>(); + mKeyboardMode = modeId; + loadKeyboard(context, context.getResources().getXml(xmlLayoutResId)); + } + + /** + * Creates a keyboard from the given xml key layout file. Weeds out rows + * that have a keyboard mode defined but don't match the specified mode. + * @param context the application or service context + * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. + * @param modeId keyboard mode identifier + */ + public MyKeyBoard(Context context, @XmlRes int xmlLayoutResId, int modeId) { + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + mDisplayWidth = dm.widthPixels; + mDisplayHeight = dm.heightPixels; + //Log.v(TAG, "keyboard's display metrics:" + dm); + + mDefaultHorizontalGap = 0; + mDefaultWidth = mDisplayWidth / 10; + mDefaultVerticalGap = 0; + mDefaultHeight = mDefaultWidth; + mKeys = new ArrayList<>(); + mModifierKeys = new ArrayList<>(); + mKeyboardMode = modeId; + loadKeyboard(context, context.getResources().getXml(xmlLayoutResId)); + } + + public MyKeyBoard(Context context, int layoutTemplateResId, + CharSequence characters, int columns, int horizontalPadding) { + this(context, layoutTemplateResId); + int x = 0; + int y = 0; + int column = 0; + mTotalWidth = 0; + + MyKeyBoard.Row row = new MyKeyBoard.Row(this); + row.defaultHeight = mDefaultHeight; + row.defaultWidth = mDefaultWidth; + row.defaultHorizontalGap = mDefaultHorizontalGap; + row.verticalGap = mDefaultVerticalGap; + row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM; + final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns; + for (int i = 0; i < characters.length(); i++) { + char c = characters.charAt(i); + if (column >= maxColumns + || x + mDefaultWidth + horizontalPadding > mDisplayWidth) { + x = 0; + y += mDefaultVerticalGap + mDefaultHeight; + column = 0; + } + final MyKeyBoard.Key key = new MyKeyBoard.Key(row); + key.x = x; + key.y = y; + key.label = String.valueOf(c); + key.codes = new int[] { c }; + column++; + x += key.width + key.gap; + mKeys.add(key); + row.mKeys.add(key); + if (x > mTotalWidth) { + mTotalWidth = x; + } + } + mTotalHeight = y + mDefaultHeight; + rows.add(row); + } + + final void resize(int newWidth, int newHeight) { + int numRows = rows.size(); + for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) { + MyKeyBoard.Row row = rows.get(rowIndex); + int numKeys = row.mKeys.size(); + int totalGap = 0; + int totalWidth = 0; + for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) { + MyKeyBoard.Key key = row.mKeys.get(keyIndex); + if (keyIndex > 0) { + totalGap += key.gap; + } + totalWidth += key.width; + } + if (totalGap + totalWidth > newWidth) { + int x = 0; + float scaleFactor = (float)(newWidth - totalGap) / totalWidth; + for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) { + MyKeyBoard.Key key = row.mKeys.get(keyIndex); + key.width *= scaleFactor; + key.x = x; + x += key.width + key.gap; + } + } + } + mTotalWidth = newWidth; + // TODO: This does not adjust the vertical placement according to the new size. + // The main problem in the previous code was horizontal placement/size, but we should + // also recalculate the vertical sizes/positions when we get this resize call. + } + + public List getKeys() { + return mKeys; + } + + public List getModifierKeys() { + return mModifierKeys; + } + + protected int getHorizontalGap() { + return mDefaultHorizontalGap; + } + + protected void setHorizontalGap(int gap) { + mDefaultHorizontalGap = gap; + } + + protected int getVerticalGap() { + return mDefaultVerticalGap; + } + + protected void setVerticalGap(int gap) { + mDefaultVerticalGap = gap; + } + + protected int getKeyHeight() { + return mDefaultHeight; + } + + protected void setKeyHeight(int height) { + mDefaultHeight = height; + } + + protected int getKeyWidth() { + return mDefaultWidth; + } + + protected void setKeyWidth(int width) { + mDefaultWidth = width; + } + + /** + * Returns the total height of the keyboard + * @return the total height of the keyboard + */ + public int getHeight() { + return mTotalHeight; + } + + public int getMinWidth() { + return mTotalWidth; + } + + public boolean setShifted(boolean shiftState) { + for (MyKeyBoard.Key shiftKey : mShiftKeys) { + if (shiftKey != null) { + shiftKey.on = shiftState; + } + } + if (mShifted != shiftState) { + mShifted = shiftState; + return true; + } + return false; + } + + public boolean isShifted() { + return mShifted; + } + + /** + * @hide + */ + public int[] getShiftKeyIndices() { + return mShiftKeyIndices; + } + + public int getShiftKeyIndex() { + return mShiftKeyIndices[0]; + } + + private void computeNearestNeighbors() { + // Round-up so we don't have any pixels outside the grid + mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH; + mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT; + mGridNeighbors = new int[GRID_SIZE][]; + int[] indices = new int[mKeys.size()]; + final int gridWidth = GRID_WIDTH * mCellWidth; + final int gridHeight = GRID_HEIGHT * mCellHeight; + for (int x = 0; x < gridWidth; x += mCellWidth) { + for (int y = 0; y < gridHeight; y += mCellHeight) { + int count = 0; + for (int i = 0; i < mKeys.size(); i++) { + final MyKeyBoard.Key key = mKeys.get(i); + if (key.squaredDistanceFrom(x, y) < mProximityThreshold || + key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold || + key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1) + < mProximityThreshold || + key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) { + indices[count++] = i; + } + } + int [] cell = new int[count]; + System.arraycopy(indices, 0, cell, 0, count); + mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell; + } + } + } + + /** + * Returns the indices of the keys that are closest to the given point. + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + * @return the array of integer indices for the nearest keys to the given point. If the given + * point is out of range, then an array of size zero is returned. + */ + public int[] getNearestKeys(int x, int y) { + if (mGridNeighbors == null) computeNearestNeighbors(); + if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) { + int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth); + if (index < GRID_SIZE) { + return mGridNeighbors[index]; + } + } + return new int[0]; + } + + protected MyKeyBoard.Row createRowFromXml(Resources res, XmlResourceParser parser) { + return new MyKeyBoard.Row(res, this, parser); + } + + protected MyKeyBoard.Key createKeyFromXml(Resources res, MyKeyBoard.Row parent, int x, int y, + XmlResourceParser parser) { + return new MyKeyBoard.Key(res, parent, x, y, parser); + } + + private void loadKeyboard(Context context, XmlResourceParser parser) { + boolean inKey = false; + boolean inRow = false; + boolean leftMostKey = false; + int row = 0; + int x = 0; + int y = 0; + MyKeyBoard.Key key = null; + MyKeyBoard.Row currentRow = null; + Resources res = context.getResources(); + boolean skipRow = false; + + try { + int event; + while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.START_TAG) { + String tag = parser.getName(); + if (TAG_ROW.equals(tag)) { + inRow = true; + x = 0; + currentRow = createRowFromXml(res, parser); + rows.add(currentRow); + skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode; + if (skipRow) { + skipToEndOfRow(parser); + inRow = false; + } + } else if (TAG_KEY.equals(tag)) { + inKey = true; + key = createKeyFromXml(res, currentRow, x, y, parser); + mKeys.add(key); + if (key.codes[0] == KEYCODE_SHIFT) { + // Find available shift key slot and put this shift key in it + for (int i = 0; i < mShiftKeys.length; i++) { + if (mShiftKeys[i] == null) { + mShiftKeys[i] = key; + mShiftKeyIndices[i] = mKeys.size()-1; + break; + } + } + mModifierKeys.add(key); + } else if (key.codes[0] == KEYCODE_ALT) { + mModifierKeys.add(key); + } + currentRow.mKeys.add(key); + } else if (TAG_KEYBOARD.equals(tag)) { + parseKeyboardAttributes(res, parser); + } + } else if (event == XmlResourceParser.END_TAG) { + if (inKey) { + inKey = false; + x += key.gap + key.width; + if (x > mTotalWidth) { + mTotalWidth = x; + } + } else if (inRow) { + inRow = false; + y += currentRow.verticalGap; + y += currentRow.defaultHeight; + row++; + } else { + // TODO: error or extend? + } + } + } + } catch (Exception e) { + Log.e(TAG, "Parse error:" + e); + e.printStackTrace(); + } + mTotalHeight = y - mDefaultVerticalGap; + } + + private void skipToEndOfRow(XmlResourceParser parser) + throws XmlPullParserException, IOException { + int event; + while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.END_TAG + && parser.getName().equals(TAG_ROW)) { + break; + } + } + } + + private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) { + TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.My_Keyboard_view); + + mDefaultWidth = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyWidth, + mDisplayWidth, mDisplayWidth / 10); + mDefaultHeight = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_keyHeight, + mDisplayHeight, 50); + mDefaultHorizontalGap = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_horizontalGap, + mDisplayWidth, 0); + mDefaultVerticalGap = getDimensionOrFraction(a, + R.styleable.My_Keyboard_view_android_verticalGap, + mDisplayHeight, 0); + mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE); + mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison + a.recycle(); + } + + static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) { + TypedValue value = a.peekValue(index); + if (value == null) return defValue; + if (value.type == TypedValue.TYPE_DIMENSION) { + return a.getDimensionPixelOffset(index, defValue); + } else if (value.type == TypedValue.TYPE_FRACTION) { + // Round it to avoid values like 47.9999 from getting truncated + return Math.round(a.getFraction(index, base, base, defValue)); + } + return defValue; + } +} + diff --git a/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoardView.java b/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoardView.java new file mode 100644 index 0000000..a4a9ed4 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/customer/MyKeyBoardView.java @@ -0,0 +1,1386 @@ +package com.input.board.appskey.keyboard.customer; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.media.AudioManager; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.PopupWindow; +import android.widget.TextView; + +import com.input.board.appskey.keyboard.R; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MyKeyBoardView extends View implements View.OnClickListener { + + /** + * Listener for virtual keyboard events. + */ + public interface OnKeyboardActionListener { + + + void onPress(int primaryCode); + + /** + * Called when the user releases a key. This is sent after the {@link #onKey} is called. + * For keys that repeat, this is only called once. + * @param primaryCode the code of the key that was released + */ + void onRelease(int primaryCode); + + void onKey(int primaryCode, int[] keyCodes); + + /** + * Sends a sequence of characters to the listener. + * @param text the sequence of characters to be displayed. + */ + void onText(CharSequence text); + + /** + * Called when the user quickly moves the finger from right to left. + */ + void swipeLeft(); + + /** + * Called when the user quickly moves the finger from left to right. + */ + void swipeRight(); + + /** + * Called when the user quickly moves the finger from up to down. + */ + void swipeDown(); + + /** + * Called when the user quickly moves the finger from down to up. + */ + void swipeUp(); + } + + private static final boolean DEBUG = false; + private static final int NOT_A_KEY = -1; + private static final int[] KEY_DELETE = { MyKeyBoard.KEYCODE_DELETE }; + private static final int[] LONG_PRESSABLE_STATE_SET = { R.styleable.IM_KeyboardViewPreviewState_android_state_long_pressable }; + + private Context mContext; + private MyKeyBoard mKeyboard; + private int mCurrentKeyIndex = NOT_A_KEY; + + private int mLabelTextSize; + private int mKeyTextSize; + private int mKeyTextColor; + private float mShadowRadius; + private int mShadowColor; + private float mBackgroundDimAmount; + + private TextView mPreviewText; + private PopupWindow mPreviewPopup; + private int mPreviewTextSizeLarge; + private int mPreviewOffset; + private int mPreviewHeight; + // Working variable + private final int[] mCoordinates = new int[2]; + + private PopupWindow mPopupKeyboard; + private View mMiniKeyboardContainer; + private MyKeyBoardView mMiniKeyboard; + private boolean mMiniKeyboardOnScreen; + private View mPopupParent; + private int mMiniKeyboardOffsetX; + private int mMiniKeyboardOffsetY; + private Map mMiniKeyboardCache; + private MyKeyBoard.Key[] mKeys; + + + private MyKeyBoardView.OnKeyboardActionListener mKeyboardActionListener; + + private static final int MSG_SHOW_PREVIEW = 1; + private static final int MSG_REMOVE_PREVIEW = 2; + private static final int MSG_REPEAT = 3; + private static final int MSG_LONGPRESS = 4; + + private static final int DELAY_BEFORE_PREVIEW = 0; + private static final int DELAY_AFTER_PREVIEW = 70; + private static final int DEBOUNCE_TIME = 70; + + private int mVerticalCorrection; + private int mProximityThreshold; + + private boolean mPreviewCentered = false; + private boolean mShowPreview = true; + private boolean mShowTouchPoints = true; + private int mPopupPreviewX; + private int mPopupPreviewY; + + private int mLastX; + private int mLastY; + private int mStartX; + private int mStartY; + + private boolean mProximityCorrectOn; + + private Paint mPaint; + private Rect mPadding; + + private long mDownTime; + private long mLastMoveTime; + private int mLastKey; + private int mLastCodeX; + private int mLastCodeY; + private int mCurrentKey = NOT_A_KEY; + private int mDownKey = NOT_A_KEY; + private long mLastKeyTime; + private long mCurrentKeyTime; + private int[] mKeyIndices = new int[12]; + private GestureDetector mGestureDetector; + private int mPopupX; + private int mPopupY; + private int mRepeatKeyIndex = NOT_A_KEY; + private int mPopupLayout; + private boolean mAbortKey; + private MyKeyBoard.Key mInvalidatedKey; + private Rect mClipRegion = new Rect(0, 0, 0, 0); + private boolean mPossiblePoly; + private SwipeTracker mSwipeTracker = new SwipeTracker(); + private int mSwipeThreshold; + private boolean mDisambiguateSwipe; + + // Variables for dealing with multiple pointers + private int mOldPointerCount = 1; + private float mOldPointerX; + private float mOldPointerY; + + private Drawable mKeyBackground; + + private static final int REPEAT_INTERVAL = 50; // ~20 keys per second + private static final int REPEAT_START_DELAY = 300; + private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); + + private static int MAX_NEARBY_KEYS = 12; + private int[] mDistances = new int[MAX_NEARBY_KEYS]; + + // For multi-tap + private int mLastSentIndex; + private int mTapCount; + private long mLastTapTime; + private boolean mInMultiTap; + private static final int MULTITAP_INTERVAL = 600; // milliseconds + private StringBuilder mPreviewLabel = new StringBuilder(1); + + /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ + private boolean mDrawPending; + /** The dirty region in the keyboard bitmap */ + private Rect mDirtyRect = new Rect(); + /** The keyboard bitmap for faster updates */ + private Bitmap mBuffer; + /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ + private boolean mKeyboardChanged; + /** The canvas for the above mutable keyboard bitmap */ + private Canvas mCanvas; + /** The accessibility manager for accessibility support */ +// private AccessibilityManager mAccessibilityManager; + /** The audio manager for accessibility support */ + private AudioManager mAudioManager; + /** Whether the requirement of a headset to hear passwords if accessibility is enabled is announced. */ + private boolean mHeadsetRequiredToHearPasswordsAnnounced; + + Handler mHandler; + + public MyKeyBoardView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MyKeyBoardView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public MyKeyBoardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mContext = context; + TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.My_KeyboardView, defStyleAttr, defStyleRes); + + LayoutInflater inflate = + (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + int previewLayout = 0; + int keyTextSize = 0; + + int n = a.getIndexCount(); + + for (int i = 0; i < n; i++) { + int attr = a.getIndex(i); + + if (attr == R.styleable.My_KeyboardView_android_keyBackground) { + mKeyBackground = a.getDrawable(attr); + } else if (attr == R.styleable.My_KeyboardView_android_verticalCorrection) { + mVerticalCorrection = a.getDimensionPixelOffset(attr, 0); + } else if (attr == R.styleable.My_KeyboardView_android_keyPreviewLayout) { + previewLayout = a.getResourceId(attr, 0); + } else if (attr == R.styleable.My_KeyboardView_android_keyPreviewOffset) { + mPreviewOffset = a.getDimensionPixelOffset(attr, 0); + } else if (attr == R.styleable.My_KeyboardView_android_keyPreviewHeight) { + mPreviewHeight = a.getDimensionPixelSize(attr, 80); + } else if (attr == R.styleable.My_KeyboardView_android_keyTextSize) { + mKeyTextSize = a.getDimensionPixelSize(attr, 18); + } else if (attr == R.styleable.My_KeyboardView_android_keyTextColor) { + mKeyTextColor = a.getColor(attr, 0xFF333333); + } else if (attr == R.styleable.My_KeyboardView_android_labelTextSize) { + mLabelTextSize = a.getDimensionPixelSize(attr, 14); + } else if (attr == R.styleable.My_KeyboardView_android_popupLayout) { + mPopupLayout = a.getResourceId(attr, 0); + } else if (attr == R.styleable.My_KeyboardView_android_shadowColor) { + mShadowColor = a.getColor(attr, 0); + } else if (attr == R.styleable.My_KeyboardView_android_shadowRadius) { + mShadowRadius = a.getFloat(attr, 0f); + } + } + + mPreviewPopup = new PopupWindow(context); + if (previewLayout != 0) { + mPreviewText = (TextView) inflate.inflate(previewLayout, null); + mPreviewTextSizeLarge = (int) mPreviewText.getTextSize(); + mPreviewPopup.setContentView(mPreviewText); + mPreviewPopup.setBackgroundDrawable(null); + } else { + mShowPreview = false; + } + + mPreviewPopup.setTouchable(false); + + mPopupKeyboard = new PopupWindow(context); + mPopupKeyboard.setBackgroundDrawable(null); + //mPopupKeyboard.setClippingEnabled(false); + + mPopupParent = this; + //mPredicting = true; + + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setTextSize(keyTextSize); + mPaint.setTextAlign(Paint.Align.CENTER); + mPaint.setAlpha(255); + + mPadding = new Rect(0, 0, 0, 0); + mMiniKeyboardCache = new HashMap(); + mKeyBackground.getPadding(mPadding); + + mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density); +// mDisambiguateSwipe = getResources().getBoolean( +// R.bool.config_swipeDisambiguation); + + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + resetMultiTap(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + initGestureDetector(); + if (mHandler == null) { + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SHOW_PREVIEW: + showKey(msg.arg1); + break; + case MSG_REMOVE_PREVIEW: + mPreviewText.setVisibility(INVISIBLE); + break; + case MSG_REPEAT: + if (repeatKey()) { + Message repeat = Message.obtain(this, MSG_REPEAT); + sendMessageDelayed(repeat, REPEAT_INTERVAL); + } + break; + case MSG_LONGPRESS: + openPopupIfRequired((MotionEvent) msg.obj); + break; + } + } + }; + } + } + + private void initGestureDetector() { + if (mGestureDetector == null) { + mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent me1, MotionEvent me2, + float velocityX, float velocityY) { + if (mPossiblePoly) return false; + final float absX = Math.abs(velocityX); + final float absY = Math.abs(velocityY); + float deltaX = me2.getX() - me1.getX(); + float deltaY = me2.getY() - me1.getY(); + int travelX = getWidth() / 2; // Half the keyboard width + int travelY = getHeight() / 2; // Half the keyboard height + mSwipeTracker.computeCurrentVelocity(1000); + final float endingVelocityX = mSwipeTracker.getXVelocity(); + final float endingVelocityY = mSwipeTracker.getYVelocity(); + boolean sendDownKey = false; + if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { + if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { + sendDownKey = true; + } else { + swipeRight(); + return true; + } + } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { + if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { + sendDownKey = true; + } else { + swipeLeft(); + return true; + } + } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { + if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { + sendDownKey = true; + } else { + swipeUp(); + return true; + } + } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { + if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { + sendDownKey = true; + } else { + swipeDown(); + return true; + } + } + + if (sendDownKey) { + detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime()); + } + return false; + } + }); + + mGestureDetector.setIsLongpressEnabled(false); + } + } + + public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { + mKeyboardActionListener = listener; + } + + protected OnKeyboardActionListener getOnKeyboardActionListener() { + return mKeyboardActionListener; + } + + + public void setKeyboard(MyKeyBoard keyboard) { + if (mKeyboard != null) { + showPreview(NOT_A_KEY); + } + // Remove any pending messages + removeMessages(); + mKeyboard = keyboard; + List keys = mKeyboard.getKeys(); + mKeys = keys.toArray(new MyKeyBoard.Key[keys.size()]); + requestLayout(); + // Hint to reallocate the buffer if the size changed + mKeyboardChanged = true; + invalidateAllKeys(); + computeProximityThreshold(keyboard); + mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views + // Switching to a different keyboard should abort any pending keys so that the key up + // doesn't get delivered to the old or new keyboard + mAbortKey = true; // Until the next ACTION_DOWN + } + + + public MyKeyBoard getKeyboard() { + return mKeyboard; + } + + + public boolean setShifted(boolean shifted) { + if (mKeyboard != null) { + if (mKeyboard.setShifted(shifted)) { + // The whole keyboard probably needs to be redrawn + invalidateAllKeys(); + return true; + } + } + return false; + } + + + public boolean isShifted() { + if (mKeyboard != null) { + return mKeyboard.isShifted(); + } + return false; + } + + + public void setPreviewEnabled(boolean previewEnabled) { + mShowPreview = previewEnabled; + } + + /** + * Returns the enabled state of the key feedback popup. + * @return whether or not the key feedback popup is enabled + * @see #setPreviewEnabled(boolean) + */ + public boolean isPreviewEnabled() { + return mShowPreview; + } + + public void setVerticalCorrection(int verticalOffset) { + + } + public void setPopupParent(View v) { + mPopupParent = v; + } + + public void setPopupOffset(int x, int y) { + mMiniKeyboardOffsetX = x; + mMiniKeyboardOffsetY = y; + if (mPreviewPopup.isShowing()) { + mPreviewPopup.dismiss(); + } + } + + public void setProximityCorrectionEnabled(boolean enabled) { + mProximityCorrectOn = enabled; + } + + /** + * Returns true if proximity correction is enabled. + */ + public boolean isProximityCorrectionEnabled() { + return mProximityCorrectOn; + } + + /** + * Popup keyboard close button clicked. + * @hide + */ + public void onClick(View v) { + dismissPopupKeyboard(); + } + + private CharSequence adjustCase(CharSequence label) { + if (mKeyboard.isShifted() && label != null && label.length() < 3 + && Character.isLowerCase(label.charAt(0))) { + label = label.toString().toUpperCase(); + } + return label; + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Round up a little + if (mKeyboard == null) { + setMeasuredDimension(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); + } else { + int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); + if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) { + width = MeasureSpec.getSize(widthMeasureSpec); + } + setMeasuredDimension(width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom()); + } + } + + /** + * Compute the average distance between adjacent keys (horizontally and vertically) + * and square it to get the proximity threshold. We use a square here and in computing + * the touch distance from a key's center to avoid taking a square root. + * @param keyboard + */ + private void computeProximityThreshold(MyKeyBoard keyboard) { + if (keyboard == null) return; + final MyKeyBoard.Key[] keys = mKeys; + if (keys == null) return; + int length = keys.length; + int dimensionSum = 0; + for (int i = 0; i < length; i++) { + MyKeyBoard.Key key = keys[i]; + dimensionSum += Math.min(key.width, key.height) + key.gap; + } + if (dimensionSum < 0 || length == 0) return; + mProximityThreshold = (int) (dimensionSum * 1.4f / length); + mProximityThreshold *= mProximityThreshold; // Square it + } + + @Override + public void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (mKeyboard != null) { + mKeyboard.resize(w, h); + } + // Release the buffer, if any and it will be reallocated on the next draw + mBuffer = null; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); +// if (mDrawPending || mBuffer == null || mKeyboardChanged) { +// onBufferDraw(); +// } +// canvas.drawBitmap(mBuffer, 0, 0, null); + } + + private void onBufferDraw() { + if (mBuffer == null || mKeyboardChanged) { + if (mBuffer == null || mKeyboardChanged && + (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { + // Make sure our bitmap is at least 1x1 + final int width = Math.max(1, getWidth()); + final int height = Math.max(1, getHeight()); + mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + mCanvas = new Canvas(mBuffer); + } + invalidateAllKeys(); + mKeyboardChanged = false; + } + + if (mKeyboard == null) return; + + mCanvas.save(); + final Canvas canvas = mCanvas; + canvas.clipRect(mDirtyRect); + + final Paint paint = mPaint; + final Drawable keyBackground = mKeyBackground; + final Rect clipRegion = mClipRegion; + final Rect padding = mPadding; + final int kbdPaddingLeft = getPaddingLeft(); + final int kbdPaddingTop = getPaddingTop(); + final MyKeyBoard.Key[] keys = mKeys; + final MyKeyBoard.Key invalidKey = mInvalidatedKey; + + paint.setColor(mKeyTextColor); + boolean drawSingleKey = false; + if (invalidKey != null && canvas.getClipBounds(clipRegion)) { + // Is clipRegion completely contained within the invalidated key? + if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left && + invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top && + invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right && + invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) { + drawSingleKey = true; + } + } + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + final int keyCount = keys.length; + for (int i = 0; i < keyCount; i++) { + final MyKeyBoard.Key key = keys[i]; + if (drawSingleKey && invalidKey != key) { + continue; + } + int[] drawableState = key.getCurrentDrawableState(); + keyBackground.setState(drawableState); + + // Switch the character to uppercase if shift is pressed + String label = key.label == null? null : adjustCase(key.label).toString(); + + final Rect bounds = keyBackground.getBounds(); + if (key.width != bounds.right || + key.height != bounds.bottom) { + keyBackground.setBounds(0, 0, key.width, key.height); + } + canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop); + keyBackground.draw(canvas); + + if (label != null) { + // For characters, use large font. For labels like "Done", use small font. + if (label.length() > 1 && key.codes.length < 2) { + paint.setTextSize(mLabelTextSize); + paint.setTypeface(Typeface.DEFAULT_BOLD); + } else { + paint.setTextSize(mKeyTextSize); + paint.setTypeface(Typeface.DEFAULT); + } + // Draw a drop shadow for the text + paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); + // Draw the text + canvas.drawText(label, + (key.width - padding.left - padding.right) / 2 + + padding.left, + (key.height - padding.top - padding.bottom) / 2 + + (paint.getTextSize() - paint.descent()) / 2 + padding.top, + paint); + // Turn off drop shadow + paint.setShadowLayer(0, 0, 0, 0); + } else if (key.icon != null) { + final int drawableX = (key.width - padding.left - padding.right + - key.icon.getIntrinsicWidth()) / 2 + padding.left; + final int drawableY = (key.height - padding.top - padding.bottom + - key.icon.getIntrinsicHeight()) / 2 + padding.top; + canvas.translate(drawableX, drawableY); + key.icon.setBounds(0, 0, + key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight()); + key.icon.draw(canvas); + canvas.translate(-drawableX, -drawableY); + } + canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop); + } + mInvalidatedKey = null; + // Overlay a dark rectangle to dim the keyboard + if (mMiniKeyboardOnScreen) { +// paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); + canvas.drawRect(0, 0, getWidth(), getHeight(), paint); + } + + if (DEBUG && mShowTouchPoints) { + paint.setAlpha(128); + paint.setColor(0xFFFF0000); + canvas.drawCircle(mStartX, mStartY, 3, paint); + canvas.drawLine(mStartX, mStartY, mLastX, mLastY, paint); + paint.setColor(0xFF0000FF); + canvas.drawCircle(mLastX, mLastY, 3, paint); + paint.setColor(0xFF00FF00); + canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint); + } + mCanvas.restore(); + mDrawPending = false; + mDirtyRect.setEmpty(); + } + + private int getKeyIndices(int x, int y, int[] allKeys) { + final MyKeyBoard.Key[] keys = mKeys; + int primaryIndex = NOT_A_KEY; + int closestKey = NOT_A_KEY; + int closestKeyDist = mProximityThreshold + 1; + java.util.Arrays.fill(mDistances, Integer.MAX_VALUE); + int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y); + final int keyCount = nearestKeyIndices.length; + for (int i = 0; i < keyCount; i++) { + final MyKeyBoard.Key key = keys[nearestKeyIndices[i]]; + int dist = 0; + boolean isInside = key.isInside(x,y); + if (isInside) { + primaryIndex = nearestKeyIndices[i]; + } + + if (((mProximityCorrectOn + && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) + || isInside) + && key.codes[0] > 32) { + // Find insertion point + final int nCodes = key.codes.length; + if (dist < closestKeyDist) { + closestKeyDist = dist; + closestKey = nearestKeyIndices[i]; + } + + if (allKeys == null) continue; + + for (int j = 0; j < mDistances.length; j++) { + if (mDistances[j] > dist) { + // Make space for nCodes codes + System.arraycopy(mDistances, j, mDistances, j + nCodes, + mDistances.length - j - nCodes); + System.arraycopy(allKeys, j, allKeys, j + nCodes, + allKeys.length - j - nCodes); + for (int c = 0; c < nCodes; c++) { + allKeys[j + c] = key.codes[c]; + mDistances[j + c] = dist; + } + break; + } + } + } + } + if (primaryIndex == NOT_A_KEY) { + primaryIndex = closestKey; + } + return primaryIndex; + } + + private void detectAndSendKey(int index, int x, int y, long eventTime) { + if (index != NOT_A_KEY && index < mKeys.length) { + final MyKeyBoard.Key key = mKeys[index]; + if (key.text != null) { + mKeyboardActionListener.onText(key.text); + mKeyboardActionListener.onRelease(NOT_A_KEY); + } else { + int code = key.codes[0]; + //TextEntryState.keyPressedAt(key, x, y); + int[] codes = new int[MAX_NEARBY_KEYS]; + Arrays.fill(codes, NOT_A_KEY); + getKeyIndices(x, y, codes); + // Multi-tap + if (mInMultiTap) { + if (mTapCount != -1) { + mKeyboardActionListener.onKey(MyKeyBoard.KEYCODE_DELETE, KEY_DELETE); + } else { + mTapCount = 0; + } + code = key.codes[mTapCount]; + } + mKeyboardActionListener.onKey(code, codes); + mKeyboardActionListener.onRelease(code); + } + mLastSentIndex = index; + mLastTapTime = eventTime; + } + } + + /** + * Handle multi-tap keys by producing the key label for the current multi-tap state. + */ + private CharSequence getPreviewText(MyKeyBoard.Key key) { + if (mInMultiTap) { + // Multi-tap + mPreviewLabel.setLength(0); + mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]); + return adjustCase(mPreviewLabel); + } else { + return adjustCase(key.label); + } + } + + private void showPreview(int keyIndex) { + int oldKeyIndex = mCurrentKeyIndex; + final PopupWindow previewPopup = mPreviewPopup; + + mCurrentKeyIndex = keyIndex; + // Release the old key and press the new key + final MyKeyBoard.Key[] keys = mKeys; + if (oldKeyIndex != mCurrentKeyIndex) { + if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) { + MyKeyBoard.Key oldKey = keys[oldKeyIndex]; + oldKey.onReleased(mCurrentKeyIndex == NOT_A_KEY); + invalidateKey(oldKeyIndex); + final int keyCode = oldKey.codes[0]; + } + if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) { + MyKeyBoard.Key newKey = keys[mCurrentKeyIndex]; + newKey.onPressed(); + invalidateKey(mCurrentKeyIndex); + final int keyCode = newKey.codes[0]; + } + } + // If key changed and preview is on ... + if (oldKeyIndex != mCurrentKeyIndex && mShowPreview) { + mHandler.removeMessages(MSG_SHOW_PREVIEW); + if (previewPopup.isShowing()) { + if (keyIndex == NOT_A_KEY) { + mHandler.sendMessageDelayed(mHandler + .obtainMessage(MSG_REMOVE_PREVIEW), + DELAY_AFTER_PREVIEW); + } + } + if (keyIndex != NOT_A_KEY) { + if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { + // Show right away, if it's already visible and finger is moving around + showKey(keyIndex); + } else { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), + DELAY_BEFORE_PREVIEW); + } + } + } + } + + private void showKey(final int keyIndex) { + final PopupWindow previewPopup = mPreviewPopup; + final MyKeyBoard.Key[] keys = mKeys; + if (keyIndex < 0 || keyIndex >= mKeys.length) return; + MyKeyBoard.Key key = keys[keyIndex]; + if (key.icon != null) { + mPreviewText.setCompoundDrawables(null, null, null, + key.iconPreview != null ? key.iconPreview : key.icon); + mPreviewText.setText(null); + } else { + mPreviewText.setCompoundDrawables(null, null, null, null); + mPreviewText.setText(getPreviewText(key)); + if (key.label!=null && key.label.length() > 1 && key.codes.length < 2) { + mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize); + mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); + } else { + mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge); + mPreviewText.setTypeface(Typeface.DEFAULT); + } + } + mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width + + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight()); + final int popupHeight = mPreviewHeight; + ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams(); + if (lp != null) { + lp.width = popupWidth; + lp.height = popupHeight; + } + if (!mPreviewCentered) { + mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft(); + mPopupPreviewY = key.y - popupHeight + mPreviewOffset; + } else { + // TODO: Fix this if centering is brought back + mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2; + mPopupPreviewY = - mPreviewText.getMeasuredHeight(); + } + mHandler.removeMessages(MSG_REMOVE_PREVIEW); + getLocationInWindow(mCoordinates); + mCoordinates[0] += mMiniKeyboardOffsetX; // Offset may be zero + mCoordinates[1] += mMiniKeyboardOffsetY; // Offset may be zero + + // Set the preview background state + mPreviewText.getBackground().setState( + key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); + mPopupPreviewX += mCoordinates[0]; + mPopupPreviewY += mCoordinates[1]; + + // If the popup cannot be shown above the key, put it on the side + getLocationOnScreen(mCoordinates); + if (mPopupPreviewY + mCoordinates[1] < 0) { + // If the key you're pressing is on the left side of the keyboard, show the popup on + // the right, offset by enough to see at least one key to the left/right. + if (key.x + key.width <= getWidth() / 2) { + mPopupPreviewX += (int) (key.width * 2.5); + } else { + mPopupPreviewX -= (int) (key.width * 2.5); + } + mPopupPreviewY += popupHeight; + } + + if (previewPopup.isShowing()) { + previewPopup.update(mPopupPreviewX, mPopupPreviewY, + popupWidth, popupHeight); + } else { + previewPopup.setWidth(popupWidth); + previewPopup.setHeight(popupHeight); + previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, + mPopupPreviewX, mPopupPreviewY); + } + mPreviewText.setVisibility(VISIBLE); + } + + + public void invalidateAllKeys() { + mDirtyRect.union(0, 0, getWidth(), getHeight()); + mDrawPending = true; + invalidate(); + } + + + public void invalidateKey(int keyIndex) { + if (mKeys == null) return; + if (keyIndex < 0 || keyIndex >= mKeys.length) { + return; + } + final MyKeyBoard.Key key = mKeys[keyIndex]; + mInvalidatedKey = key; + mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(), + key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); + onBufferDraw(); + invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(), + key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); + } + + private boolean openPopupIfRequired(MotionEvent me) { + // Check if we have a popup layout specified first. + if (mPopupLayout == 0) { + return false; + } + if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) { + return false; + } + + MyKeyBoard.Key popupKey = mKeys[mCurrentKey]; + boolean result = onLongPress(popupKey); + if (result) { + mAbortKey = true; + showPreview(NOT_A_KEY); + } + return result; + } + + protected boolean onLongPress(MyKeyBoard.Key popupKey) { + int popupKeyboardId = popupKey.popupResId; + + if (popupKeyboardId != 0) { + mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey); + if (mMiniKeyboardContainer == null) { + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null); + mMiniKeyboard = mMiniKeyboardContainer.findViewById( + R.id.custom_input_view); +// View closeButton = mMiniKeyboardContainer.findViewById( +// R.id.closeButton); +// if (closeButton != null) closeButton.setOnClickListener(this); + mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { + public void onKey(int primaryCode, int[] keyCodes) { + mKeyboardActionListener.onKey(primaryCode, keyCodes); + dismissPopupKeyboard(); + } + + public void onText(CharSequence text) { + mKeyboardActionListener.onText(text); + dismissPopupKeyboard(); + } + + public void swipeLeft() { } + public void swipeRight() { } + public void swipeUp() { } + public void swipeDown() { } + public void onPress(int primaryCode) { + mKeyboardActionListener.onPress(primaryCode); + } + public void onRelease(int primaryCode) { + mKeyboardActionListener.onRelease(primaryCode); + } + }); + //mInputView.setSuggest(mSuggest); + MyKeyBoard keyboard; + if (popupKey.popupCharacters != null) { + keyboard = new MyKeyBoard(getContext(), popupKeyboardId, + popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight()); + } else { + keyboard = new MyKeyBoard(getContext(), popupKeyboardId); + } + mMiniKeyboard.setKeyboard(keyboard); + mMiniKeyboard.setPopupParent(this); + mMiniKeyboardContainer.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); + + mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer); + } else { + mMiniKeyboard = mMiniKeyboardContainer.findViewById( + R.id.custom_input_view); + } + getLocationInWindow(mCoordinates); + mPopupX = popupKey.x + getPaddingLeft(); + mPopupY = popupKey.y + getPaddingTop(); + mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth(); + mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight(); + final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mCoordinates[0]; + final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mCoordinates[1]; + mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y); + mMiniKeyboard.setShifted(isShifted()); + mPopupKeyboard.setContentView(mMiniKeyboardContainer); + mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth()); + mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight()); + mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y); + mMiniKeyboardOnScreen = true; + //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me)); + invalidateAllKeys(); + return true; + } + return false; + } + + + + @Override + public boolean onTouchEvent(MotionEvent me) { + // Convert multi-pointer up/down events to single up/down events to + // deal with the typical multi-pointer behavior of two-thumb typing + final int pointerCount = me.getPointerCount(); + final int action = me.getAction(); + boolean result = false; + final long now = me.getEventTime(); + + if (pointerCount != mOldPointerCount) { + if (pointerCount == 1) { + // Send a down event for the latest pointer + MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, + me.getX(), me.getY(), me.getMetaState()); + result = onModifiedTouchEvent(down, false); + down.recycle(); + // If it's an up action, then deliver the up as well. + if (action == MotionEvent.ACTION_UP) { + result = onModifiedTouchEvent(me, true); + } + } else { + // Send an up event for the last pointer + MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, + mOldPointerX, mOldPointerY, me.getMetaState()); + result = onModifiedTouchEvent(up, true); + up.recycle(); + } + } else { + if (pointerCount == 1) { + result = onModifiedTouchEvent(me, false); + mOldPointerX = me.getX(); + mOldPointerY = me.getY(); + } else { + // Don't do anything when 2 pointers are down and moving. + result = true; + } + } + mOldPointerCount = pointerCount; + + + return result; + } + + private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) { + int touchX = (int) me.getX() - getPaddingLeft(); + int touchY = (int) me.getY() - getPaddingTop(); + if (touchY >= -mVerticalCorrection) + touchY += mVerticalCorrection; + final int action = me.getAction(); + final long eventTime = me.getEventTime(); + int keyIndex = getKeyIndices(touchX, touchY, null); + mPossiblePoly = possiblePoly; + + // Track the last few movements to look for spurious swipes. + if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear(); + mSwipeTracker.addMovement(me); + + // Ignore all motion events until a DOWN. + if (mAbortKey + && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) { + mRepeatKeyIndex = NOT_A_KEY; + return true; + } + + if (mGestureDetector.onTouchEvent(me)) { + showPreview(NOT_A_KEY); + mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); + return true; + } + + // Needs to be called after the gesture detector gets a turn, as it may have + // displayed the mini keyboard + if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) { + mRepeatKeyIndex = NOT_A_KEY; + return true; + } + + switch (action) { + case MotionEvent.ACTION_DOWN: + mAbortKey = false; + mStartX = touchX; + mStartY = touchY; + mLastCodeX = touchX; + mLastCodeY = touchY; + mLastKeyTime = 0; + mCurrentKeyTime = 0; + mLastKey = NOT_A_KEY; + mCurrentKey = keyIndex; + mDownKey = keyIndex; + mDownTime = me.getEventTime(); + mLastMoveTime = mDownTime; + checkMultiTap(eventTime, keyIndex); + mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? + mKeys[keyIndex].codes[0] : 0); + if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { + mRepeatKeyIndex = mCurrentKey; + Message msg = mHandler.obtainMessage(MSG_REPEAT); + mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY); + repeatKey(); + // Delivering the key could have caused an abort + if (mAbortKey) { + mRepeatKeyIndex = NOT_A_KEY; + break; + } + } + if (mCurrentKey != NOT_A_KEY) { + Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); + mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); + } + showPreview(keyIndex); + break; + + case MotionEvent.ACTION_MOVE: + boolean continueLongPress = false; + if (keyIndex != NOT_A_KEY) { + if (mCurrentKey == NOT_A_KEY) { + mCurrentKey = keyIndex; + mCurrentKeyTime = eventTime - mDownTime; + } else { + if (keyIndex == mCurrentKey) { + mCurrentKeyTime += eventTime - mLastMoveTime; + continueLongPress = true; + } else if (mRepeatKeyIndex == NOT_A_KEY) { + resetMultiTap(); + mLastKey = mCurrentKey; + mLastCodeX = mLastX; + mLastCodeY = mLastY; + mLastKeyTime = + mCurrentKeyTime + eventTime - mLastMoveTime; + mCurrentKey = keyIndex; + mCurrentKeyTime = 0; + } + } + } + if (!continueLongPress) { + // Cancel old longpress + mHandler.removeMessages(MSG_LONGPRESS); + // Start new longpress if key has changed + if (keyIndex != NOT_A_KEY) { + Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); + mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); + } + } + showPreview(mCurrentKey); + mLastMoveTime = eventTime; + break; + + case MotionEvent.ACTION_UP: + removeMessages(); + if (keyIndex == mCurrentKey) { + mCurrentKeyTime += eventTime - mLastMoveTime; + } else { + resetMultiTap(); + mLastKey = mCurrentKey; + mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime; + mCurrentKey = keyIndex; + mCurrentKeyTime = 0; + } + if (mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME + && mLastKey != NOT_A_KEY) { + mCurrentKey = mLastKey; + touchX = mLastCodeX; + touchY = mLastCodeY; + } + showPreview(NOT_A_KEY); + Arrays.fill(mKeyIndices, NOT_A_KEY); + // If we're not on a repeating key (which sends on a DOWN event) + if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) { + detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); + } + invalidateKey(keyIndex); + mRepeatKeyIndex = NOT_A_KEY; + break; + case MotionEvent.ACTION_CANCEL: + removeMessages(); + dismissPopupKeyboard(); + mAbortKey = true; + showPreview(NOT_A_KEY); + invalidateKey(mCurrentKey); + break; + } + mLastX = touchX; + mLastY = touchY; + return true; + } + + private boolean repeatKey() { + if(mRepeatKeyIndex != NOT_A_KEY){ + MyKeyBoard.Key key = mKeys[mRepeatKeyIndex]; + detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime); + return true; + } + return false; + } + + protected void swipeRight() { + mKeyboardActionListener.swipeRight(); + } + + protected void swipeLeft() { + mKeyboardActionListener.swipeLeft(); + } + + protected void swipeUp() { + mKeyboardActionListener.swipeUp(); + } + + protected void swipeDown() { + mKeyboardActionListener.swipeDown(); + } + + public void closing() { + if (mPreviewPopup.isShowing()) { + mPreviewPopup.dismiss(); + } + removeMessages(); + + dismissPopupKeyboard(); + mBuffer = null; + mCanvas = null; + mMiniKeyboardCache.clear(); + } + + private void removeMessages() { + if (mHandler != null) { + mHandler.removeMessages(MSG_REPEAT); + mHandler.removeMessages(MSG_LONGPRESS); + mHandler.removeMessages(MSG_SHOW_PREVIEW); + } + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + closing(); + } + + private void dismissPopupKeyboard() { + if (mPopupKeyboard.isShowing()) { + mPopupKeyboard.dismiss(); + mMiniKeyboardOnScreen = false; + invalidateAllKeys(); + } + } + + public boolean handleBack() { + if (mPopupKeyboard.isShowing()) { + dismissPopupKeyboard(); + return true; + } + return false; + } + + private void resetMultiTap() { + mLastSentIndex = NOT_A_KEY; + mTapCount = 0; + mLastTapTime = -1; + mInMultiTap = false; + } + + private void checkMultiTap(long eventTime, int keyIndex) { + if (keyIndex == NOT_A_KEY) return; + MyKeyBoard.Key key = mKeys[keyIndex]; + if (key.codes.length > 1) { + mInMultiTap = true; + if (eventTime < mLastTapTime + MULTITAP_INTERVAL + && keyIndex == mLastSentIndex) { + mTapCount = (mTapCount + 1) % key.codes.length; + return; + } else { + mTapCount = -1; + return; + } + } + if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) { + resetMultiTap(); + } + } + + private static class SwipeTracker { + + static final int NUM_PAST = 4; + static final int LONGEST_PAST_TIME = 200; + + final float mPastX[] = new float[NUM_PAST]; + final float mPastY[] = new float[NUM_PAST]; + final long mPastTime[] = new long[NUM_PAST]; + + float mYVelocity; + float mXVelocity; + + public void clear() { + mPastTime[0] = 0; + } + + public void addMovement(MotionEvent ev) { + long time = ev.getEventTime(); + final int N = ev.getHistorySize(); + for (int i=0; i= 0) { + final int start = drop+1; + final int count = NUM_PAST-drop-1; + System.arraycopy(pastX, start, pastX, 0, count); + System.arraycopy(pastY, start, pastY, 0, count); + System.arraycopy(pastTime, start, pastTime, 0, count); + i -= (drop+1); + } + pastX[i] = x; + pastY[i] = y; + pastTime[i] = time; + i++; + if (i < NUM_PAST) { + pastTime[i] = 0; + } + } + + public void computeCurrentVelocity(int units) { + computeCurrentVelocity(units, Float.MAX_VALUE); + } + + public void computeCurrentVelocity(int units, float maxVelocity) { + final float[] pastX = mPastX; + final float[] pastY = mPastY; + final long[] pastTime = mPastTime; + + final float oldestX = pastX[0]; + final float oldestY = pastY[0]; + final long oldestTime = pastTime[0]; + float accumX = 0; + float accumY = 0; + int N=0; + while (N < NUM_PAST) { + if (pastTime[N] == 0) { + break; + } + N++; + } + + for (int i=1; i < N; i++) { + final int dur = (int)(pastTime[i] - oldestTime); + if (dur == 0) continue; + float dist = pastX[i] - oldestX; + float vel = (dist/dur) * units; // pixels/frame. + if (accumX == 0) accumX = vel; + else accumX = (accumX + vel) * .5f; + + dist = pastY[i] - oldestY; + vel = (dist/dur) * units; // pixels/frame. + if (accumY == 0) accumY = vel; + else accumY = (accumY + vel) * .5f; + } + mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) + : Math.min(accumX, maxVelocity); + mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) + : Math.min(accumY, maxVelocity); + } + + public float getXVelocity() { + return mXVelocity; + } + + public float getYVelocity() { + return mYVelocity; + } + } +} + diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DesFile.java b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DesFile.java new file mode 100644 index 0000000..2c730e7 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DesFile.java @@ -0,0 +1,120 @@ +package com.input.board.appskey.keyboard.fileTools; + +import com.input.board.appskey.keyboard.KeyBoardApp; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class DesFile { + private static String mAlgorithm = "AES"; + + private static String transFormation = mAlgorithm + "/CBC/PKCS5Padding"; + private static IvParameterSpec mIv = new IvParameterSpec("109jksue6yw12rfd".getBytes()); + + private static Key getFileKey() { + return new SecretKeySpec("keyli123879dfgft".getBytes(), mAlgorithm); + } + + + private static byte[] DECRYPT(byte[] byteData) { + Cipher mCipher = null; + try { + mCipher = Cipher.getInstance(transFormation); + mCipher.init(Cipher.DECRYPT_MODE, getFileKey(), mIv); + return mCipher.doFinal(byteData); + } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | + IllegalBlockSizeException | BadPaddingException | + InvalidAlgorithmParameterException exception) { + return null; + } + + } + + public static String deFile(InputStream inputStream) { + ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); + int byteRead = 0; + byte[] bytesBuffer = new byte[4096]; + try { + while ((byteRead = inputStream.read(bytesBuffer)) != -1) { + arrayOutputStream.write(bytesBuffer, 0, byteRead); + } + byte[] bytesContent = arrayOutputStream.toByteArray(); + + String deFilePath = KeyBoardApp.appInstance.getCacheDir() + "/decrypt.json"; + File fileDec = new File(deFilePath); + judeFileExist(fileDec); + FileOutputStream fileOutputStream = new FileOutputStream(fileDec); + byte[] decryptContent = DECRYPT(bytesContent); + fileOutputStream.write(decryptContent); + fileOutputStream.close(); + + + + return deFilePath; + + } catch (Exception exception) { + + return ""; + } + + } + + + + private static void judeFileExist(File file) { + try { + if(file.exists()){ + file.delete(); + } + if (!file.exists()) { + file.createNewFile(); + } + + } catch (Exception ex) { + + } + } + + public static String getJsonString(String path) { + try { + FileInputStream fileInputStream = new FileInputStream(path); + char[] charArray = new char[fileInputStream.available()]; + int readCount = 0; + InputStreamReader streamReader = new InputStreamReader(fileInputStream); + BufferedReader bufferedReader = new BufferedReader(streamReader); + StringWriter stringWriter = new StringWriter(); + + while ((readCount = bufferedReader.read(charArray)) != -1) { + stringWriter.write(charArray, 0, readCount); + } + String s = stringWriter.toString(); + + + return s; + + } catch (IOException e) { + + return ""; + } + + } + +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DownloadZip.java b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DownloadZip.java new file mode 100644 index 0000000..5064b4a --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/DownloadZip.java @@ -0,0 +1,174 @@ +package com.input.board.appskey.keyboard.fileTools; + +import com.input.board.appskey.keyboard.KeyBoardApp; +import com.input.board.appskey.keyboard.interf.onApplyCallback; + +import net.sf.sevenzipjbinding.ArchiveFormat; +import net.sf.sevenzipjbinding.IArchiveOpenCallback; +import net.sf.sevenzipjbinding.IInArchive; +import net.sf.sevenzipjbinding.SevenZip; +import net.sf.sevenzipjbinding.SevenZipException; +import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; +import net.sf.sevenzipjbinding.impl.RandomAccessFileOutStream; +import net.sf.sevenzipjbinding.simple.ISimpleInArchive; +import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class DownloadZip { + + public static void startDownloadZip(String zipPath, onApplyCallback callback) { + OkHttpClient clientZip = new OkHttpClient().newBuilder(). + connectTimeout(20, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS).build(); + Request.Builder builder = new Request.Builder(); + Request request = builder.get().url(zipPath).build(); + + clientZip.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callback.onApplyResult(null); + } + + @Override + public void onResponse(Call call, Response response) { + + InputStream inputStream = response.body().byteStream(); + long l = response.body().contentLength(); + MediaType mediaType = response.body().contentType(); + + saveZipFile(inputStream, getServiceZipName(zipPath),callback); + + } + }); + } + + + private static String getServiceZipName(String zipPath) { + String pointStr = "/"; + int lastIndexOf = zipPath.lastIndexOf(pointStr); + String zipName = zipPath.substring(lastIndexOf + pointStr.length()); + + + return zipName; + } + + private static String getunZipFolderName(String zipPath) { + String pointStr = "."; + int lastIndexOf = zipPath.lastIndexOf(pointStr); + String zipName = zipPath.substring(0, lastIndexOf); + + + return zipName; + } + + private static void saveZipFile(InputStream inputStream, String zipFileName,onApplyCallback callback) { + File zipfFile = new File(KeyBoardApp.appInstance.getFilesDir(), zipFileName); + byte[] bytes = new byte[4096]; + int readLength = 0; + InputStream is = inputStream; + FileOutputStream fileOs = null; + try { + fileOs = new FileOutputStream(zipfFile); + + while ((readLength = is.read(bytes)) != -1) { + fileOs.write(bytes, 0, readLength); + } + fileOs.flush(); + + } catch (Exception exception) { + + } finally { + try { + if (is != null) { + is.close(); + } + if (fileOs != null) { + fileOs.close(); + } + } catch (IOException ioException) { + + } + un7ZZipFile(zipfFile,callback); + } + + } + + private static void un7ZZipFile(File saveZipFile,onApplyCallback callback) { + List fileList = new ArrayList<>(); + String folderName = getunZipFolderName(saveZipFile.getName()); + String replace = folderName.replace(".", ""); + String unzipFolderPath = KeyBoardApp.appInstance.getFilesDir().getPath()+"/"+replace; + try { + RandomAccessFileInStream inStream = new RandomAccessFileInStream(new RandomAccessFile(saveZipFile, "r")); + IInArchive open = SevenZip.openInArchive(ArchiveFormat.SEVEN_ZIP, inStream, new IArchiveOpenCallback() { + @Override + public void setTotal(Long files, Long bytes) { + + } + + @Override + public void setCompleted(Long files, Long bytes) { + + } + }); + + + ISimpleInArchive simple = open.getSimpleInterface(); + for (ISimpleInArchiveItem archiveItem : simple.getArchiveItems()) { + RandomAccessFileOutStream outStream = null; + try { + File itemFile; + if (archiveItem.isFolder()) { + File itemFolder = new File(unzipFolderPath, archiveItem.getPath()); + boolean mkdirs = itemFolder.mkdirs(); + continue; + } else { + itemFile = new File(unzipFolderPath, archiveItem.getPath()); + if (!itemFile.getParentFile().exists()) { + boolean mkdirs = itemFile.getParentFile().mkdirs(); + } + } + outStream = new RandomAccessFileOutStream(new RandomAccessFile(itemFile, "rw")); + archiveItem.extractSlow(outStream); + fileList.add(itemFile); + } finally { + if (outStream != null) { + outStream.close(); + } + } + } + + + + inStream.close(); + open.close(); + + } catch (FileNotFoundException | SevenZipException exception) { + + } catch (IOException ioException) { + + }finally { + callback.onApplyResult(fileList); + } + } + + + +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/InputMethodCom.java b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/InputMethodCom.java new file mode 100644 index 0000000..de185a8 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/InputMethodCom.java @@ -0,0 +1,31 @@ +package com.input.board.appskey.keyboard.fileTools; + +import android.content.Context; +import android.provider.Settings; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; + +import com.input.board.appskey.keyboard.KeyBoardApp; + +public class InputMethodCom { + private static Object systemService = KeyBoardApp.appInstance.getSystemService(Context.INPUT_METHOD_SERVICE); + private static InputMethodManager inputMethodManager = (InputMethodManager) systemService; + public static boolean checkSetDefault(Context con){ + String defaultId = Settings.Secure.getString(con.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + if(defaultId != null && defaultId.startsWith(con.getPackageName())){ + return true; + }else { + return false; + } + } + + public static boolean checkEnable(Context con){ + for (InputMethodInfo methodInfo : inputMethodManager.getEnabledInputMethodList()) { + if(methodInfo.getId().startsWith(con.getPackageName())){ + return true; + } + } + return false; + + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/SpSkin.kt b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/SpSkin.kt new file mode 100644 index 0000000..01e57f6 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/SpSkin.kt @@ -0,0 +1,19 @@ +package com.input.board.appskey.keyboard.fileTools + +import android.content.Context +import com.input.board.appskey.keyboard.KeyBoardApp + +object SpSkin { + + val SP_NAME = "keyboard_skin" + val SKIN_PATH = "skin_path" + val spSkin = KeyBoardApp.appInstance.getSharedPreferences(SP_NAME,Context.MODE_PRIVATE) + + fun updateSkinPath(skinPath:String){ + spSkin.edit().putString(SKIN_PATH,skinPath).apply() + } + + fun getSkinPath( ):String?{ + return spSkin.getString(SKIN_PATH,null) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/StatusBarSettings.java b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/StatusBarSettings.java new file mode 100644 index 0000000..21d9288 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/StatusBarSettings.java @@ -0,0 +1,11 @@ +package com.input.board.appskey.keyboard.fileTools; + +import android.app.Activity; +import android.view.View; + +public class StatusBarSettings { + + public void setStatusBarTextColor(Activity activity){ + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/fileTools/UnzipOutStream.java b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/UnzipOutStream.java new file mode 100644 index 0000000..5c79e1b --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/fileTools/UnzipOutStream.java @@ -0,0 +1,16 @@ +package com.input.board.appskey.keyboard.fileTools; + +import net.sf.sevenzipjbinding.ISequentialOutStream; +import net.sf.sevenzipjbinding.SevenZipException; + +public class UnzipOutStream implements ISequentialOutStream { + @Override + public int write(byte[] data) throws SevenZipException { + if (data == null) { + if(data.length == 0){ + throw new SevenZipException("null data"); + } + } + return data.length; + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/interf/onAdShowCallback.kt b/app/src/main/java/com/input/board/appskey/keyboard/interf/onAdShowCallback.kt new file mode 100644 index 0000000..dbb8442 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/interf/onAdShowCallback.kt @@ -0,0 +1,8 @@ +package com.input.board.appskey.keyboard.interf + +interface onAdShowCallback { + + fun displayed( ) + fun displayFail( ) + fun hidden( ) +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/interf/onApplyCallback.kt b/app/src/main/java/com/input/board/appskey/keyboard/interf/onApplyCallback.kt new file mode 100644 index 0000000..37e6fcd --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/interf/onApplyCallback.kt @@ -0,0 +1,8 @@ +package com.input.board.appskey.keyboard.interf + +import java.io.File + +interface onApplyCallback { + + fun onApplyResult(fileList: List?) +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/interf/onClickCallback.kt b/app/src/main/java/com/input/board/appskey/keyboard/interf/onClickCallback.kt new file mode 100644 index 0000000..ccb069c --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/interf/onClickCallback.kt @@ -0,0 +1,6 @@ +package com.input.board.appskey.keyboard.interf + +interface onClickCallback { + + fun onClickCall( ) +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/ChildBean.java b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/ChildBean.java new file mode 100644 index 0000000..0ede442 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/ChildBean.java @@ -0,0 +1,57 @@ +package com.input.board.appskey.keyboard.jsonBean; + +public class ChildBean { + private String titleName; + + private String thumbUrl; + + private String thumbGif; + + private String zipPath; + + private String imgPath; + private String imgGif; + + + public void setImgGif(String imgGif) { + this.imgGif = imgGif; + } + + public String getTitleName(){ + + return titleName; + } + + public String getThumbUrl(){ + + return thumbUrl; + } + public String getThumbGif(){ + + return thumbGif; + } + public String getZipPath(){ + + return zipPath; + } + public String getImgPath(){ + + return imgPath; + } + + public void setTitleName(String name){ + this.titleName = name; + } + public void setThumbUrl(String thumbUrl){ + this.thumbUrl = thumbUrl; + } + public void setThumbGif(String thumbGif){ + this.thumbGif = thumbGif; + } + public void setZipPath(String path){this.zipPath = path;} + public void setImgPath(String imgPath){this.imgPath = imgPath;} + + public String getImgGif() { + return imgGif; + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/InfoBean.java b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/InfoBean.java new file mode 100644 index 0000000..1d2f8cc --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/InfoBean.java @@ -0,0 +1,34 @@ +package com.input.board.appskey.keyboard.jsonBean; + +public class InfoBean { + private String titleName; + + private String thumbUrl; + + private String thumbGif; + + public String getTitleName(){ + + return titleName; + } + + public String getThumbUrl(){ + + return thumbUrl; + } + public String getThumbGif(){ + + return thumbUrl; + } + + + public void setTitleName(String name){ + this.titleName = name; + } + public void setThumbUrl(String name){ + this.thumbUrl = name; + } + public void setThumbGif(String name){ + this.thumbGif = name; + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/MainBean.java b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/MainBean.java new file mode 100644 index 0000000..af7c269 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/jsonBean/MainBean.java @@ -0,0 +1,27 @@ +package com.input.board.appskey.keyboard.jsonBean; + +import java.util.List; + +public class MainBean { + private String parentName; + + private List keyboardList; + + public String getParentName() { + + return parentName; + } + + public List getKeyboardList() { + + return keyboardList; + } + + public void setParentName(String name) { + this.parentName = name; + } + + public void setKeyboardList(List keyboardList) { + this.keyboardList = keyboardList; + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ForYouAdapter.java b/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ForYouAdapter.java new file mode 100644 index 0000000..0315040 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ForYouAdapter.java @@ -0,0 +1,130 @@ +package com.input.board.appskey.keyboard.recyAdapter; + + +import static com.bumptech.glide.integration.webp.decoder.WebpDrawable.LOOP_FOREVER; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.RecyclerView; + +import com.input.board.appskey.keyboard.R; +import com.input.board.appskey.keyboard.interf.onClickCallback; +import com.input.board.appskey.keyboard.jsonBean.ChildBean; +import com.input.board.appskey.keyboard.viewUi.ApplyActivity; +import com.bumptech.glide.Glide; +import com.bumptech.glide.integration.webp.decoder.WebpDrawable; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.Target; + +import java.util.List; + +public class ForYouAdapter extends RecyclerView.Adapter { + + private Context mContext; + private List mList; + + private onClickCallback mCallBack; + + public ForYouAdapter(Context context) { + mContext = context; + } + + public void setForYouList(List list) { + this.mList = list; + notifyDataSetChanged(); + } + + + public void setClickAction(onClickCallback callback){ + mCallBack = callback; + } + + @NonNull + @Override + public ForYouViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + + View view = LayoutInflater.from(mContext).inflate(R.layout.item_for_you_layout, parent, false); + return new ForYouViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ForYouViewHolder holder, int position) { + ChildBean childBean = mList.get(position); + String thumbGif = childBean.getThumbGif(); + String thumb = childBean.getThumbUrl(); + if(!thumbGif.isEmpty()){ + loadWepJif(thumbGif,holder.itemImg); + }else { + Glide.with(mContext).load(thumb).into(holder.itemImg); + } + + holder.cardView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intentApply = new Intent(mContext, ApplyActivity.class); + intentApply.putExtra(ApplyActivity.DISPLAY_URL_KEY,childBean.getImgPath()); + intentApply.putExtra(ApplyActivity.ZIP_URL_KEY,childBean.getZipPath()); + intentApply.putExtra(ApplyActivity.NAME_KEY,childBean.getTitleName()); + intentApply.putExtra(ApplyActivity.GIF_KEY,childBean.getImgGif()); + mContext.startActivity(intentApply); + if(mCallBack != null){ + mCallBack.onClickCall(); + } + + } + }); + + + } + + @Override + public int getItemCount() { + return mList.size(); + } + + public static class ForYouViewHolder extends RecyclerView.ViewHolder { + + private CardView cardView; + private ImageView itemImg; + + public ForYouViewHolder(@NonNull View itemView) { + super(itemView); + cardView = itemView.findViewById(R.id.card_view); + itemImg = itemView.findViewById(R.id.item_image); + + } + + } + + @SuppressLint("CheckResult") + private void loadWepJif(String webpGifUrl,ImageView view){ + Glide.with(mContext).load(webpGifUrl).addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + if(resource instanceof WebpDrawable){ + WebpDrawable webpDrawable = (WebpDrawable) resource; + webpDrawable.setLoopCount(LOOP_FOREVER); + } + + return false; + } + }).into(view); + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ViewAllTabAdapter.java b/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ViewAllTabAdapter.java new file mode 100644 index 0000000..8c055b1 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/recyAdapter/ViewAllTabAdapter.java @@ -0,0 +1,109 @@ +package com.input.board.appskey.keyboard.recyAdapter; + +import android.content.Context; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.input.board.appskey.keyboard.R; +import com.input.board.appskey.keyboard.jsonBean.MainBean; + +import java.util.List; + +public class ViewAllTabAdapter extends RecyclerView.Adapter { + + private Context mContext; + private List viewAllList; + + private OnChangListener mOnChangListener; + + private int selectedPos = 0; + + private int oldSelectedPos = -1; + + public ViewAllTabAdapter(Context context) { + mContext = context; + } + + public void setSelectedPos(int selectedPosition) { + oldSelectedPos = selectedPos; + selectedPos = selectedPosition; + notifyItemChanged(oldSelectedPos); + notifyItemChanged(selectedPos); + } + + + public void setChangeListener(OnChangListener onChangListener){ + mOnChangListener = onChangListener; + } + + + @NonNull + @Override + public ViewAllTabAdapter.ViewAllTabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(mContext).inflate(R.layout.item_view_all_tab, parent, false); + return new ViewAllTabAdapter.ViewAllTabViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewAllTabAdapter.ViewAllTabViewHolder holder, int pos) { + MainBean mainBean = viewAllList.get(pos); + + holder.tabTitle.setText(mainBean.getParentName().toUpperCase()); + if (pos == selectedPos) { + holder.tabTitle.setTextColor(mContext.getColor(R.color.white)); + holder.tabTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP,15); + holder.tabIndicator.setVisibility(View.VISIBLE); + } else { + holder.tabTitle.setTextColor(mContext.getColor(R.color.view_all_text_color)); + holder.tabTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP,13); + holder.tabIndicator.setVisibility(View.GONE); + } + holder.itemLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + oldSelectedPos = selectedPos; + selectedPos = holder.getAdapterPosition(); + + notifyItemChanged(oldSelectedPos); + notifyItemChanged(selectedPos); + mOnChangListener.onChange(selectedPos); + } + }); + } + + @Override + public int getItemCount() { + return viewAllList.size(); + } + public void tabTitleName(List list) { + viewAllList = list; + notifyDataSetChanged(); + } + + static class ViewAllTabViewHolder extends RecyclerView.ViewHolder { + + + private TextView tabTitle; + private LinearLayout tabIndicator; + private RelativeLayout itemLayout; + + public ViewAllTabViewHolder(@NonNull View itemView) { + super(itemView); + tabTitle = itemView.findViewById(R.id.text_tab_view); + tabIndicator = itemView.findViewById(R.id.layout_indicator); + itemLayout = itemView.findViewById(R.id.item_layout); + } + } + + public interface OnChangListener{ + void onChange(int posit); + } +} diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ApplyActivity.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ApplyActivity.kt new file mode 100644 index 0000000..d202f31 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ApplyActivity.kt @@ -0,0 +1,246 @@ +package com.input.board.appskey.keyboard.viewUi + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.applovin.mediation.ads.MaxInterstitialAd +import com.input.board.appskey.keyboard.KeyBoardApp +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.fileTools.DownloadZip +import com.input.board.appskey.keyboard.fileTools.InputMethodCom +import com.input.board.appskey.keyboard.fileTools.SpSkin +import com.input.board.appskey.keyboard.interf.onApplyCallback +import com.input.board.appskey.keyboard.interf.onClickCallback +import com.input.board.appskey.keyboard.recyAdapter.ForYouAdapter +import com.bumptech.glide.Glide +import com.bumptech.glide.integration.webp.decoder.WebpDrawable +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import com.input.board.appskey.keyboard.customer.IntersitialAd +import com.input.board.appskey.keyboard.interf.onAdShowCallback +import java.io.File + +class ApplyActivity : AppCompatActivity() { + companion object { + @JvmField + var DISPLAY_URL_KEY: String = "display_url_key" + + @JvmField + val ZIP_URL_KEY = "zip_url_key" + + + @JvmField + val NAME_KEY = "name_key" + + @JvmField + val GIF_KEY = "gif_key" + } + + private lateinit var displayUrl: String + private lateinit var gifUrl: String + private lateinit var zipUrl: String + private lateinit var name: String + private lateinit var applyBtn: LinearLayout + private lateinit var imgData: ImageView + private lateinit var imgBack: ImageView + private lateinit var textName: TextView + private lateinit var interAd:List + private lateinit var recommendedRecycler: RecyclerView + private lateinit var viewAllLayout: LinearLayout + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_apply) + showEnterAd() + findViewId() + getExtraData() + displayData() + setApply() + onClick() + } + + + private fun showEnterAd(){ + interAd = IntersitialAd.loadInterAd(this).also { + IntersitialAd.showInterAd(it)?.let { cacheAd -> + cacheAd.showAd() + IntersitialAd.setAdCallback(cacheAd, object : onAdShowCallback { + override fun displayed() { + interAd = IntersitialAd.loadInterAd(this@ApplyActivity) + } + + override fun displayFail() { + + } + + override fun hidden() { + + } + }) + } + } + } + private fun getExtraData() { + displayUrl = intent.getStringExtra(DISPLAY_URL_KEY).toString() + zipUrl = intent.getStringExtra(ZIP_URL_KEY).toString() + name = intent.getStringExtra(NAME_KEY).toString() + gifUrl = intent.getStringExtra(GIF_KEY).toString() + + + } + + private fun findViewId() { + applyBtn = findViewById(R.id.layoutDownloadApply) + imgData = findViewById(R.id.image_data) + imgBack = findViewById(R.id.back) + textName = findViewById(R.id.textview_data_name) + recommendedRecycler = findViewById(R.id.recommended_recycler) + viewAllLayout = findViewById(R.id.layout_view_all) + } + + private fun displayData() { + textName.text = name + if (gifUrl.isNotEmpty()) { + loadImgGif() + } else { + Glide.with(this).load(displayUrl).into(imgData) + } + } + + + private fun onClick() { + imgBack.setOnClickListener { + finish() + } + viewAllLayout.setOnClickListener { + startActivity(Intent(this, ViewAllActivity::class.java)) + finish() + } + + val forYouList = KeyBoardApp.list.filter { + it.parentName == "for you" + + } + + recommendedRecycler.run { + adapter = ForYouAdapter(this@ApplyActivity).apply { + val shuffled = forYouList[0].keyboardList.shuffled() + setForYouList(shuffled) + }.apply { + setClickAction(object : onClickCallback { + override fun onClickCall() { + finish() + } + + }) + } + + layoutManager = GridLayoutManager(this@ApplyActivity, 2) + + } + + } + + @SuppressLint("CheckResult") + private fun loadImgGif() { + Glide.with(this).load(gifUrl).addListener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + + return false + } + + override fun onResourceReady( + resource: Drawable, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + if (resource is WebpDrawable) { + resource.loopCount = WebpDrawable.LOOP_FOREVER + } + + return false + } + + }).into(imgData) + } + + private fun setApply() { + applyBtn.setOnClickListener { + val checkEnable = InputMethodCom.checkEnable(this) + val checkSetDefault = InputMethodCom.checkSetDefault(this) + if(!checkEnable || !checkSetDefault){ + Toast.makeText(this@ApplyActivity,getString(R.string.text_promt),Toast.LENGTH_SHORT).show() + startActivity(Intent(this,KeyBoardSettingActivity::class.java)) + return@setOnClickListener + } + startDown() + + } + } + + private fun startDown(){ + applyBtn.isEnabled = false + DownloadZip.startDownloadZip(zipUrl, object : onApplyCallback { + override fun onApplyResult(fileList: List?) { + runOnUiThread { + applyBtn.isEnabled = true + } + if(fileList.isNullOrEmpty()){ + runOnUiThread { + Toast.makeText(this@ApplyActivity,getString(R.string.download_fail),Toast.LENGTH_SHORT).show() + } + }else{ + fileList[0]?.path?.let { + val skinParentPath = it.substringBeforeLast("res") + SpSkin.updateSkinPath(skinParentPath) + runOnUiThread{ + showInterAd() + } + + } + } + + + } + + }) + } + private fun showInterAd(){ + val iAd = IntersitialAd.showInterAd(interAd) + if(iAd != null){ + iAd.showAd() + IntersitialAd.setAdCallback(iAd,object :onAdShowCallback{ + override fun displayed() { + } + + override fun displayFail() { + } + + override fun hidden() { + Toast.makeText(this@ApplyActivity,getString(R.string.theme_application_successful),Toast.LENGTH_SHORT).show() + interAd = IntersitialAd.loadInterAd(this@ApplyActivity) + } + + }) + }else{ + Toast.makeText(this@ApplyActivity,getString(R.string.theme_application_successful),Toast.LENGTH_SHORT).show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/FragmentContent.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/FragmentContent.kt new file mode 100644 index 0000000..cfdb7d1 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/FragmentContent.kt @@ -0,0 +1,59 @@ +package com.input.board.appskey.keyboard.viewUi + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.recyAdapter.ForYouAdapter + +private const val ARG_PARAM1 = "list_index" + +class FragmentContent : Fragment() { + + private var paramIndex: Int = 0 + private lateinit var contentRecy:RecyclerView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + paramIndex = it.getInt(ARG_PARAM1) + } + } + companion object { + + @JvmStatic + fun newInstance(index: Int) = + FragmentContent().apply { + arguments = Bundle().apply { + putInt(ARG_PARAM1, index) + } + } + } + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val viewMain = inflater.inflate(R.layout.fragment_content, container, false) + contentRecy = viewMain.findViewById(R.id.content_recycler) + initContent() + return viewMain + } + + private fun initContent(){ + val viewAllActivity = requireActivity() as ViewAllActivity + val mainBean = viewAllActivity.viewAllList[paramIndex] + contentRecy.run { + this.adapter = ForYouAdapter(requireContext()).apply { + val shufflList = mainBean.keyboardList.shuffled() + setForYouList(shufflList) + } + this.layoutManager = GridLayoutManager(requireContext(),2) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/HomeActivity.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/HomeActivity.kt new file mode 100644 index 0000000..c0893c0 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/HomeActivity.kt @@ -0,0 +1,97 @@ +package com.input.board.appskey.keyboard.viewUi + +import android.annotation.SuppressLint +import android.content.Intent +import android.net.Uri +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.cardview.widget.CardView +import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.input.board.appskey.keyboard.KeyBoardApp +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.recyAdapter.ForYouAdapter + +class HomeActivity : AppCompatActivity() { + + private lateinit var forYouRecycler: RecyclerView + private lateinit var viewAllLayout: LinearLayout + + private lateinit var menuView: ImageView + private lateinit var drawView: DrawerLayout + + private lateinit var keySetting: LinearLayout + private lateinit var layoutRate: LinearLayout + private lateinit var layoutPrivacy: LinearLayout + + private lateinit var cardView:CardView + + @SuppressLint("MissingInflatedId") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_home) + findViewId() + setForYouData() + onClick() + } + + private fun findViewId() { + forYouRecycler = findViewById(R.id.for_you_recycler) + viewAllLayout = findViewById(R.id.layout_view_all) + menuView = findViewById(R.id.imageMenu) + drawView = findViewById(R.id.drawLayout) + keySetting = findViewById(R.id.layout_keyboard_setting) + layoutRate = findViewById(R.id.layout_rate) + layoutPrivacy = findViewById(R.id.layout_privacy) + cardView = findViewById(R.id.card_banner) + } + + + private fun onClick() { + cardView.setOnClickListener { + startActivity(Intent(this, ViewAllActivity::class.java)) + } + viewAllLayout.setOnClickListener { + startActivity(Intent(this, ViewAllActivity::class.java)) + } + menuView.setOnClickListener { + drawView.openDrawer(GravityCompat.END) + } + keySetting.setOnClickListener { + startActivity(Intent(this, KeyBoardSettingActivity::class.java)) + } + layoutRate.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(getString(R.string.app_google_play)+packageName) + startActivity(intent) + } + layoutPrivacy.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(getString(R.string.app_privacy_path)) + startActivity(intent) + } + + } + + private fun setForYouData() { + val forYouList = KeyBoardApp.list.filter { + it.parentName == "for you" + + } + forYouRecycler.run { + adapter = ForYouAdapter(this@HomeActivity).apply { + val keyboardList = forYouList[0].keyboardList.shuffled() + setForYouList(keyboardList) + } + layoutManager = GridLayoutManager(this@HomeActivity, 2) + + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/KeyBoardSettingActivity.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/KeyBoardSettingActivity.kt new file mode 100644 index 0000000..a457139 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/KeyBoardSettingActivity.kt @@ -0,0 +1,116 @@ +package com.input.board.appskey.keyboard.viewUi + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.provider.Settings +import android.view.inputmethod.InputMethodManager +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.fileTools.InputMethodCom + +class KeyBoardSettingActivity : AppCompatActivity() { + + private lateinit var layoutStepOne: LinearLayout + private lateinit var imgBack: ImageView + private lateinit var layoutStepTwo: LinearLayout + private lateinit var imgStepOkOne: ImageView + private lateinit var imgStepOkTwo: ImageView + private lateinit var intentFilter:IntentFilter + private var myreceiver:BroadcastReceiver?= null + + private lateinit var stepOne:TextView + private lateinit var stepTwo:TextView + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_key_board_setting) + findViewId() + onViewStep() + getReceiver() + } + + @SuppressLint("SuspiciousIndentation") + private fun findViewId() { + layoutStepOne = findViewById(R.id.linear_step_one) + layoutStepTwo = findViewById(R.id.linear_step_two) + imgBack = findViewById(R.id.back) + imgStepOkOne = findViewById(R.id.ok_one) + imgStepOkTwo = findViewById(R.id.ok_two) + stepOne= findViewById(R.id.text_step_one) + stepTwo= findViewById(R.id.text_step_two) + } + + private fun onViewStep(){ + imgBack.setOnClickListener { + finish() + } + layoutStepOne.setOnClickListener { + startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)) + } + layoutStepTwo.setOnClickListener { + val inputMethodManager = this.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showInputMethodPicker() + } + } + + override fun onResume() { + super.onResume() + updateUI() + } + + private fun getReceiver(){ + myreceiver = object :BroadcastReceiver(){ + override fun onReceive(context: Context?, intent: Intent?) { + updateUI() + } + } + intentFilter = IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED) + + registerReceiver(myreceiver,intentFilter) + } + + private fun updateUI(){ + + val checkEnable = InputMethodCom.checkEnable(this) + val checkSetDefault = InputMethodCom.checkSetDefault(this) + if(checkEnable){ + layoutStepOne.isEnabled =false + layoutStepOne.isSelected = true + imgStepOkOne.isVisible = true + stepOne.setTextColor(getColor(R.color.step_true)) + }else{ + layoutStepOne.isEnabled =true + layoutStepOne.isSelected = false + imgStepOkOne.isVisible = false + stepOne.setTextColor(getColor(R.color.white)) + } + + if(checkSetDefault){ + layoutStepTwo.isEnabled =false + layoutStepTwo.isSelected = true + imgStepOkTwo.isVisible = true + stepTwo.setTextColor(getColor(R.color.step_true)) + }else{ + layoutStepTwo.isEnabled =true + layoutStepTwo.isSelected = false + imgStepOkTwo.isVisible = false + stepTwo.setTextColor(getColor(R.color.white)) + } + + + } + + override fun onDestroy() { + super.onDestroy() + if(myreceiver != null){ + unregisterReceiver(myreceiver) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/NovaActivity.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/NovaActivity.kt new file mode 100644 index 0000000..350861d --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/NovaActivity.kt @@ -0,0 +1,92 @@ +package com.input.board.appskey.keyboard.viewUi + +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.ImageView +import android.widget.ProgressBar +import com.applovin.mediation.MaxAd +import com.applovin.mediation.MaxAdListener +import com.applovin.mediation.MaxError +import com.applovin.mediation.ads.MaxInterstitialAd +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.customer.IntersitialAd +import java.util.Timer +import java.util.TimerTask + +class NovaActivity : AppCompatActivity() ,MaxAdListener{ + + private lateinit var progressBar: ProgressBar + private lateinit var novaIm: ImageView + private var tim = 15 + private lateinit var myTimer: Timer + + private var isShowInterAd = false + + private lateinit var mAds:List + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_nova) + mAds = IntersitialAd.loadInterAd(this) + findView() + myTimer = Timer() + count() + } + + private fun findView() { + novaIm = findViewById(R.id.nova_picture) + progressBar = findViewById(R.id.nova_progress) + } + + private fun count(){ + myTimer.schedule(object :TimerTask(){ + override fun run() { + tim -= 1 + IntersitialAd.showInterAd(mAds)?.let { + isShowInterAd = true + myTimer.cancel() + it.showAd() + it.setListener(this@NovaActivity) + } + if(tim == 0){ + myTimer.cancel() + if(!isShowInterAd){ + toHome() + } + + } + } + + },0L,1000L) + } + + private fun toHome(){ + startActivity(Intent(this,HomeActivity::class.java)) + } + + override fun onAdLoaded(p0: MaxAd) { + + } + + override fun onAdDisplayed(p0: MaxAd) { + IntersitialAd.loadInterAd(this) + } + + override fun onAdHidden(p0: MaxAd) { + toHome() + } + + override fun onAdClicked(p0: MaxAd) { + + } + + override fun onAdLoadFailed(p0: String, p1: MaxError) { + + } + + override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) { + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ViewAllActivity.kt b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ViewAllActivity.kt new file mode 100644 index 0000000..4e235b8 --- /dev/null +++ b/app/src/main/java/com/input/board/appskey/keyboard/viewUi/ViewAllActivity.kt @@ -0,0 +1,107 @@ +package com.input.board.appskey.keyboard.viewUi + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.ImageView +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentStatePagerAdapter +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager.widget.ViewPager +import androidx.viewpager.widget.ViewPager.OnPageChangeListener +import com.input.board.appskey.keyboard.KeyBoardApp +import com.input.board.appskey.keyboard.R +import com.input.board.appskey.keyboard.jsonBean.MainBean +import com.input.board.appskey.keyboard.recyAdapter.ViewAllTabAdapter + +class ViewAllActivity : AppCompatActivity() { + private lateinit var tabRecycler: RecyclerView + private lateinit var viewContent: ViewPager + lateinit var viewAllList:MutableList + private lateinit var tabAdapter:ViewAllTabAdapter + + private lateinit var imgBack: ImageView + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_view_all) + findViewId() + setTabRecycler() + setContent() + } + + private fun findViewId(){ + tabRecycler = findViewById(R.id.tab_recycler) + viewContent = findViewById(R.id.view_pager_content) + imgBack = findViewById(R.id.back) + imgBack.setOnClickListener { + finish() + } + } + + private fun setTabRecycler(){ + val first = KeyBoardApp.list.filter { + it.parentName == "for you" + } + val otherList = KeyBoardApp.list.filter { + it.parentName != "for you" + } + val shuffled = otherList.shuffled() + + viewAllList = mutableListOf().apply { + add(first[0]) + addAll(shuffled) + } + tabAdapter = ViewAllTabAdapter(this@ViewAllActivity).apply { + tabTitleName(viewAllList) + setChangeListener (object : ViewAllTabAdapter.OnChangListener{ + override fun onChange(posit: Int) { + viewContent.setCurrentItem(posit,false) + } + + }) + } + tabRecycler.run { + adapter = tabAdapter + layoutManager = LinearLayoutManager(this@ViewAllActivity,RecyclerView.HORIZONTAL,false) + } + } + + private fun setContent(){ + val fragList = mutableListOf() + for(index in 0 until viewAllList.size){ + fragList.add(FragmentContent.newInstance(index)) + } + + viewContent.adapter = object :FragmentStatePagerAdapter(supportFragmentManager, + BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT){ + override fun getCount(): Int = fragList.size + + override fun getItem(position: Int): Fragment = fragList[position] + + } + var curViewPager = 0 + viewContent.addOnPageChangeListener(object :OnPageChangeListener{ + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int + ) { + if(curViewPager != position){ + tabAdapter.setSelectedPos(position) + tabRecycler.scrollToPosition(position) + curViewPager = position + } + + } + + override fun onPageSelected(position: Int) { + + } + + override fun onPageScrollStateChanged(state: Int) { + + } + + }) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/apply_btn_bg.xml b/app/src/main/res/drawable/apply_btn_bg.xml new file mode 100644 index 0000000..1ac50ab --- /dev/null +++ b/app/src/main/res/drawable/apply_btn_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/apply_step_false.xml b/app/src/main/res/drawable/apply_step_false.xml new file mode 100644 index 0000000..8cc2343 --- /dev/null +++ b/app/src/main/res/drawable/apply_step_false.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/draw_layout.xml b/app/src/main/res/drawable/draw_layout.xml new file mode 100644 index 0000000..c76d516 --- /dev/null +++ b/app/src/main/res/drawable/draw_layout.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/key_drawable.xml b/app/src/main/res/drawable/key_drawable.xml new file mode 100644 index 0000000..00380b0 --- /dev/null +++ b/app/src/main/res/drawable/key_drawable.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/settings_step_selector.xml b/app/src/main/res/drawable/settings_step_selector.xml new file mode 100644 index 0000000..52ca090 --- /dev/null +++ b/app/src/main/res/drawable/settings_step_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/view_all_bg.xml b/app/src/main/res/drawable/view_all_bg.xml new file mode 100644 index 0000000..24d8224 --- /dev/null +++ b/app/src/main/res/drawable/view_all_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_apply.xml b/app/src/main/res/layout/activity_apply.xml new file mode 100644 index 0000000..b5f5461 --- /dev/null +++ b/app/src/main/res/layout/activity_apply.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 0000000..c686cce --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_key_board_setting.xml b/app/src/main/res/layout/activity_key_board_setting.xml new file mode 100644 index 0000000..b2bfea5 --- /dev/null +++ b/app/src/main/res/layout/activity_key_board_setting.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_nova.xml b/app/src/main/res/layout/activity_nova.xml new file mode 100644 index 0000000..ace46db --- /dev/null +++ b/app/src/main/res/layout/activity_nova.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_view_all.xml b/app/src/main/res/layout/activity_view_all.xml new file mode 100644 index 0000000..5b068dc --- /dev/null +++ b/app/src/main/res/layout/activity_view_all.xml @@ -0,0 +1,34 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/custome_input_view.xml b/app/src/main/res/layout/custome_input_view.xml new file mode 100644 index 0000000..ac3215d --- /dev/null +++ b/app/src/main/res/layout/custome_input_view.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml new file mode 100644 index 0000000..b702ce9 --- /dev/null +++ b/app/src/main/res/layout/fragment_content.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_for_you_layout.xml b/app/src/main/res/layout/item_for_you_layout.xml new file mode 100644 index 0000000..c7c853f --- /dev/null +++ b/app/src/main/res/layout/item_for_you_layout.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_view_all_tab.xml b/app/src/main/res/layout/item_view_all_tab.xml new file mode 100644 index 0000000..9c6b186 --- /dev/null +++ b/app/src/main/res/layout/item_view_all_tab.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/board_setting.png b/app/src/main/res/mipmap-xxxhdpi/board_setting.png new file mode 100644 index 0000000..e86870c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/board_setting.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/direction_back.png b/app/src/main/res/mipmap-xxxhdpi/direction_back.png new file mode 100644 index 0000000..86d9c4e Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/direction_back.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/down_apply.png b/app/src/main/res/mipmap-xxxhdpi/down_apply.png new file mode 100644 index 0000000..6b72a74 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/down_apply.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/home_banner.png b/app/src/main/res/mipmap-xxxhdpi/home_banner.png new file mode 100644 index 0000000..a0e7b74 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/home_banner.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/key_normal.png b/app/src/main/res/mipmap-xxxhdpi/key_normal.png new file mode 100644 index 0000000..423191b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/key_normal.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/key_pressed_on.png b/app/src/main/res/mipmap-xxxhdpi/key_pressed_on.png new file mode 100644 index 0000000..156ea42 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/key_pressed_on.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/logo_nova_keyboard.png b/app/src/main/res/mipmap-xxxhdpi/logo_nova_keyboard.png new file mode 100644 index 0000000..0f2aea2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/logo_nova_keyboard.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/main_prink_bg.png b/app/src/main/res/mipmap-xxxhdpi/main_prink_bg.png new file mode 100644 index 0000000..f0214fd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/main_prink_bg.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/menu.png b/app/src/main/res/mipmap-xxxhdpi/menu.png new file mode 100644 index 0000000..9f4270b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/menu.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/nova_bg.png b/app/src/main/res/mipmap-xxxhdpi/nova_bg.png new file mode 100644 index 0000000..2e891b3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/nova_bg.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/nova_picture.png b/app/src/main/res/mipmap-xxxhdpi/nova_picture.png new file mode 100644 index 0000000..3fdc458 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/nova_picture.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/rate_us.png b/app/src/main/res/mipmap-xxxhdpi/rate_us.png new file mode 100644 index 0000000..e0f817e Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/rate_us.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/rivacy.png b/app/src/main/res/mipmap-xxxhdpi/rivacy.png new file mode 100644 index 0000000..846f990 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/rivacy.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/status_ok.png b/app/src/main/res/mipmap-xxxhdpi/status_ok.png new file mode 100644 index 0000000..4e1e7c1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/status_ok.png differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..6504cf4 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..5a85eff --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #66FFFFFF + #B3FFFFFF + #FB5BAF + #D15BFB + #ED4C97 + #1A000000 + #ffbbbbbb + #666666 + #FA93A8 + #F1A5D2 + \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml new file mode 100644 index 0000000..2bd26d6 --- /dev/null +++ b/app/src/main/res/values/dimen.xml @@ -0,0 +1,7 @@ + + + + 15sp + 48dp + 5dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1ab3f25 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,17 @@ + + Nova keyboard + View All + Download & Apply + Rate us + Privacy + https://play.google.com/store/apps/details?id= + https://marielathama.bitbucket.io/privacy.html + Recommended Themes + Keyboard settings + Activate PassionCool Keyboard to enable MORE functions! + Step 1:Select + Step 2:Enable + Theme application successful + Download failed, please try again + For normal use, please enter the setting to complete the setting steps. + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..b0f1288 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/value.xml b/app/src/main/res/values/value.xml new file mode 100644 index 0000000..9a8a11a --- /dev/null +++ b/app/src/main/res/values/value.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/a.xml b/app/src/main/res/xml/a.xml new file mode 100644 index 0000000..c3851a2 --- /dev/null +++ b/app/src/main/res/xml/a.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/b.xml b/app/src/main/res/xml/b.xml new file mode 100644 index 0000000..a1c1caa --- /dev/null +++ b/app/src/main/res/xml/b.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/c.xml b/app/src/main/res/xml/c.xml new file mode 100644 index 0000000..6005007 --- /dev/null +++ b/app/src/main/res/xml/c.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/keyborad.xml b/app/src/main/res/xml/keyborad.xml new file mode 100644 index 0000000..fd06865 --- /dev/null +++ b/app/src/main/res/xml/keyborad.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/input/board/appskey/keyboard/ExampleUnitTest.kt b/app/src/test/java/com/input/board/appskey/keyboard/ExampleUnitTest.kt new file mode 100644 index 0000000..8f3544e --- /dev/null +++ b/app/src/test/java/com/input/board/appskey/keyboard/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.input.board.appskey.keyboard + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..bbc20f7 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,12 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.0.1" apply false + id("org.jetbrains.kotlin.android") version "1.9.0" apply false + id("com.google.gms.google-services") version "4.3.15" apply false + id ("com.google.firebase.crashlytics") version "2.9.2" apply false +} +buildscript{ + dependencies{ + classpath("com.applovin.quality:AppLovinQualityServiceGradlePlugin:+") + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..15938f0 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 21 17:14:29 CST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..695f88e --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,22 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + maven{url = uri("https://artifacts.applovin.com/android")} + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven("https://jitpack.io") + maven ("https://artifact.bytedance.com/repository/pangle") + + } +} + +rootProject.name = "MyKeyBoard" +include(":app") + \ No newline at end of file