initial commit
22
.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
notes.txt
|
||||||
|
/files
|
||||||
|
/misc
|
||||||
|
/TODO.txt
|
||||||
|
tmp/
|
||||||
|
*_bak*
|
||||||
|
*_tmp
|
||||||
|
*.bak*
|
||||||
|
/.idea
|
||||||
|
*.apk
|
||||||
|
*.7z
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
fabric.properties
|
||||||
|
*BAK.java
|
||||||
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# LeanKey Keyboard sources
|
||||||
|
|
||||||
|
Original app can be found on the [Google Play](https://play.google.com/store/apps/details?id=org.liskovsoft.androidtv.rukeyboard)
|
||||||
40
build.gradle
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:2.3.0'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
// WARNING: don't use 'project.ext' because IDE 'Cannot infer argument type'
|
||||||
|
// https://stackoverflow.com/questions/20404476/how-to-define-common-android-properties-for-all-modules-using-gradle
|
||||||
|
// Gradle constants example: https://github.com/google/ExoPlayer
|
||||||
|
ext {
|
||||||
|
compileSdkVersion = 27
|
||||||
|
buildToolsVersion = "27.0.3"
|
||||||
|
minSdkVersion = 14
|
||||||
|
targetSdkVersion = 27
|
||||||
|
appCompatVersion = 'com.android.support:appcompat-v7:27.+'
|
||||||
|
espressoCoreVersion = 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||||
|
junitVersion = 'junit:junit:4.12'
|
||||||
|
supportVersion = 'com.android.support:support-v4:27.+'
|
||||||
|
robolectricVersion = 'org.robolectric:robolectric:3.5.1'
|
||||||
|
crashlyticsVersion = 'com.crashlytics.sdk.android:crashlytics:2.8.0@aar'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
// com.android.support libs
|
||||||
|
maven { url 'https://maven.google.com' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
13
gradle.properties
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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=-Xmx1536m
|
||||||
|
# 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
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Mon Dec 28 10:00:20 PST 2015
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||||
160
gradlew
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# 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
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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" ] ; 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, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; 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=$((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
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@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
|
||||||
|
|
||||||
|
@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=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@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 init
|
||||||
|
|
||||||
|
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 init
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
: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 %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
: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
|
||||||
1
leankeykeyboard/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
32
leankeykeyboard/build.gradle
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion project.properties.compileSdkVersion
|
||||||
|
buildToolsVersion project.properties.buildToolsVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.liskovsoft.leankeykeyboard"
|
||||||
|
minSdkVersion project.properties.minSdkVersion
|
||||||
|
targetSdkVersion project.properties.targetSdkVersion
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
androidTestCompile(project.properties.espressoCoreVersion, {
|
||||||
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
|
})
|
||||||
|
compile project.properties.appCompatVersion
|
||||||
|
testCompile project.properties.junitVersion
|
||||||
|
}
|
||||||
25
leankeykeyboard/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in C:\portable\dev\android-sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.liskovsoft.leankeykeyboard;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumentation test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() throws Exception {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("com.liskovsoft.leankeykeyboard", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
33
leankeykeyboard/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.liskovsoft.leankeykeyboard">
|
||||||
|
<uses-feature android:name="android.software.leanback" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.microphone" android:required="false"/>
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
|
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
|
||||||
|
<application android:banner="@drawable/banner_app" android:label="LeanKey Keyboard" android:icon="@drawable/ic_launcher">
|
||||||
|
<activity android:launchMode="singleTop" android:name="com.liskovsoft.inputchooser.LaunchAppActivity">
|
||||||
|
<meta-data android:name="package" android:value="com.android.settings"/>
|
||||||
|
<meta-data android:name="class" android:value="com.android.settings.Settings"/>
|
||||||
|
<meta-data android:name="intent" android:value="android.settings.INPUT_METHOD_SETTINGS"/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<service android:label="LeanKey Keyboard" android:name="com.google.leanback.ime.LeanbackImeService" android:permission="android.permission.BIND_INPUT_METHOD">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.view.InputMethod"/>
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.view.im" android:resource="@xml/method"/>
|
||||||
|
</service>
|
||||||
|
<receiver android:name="com.liskovsoft.inputchooser.RestartServiceReceiver" android:exported="true" android:enabled="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
<data android:scheme="package"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.addons;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface AddOn {
|
||||||
|
interface AddOnResourceMapping {
|
||||||
|
/*@AttrRes
|
||||||
|
int getLocalAttrIdFromRemote(@AttrRes int remoteAttributeResourceId);*/
|
||||||
|
|
||||||
|
int[] getRemoteStyleableArrayFromLocal(int[] localStyleableArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
int INVALID_RES_ID = 0;
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
String getDescription();
|
||||||
|
|
||||||
|
String getPackageName();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Context getPackageContext();
|
||||||
|
|
||||||
|
int getSortIndex();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
AddOnResourceMapping getResourceMapping();
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.addons;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.util.SparseArrayCompat;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.anysoftkeyboard.utils.Logger;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public abstract class AddOnImpl implements AddOn {
|
||||||
|
|
||||||
|
private static final String TAG = "ASK_AddOnImpl";
|
||||||
|
private final String mId;
|
||||||
|
private final String mName;
|
||||||
|
private final String mDescription;
|
||||||
|
private final String mPackageName;
|
||||||
|
private final Context mAskAppContext;
|
||||||
|
private WeakReference<Context> mPackageContext;
|
||||||
|
private final int mSortIndex;
|
||||||
|
private final AddOnResourceMappingImpl mAddOnResourceMapping;
|
||||||
|
|
||||||
|
protected AddOnImpl(Context askContext, Context packageContext, String id, int nameResId,
|
||||||
|
String description, int sortIndex) {
|
||||||
|
mId = id;
|
||||||
|
mAskAppContext = askContext;
|
||||||
|
mName = packageContext.getString(nameResId);
|
||||||
|
mDescription = description;
|
||||||
|
mPackageName = packageContext.getPackageName();
|
||||||
|
mPackageContext = new WeakReference<>(packageContext);
|
||||||
|
mSortIndex = sortIndex;
|
||||||
|
mAddOnResourceMapping = new AddOnResourceMappingImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getId() {
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getDescription() {
|
||||||
|
return mDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPackageName() {
|
||||||
|
return mPackageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public final Context getPackageContext() {
|
||||||
|
Context c = mPackageContext.get();
|
||||||
|
if (c == null) {
|
||||||
|
try {
|
||||||
|
c = mAskAppContext.createPackageContext(mPackageName, Context.CONTEXT_IGNORE_SECURITY);
|
||||||
|
mPackageContext = new WeakReference<>(c);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
Logger.w(TAG, "Failed to find package %s!", mPackageName);
|
||||||
|
Logger.w(TAG, "Failed to find package! ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getSortIndex() {
|
||||||
|
return mSortIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getId().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof AddOn &&
|
||||||
|
((AddOn) o).getId().equals(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AddOnResourceMapping getResourceMapping() {
|
||||||
|
return mAddOnResourceMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AddOnResourceMappingImpl implements AddOnResourceMapping {
|
||||||
|
private final WeakReference<AddOnImpl> mAddOnWeakReference;
|
||||||
|
private final SparseIntArray mAttributesMapping = new SparseIntArray();
|
||||||
|
private final SparseArrayCompat<int[]> mStyleableArrayMapping = new SparseArrayCompat<>();
|
||||||
|
|
||||||
|
private AddOnResourceMappingImpl(@NonNull AddOnImpl addOn) {
|
||||||
|
mAddOnWeakReference = new WeakReference<>(addOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getRemoteStyleableArrayFromLocal(int[] localStyleableArray) {
|
||||||
|
int localStyleableId = Arrays.hashCode(localStyleableArray);
|
||||||
|
int indexOfRemoteArray = mStyleableArrayMapping.indexOfKey(localStyleableId);
|
||||||
|
if (indexOfRemoteArray >= 0) return mStyleableArrayMapping.valueAt(indexOfRemoteArray);
|
||||||
|
AddOnImpl addOn = mAddOnWeakReference.get();
|
||||||
|
if (addOn == null) return new int[0];
|
||||||
|
Context askContext = addOn.mAskAppContext;
|
||||||
|
Context remoteContext = addOn.getPackageContext();
|
||||||
|
if (remoteContext == null) return new int[0];
|
||||||
|
int[] remoteAttrIds = Support.createBackwardCompatibleStyleable(localStyleableArray, askContext, remoteContext, mAttributesMapping);
|
||||||
|
mStyleableArrayMapping.put(localStyleableId, remoteAttrIds);
|
||||||
|
return remoteAttrIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.addons;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
//import com.anysoftkeyboard.AnySoftKeyboard;
|
||||||
|
import com.anysoftkeyboard.utils.Logger;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class AddOnsFactory<E extends AddOn> {
|
||||||
|
|
||||||
|
private static final class AddOnsComparator implements Comparator<AddOn> {
|
||||||
|
private final String mAskPackageName;
|
||||||
|
|
||||||
|
private AddOnsComparator(Context askContext) {
|
||||||
|
mAskPackageName = askContext.getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(AddOn k1, AddOn k2) {
|
||||||
|
String c1 = k1.getPackageName();
|
||||||
|
String c2 = k2.getPackageName();
|
||||||
|
|
||||||
|
if (c1.equals(c2))
|
||||||
|
return k1.getSortIndex() - k2.getSortIndex();
|
||||||
|
else if (c1.equals(mAskPackageName))//I want to make sure ASK packages are first
|
||||||
|
return -1;
|
||||||
|
else if (c2.equals(mAskPackageName))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return c1.compareToIgnoreCase(c2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static ArrayList<AddOnsFactory<?>> mActiveInstances = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final String sTAG = "AddOnsFactory";
|
||||||
|
|
||||||
|
//public static void onPackageChanged(final Intent eventIntent, final AnySoftKeyboard ask) {
|
||||||
|
// boolean cleared = false;
|
||||||
|
// boolean recreateView = false;
|
||||||
|
// for (AddOnsFactory<?> factory : mActiveInstances) {
|
||||||
|
// try {
|
||||||
|
// if (factory.isEventRequiresCacheRefresh(eventIntent, ask.getApplicationContext())) {
|
||||||
|
// cleared = true;
|
||||||
|
// if (factory.isEventRequiresViewReset(eventIntent, ask.getApplicationContext())) recreateView = true;
|
||||||
|
// Logger.d(sTAG, factory.getClass().getName() + " will handle this package-changed event. Also recreate view? " + recreateView);
|
||||||
|
// factory.clearAddOnList();
|
||||||
|
// }
|
||||||
|
// } catch (NameNotFoundException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (cleared) ask.resetKeyboardView(recreateView);
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static AddOn locateAddOn(String id, Context askContext) {
|
||||||
|
for (AddOnsFactory<?> factory : mActiveInstances) {
|
||||||
|
AddOn addOn = factory.getAddOnById(id, askContext);
|
||||||
|
if (addOn != null) {
|
||||||
|
Logger.d(sTAG, "Located addon with id " + addOn.getId() + " of type " + addOn.getClass().getName());
|
||||||
|
return addOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final String TAG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the interface name that a broadcast receiver implementing an
|
||||||
|
* external addon should say that it supports -- that is, this is the
|
||||||
|
* action it uses for its intent filter.
|
||||||
|
*/
|
||||||
|
private final String RECEIVER_INTERFACE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name under which an external addon broadcast receiver component
|
||||||
|
* publishes information about itself.
|
||||||
|
*/
|
||||||
|
private final String RECEIVER_META_DATA;
|
||||||
|
|
||||||
|
private final ArrayList<E> mAddOns = new ArrayList<>();
|
||||||
|
private final HashMap<String, E> mAddOnsById = new HashMap<>();
|
||||||
|
|
||||||
|
private final boolean mReadExternalPacksToo;
|
||||||
|
private final String ROOT_NODE_TAG;
|
||||||
|
private final String ADDON_NODE_TAG;
|
||||||
|
//private final int mBuildInAddOnsResId;
|
||||||
|
|
||||||
|
private static final String XML_PREF_ID_ATTRIBUTE = "id";
|
||||||
|
private static final String XML_NAME_RES_ID_ATTRIBUTE = "nameResId";
|
||||||
|
private static final String XML_DESCRIPTION_ATTRIBUTE = "description";
|
||||||
|
private static final String XML_SORT_INDEX_ATTRIBUTE = "index";
|
||||||
|
|
||||||
|
protected AddOnsFactory(String tag, String receiverInterface, String receiverMetaData, String rootNodeTag, String addonNodeTag, int buildInAddonResId, boolean readExternalPacksToo) {
|
||||||
|
TAG = tag;
|
||||||
|
RECEIVER_INTERFACE = receiverInterface;
|
||||||
|
RECEIVER_META_DATA = receiverMetaData;
|
||||||
|
ROOT_NODE_TAG = rootNodeTag;
|
||||||
|
ADDON_NODE_TAG = addonNodeTag;
|
||||||
|
//mBuildInAddOnsResId = buildInAddonResId;
|
||||||
|
mReadExternalPacksToo = readExternalPacksToo;
|
||||||
|
|
||||||
|
mActiveInstances.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isEventRequiresCacheRefresh(Intent eventIntent, Context context) {
|
||||||
|
String action = eventIntent.getAction();
|
||||||
|
String packageNameSchemePart = eventIntent.getData().getSchemeSpecificPart();
|
||||||
|
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
||||||
|
//will reset only if the new package has my addons
|
||||||
|
boolean hasAddon = isPackageContainAnAddon(context, packageNameSchemePart);
|
||||||
|
if (hasAddon) {
|
||||||
|
Logger.d(TAG, "It seems that an addon exists in a newly installed package " + packageNameSchemePart + ". I need to reload stuff.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (Intent.ACTION_PACKAGE_REPLACED.equals(action) || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
|
||||||
|
//If I'm managing OR it contains an addon (could be new feature in the package), I want to reset.
|
||||||
|
boolean isPackagedManaged = isPackageManaged(packageNameSchemePart);
|
||||||
|
if (isPackagedManaged) {
|
||||||
|
Logger.d(TAG, "It seems that an addon I use (in package " + packageNameSchemePart + ") has been changed. I need to reload stuff.");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
boolean hasAddon = isPackageContainAnAddon(context, packageNameSchemePart);
|
||||||
|
if (hasAddon) {
|
||||||
|
Logger.d(TAG, "It seems that an addon exists in an updated package " + packageNameSchemePart + ". I need to reload stuff.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else //removed
|
||||||
|
{
|
||||||
|
//so only if I manage this package, I want to reset
|
||||||
|
boolean isPackagedManaged = isPackageManaged(packageNameSchemePart);
|
||||||
|
if (isPackagedManaged) {
|
||||||
|
Logger.d(TAG, "It seems that an addon I use (in package " + packageNameSchemePart + ") has been removed. I need to reload stuff.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isPackageManaged(String packageNameSchemePart) {
|
||||||
|
for (AddOn addOn : mAddOns) {
|
||||||
|
if (addOn.getPackageName().equals(packageNameSchemePart)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isPackageContainAnAddon(Context context, String packageNameSchemePart) {
|
||||||
|
PackageInfo newPackage;
|
||||||
|
try {
|
||||||
|
newPackage = context.getPackageManager().getPackageInfo(packageNameSchemePart, PackageManager.GET_RECEIVERS + PackageManager
|
||||||
|
.GET_META_DATA);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (newPackage.receivers != null) {
|
||||||
|
ActivityInfo[] receivers = newPackage.receivers;
|
||||||
|
for (ActivityInfo aReceiver : receivers) {
|
||||||
|
//issue 904
|
||||||
|
if (aReceiver == null || aReceiver.applicationInfo == null || !aReceiver.enabled || !aReceiver.applicationInfo.enabled)
|
||||||
|
continue;
|
||||||
|
final XmlPullParser xml = aReceiver.loadXmlMetaData(context.getPackageManager(), RECEIVER_META_DATA);
|
||||||
|
if (xml != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isEventRequiresViewReset(Intent eventIntent, Context context) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void clearAddOnList() {
|
||||||
|
mAddOns.clear();
|
||||||
|
mAddOnsById.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized E getAddOnById(String id, Context askContext) {
|
||||||
|
if (mAddOnsById.size() == 0) {
|
||||||
|
loadAddOns(askContext);
|
||||||
|
}
|
||||||
|
return mAddOnsById.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized final List<E> getAllAddOns(Context askContext) {
|
||||||
|
Logger.d(TAG, "getAllAddOns has %d add on for %s", mAddOns.size(), getClass().getName());
|
||||||
|
if (mAddOns.size() == 0) {
|
||||||
|
loadAddOns(askContext);
|
||||||
|
}
|
||||||
|
Logger.d(TAG, "getAllAddOns will return %d add on for %s", mAddOns.size(), getClass().getName());
|
||||||
|
return Collections.unmodifiableList(mAddOns);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void loadAddOns(final Context askContext) {
|
||||||
|
clearAddOnList();
|
||||||
|
|
||||||
|
//ArrayList<E> local = getAddOnsFromResId(askContext, askContext, mBuildInAddOnsResId);
|
||||||
|
//for (E addon : local) {
|
||||||
|
// Logger.d(TAG, "Local add-on %s loaded", addon.getId());
|
||||||
|
//}
|
||||||
|
//mAddOns.addAll(local);
|
||||||
|
ArrayList<E> external = getExternalAddOns(askContext);
|
||||||
|
for (E addon : external) {
|
||||||
|
Logger.d(TAG, "External add-on %s loaded", addon.getId());
|
||||||
|
}
|
||||||
|
mAddOns.addAll(external);
|
||||||
|
Logger.d(TAG, "Have %d add on for %s", mAddOns.size(), getClass().getName());
|
||||||
|
|
||||||
|
buildOtherDataBasedOnNewAddOns(mAddOns);
|
||||||
|
|
||||||
|
//sorting the keyboards according to the requested
|
||||||
|
//sort order (from minimum to maximum)
|
||||||
|
Collections.sort(mAddOns, new AddOnsComparator(askContext));
|
||||||
|
Logger.d(TAG, "Have %d add on for %s (after sort)", mAddOns.size(), getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void buildOtherDataBasedOnNewAddOns(ArrayList<E> newAddOns) {
|
||||||
|
for (E addOn : newAddOns)
|
||||||
|
mAddOnsById.put(addOn.getId(), addOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<E> getExternalAddOns(Context askContext) {
|
||||||
|
final ArrayList<E> externalAddOns = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!mReadExternalPacksToo)//this will disable external packs (API careful stage)
|
||||||
|
return externalAddOns;
|
||||||
|
|
||||||
|
final List<ResolveInfo> broadcastReceivers =
|
||||||
|
askContext.getPackageManager().queryBroadcastReceivers(new Intent(RECEIVER_INTERFACE), PackageManager.GET_META_DATA);
|
||||||
|
|
||||||
|
|
||||||
|
for (final ResolveInfo receiver : broadcastReceivers) {
|
||||||
|
if (receiver.activityInfo == null) {
|
||||||
|
Logger.e(TAG, "BroadcastReceiver has null ActivityInfo. Receiver's label is "
|
||||||
|
+ receiver.loadLabel(askContext.getPackageManager()));
|
||||||
|
Logger.e(TAG, "Is the external keyboard a service instead of BroadcastReceiver?");
|
||||||
|
// Skip to next receiver
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!receiver.activityInfo.enabled || !receiver.activityInfo.applicationInfo.enabled) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Context externalPackageContext = askContext.createPackageContext(receiver.activityInfo.packageName, Context.CONTEXT_IGNORE_SECURITY);
|
||||||
|
final ArrayList<E> packageAddOns = getAddOnsFromActivityInfo(askContext, externalPackageContext, receiver.activityInfo);
|
||||||
|
|
||||||
|
externalAddOns.addAll(packageAddOns);
|
||||||
|
} catch (final NameNotFoundException e) {
|
||||||
|
Logger.e(TAG, "Did not find package: " + receiver.activityInfo.packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return externalAddOns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<E> getAddOnsFromResId(Context askContext, Context context, int addOnsResId) {
|
||||||
|
final XmlPullParser xml = context.getResources().getXml(addOnsResId);
|
||||||
|
if (xml == null)
|
||||||
|
return new ArrayList<>();
|
||||||
|
return parseAddOnsFromXml(askContext, context, xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<E> getAddOnsFromActivityInfo(Context askContext, Context context, ActivityInfo ai) {
|
||||||
|
final XmlPullParser xml = ai.loadXmlMetaData(context.getPackageManager(), RECEIVER_META_DATA);
|
||||||
|
if (xml == null)//issue 718: maybe a bad package?
|
||||||
|
return new ArrayList<>();
|
||||||
|
return parseAddOnsFromXml(askContext, context, xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<E> parseAddOnsFromXml(Context askContext, Context context, XmlPullParser xml) {
|
||||||
|
final ArrayList<E> addOns = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
int event;
|
||||||
|
boolean inRoot = false;
|
||||||
|
while ((event = xml.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
final String tag = xml.getName();
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
if (ROOT_NODE_TAG.equals(tag)) {
|
||||||
|
inRoot = true;
|
||||||
|
} else if (inRoot && ADDON_NODE_TAG.equals(tag)) {
|
||||||
|
final AttributeSet attrs = Xml.asAttributeSet(xml);
|
||||||
|
E addOn = createAddOnFromXmlAttributes(askContext, attrs, context);
|
||||||
|
if (addOn != null) {
|
||||||
|
addOns.add(addOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event == XmlPullParser.END_TAG) {
|
||||||
|
if (ROOT_NODE_TAG.equals(tag)) {
|
||||||
|
inRoot = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
Logger.e(TAG, "IO error:" + e);
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (final XmlPullParserException e) {
|
||||||
|
Logger.e(TAG, "Parse error:" + e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return addOns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private E createAddOnFromXmlAttributes(Context askContext, AttributeSet attrs, Context context) {
|
||||||
|
final String prefId = attrs.getAttributeValue(null, XML_PREF_ID_ATTRIBUTE);
|
||||||
|
final int nameId = attrs.getAttributeResourceValue(null, XML_NAME_RES_ID_ATTRIBUTE, AddOn.INVALID_RES_ID);
|
||||||
|
final int descriptionInt = attrs.getAttributeResourceValue(null, XML_DESCRIPTION_ATTRIBUTE, AddOn.INVALID_RES_ID);
|
||||||
|
//NOTE, to be compatible we need this. because the most of descriptions are
|
||||||
|
//without @string/adb
|
||||||
|
String description;
|
||||||
|
if (descriptionInt != AddOn.INVALID_RES_ID) {
|
||||||
|
description = context.getResources().getString(descriptionInt);
|
||||||
|
} else {
|
||||||
|
description = attrs.getAttributeValue(null, XML_DESCRIPTION_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int sortIndex = attrs.getAttributeUnsignedIntValue(null, XML_SORT_INDEX_ATTRIBUTE, 1);
|
||||||
|
|
||||||
|
// asserting
|
||||||
|
if ((prefId == null) || (nameId == AddOn.INVALID_RES_ID)) {
|
||||||
|
Logger.e(TAG, "External add-on does not include all mandatory details! Will not create add-on.");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Logger.d(TAG, "External addon details: prefId:" + prefId + " nameId:" + nameId);
|
||||||
|
return createConcreteAddOn(askContext, context, prefId, nameId, description, sortIndex, attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract E createConcreteAddOn(Context askContext, Context context, String prefId, int nameId, String description, int sortIndex, AttributeSet attrs);
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.anysoftkeyboard.addons;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.anysoftkeyboard.utils.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class Support {
|
||||||
|
private static final String TAG = Support.class.getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mapping between the local styleable and the remote.
|
||||||
|
* @param localStyleableArray the local styleable to map against
|
||||||
|
* @param localContext local APK's Context
|
||||||
|
* @param remoteContext remote package's Context
|
||||||
|
* @param attributeIdMap a mapping between the remote-id -> local-id
|
||||||
|
* @return Always returns the remote version of localStyleableArray
|
||||||
|
*/
|
||||||
|
public static int[] createBackwardCompatibleStyleable(@NonNull int[] localStyleableArray, @NonNull Context localContext, @NonNull Context remoteContext, @NonNull SparseIntArray attributeIdMap) {
|
||||||
|
if (localContext == null) throw new NullPointerException("askContext can not be null");
|
||||||
|
if (remoteContext == null) throw new NullPointerException("context can not be null");
|
||||||
|
|
||||||
|
final String remotePackageName = remoteContext.getPackageName();
|
||||||
|
if (localContext.getPackageName().equals(remotePackageName)) {
|
||||||
|
Logger.d(TAG, "This is a local context ("+remotePackageName+"), optimization will be done.");
|
||||||
|
//optimization
|
||||||
|
for(int attrId : localStyleableArray) {
|
||||||
|
attributeIdMap.put(attrId, attrId);
|
||||||
|
}
|
||||||
|
return localStyleableArray;
|
||||||
|
}
|
||||||
|
final Resources localRes = localContext.getResources();
|
||||||
|
final Resources remoteRes = remoteContext.getResources();
|
||||||
|
List<Integer> styleableIdList = new ArrayList<>(localStyleableArray.length);
|
||||||
|
for(int attrId : localStyleableArray) {
|
||||||
|
final boolean isAndroidAttribute = localRes.getResourcePackageName(attrId).equals("android");
|
||||||
|
final int remoteAttrId;
|
||||||
|
|
||||||
|
if (isAndroidAttribute) {
|
||||||
|
//android attribute IDs are the same always. So, I can optimize.
|
||||||
|
remoteAttrId = attrId;
|
||||||
|
} else {
|
||||||
|
final String attributeName = localRes.getResourceEntryName(attrId);
|
||||||
|
remoteAttrId = remoteRes.getIdentifier(attributeName, "attr", remotePackageName);
|
||||||
|
Logger.d(TAG, "attr "+attributeName+", local id "+attrId+", remote id "+remoteAttrId);
|
||||||
|
}
|
||||||
|
if (remoteAttrId != 0) {
|
||||||
|
attributeIdMap.put(remoteAttrId, attrId);
|
||||||
|
styleableIdList.add(remoteAttrId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int[] remoteMappedStyleable = new int[styleableIdList.size()];
|
||||||
|
for(int i=0; i<remoteMappedStyleable.length; i++) {
|
||||||
|
remoteMappedStyleable[i] = styleableIdList.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteMappedStyleable;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.keyboards;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.anysoftkeyboard.addons.AddOn;
|
||||||
|
import com.anysoftkeyboard.addons.AddOnImpl;
|
||||||
|
|
||||||
|
public class KeyboardAddOnAndBuilder extends AddOnImpl {
|
||||||
|
|
||||||
|
public static final String KEYBOARD_PREF_PREFIX = "keyboard_";
|
||||||
|
|
||||||
|
private final int mResId;
|
||||||
|
private final int mLandscapeResId;
|
||||||
|
//private final int mIconResId;
|
||||||
|
private final String mDefaultDictionary;
|
||||||
|
private final int mQwertyTranslationId;
|
||||||
|
private final String mAdditionalIsLetterExceptions;
|
||||||
|
private final String mSentenceSeparators;
|
||||||
|
private final boolean mKeyboardDefaultEnabled;
|
||||||
|
|
||||||
|
public KeyboardAddOnAndBuilder(Context askContext, Context packageContext, String id, int nameResId,
|
||||||
|
int layoutResId, int landscapeLayoutResId,
|
||||||
|
String defaultDictionary, int iconResId,
|
||||||
|
int physicalTranslationResId,
|
||||||
|
String additionalIsLetterExceptions,
|
||||||
|
String sentenceSeparators,
|
||||||
|
String description,
|
||||||
|
int keyboardIndex,
|
||||||
|
boolean keyboardDefaultEnabled) {
|
||||||
|
super(askContext, packageContext, KEYBOARD_PREF_PREFIX + id, nameResId, description, keyboardIndex);
|
||||||
|
|
||||||
|
mResId = layoutResId;
|
||||||
|
if (landscapeLayoutResId == AddOn.INVALID_RES_ID) {
|
||||||
|
mLandscapeResId = mResId;
|
||||||
|
} else {
|
||||||
|
mLandscapeResId = landscapeLayoutResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDefaultDictionary = defaultDictionary;
|
||||||
|
//mIconResId = iconResId;
|
||||||
|
mAdditionalIsLetterExceptions = additionalIsLetterExceptions;
|
||||||
|
mSentenceSeparators = sentenceSeparators;
|
||||||
|
mQwertyTranslationId = physicalTranslationResId;
|
||||||
|
mKeyboardDefaultEnabled = keyboardDefaultEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getKeyboardDefaultEnabled() {
|
||||||
|
return mKeyboardDefaultEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyboardLocale() {
|
||||||
|
return mDefaultDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSentenceSeparators() {
|
||||||
|
return mSentenceSeparators;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public android.inputmethodservice.Keyboard createKeyboard() {
|
||||||
|
Context remoteContext = getPackageContext();
|
||||||
|
if (remoteContext == null) return null;
|
||||||
|
return new android.inputmethodservice.Keyboard(remoteContext, mLandscapeResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@Nullable
|
||||||
|
//public android.inputmethodservice.Keyboard createKeyboard(Context askContext, @Keyboard.KeyboardRowModeId int mode) {
|
||||||
|
// Context remoteContext = getPackageContext();
|
||||||
|
// if (remoteContext == null) return null;
|
||||||
|
// return new ExternalAnyKeyboard(this, askContext, remoteContext, mResId, mLandscapeResId, getId(), getName(), mIconResId, mQwertyTranslationId, mDefaultDictionary, mAdditionalIsLetterExceptions, mSentenceSeparators, mode);
|
||||||
|
//}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.keyboards;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.inputmethodservice.*;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import com.anysoftkeyboard.addons.AddOn;
|
||||||
|
import com.anysoftkeyboard.addons.AddOnsFactory;
|
||||||
|
import com.anysoftkeyboard.utils.Logger;
|
||||||
|
//import com.menny.android.anysoftkeyboard.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class KeyboardFactory extends AddOnsFactory<KeyboardAddOnAndBuilder> {
|
||||||
|
private static final String TAG = "ASK_KF";
|
||||||
|
|
||||||
|
private static final String XML_LAYOUT_RES_ID_ATTRIBUTE = "layoutResId";
|
||||||
|
private static final String XML_LANDSCAPE_LAYOUT_RES_ID_ATTRIBUTE = "landscapeResId";
|
||||||
|
private static final String XML_ICON_RES_ID_ATTRIBUTE = "iconResId";
|
||||||
|
private static final String XML_DICTIONARY_NAME_ATTRIBUTE = "defaultDictionaryLocale";
|
||||||
|
private static final String XML_ADDITIONAL_IS_LETTER_EXCEPTIONS_ATTRIBUTE = "additionalIsLetterExceptions";
|
||||||
|
private static final String XML_SENTENCE_SEPARATOR_CHARACTERS_ATTRIBUTE = "sentenceSeparators";
|
||||||
|
private static final String DEFAULT_SENTENCE_SEPARATORS = ".,!?)]:;";
|
||||||
|
private static final String XML_PHYSICAL_TRANSLATION_RES_ID_ATTRIBUTE = "physicalKeyboardMappingResId";
|
||||||
|
private static final String XML_DEFAULT_ATTRIBUTE = "defaultEnabled";
|
||||||
|
|
||||||
|
public KeyboardFactory() {
|
||||||
|
super(TAG, "com.liskovsoft.leankey.langpack.KEYBOARD", "com.liskovsoft.leankey.langpack.keyboards",
|
||||||
|
"Keyboards", "Keyboard",
|
||||||
|
0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyboardAddOnAndBuilder> getAllAvailableKeyboards(Context askContext) {
|
||||||
|
return getAllAddOns(askContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyboardAddOnAndBuilder> getEnabledKeyboards(Context askContext) {
|
||||||
|
final List<KeyboardAddOnAndBuilder> allAddOns = getAllAddOns(askContext);
|
||||||
|
Logger.i(TAG, "Creating enabled addons list. I have a total of " + allAddOns.size() + " addons");
|
||||||
|
|
||||||
|
//getting shared prefs to determine which to create.
|
||||||
|
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(askContext);
|
||||||
|
|
||||||
|
final ArrayList<KeyboardAddOnAndBuilder> enabledAddOns = new ArrayList<>();
|
||||||
|
for (int addOnIndex = 0; addOnIndex < allAddOns.size(); addOnIndex++) {
|
||||||
|
final KeyboardAddOnAndBuilder addOn = allAddOns.get(addOnIndex);
|
||||||
|
|
||||||
|
final boolean addOnEnabled = sharedPreferences.getBoolean(addOn.getId(), addOn.getKeyboardDefaultEnabled());
|
||||||
|
|
||||||
|
if (addOnEnabled) {
|
||||||
|
enabledAddOns.add(addOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix: issue 219
|
||||||
|
// Check if there is any keyboards created if not, lets create a default english keyboard
|
||||||
|
if (enabledAddOns.size() == 0) {
|
||||||
|
final SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
final KeyboardAddOnAndBuilder addOn = allAddOns.get(0);
|
||||||
|
editor.putBoolean(addOn.getId(), true);
|
||||||
|
editor.commit();
|
||||||
|
enabledAddOns.add(addOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final KeyboardAddOnAndBuilder addOn : enabledAddOns) {
|
||||||
|
Logger.d(TAG, "Factory provided addon: %s", addOn.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabledAddOns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyboardAddOnAndBuilder createConcreteAddOn(Context askContext, Context context, String prefId, int nameId, String description, int sortIndex, AttributeSet attrs) {
|
||||||
|
final int layoutResId = attrs.getAttributeResourceValue(null, XML_LAYOUT_RES_ID_ATTRIBUTE, AddOn.INVALID_RES_ID);
|
||||||
|
final int landscapeLayoutResId = attrs.getAttributeResourceValue(null, XML_LANDSCAPE_LAYOUT_RES_ID_ATTRIBUTE, AddOn.INVALID_RES_ID);
|
||||||
|
//final int iconResId = attrs.getAttributeResourceValue(null, XML_ICON_RES_ID_ATTRIBUTE, R.drawable.sym_keyboard_notification_icon);
|
||||||
|
final int iconResId = 0;
|
||||||
|
final String defaultDictionary = attrs.getAttributeValue(null, XML_DICTIONARY_NAME_ATTRIBUTE);
|
||||||
|
final String additionalIsLetterExceptions = attrs.getAttributeValue(null, XML_ADDITIONAL_IS_LETTER_EXCEPTIONS_ATTRIBUTE);
|
||||||
|
String sentenceSeparators = attrs.getAttributeValue(null, XML_SENTENCE_SEPARATOR_CHARACTERS_ATTRIBUTE);
|
||||||
|
if (TextUtils.isEmpty(sentenceSeparators)) sentenceSeparators = DEFAULT_SENTENCE_SEPARATORS;
|
||||||
|
final int physicalTranslationResId = attrs.getAttributeResourceValue(null, XML_PHYSICAL_TRANSLATION_RES_ID_ATTRIBUTE, AddOn.INVALID_RES_ID);
|
||||||
|
// A keyboard is enabled by default if it is the first one (index==1)
|
||||||
|
final boolean keyboardDefault = attrs.getAttributeBooleanValue(null, XML_DEFAULT_ATTRIBUTE, sortIndex == 1);
|
||||||
|
|
||||||
|
// asserting
|
||||||
|
if ((prefId == null) || (nameId == AddOn.INVALID_RES_ID) || (layoutResId == AddOn.INVALID_RES_ID)) {
|
||||||
|
Logger.e(TAG, "External Keyboard does not include all mandatory details! Will not create keyboard.");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Logger.d(TAG,
|
||||||
|
"External keyboard details: prefId:" + prefId + " nameId:"
|
||||||
|
+ nameId + " resId:" + layoutResId
|
||||||
|
+ " landscapeResId:" + landscapeLayoutResId
|
||||||
|
+ " iconResId:" + iconResId + " defaultDictionary:"
|
||||||
|
+ defaultDictionary);
|
||||||
|
return new KeyboardAddOnAndBuilder(askContext, context,
|
||||||
|
prefId, nameId, layoutResId, landscapeLayoutResId,
|
||||||
|
defaultDictionary, iconResId, physicalTranslationResId,
|
||||||
|
additionalIsLetterExceptions, sentenceSeparators,
|
||||||
|
description, sortIndex, keyboardDefault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMultipleAlphabets(Context askContext) {
|
||||||
|
return getEnabledKeyboards(askContext).size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard createKeyboard(Context context) {
|
||||||
|
List<KeyboardAddOnAndBuilder> keyboardBuilders = getAllAvailableKeyboards(context);
|
||||||
|
if (keyboardBuilders.size() == 0)
|
||||||
|
return new Keyboard(context, 0x7f04000c); // ru keyboard resource id
|
||||||
|
// remember, only one external keyboard supported
|
||||||
|
return keyboardBuilders.get(0).createKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
public class BuildConfig {
|
||||||
|
public final static boolean TESTING_BUILD = true;
|
||||||
|
public final static boolean DEBUG = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger messages to Android's LogCat. Should be used only in DEBUG builds.
|
||||||
|
*/
|
||||||
|
public class LogCatLogProvider implements LogProvider {
|
||||||
|
@Override
|
||||||
|
public void v(String TAG, String text) {
|
||||||
|
Log.v(TAG, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void d(String TAG, String text) {
|
||||||
|
Log.d(TAG, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void yell(String TAG, String text) {
|
||||||
|
Log.w(TAG+"-YELL", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void i(String TAG, String text) {
|
||||||
|
Log.i(TAG, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void w(String TAG, String text) {
|
||||||
|
Log.w(TAG, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void e(String TAG, String text) {
|
||||||
|
Log.e(TAG, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wtf(String TAG, String text) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
|
||||||
|
Log.wtf(TAG, text);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG+" WTF", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
public interface LogProvider {
|
||||||
|
|
||||||
|
void v(String TAG, String text);
|
||||||
|
|
||||||
|
void d(String TAG, String text);
|
||||||
|
|
||||||
|
void yell(String TAG, String text);
|
||||||
|
|
||||||
|
void i(String TAG, String text);
|
||||||
|
|
||||||
|
void w(String TAG, String text);
|
||||||
|
|
||||||
|
void e(String TAG, String text);
|
||||||
|
|
||||||
|
void wtf(String TAG, String text);
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class Logger {
|
||||||
|
public static final String NEW_LINE = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
private static final StringBuilder msFormatBuilder = new StringBuilder(1024);
|
||||||
|
private static final java.util.Formatter msFormatter = new java.util.Formatter(msFormatBuilder, Locale.US);
|
||||||
|
|
||||||
|
private static final String[] msLogs = new String[BuildConfig.TESTING_BUILD ? 225 : 0];
|
||||||
|
private static final String LVL_V = "V";
|
||||||
|
private static final String LVL_D = "D";
|
||||||
|
private static final String LVL_YELL = "YELL";
|
||||||
|
private static final String LVL_I = "I";
|
||||||
|
private static final String LVL_W = "W";
|
||||||
|
private static final String LVL_E = "E";
|
||||||
|
private static final String LVL_WTF = "WTF";
|
||||||
|
private static int msLogIndex = 0;
|
||||||
|
@NonNull
|
||||||
|
private static LogProvider msLogger = new LogCatLogProvider();
|
||||||
|
|
||||||
|
private Logger() {
|
||||||
|
//no instances please.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLogProvider(@NonNull LogProvider logProvider) {
|
||||||
|
msLogger = logProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static void addLog(String level, String tag, String message) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
msLogs[msLogIndex] = System.currentTimeMillis() + "-" + level + "-[" + tag + "] " + message;
|
||||||
|
msLogIndex = (msLogIndex + 1) % msLogs.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static void addLog(String level, String tag, String message, Throwable t) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
addLog(level, tag, message);
|
||||||
|
addLog(level, tag, getStackTrace(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public synchronized static ArrayList<String> getAllLogLinesList() {
|
||||||
|
ArrayList<String> lines = new ArrayList<>(msLogs.length);
|
||||||
|
if (msLogs.length > 0) {
|
||||||
|
int index = msLogIndex;
|
||||||
|
do {
|
||||||
|
index--;
|
||||||
|
if (index == -1) index = msLogs.length - 1;
|
||||||
|
String logLine = msLogs[index];
|
||||||
|
if (logLine == null)
|
||||||
|
break;
|
||||||
|
lines.add(msLogs[index]);
|
||||||
|
}
|
||||||
|
while (index != msLogIndex);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public synchronized static String getAllLogLines() {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
ArrayList<String> lines = getAllLogLinesList();
|
||||||
|
//now to build the string
|
||||||
|
StringBuilder sb = new StringBuilder("Log contains " + lines.size() + " lines:");
|
||||||
|
while (lines.size() > 0) {
|
||||||
|
String line = lines.remove(lines.size() - 1);
|
||||||
|
sb.append(NEW_LINE);
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} else {
|
||||||
|
return "Not supported in RELEASE mode!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void v(String TAG, String text, Object... args) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.v(TAG, msg);
|
||||||
|
addLog(LVL_V, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void v(String TAG, String text, Throwable t) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
msLogger.v(TAG, text + NEW_LINE + t);
|
||||||
|
addLog(LVL_V, TAG, text, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void d(String TAG, String text) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
msLogger.d(TAG, text);
|
||||||
|
addLog(LVL_D, TAG, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void d(String TAG, String text, Object... args) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.d(TAG, msg);
|
||||||
|
addLog(LVL_D, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void d(String TAG, String text, Throwable t) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
msLogger.d(TAG, text + NEW_LINE + t);
|
||||||
|
addLog(LVL_D, TAG, text, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void yell(String TAG, String text, Object... args) {
|
||||||
|
if (BuildConfig.TESTING_BUILD) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.yell(TAG, msg);
|
||||||
|
addLog(LVL_YELL, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void i(String TAG, String text, Object... args) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.i(TAG, msg);
|
||||||
|
addLog(LVL_I, TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void i(String TAG, String text, Throwable t) {
|
||||||
|
msLogger.i(TAG, text + NEW_LINE + t);
|
||||||
|
addLog(LVL_I, TAG, text, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void w(String TAG, String text, Object... args) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.w(TAG, msg);
|
||||||
|
addLog(LVL_W, TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void w(String TAG, String text, Throwable t) {
|
||||||
|
msLogger.w(TAG, text + NEW_LINE + t);
|
||||||
|
addLog(LVL_W, TAG, text, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void e(String TAG, String text, Object... args) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.e(TAG, msg);
|
||||||
|
addLog(LVL_E, TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: remove this method
|
||||||
|
public synchronized static void e(String TAG, String text, Throwable t) {
|
||||||
|
msLogger.e(TAG, text + NEW_LINE + t);
|
||||||
|
addLog(LVL_E, TAG, text, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void w(String TAG, Throwable e, String text, Object... args) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
msLogger.e(TAG, msg + NEW_LINE + e);
|
||||||
|
addLog(LVL_E, TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void wtf(String TAG, String text, Object... args) {
|
||||||
|
String msg = args == null ? text : msFormatter.format(text, args).toString();
|
||||||
|
msFormatBuilder.setLength(0);
|
||||||
|
addLog(LVL_WTF, TAG, msg);
|
||||||
|
msLogger.wtf(TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void wtf(String TAG, String text, Throwable t) {
|
||||||
|
addLog(LVL_WTF, TAG, text, t);
|
||||||
|
msLogger.wtf(TAG, text + NEW_LINE + t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStackTrace(Throwable ex) {
|
||||||
|
StackTraceElement[] stackTrace = ex.getStackTrace();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (StackTraceElement element : stackTrace) {
|
||||||
|
sb.append("at ");//this is required for easy Proguard decoding.
|
||||||
|
sb.append(element.toString());
|
||||||
|
sb.append(NEW_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ex.getCause() == null)
|
||||||
|
return sb.toString();
|
||||||
|
else {
|
||||||
|
ex = ex.getCause();
|
||||||
|
String cause = getStackTrace(ex);
|
||||||
|
sb.append("*** Cause: ").append(ex.getClass().getName());
|
||||||
|
sb.append(NEW_LINE);
|
||||||
|
sb.append("** Message: ").append(ex.getMessage());
|
||||||
|
sb.append(NEW_LINE);
|
||||||
|
sb.append("** Stack track: ").append(cause);
|
||||||
|
sb.append(NEW_LINE);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doesn't do anything. For release.
|
||||||
|
*/
|
||||||
|
public class NullLogProvider implements LogProvider {
|
||||||
|
@Override
|
||||||
|
public void v(String TAG, String text) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void d(String TAG, String text) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void yell(String TAG, String text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void i(String TAG, String text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void w(String TAG, String text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void e(String TAG, String text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wtf(String TAG, String text) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,825 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class XmlUtils {
|
||||||
|
|
||||||
|
public static void skipCurrentTag(XmlPullParser parser)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
int outerDepth = parser.getDepth();
|
||||||
|
int type;
|
||||||
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||||
|
&& (type != XmlPullParser.END_TAG
|
||||||
|
|| parser.getDepth() > outerDepth)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int convertValueToList(CharSequence value, String[] options, int defaultValue) {
|
||||||
|
if (null != value) {
|
||||||
|
for (int i = 0; i < options.length; i++) {
|
||||||
|
if (value.equals(options[i]))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean convertValueToBoolean(CharSequence value, boolean defaultValue) {
|
||||||
|
boolean result = false;
|
||||||
|
|
||||||
|
if (null == value)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if (value.equals("1")
|
||||||
|
|| value.equals("true")
|
||||||
|
|| value.equals("TRUE"))
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int convertValueToInt(CharSequence charSeq, int defaultValue) {
|
||||||
|
if (null == charSeq)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
String nm = charSeq.toString();
|
||||||
|
|
||||||
|
// XXX This code is copied from Integer.decode() so we don't
|
||||||
|
// have to instantiate an Integer!
|
||||||
|
|
||||||
|
int value;
|
||||||
|
int sign = 1;
|
||||||
|
int index = 0;
|
||||||
|
int len = nm.length();
|
||||||
|
int base = 10;
|
||||||
|
|
||||||
|
if ('-' == nm.charAt(0)) {
|
||||||
|
sign = -1;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('0' == nm.charAt(index)) {
|
||||||
|
// Quick check for a zero by itself
|
||||||
|
if (index == (len - 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char c = nm.charAt(index + 1);
|
||||||
|
|
||||||
|
if ('x' == c || 'X' == c) {
|
||||||
|
index += 2;
|
||||||
|
base = 16;
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
base = 8;
|
||||||
|
}
|
||||||
|
} else if ('#' == nm.charAt(index)) {
|
||||||
|
index++;
|
||||||
|
base = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.parseInt(nm.substring(index), base) * sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int convertValueToUnsignedInt(String value, int defaultValue) {
|
||||||
|
if (null == value)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return parseUnsignedIntAttribute(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int parseUnsignedIntAttribute(CharSequence charSeq) {
|
||||||
|
String value = charSeq.toString();
|
||||||
|
|
||||||
|
long bits;
|
||||||
|
int index = 0;
|
||||||
|
int len = value.length();
|
||||||
|
int base = 10;
|
||||||
|
|
||||||
|
if ('0' == value.charAt(index)) {
|
||||||
|
// Quick check for zero by itself
|
||||||
|
if (index == (len - 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char c = value.charAt(index + 1);
|
||||||
|
|
||||||
|
if ('x' == c || 'X' == c) { // check for hex
|
||||||
|
index += 2;
|
||||||
|
base = 16;
|
||||||
|
} else { // check for octal
|
||||||
|
index++;
|
||||||
|
base = 8;
|
||||||
|
}
|
||||||
|
} else if ('#' == value.charAt(index)) {
|
||||||
|
index++;
|
||||||
|
base = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) Long.parseLong(value.substring(index), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten a Map into an output stream as XML. The map can later be
|
||||||
|
* read back with readMapXml().
|
||||||
|
*
|
||||||
|
* @param val The map to be flattened.
|
||||||
|
* @param out Where to write the XML data.
|
||||||
|
*
|
||||||
|
* @see #writeMapXml(Map, String, XmlSerializer)
|
||||||
|
* @see #writeListXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
* @see #readMapXml
|
||||||
|
*/
|
||||||
|
// public static final void writeMapXml(Map val, OutputStream out)
|
||||||
|
// throws XmlPullParserException, java.io.IOException {
|
||||||
|
// XmlSerializer serializer = new FastXmlSerializer();
|
||||||
|
// serializer.setOutput(out, "utf-8");
|
||||||
|
// serializer.startDocument(null, true);
|
||||||
|
// serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||||
|
// writeMapXml(val, null, serializer);
|
||||||
|
// serializer.endDocument();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten a List into an output stream as XML. The list can later be
|
||||||
|
* read back with readListXml().
|
||||||
|
*
|
||||||
|
* @param val The list to be flattened.
|
||||||
|
* @param out Where to write the XML data.
|
||||||
|
* @see #writeListXml(List, String, XmlSerializer)
|
||||||
|
* @see #writeMapXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
* @see #readListXml
|
||||||
|
*/
|
||||||
|
public static void writeListXml(List val, OutputStream out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
XmlSerializer serializer = Xml.newSerializer();
|
||||||
|
serializer.setOutput(out, "utf-8");
|
||||||
|
serializer.startDocument(null, true);
|
||||||
|
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||||
|
writeListXml(val, null, serializer);
|
||||||
|
serializer.endDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten a Map into an XmlSerializer. The map can later be read back
|
||||||
|
* with readThisMapXml().
|
||||||
|
*
|
||||||
|
* @param val The map to be flattened.
|
||||||
|
* @param name Name attribute to include with this list's tag, or null for
|
||||||
|
* none.
|
||||||
|
* @param out XmlSerializer to write the map into.
|
||||||
|
* @see #writeListXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
* @see #readMapXml
|
||||||
|
*/
|
||||||
|
public static void writeMapXml(Map val, String name, XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
if (val == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set s = val.entrySet();
|
||||||
|
Iterator i = s.iterator();
|
||||||
|
|
||||||
|
out.startTag(null, "map");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
Map.Entry e = (Map.Entry) i.next();
|
||||||
|
writeValueXml(e.getValue(), (String) e.getKey(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.endTag(null, "map");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten a List into an XmlSerializer. The list can later be read back
|
||||||
|
* with readThisListXml().
|
||||||
|
*
|
||||||
|
* @param val The list to be flattened.
|
||||||
|
* @param name Name attribute to include with this list's tag, or null for
|
||||||
|
* none.
|
||||||
|
* @param out XmlSerializer to write the list into.
|
||||||
|
* @see #writeListXml(List, OutputStream)
|
||||||
|
* @see #writeMapXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
* @see #readListXml
|
||||||
|
*/
|
||||||
|
public static void writeListXml(List val, String name, XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
if (val == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.startTag(null, "list");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int N = val.size();
|
||||||
|
int i = 0;
|
||||||
|
while (i < N) {
|
||||||
|
writeValueXml(val.get(i), null, out);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.endTag(null, "list");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeSetXml(Set val, String name, XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
if (val == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.startTag(null, "set");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object v : val) {
|
||||||
|
writeValueXml(v, null, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.endTag(null, "set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten a byte[] into an XmlSerializer. The list can later be read back
|
||||||
|
* with readThisByteArrayXml().
|
||||||
|
*
|
||||||
|
* @param val The byte array to be flattened.
|
||||||
|
* @param name Name attribute to include with this array's tag, or null for
|
||||||
|
* none.
|
||||||
|
* @param out XmlSerializer to write the array into.
|
||||||
|
* @see #writeMapXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
*/
|
||||||
|
public static void writeByteArrayXml(byte[] val, String name,
|
||||||
|
XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
|
||||||
|
if (val == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.startTag(null, "byte-array");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int N = val.length;
|
||||||
|
out.attribute(null, "num", Integer.toString(N));
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(val.length * 2);
|
||||||
|
for (byte b : val) {
|
||||||
|
int h = b >> 4;
|
||||||
|
sb.append(h >= 10 ? ('a' + h - 10) : ('0' + h));
|
||||||
|
h = b & 0xff;
|
||||||
|
sb.append(h >= 10 ? ('a' + h - 10) : ('0' + h));
|
||||||
|
}
|
||||||
|
|
||||||
|
out.text(sb.toString());
|
||||||
|
|
||||||
|
out.endTag(null, "byte-array");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten an int[] into an XmlSerializer. The list can later be read back
|
||||||
|
* with readThisIntArrayXml().
|
||||||
|
*
|
||||||
|
* @param val The int array to be flattened.
|
||||||
|
* @param name Name attribute to include with this array's tag, or null for
|
||||||
|
* none.
|
||||||
|
* @param out XmlSerializer to write the array into.
|
||||||
|
* @see #writeMapXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
* @see #readThisIntArrayXml
|
||||||
|
*/
|
||||||
|
public static void writeIntArrayXml(int[] val, String name,
|
||||||
|
XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
|
||||||
|
if (val == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.startTag(null, "int-array");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int N = val.length;
|
||||||
|
out.attribute(null, "num", Integer.toString(N));
|
||||||
|
|
||||||
|
for (int aVal : val) {
|
||||||
|
out.startTag(null, "item");
|
||||||
|
out.attribute(null, "value", Integer.toString(aVal));
|
||||||
|
out.endTag(null, "item");
|
||||||
|
}
|
||||||
|
|
||||||
|
out.endTag(null, "int-array");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flatten an object's value into an XmlSerializer. The value can later
|
||||||
|
* be read back with readThisValueXml().
|
||||||
|
* <p/>
|
||||||
|
* Currently supported value types are: null, String, Integer, Long,
|
||||||
|
* Float, Double Boolean, Map, List.
|
||||||
|
*
|
||||||
|
* @param v The object to be flattened.
|
||||||
|
* @param name Name attribute to include with this value's tag, or null
|
||||||
|
* for none.
|
||||||
|
* @param out XmlSerializer to write the object into.
|
||||||
|
* @see #writeMapXml
|
||||||
|
* @see #writeListXml
|
||||||
|
* @see #readValueXml
|
||||||
|
*/
|
||||||
|
public static void writeValueXml(Object v, String name, XmlSerializer out)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
String typeStr;
|
||||||
|
if (v == null) {
|
||||||
|
out.startTag(null, "null");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
out.endTag(null, "null");
|
||||||
|
return;
|
||||||
|
} else if (v instanceof String) {
|
||||||
|
out.startTag(null, "string");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
out.text(v.toString());
|
||||||
|
out.endTag(null, "string");
|
||||||
|
return;
|
||||||
|
} else if (v instanceof Integer) {
|
||||||
|
typeStr = "int";
|
||||||
|
} else if (v instanceof Long) {
|
||||||
|
typeStr = "long";
|
||||||
|
} else if (v instanceof Float) {
|
||||||
|
typeStr = "float";
|
||||||
|
} else if (v instanceof Double) {
|
||||||
|
typeStr = "double";
|
||||||
|
} else if (v instanceof Boolean) {
|
||||||
|
typeStr = "boolean";
|
||||||
|
} else if (v instanceof byte[]) {
|
||||||
|
writeByteArrayXml((byte[]) v, name, out);
|
||||||
|
return;
|
||||||
|
} else if (v instanceof int[]) {
|
||||||
|
writeIntArrayXml((int[]) v, name, out);
|
||||||
|
return;
|
||||||
|
} else if (v instanceof Map) {
|
||||||
|
writeMapXml((Map) v, name, out);
|
||||||
|
return;
|
||||||
|
} else if (v instanceof List) {
|
||||||
|
writeListXml((List) v, name, out);
|
||||||
|
return;
|
||||||
|
} else if (v instanceof Set) {
|
||||||
|
writeSetXml((Set) v, name, out);
|
||||||
|
return;
|
||||||
|
} else if (v instanceof CharSequence) {
|
||||||
|
// XXX This is to allow us to at least write something if
|
||||||
|
// we encounter styled text... but it means we will drop all
|
||||||
|
// of the styling information. :(
|
||||||
|
out.startTag(null, "string");
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
out.text(v.toString());
|
||||||
|
out.endTag(null, "string");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("writeValueXml: unable to write value " + v);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.startTag(null, typeStr);
|
||||||
|
if (name != null) {
|
||||||
|
out.attribute(null, "name", name);
|
||||||
|
}
|
||||||
|
out.attribute(null, "value", v.toString());
|
||||||
|
out.endTag(null, typeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a HashMap from an InputStream containing XML. The stream can
|
||||||
|
* previously have been written by writeMapXml().
|
||||||
|
*
|
||||||
|
* @param in The InputStream from which to read.
|
||||||
|
* @return HashMap The resulting map.
|
||||||
|
* @see #readListXml
|
||||||
|
* @see #readValueXml
|
||||||
|
* @see #readThisMapXml
|
||||||
|
* #see #writeMapXml
|
||||||
|
*/
|
||||||
|
public static HashMap readMapXml(InputStream in)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setInput(in, null);
|
||||||
|
return (HashMap) readValueXml(parser, new String[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an ArrayList from an InputStream containing XML. The stream can
|
||||||
|
* previously have been written by writeListXml().
|
||||||
|
*
|
||||||
|
* @param in The InputStream from which to read.
|
||||||
|
* @return ArrayList The resulting list.
|
||||||
|
* @see #readMapXml
|
||||||
|
* @see #readValueXml
|
||||||
|
* @see #readThisListXml
|
||||||
|
* @see #writeListXml
|
||||||
|
*/
|
||||||
|
public static ArrayList readListXml(InputStream in)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setInput(in, null);
|
||||||
|
return (ArrayList) readValueXml(parser, new String[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a HashSet from an InputStream containing XML. The stream can
|
||||||
|
* previously have been written by writeSetXml().
|
||||||
|
*
|
||||||
|
* @param in The InputStream from which to read.
|
||||||
|
* @return HashSet The resulting set.
|
||||||
|
* @throws XmlPullParserException
|
||||||
|
* @throws java.io.IOException
|
||||||
|
* @see #readValueXml
|
||||||
|
* @see #readThisSetXml
|
||||||
|
* @see #writeSetXml
|
||||||
|
*/
|
||||||
|
public static HashSet readSetXml(InputStream in)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setInput(in, null);
|
||||||
|
return (HashSet) readValueXml(parser, new String[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a HashMap object from an XmlPullParser. The XML data could
|
||||||
|
* previously have been generated by writeMapXml(). The XmlPullParser
|
||||||
|
* must be positioned <em>after</em> the tag that begins the map.
|
||||||
|
*
|
||||||
|
* @param parser The XmlPullParser from which to read the map data.
|
||||||
|
* @param endTag Name of the tag that will end the map, usually "map".
|
||||||
|
* @param name An array of one string, used to return the name attribute
|
||||||
|
* of the map's tag.
|
||||||
|
* @return HashMap The newly generated map.
|
||||||
|
* @see #readMapXml
|
||||||
|
*/
|
||||||
|
public static HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
Object val = readThisValueXml(parser, name);
|
||||||
|
if (name[0] != null) {
|
||||||
|
map.put(name[0], val);
|
||||||
|
} else {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Map value without name attribute: " + parser.getName());
|
||||||
|
}
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(endTag)) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Expected " + endTag + " end tag at: " + parser.getName());
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Document ended before " + endTag + " end tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an ArrayList object from an XmlPullParser. The XML data could
|
||||||
|
* previously have been generated by writeListXml(). The XmlPullParser
|
||||||
|
* must be positioned <em>after</em> the tag that begins the list.
|
||||||
|
*
|
||||||
|
* @param parser The XmlPullParser from which to read the list data.
|
||||||
|
* @param endTag Name of the tag that will end the list, usually "list".
|
||||||
|
* @param name An array of one string, used to return the name attribute
|
||||||
|
* of the list's tag.
|
||||||
|
* @return HashMap The newly generated list.
|
||||||
|
* @see #readListXml
|
||||||
|
*/
|
||||||
|
public static ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
ArrayList<Object> list = new ArrayList<>();
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
Object val = readThisValueXml(parser, name);
|
||||||
|
list.add(val);
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(endTag)) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Expected " + endTag + " end tag at: " + parser.getName());
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Document ended before " + endTag + " end tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a HashSet object from an XmlPullParser. The XML data could previously
|
||||||
|
* have been generated by writeSetXml(). The XmlPullParser must be positioned
|
||||||
|
* <em>after</em> the tag that begins the set.
|
||||||
|
*
|
||||||
|
* @param parser The XmlPullParser from which to read the set data.
|
||||||
|
* @param endTag Name of the tag that will end the set, usually "set".
|
||||||
|
* @param name An array of one string, used to return the name attribute
|
||||||
|
* of the set's tag.
|
||||||
|
* @return HashSet The newly generated set.
|
||||||
|
* @throws XmlPullParserException
|
||||||
|
* @throws java.io.IOException
|
||||||
|
* @see #readSetXml
|
||||||
|
*/
|
||||||
|
public static HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
HashSet<Object> set = new HashSet<>();
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
Object val = readThisValueXml(parser, name);
|
||||||
|
set.add(val);
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(endTag)) {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Expected " + endTag + " end tag at: " + parser.getName());
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Document ended before " + endTag + " end tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an int[] object from an XmlPullParser. The XML data could
|
||||||
|
* previously have been generated by writeIntArrayXml(). The XmlPullParser
|
||||||
|
* must be positioned <em>after</em> the tag that begins the list.
|
||||||
|
*
|
||||||
|
* @param parser The XmlPullParser from which to read the list data.
|
||||||
|
* @param endTag Name of the tag that will end the list, usually "list".
|
||||||
|
* @param name An array of one string, used to return the name attribute
|
||||||
|
* of the list's tag.
|
||||||
|
* @return Returns a newly generated int[].
|
||||||
|
* @see #readListXml
|
||||||
|
*/
|
||||||
|
public static int[] readThisIntArrayXml(XmlPullParser parser, String endTag, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
|
||||||
|
int num;
|
||||||
|
try {
|
||||||
|
num = Integer.parseInt(parser.getAttributeValue(null, "num"));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Need num attribute in byte-array");
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Not a number in num attribute in byte-array");
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] array = new int[num];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
if (parser.getName().equals("item")) {
|
||||||
|
try {
|
||||||
|
array[i] = Integer.parseInt(
|
||||||
|
parser.getAttributeValue(null, "value"));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Need value attribute in item");
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Not a number in value attribute in item");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Expected item tag at: " + parser.getName());
|
||||||
|
}
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(endTag)) {
|
||||||
|
return array;
|
||||||
|
} else if (parser.getName().equals("item")) {
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Expected " + endTag + " end tag at: "
|
||||||
|
+ parser.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Document ended before " + endTag + " end tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a flattened object from an XmlPullParser. The XML data could
|
||||||
|
* previously have been written with writeMapXml(), writeListXml(), or
|
||||||
|
* writeValueXml(). The XmlPullParser must be positioned <em>at</em> the
|
||||||
|
* tag that defines the value.
|
||||||
|
*
|
||||||
|
* @param parser The XmlPullParser from which to read the object.
|
||||||
|
* @param name An array of one string, used to return the name attribute
|
||||||
|
* of the value's tag.
|
||||||
|
* @return Object The newly generated value object.
|
||||||
|
* @see #readMapXml
|
||||||
|
* @see #readListXml
|
||||||
|
* @see #writeValueXml
|
||||||
|
*/
|
||||||
|
public static Object readValueXml(XmlPullParser parser, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
return readThisValueXml(parser, name);
|
||||||
|
} else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Unexpected end tag at: " + parser.getName());
|
||||||
|
} else if (eventType == XmlPullParser.TEXT) {
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Unexpected text: " + parser.getText());
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Unexpected end of document");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object readThisValueXml(XmlPullParser parser, String[] name)
|
||||||
|
throws XmlPullParserException, java.io.IOException {
|
||||||
|
final String valueName = parser.getAttributeValue(null, "name");
|
||||||
|
final String tagName = parser.getName();
|
||||||
|
|
||||||
|
Object res;
|
||||||
|
|
||||||
|
switch (tagName) {
|
||||||
|
case "null":
|
||||||
|
res = null;
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
String value = "";
|
||||||
|
int eventType;
|
||||||
|
while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals("string")) {
|
||||||
|
name[0] = valueName;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException("Unexpected end tag in <string>: " + parser.getName());
|
||||||
|
} else if (eventType == XmlPullParser.TEXT) {
|
||||||
|
value += parser.getText();
|
||||||
|
} else if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
throw new XmlPullParserException("Unexpected start tag in <string>: " + parser.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Unexpected end of document in <string>");
|
||||||
|
case "int":
|
||||||
|
res = Integer.parseInt(parser.getAttributeValue(null, "value"));
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
res = Long.valueOf(parser.getAttributeValue(null, "value"));
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
res = Float.valueOf(parser.getAttributeValue(null, "value"));
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
res = Double.valueOf(parser.getAttributeValue(null, "value"));
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
res = Boolean.valueOf(parser.getAttributeValue(null, "value"));
|
||||||
|
break;
|
||||||
|
case "int-array":
|
||||||
|
parser.next();
|
||||||
|
res = readThisIntArrayXml(parser, "int-array", name);
|
||||||
|
name[0] = valueName;
|
||||||
|
return res;
|
||||||
|
case "map":
|
||||||
|
parser.next();
|
||||||
|
res = readThisMapXml(parser, "map", name);
|
||||||
|
name[0] = valueName;
|
||||||
|
return res;
|
||||||
|
case "list":
|
||||||
|
parser.next();
|
||||||
|
res = readThisListXml(parser, "list", name);
|
||||||
|
name[0] = valueName;
|
||||||
|
return res;
|
||||||
|
case "set":
|
||||||
|
parser.next();
|
||||||
|
res = readThisSetXml(parser, "set", name);
|
||||||
|
name[0] = valueName;
|
||||||
|
return res;
|
||||||
|
default:
|
||||||
|
throw new XmlPullParserException(
|
||||||
|
"Unknown tag: " + tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip through to end tag.
|
||||||
|
int eventType;
|
||||||
|
while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(tagName)) {
|
||||||
|
name[0] = valueName;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException("Unexpected end tag in <" + tagName + ">: " + parser.getName());
|
||||||
|
} else if (eventType == XmlPullParser.TEXT) {
|
||||||
|
throw new XmlPullParserException("Unexpected text in <" + tagName + ">: " + parser.getName());
|
||||||
|
} else if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
throw new XmlPullParserException("Unexpected start tag in <" + tagName + ">: " + parser.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new XmlPullParserException("Unexpected end of document in <" + tagName + ">");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException {
|
||||||
|
int type;
|
||||||
|
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != XmlPullParser.START_TAG) {
|
||||||
|
throw new XmlPullParserException("No start tag found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parser.getName().equals(firstElementName)) {
|
||||||
|
throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
|
||||||
|
", expected " + firstElementName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
int type;
|
||||||
|
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Menny Even-Danan
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.anysoftkeyboard.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes writing XML much much easier.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
|
||||||
|
* @author <a href="mailto:menny|AT| evendanan{dot} net">Menny Even Danan - just
|
||||||
|
* added some features on Henri's initial version</a>
|
||||||
|
* @version 0.2
|
||||||
|
*/
|
||||||
|
public class XmlWriter {
|
||||||
|
|
||||||
|
private static final String INDENT_STRING = " ";
|
||||||
|
private final boolean thisIsWriterOwner;// is this instance the owner?
|
||||||
|
private final Writer writer; // underlying writer
|
||||||
|
private final int indentingOffset;
|
||||||
|
private final Stack<String> stack; // of xml entity names
|
||||||
|
private final StringBuffer attrs; // current attribute string
|
||||||
|
private boolean empty; // is the current node empty
|
||||||
|
private boolean justWroteText;
|
||||||
|
private boolean closed; // is the current node closed...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an XmlWriter on top of an existing java.io.Writer.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public XmlWriter(Writer writer, boolean takeOwnership, int indentingOffset, boolean addXmlPrefix)
|
||||||
|
throws IOException {
|
||||||
|
thisIsWriterOwner = takeOwnership;
|
||||||
|
this.indentingOffset = indentingOffset;
|
||||||
|
this.writer = writer;
|
||||||
|
this.closed = true;
|
||||||
|
this.stack = new Stack<String>();
|
||||||
|
this.attrs = new StringBuffer();
|
||||||
|
if (addXmlPrefix)
|
||||||
|
this.writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlWriter(File outputFile) throws IOException {
|
||||||
|
this(new FileWriter(outputFile), true, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin to output an entity.
|
||||||
|
*
|
||||||
|
* @param String name of entity.
|
||||||
|
*/
|
||||||
|
public XmlWriter writeEntity(String name) throws IOException {
|
||||||
|
closeOpeningTag(true);
|
||||||
|
this.closed = false;
|
||||||
|
for (int tabIndex = 0; tabIndex < stack.size() + indentingOffset; tabIndex++)
|
||||||
|
this.writer.write(INDENT_STRING);
|
||||||
|
this.writer.write("<");
|
||||||
|
this.writer.write(name);
|
||||||
|
stack.add(name);
|
||||||
|
this.empty = true;
|
||||||
|
this.justWroteText = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// close off the opening tag
|
||||||
|
private void closeOpeningTag(final boolean newLine) throws IOException {
|
||||||
|
if (!this.closed) {
|
||||||
|
writeAttributes();
|
||||||
|
this.closed = true;
|
||||||
|
this.writer.write(">");
|
||||||
|
if (newLine)
|
||||||
|
this.writer.write("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out all current attributes
|
||||||
|
private void writeAttributes() throws IOException {
|
||||||
|
this.writer.write(this.attrs.toString());
|
||||||
|
this.attrs.setLength(0);
|
||||||
|
this.empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an attribute out for the current entity. Any xml characters in the
|
||||||
|
* value are escaped. Currently it does not actually throw the exception,
|
||||||
|
* but the api is set that way for future changes.
|
||||||
|
*
|
||||||
|
* @param String name of attribute.
|
||||||
|
* @param String value of attribute.
|
||||||
|
*/
|
||||||
|
public XmlWriter writeAttribute(String attr, String value) {
|
||||||
|
this.attrs.append(" ");
|
||||||
|
this.attrs.append(attr);
|
||||||
|
this.attrs.append("=\"");
|
||||||
|
this.attrs.append(escapeXml(value));
|
||||||
|
this.attrs.append("\"");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the current entity. This will throw an exception if it is called when
|
||||||
|
* there is not a currently open entity.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public XmlWriter endEntity() throws IOException {
|
||||||
|
if (this.stack.empty()) {
|
||||||
|
throw new InvalidObjectException("Called endEntity too many times. ");
|
||||||
|
}
|
||||||
|
String name = this.stack.pop();
|
||||||
|
if (name != null) {
|
||||||
|
if (this.empty) {
|
||||||
|
writeAttributes();
|
||||||
|
this.writer.write("/>\n");
|
||||||
|
} else {
|
||||||
|
if (!this.justWroteText) {
|
||||||
|
for (int tabIndex = 0; tabIndex < stack.size() + indentingOffset; tabIndex++)
|
||||||
|
this.writer.write(INDENT_STRING);
|
||||||
|
}
|
||||||
|
this.writer.write("</");
|
||||||
|
this.writer.write(name);
|
||||||
|
this.writer.write(">\n");
|
||||||
|
}
|
||||||
|
this.empty = false;
|
||||||
|
this.closed = true;
|
||||||
|
this.justWroteText = false;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close this writer. It does not close the underlying writer, but does
|
||||||
|
* throw an exception if there are as yet unclosed tags.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!this.stack.empty()) {
|
||||||
|
throw new InvalidObjectException("Tags are not all closed. " +
|
||||||
|
"Possibly, " + this.stack.pop() + " is unclosed. ");
|
||||||
|
}
|
||||||
|
if (thisIsWriterOwner) {
|
||||||
|
this.writer.flush();
|
||||||
|
this.writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output body text. Any xml characters are escaped.
|
||||||
|
*/
|
||||||
|
public XmlWriter writeText(String text) throws IOException {
|
||||||
|
closeOpeningTag(false);
|
||||||
|
this.empty = false;
|
||||||
|
this.justWroteText = true;
|
||||||
|
this.writer.write(escapeXml(text));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static functions lifted from generationjava helper classes
|
||||||
|
// to make the jar smaller.
|
||||||
|
|
||||||
|
// from XmlW
|
||||||
|
static public String escapeXml(String str) {
|
||||||
|
str = replaceString(str, "&", "&");
|
||||||
|
str = replaceString(str, "<", "<");
|
||||||
|
str = replaceString(str, ">", ">");
|
||||||
|
str = replaceString(str, "\"", """);
|
||||||
|
str = replaceString(str, "'", "'");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from StringW
|
||||||
|
static public String replaceString(String text, String repl, String with) {
|
||||||
|
return replaceString(text, repl, with, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a string with another string inside a larger string, for the
|
||||||
|
* first n values of the search string.
|
||||||
|
*
|
||||||
|
* @param text String to do search and replace in
|
||||||
|
* @param repl String to search for
|
||||||
|
* @param with String to replace with
|
||||||
|
* @param n int values to replace
|
||||||
|
* @return String with n values replacEd
|
||||||
|
*/
|
||||||
|
static public String replaceString(String text, String repl, String with, int max) {
|
||||||
|
if (text == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuffer buffer = new StringBuffer(text.length());
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
while ((end = text.indexOf(repl, start)) != -1) {
|
||||||
|
buffer.append(text.substring(start, end)).append(with);
|
||||||
|
start = end + repl.length();
|
||||||
|
|
||||||
|
if (--max == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.append(text.substring(start));
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// static public void test1() throws WritingException {
|
||||||
|
// Writer writer = new java.io.StringWriter();
|
||||||
|
// XmlWriter xmlwriter = new XmlWriter(writer);
|
||||||
|
// xmlwriter.writeEntity("person").writeAttribute("name",
|
||||||
|
// "fred").writeAttribute("age",
|
||||||
|
// "12").writeEntity("phone").writeText("4254343").endEntity().writeEntity("bob").endEntity().endEntity();
|
||||||
|
// xmlwriter.close();
|
||||||
|
// System.err.println(writer.toString());
|
||||||
|
// }
|
||||||
|
// static public void test2() throws WritingException {
|
||||||
|
// Writer writer = new java.io.StringWriter();
|
||||||
|
// XmlWriter xmlwriter = new XmlWriter(writer);
|
||||||
|
// xmlwriter.writeEntity("person");
|
||||||
|
// xmlwriter.writeAttribute("name", "fred");
|
||||||
|
// xmlwriter.writeAttribute("age", "12");
|
||||||
|
// xmlwriter.writeEntity("phone");
|
||||||
|
// xmlwriter.writeText("4254343");
|
||||||
|
// xmlwriter.endEntity();
|
||||||
|
// xmlwriter.writeEntity("bob");
|
||||||
|
// xmlwriter.endEntity();
|
||||||
|
// xmlwriter.endEntity();
|
||||||
|
// xmlwriter.close();
|
||||||
|
// System.err.println(writer.toString());
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import android.util.EventLog;
|
||||||
|
|
||||||
|
public class EventLogTags {
|
||||||
|
public static final int TIME_LEANBACK_IME_INPUT = 270900;
|
||||||
|
public static final int TOTAL_LEANBACK_IME_BACKSPACE = 270902;
|
||||||
|
|
||||||
|
public static void writeTimeLeanbackImeInput(long var0, long var2) {
|
||||||
|
EventLog.writeEvent(270900, new Object[]{var0, var2});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeTotalLeanbackImeBackspace(int var0) {
|
||||||
|
EventLog.writeEvent(270902, var0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,822 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.inputmethodservice.Keyboard.Key;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnHoverListener;
|
||||||
|
import android.view.View.OnLayoutChangeListener;
|
||||||
|
import android.view.View.OnTouchListener;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import com.google.android.pano.util.TouchNavSpaceTracker;
|
||||||
|
import com.liskovsoft.leankeykeyboard.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class LeanbackKeyboardController implements LeanbackKeyboardContainer.VoiceListener, LeanbackKeyboardContainer.DismissListener, OnTouchListener, OnHoverListener, Runnable {
|
||||||
|
public static final int CLICK_MOVEMENT_BLOCK_DURATION_MS = 500;
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
private static final int KEY_CHANGE_HISTORY_SIZE = 10;
|
||||||
|
private static final long KEY_CHANGE_REVERT_TIME_MS = 100L;
|
||||||
|
private static final String TAG = "LbKbController";
|
||||||
|
private boolean clickConsumed;
|
||||||
|
private long lastClickTime;
|
||||||
|
private LeanbackKeyboardContainer mContainer;
|
||||||
|
private InputMethodService mContext;
|
||||||
|
private LeanbackKeyboardController.DoubleClickDetector mDoubleClickDetector;
|
||||||
|
private LeanbackKeyboardContainer.KeyFocus mDownFocus;
|
||||||
|
private Handler mHandler;
|
||||||
|
private LeanbackKeyboardController.InputListener mInputListener;
|
||||||
|
ArrayList<LeanbackKeyboardController.KeyChange> mKeyChangeHistory;
|
||||||
|
private LeanbackKeyboardContainer.KeyFocus mKeyDownKeyFocus;
|
||||||
|
private boolean mKeyDownReceived;
|
||||||
|
private boolean mLongPressHandled;
|
||||||
|
private int mMoveCount;
|
||||||
|
private OnLayoutChangeListener mOnLayoutChangeListener;
|
||||||
|
public float mResizeSquareDistance;
|
||||||
|
private TouchNavSpaceTracker mSpaceTracker;
|
||||||
|
private LeanbackKeyboardContainer.KeyFocus mTempFocus;
|
||||||
|
private PointF mTempPoint;
|
||||||
|
private LeanbackKeyboardController.TouchEventListener mTouchEventListener;
|
||||||
|
private long prevTime;
|
||||||
|
|
||||||
|
public LeanbackKeyboardController(InputMethodService var1, LeanbackKeyboardController.InputListener var2) {
|
||||||
|
this(var1, var2, new TouchNavSpaceTracker(), new LeanbackKeyboardContainer(var1));
|
||||||
|
}
|
||||||
|
|
||||||
|
LeanbackKeyboardController(InputMethodService var1, LeanbackKeyboardController.InputListener var2, TouchNavSpaceTracker var3, LeanbackKeyboardContainer var4) {
|
||||||
|
this.mDoubleClickDetector = new LeanbackKeyboardController.DoubleClickDetector();
|
||||||
|
this.mOnLayoutChangeListener = new OnLayoutChangeListener() {
|
||||||
|
public void onLayoutChange(View var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) {
|
||||||
|
var2 = var4 - var2;
|
||||||
|
var3 = var5 - var3;
|
||||||
|
if (var2 > 0 && var3 > 0) {
|
||||||
|
if (LeanbackKeyboardController.this.mSpaceTracker != null) {
|
||||||
|
LeanbackKeyboardController.this.mSpaceTracker.setPixelSize((float)var2, (float)var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var2 != var8 - var6 || var3 != var9 - var7) {
|
||||||
|
LeanbackKeyboardController.this.initInputView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.mTouchEventListener = new LeanbackKeyboardController.TouchEventListener();
|
||||||
|
this.mDownFocus = new LeanbackKeyboardContainer.KeyFocus();
|
||||||
|
this.mTempFocus = new LeanbackKeyboardContainer.KeyFocus();
|
||||||
|
this.mKeyChangeHistory = new ArrayList(11);
|
||||||
|
this.mTempPoint = new PointF();
|
||||||
|
this.mKeyDownReceived = false;
|
||||||
|
this.mLongPressHandled = false;
|
||||||
|
this.mContext = var1;
|
||||||
|
this.mResizeSquareDistance = var1.getResources().getDimension(R.dimen.resize_move_distance);
|
||||||
|
this.mResizeSquareDistance *= this.mResizeSquareDistance;
|
||||||
|
this.mInputListener = var2;
|
||||||
|
this.setSpaceTracker(var3);
|
||||||
|
this.setKeyboardContainer(var4);
|
||||||
|
this.mContainer.setVoiceListener(this);
|
||||||
|
this.mContainer.setDismissListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean applyLETVFixesDown(int var1) {
|
||||||
|
switch(var1) {
|
||||||
|
case 82:
|
||||||
|
case 85:
|
||||||
|
case 89:
|
||||||
|
case 90:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean applyLETVFixesUp(int var1) {
|
||||||
|
switch(var1) {
|
||||||
|
case 82:
|
||||||
|
this.mContainer.switchToNextKeyboard();
|
||||||
|
break;
|
||||||
|
case 85:
|
||||||
|
this.fakeKeyIndex(0, 2);
|
||||||
|
break;
|
||||||
|
case 89:
|
||||||
|
this.fakeKeyCode(-5);
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
this.fakeKeyCode(32);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginLongClickCountdown() {
|
||||||
|
this.clickConsumed = false;
|
||||||
|
Handler var2 = this.mHandler;
|
||||||
|
Handler var1 = var2;
|
||||||
|
if (var2 == null) {
|
||||||
|
var1 = new Handler();
|
||||||
|
this.mHandler = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var1.removeCallbacks(this);
|
||||||
|
var1.postDelayed(this, (long)1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearKeyIfNecessary() {
|
||||||
|
++this.mMoveCount;
|
||||||
|
if (this.mMoveCount >= 3) {
|
||||||
|
this.mMoveCount = 0;
|
||||||
|
this.mKeyDownKeyFocus = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commitKey() {
|
||||||
|
this.commitKey(this.mContainer.getCurrFocus());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commitKey(LeanbackKeyboardContainer.KeyFocus var1) {
|
||||||
|
if (this.mContainer != null && var1 != null) {
|
||||||
|
switch(var1.type) {
|
||||||
|
case 1:
|
||||||
|
this.mContainer.onVoiceClick();
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
this.mInputListener.onEntry(5, 0, (CharSequence)null);
|
||||||
|
return;
|
||||||
|
case 3:
|
||||||
|
this.mInputListener.onEntry(2, 0, this.mContainer.getSuggestionText(var1.index));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Key var2 = this.mContainer.getKey(var1.type, var1.index);
|
||||||
|
if (var2 != null) {
|
||||||
|
this.handleCommitKeyboardKey(var2.codes[0], var2.label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeClickDown() {
|
||||||
|
this.mContainer.setTouchState(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeClickUp() {
|
||||||
|
LeanbackKeyboardContainer var1 = this.mContainer;
|
||||||
|
this.commitKey(var1.getCurrFocus());
|
||||||
|
var1.setTouchState(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeKeyCode(int var1) {
|
||||||
|
this.mContainer.getCurrFocus().code = var1;
|
||||||
|
this.handleCommitKeyboardKey(var1, (CharSequence)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeKeyIndex(int var1, int var2) {
|
||||||
|
LeanbackKeyboardContainer.KeyFocus var3 = this.mContainer.getCurrFocus();
|
||||||
|
var3.index = var1;
|
||||||
|
var3.type = var2;
|
||||||
|
this.commitKey(var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeLongClickDown() {
|
||||||
|
LeanbackKeyboardContainer var1 = this.mContainer;
|
||||||
|
var1.onKeyLongPress();
|
||||||
|
var1.setTouchState(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fakeLongClickUp() {
|
||||||
|
this.mContainer.setTouchState(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PointF getBestSnapPosition(PointF var1, long var2) {
|
||||||
|
if (this.mKeyChangeHistory.size() <= 1) {
|
||||||
|
return var1;
|
||||||
|
} else {
|
||||||
|
int var4 = 0;
|
||||||
|
|
||||||
|
PointF var5;
|
||||||
|
while(true) {
|
||||||
|
var5 = var1;
|
||||||
|
if (var4 >= this.mKeyChangeHistory.size() - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeanbackKeyboardController.KeyChange var6 = (LeanbackKeyboardController.KeyChange)this.mKeyChangeHistory.get(var4);
|
||||||
|
if (var2 - ((LeanbackKeyboardController.KeyChange)this.mKeyChangeHistory.get(var4 + 1)).time < 100L) {
|
||||||
|
var5 = var6.position;
|
||||||
|
this.mKeyChangeHistory.clear();
|
||||||
|
this.mKeyChangeHistory.add(new LeanbackKeyboardController.KeyChange(var2, var5));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++var4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return var5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PointF getCurrentKeyPosition() {
|
||||||
|
if (this.mContainer != null) {
|
||||||
|
LeanbackKeyboardContainer.KeyFocus var1 = this.mContainer.getCurrFocus();
|
||||||
|
return new PointF((float)var1.rect.centerX(), (float)var1.rect.centerY());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PointF getRelativePosition(View var1, MotionEvent var2) {
|
||||||
|
int[] var5 = new int[2];
|
||||||
|
var1.getLocationOnScreen(var5);
|
||||||
|
float var3 = var2.getRawX();
|
||||||
|
float var4 = var2.getRawY();
|
||||||
|
return new PointF(var3 - (float)var5[0], var4 - (float)var5[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSimplifiedKey(int var1) {
|
||||||
|
int var2 = 23;
|
||||||
|
if (var1 != 23) {
|
||||||
|
byte var3 = 66;
|
||||||
|
var2 = var3;
|
||||||
|
if (var1 != 66) {
|
||||||
|
var2 = var3;
|
||||||
|
if (var1 != 160) {
|
||||||
|
var2 = var1;
|
||||||
|
if (var1 == 96) {
|
||||||
|
var2 = var3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var1 = var2;
|
||||||
|
if (var2 == 97) {
|
||||||
|
var1 = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCommitKeyboardKey(int var1, CharSequence var2) {
|
||||||
|
switch(var1) {
|
||||||
|
case -8:
|
||||||
|
this.mContainer.dismissMiniKeyboard();
|
||||||
|
return;
|
||||||
|
case -7:
|
||||||
|
this.mContainer.startVoiceRecording();
|
||||||
|
return;
|
||||||
|
case -6:
|
||||||
|
this.mContainer.onShiftDoubleClick(this.mContainer.isCapsLockOn());
|
||||||
|
return;
|
||||||
|
case -5:
|
||||||
|
this.mInputListener.onEntry(1, 0, (CharSequence)null);
|
||||||
|
return;
|
||||||
|
case -4:
|
||||||
|
this.mInputListener.onEntry(4, 0, (CharSequence)null);
|
||||||
|
return;
|
||||||
|
case -3:
|
||||||
|
this.mInputListener.onEntry(3, 0, (CharSequence)null);
|
||||||
|
return;
|
||||||
|
case -2:
|
||||||
|
if (Log.isLoggable("LbKbController", Log.DEBUG)) {
|
||||||
|
Log.d("LbKbController", "mode change");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mContainer.onModeChangeClick();
|
||||||
|
return;
|
||||||
|
case -1:
|
||||||
|
if (Log.isLoggable("LbKbController", Log.DEBUG)) {
|
||||||
|
Log.d("LbKbController", "shift");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mContainer.onShiftClick();
|
||||||
|
return;
|
||||||
|
case 32:
|
||||||
|
this.mInputListener.onEntry(0, var1, " ");
|
||||||
|
this.mContainer.onSpaceEntry();
|
||||||
|
return;
|
||||||
|
case 46:
|
||||||
|
this.mInputListener.onEntry(0, var1, var2);
|
||||||
|
this.mContainer.onPeriodEntry();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
this.mInputListener.onEntry(0, var1, var2);
|
||||||
|
this.mContainer.onTextEntry();
|
||||||
|
if (this.mContainer.isMiniKeyboardOnScreen()) {
|
||||||
|
this.mContainer.dismissMiniKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleKeyDownEvent(int var1, int var2) {
|
||||||
|
var1 = this.getSimplifiedKey(var1);
|
||||||
|
boolean var3;
|
||||||
|
boolean var4;
|
||||||
|
if (var1 == 4) {
|
||||||
|
this.mContainer.cancelVoiceRecording();
|
||||||
|
var3 = false;
|
||||||
|
} else if (this.mContainer.isVoiceVisible()) {
|
||||||
|
if (var1 == 22 || var1 == 23 || var1 == 66) {
|
||||||
|
this.mContainer.cancelVoiceRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = true;
|
||||||
|
} else {
|
||||||
|
var4 = true;
|
||||||
|
var3 = var4;
|
||||||
|
switch(var1) {
|
||||||
|
case 19:
|
||||||
|
var3 = this.onDirectionalMove(8);
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
var3 = this.onDirectionalMove(2);
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
var3 = this.onDirectionalMove(1);
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
var3 = this.onDirectionalMove(4);
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
case 66:
|
||||||
|
if (var2 == 0) {
|
||||||
|
this.mMoveCount = 0;
|
||||||
|
this.mKeyDownKeyFocus = new LeanbackKeyboardContainer.KeyFocus();
|
||||||
|
this.mKeyDownKeyFocus.set(this.mContainer.getCurrFocus());
|
||||||
|
} else if (var2 == 1 && this.handleKeyLongPress(var1)) {
|
||||||
|
this.mKeyDownKeyFocus = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = var4;
|
||||||
|
if (this.isKeyHandledOnKeyDown(this.mContainer.getCurrKeyCode())) {
|
||||||
|
this.commitKey();
|
||||||
|
var3 = var4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 99:
|
||||||
|
this.handleCommitKeyboardKey(-5, (CharSequence)null);
|
||||||
|
var3 = var4;
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
this.handleCommitKeyboardKey(32, (CharSequence)null);
|
||||||
|
var3 = var4;
|
||||||
|
break;
|
||||||
|
case 102:
|
||||||
|
this.handleCommitKeyboardKey(-3, (CharSequence)null);
|
||||||
|
var3 = var4;
|
||||||
|
break;
|
||||||
|
case 103:
|
||||||
|
this.handleCommitKeyboardKey(-4, (CharSequence)null);
|
||||||
|
var3 = var4;
|
||||||
|
case 106:
|
||||||
|
case 107:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var3 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var4 = var3;
|
||||||
|
if (!var3) {
|
||||||
|
var4 = this.applyLETVFixesDown(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleKeyLongPress(int var1) {
|
||||||
|
boolean var2;
|
||||||
|
if (this.isEnterKey(var1) && this.mContainer.onKeyLongPress()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mLongPressHandled = var2;
|
||||||
|
if (this.mContainer.isMiniKeyboardOnScreen()) {
|
||||||
|
Log.d("LbKbController", "mini keyboard shown after long press");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mLongPressHandled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleKeyUpEvent(int var1, long var2) {
|
||||||
|
var1 = this.getSimplifiedKey(var1);
|
||||||
|
boolean var4;
|
||||||
|
boolean var5;
|
||||||
|
if (var1 == 4) {
|
||||||
|
var4 = false;
|
||||||
|
} else if (this.mContainer.isVoiceVisible()) {
|
||||||
|
var4 = true;
|
||||||
|
} else {
|
||||||
|
var5 = true;
|
||||||
|
var4 = var5;
|
||||||
|
switch(var1) {
|
||||||
|
case 19:
|
||||||
|
case 20:
|
||||||
|
case 21:
|
||||||
|
case 22:
|
||||||
|
this.clearKeyIfNecessary();
|
||||||
|
var4 = var5;
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
case 66:
|
||||||
|
if (this.mContainer.getCurrKeyCode() == -1) {
|
||||||
|
this.mDoubleClickDetector.addEvent(var2);
|
||||||
|
var4 = var5;
|
||||||
|
} else {
|
||||||
|
var4 = var5;
|
||||||
|
if (!this.isKeyHandledOnKeyDown(this.mContainer.getCurrKeyCode())) {
|
||||||
|
this.commitKey(this.mKeyDownKeyFocus);
|
||||||
|
var4 = var5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 99:
|
||||||
|
case 100:
|
||||||
|
case 102:
|
||||||
|
case 103:
|
||||||
|
break;
|
||||||
|
case 106:
|
||||||
|
this.handleCommitKeyboardKey(-2, (CharSequence)null);
|
||||||
|
var4 = var5;
|
||||||
|
break;
|
||||||
|
case 107:
|
||||||
|
this.handleCommitKeyboardKey(-6, (CharSequence)null);
|
||||||
|
var4 = var5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var4 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var5 = var4;
|
||||||
|
if (!var4) {
|
||||||
|
var5 = this.applyLETVFixesUp(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var5;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initInputView() {
|
||||||
|
this.mContainer.onInitInputView();
|
||||||
|
this.updatePositionToCurrentFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCallAllowed(int var1) {
|
||||||
|
long var2 = System.currentTimeMillis();
|
||||||
|
if (this.prevTime != 0L && var2 - this.prevTime <= (long)(var1 * 3)) {
|
||||||
|
if (var2 - this.prevTime > (long)var1) {
|
||||||
|
this.prevTime = 0L;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.prevTime = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDoubleClick() {
|
||||||
|
long var1 = System.currentTimeMillis();
|
||||||
|
long var3 = this.lastClickTime;
|
||||||
|
if (this.lastClickTime != 0L && var1 - var3 <= (long)300) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.lastClickTime = var1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEnterKey(int var1) {
|
||||||
|
var1 = this.getSimplifiedKey(var1);
|
||||||
|
return var1 == 23 || var1 == 66;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKeyHandledOnKeyDown(int var1) {
|
||||||
|
return var1 == -5 || var1 == -3 || var1 == -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveSelectorToPoint(float var1, float var2) {
|
||||||
|
LeanbackKeyboardContainer var3 = this.mContainer;
|
||||||
|
LeanbackKeyboardContainer.KeyFocus var4 = this.mTempFocus;
|
||||||
|
var3.getBestFocus(new Float(var1), new Float(var2), var4);
|
||||||
|
this.mContainer.setFocus(this.mTempFocus);
|
||||||
|
var3 = this.mContainer;
|
||||||
|
Rect var5 = this.mTempFocus.rect;
|
||||||
|
var3.alignSelector((float)var5.centerX(), (float)var5.centerY(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onDirectionalMove(int var1) {
|
||||||
|
if (this.mContainer.getNextFocusInDirection(var1, this.mDownFocus, this.mTempFocus)) {
|
||||||
|
this.mContainer.setFocus(this.mTempFocus);
|
||||||
|
this.mDownFocus.set(this.mTempFocus);
|
||||||
|
this.clearKeyIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performBestSnap(long var1) {
|
||||||
|
LeanbackKeyboardContainer.KeyFocus var3 = this.mContainer.getCurrFocus();
|
||||||
|
this.mTempPoint.x = (float)var3.rect.centerX();
|
||||||
|
this.mTempPoint.y = (float)var3.rect.centerY();
|
||||||
|
PointF var4 = this.getBestSnapPosition(this.mTempPoint, var1);
|
||||||
|
this.mContainer.getBestFocus(var4.x, var4.y, this.mTempFocus);
|
||||||
|
this.mContainer.setFocus(this.mTempFocus);
|
||||||
|
this.updatePositionToCurrentFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setKeyState(int var1, boolean var2) {
|
||||||
|
LeanbackKeyboardContainer var3 = this.mContainer;
|
||||||
|
LeanbackKeyboardContainer.KeyFocus var4 = var3.getCurrFocus();
|
||||||
|
var4.index = var1;
|
||||||
|
var4.type = 0;
|
||||||
|
byte var5;
|
||||||
|
if (var2) {
|
||||||
|
var5 = 3;
|
||||||
|
} else {
|
||||||
|
var5 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var3.setTouchState(var5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePositionToCurrentFocus() {
|
||||||
|
PointF var1 = this.getCurrentKeyPosition();
|
||||||
|
if (var1 != null && this.mSpaceTracker != null) {
|
||||||
|
this.mSpaceTracker.setPixelPosition(var1.x, var1.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean areSuggestionsEnabled() {
|
||||||
|
return this.mContainer != null ? this.mContainer.areSuggestionsEnabled() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean enableAutoEnterSpace() {
|
||||||
|
return this.mContainer != null ? this.mContainer.enableAutoEnterSpace() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getView() {
|
||||||
|
if (this.mContainer != null) {
|
||||||
|
RelativeLayout var1 = this.mContainer.getView();
|
||||||
|
var1.setClickable(true);
|
||||||
|
var1.setOnTouchListener(this);
|
||||||
|
var1.setOnHoverListener(this);
|
||||||
|
Button var2 = this.mContainer.getGoButton();
|
||||||
|
var2.setOnTouchListener(this);
|
||||||
|
var2.setOnHoverListener(this);
|
||||||
|
var2.setTag("Go");
|
||||||
|
return var1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDismiss(boolean var1) {
|
||||||
|
if (var1) {
|
||||||
|
this.mInputListener.onEntry(8, 0, (CharSequence)null);
|
||||||
|
} else {
|
||||||
|
this.mInputListener.onEntry(7, 0, (CharSequence)null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onGenericMotionEvent(MotionEvent var1) {
|
||||||
|
return this.mSpaceTracker != null && this.mContext != null && this.mContext.isInputViewShown() && this.mSpaceTracker.onGenericMotionEvent(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onHover(View var1, MotionEvent var2) {
|
||||||
|
boolean var4 = this.isCallAllowed(300);
|
||||||
|
boolean var3 = var4;
|
||||||
|
if (var4) {
|
||||||
|
if (var2.getAction() == 7) {
|
||||||
|
PointF var5 = this.getRelativePosition(this.mContainer.getView(), var2);
|
||||||
|
this.moveSelectorToPoint(var5.x, var5.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyDown(int var1, KeyEvent var2) {
|
||||||
|
this.mDownFocus.set(this.mContainer.getCurrFocus());
|
||||||
|
if (this.mSpaceTracker != null && this.mSpaceTracker.onKeyDown(var1, var2)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (this.isEnterKey(var1)) {
|
||||||
|
this.mKeyDownReceived = true;
|
||||||
|
if (var2.getRepeatCount() == 0) {
|
||||||
|
this.mContainer.setTouchState(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.handleKeyDownEvent(var1, var2.getRepeatCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyUp(int var1, KeyEvent var2) {
|
||||||
|
if (this.mSpaceTracker != null && this.mSpaceTracker.onKeyUp(var1, var2)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (this.isEnterKey(var1)) {
|
||||||
|
if (!this.mKeyDownReceived || this.mLongPressHandled) {
|
||||||
|
this.mLongPressHandled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mKeyDownReceived = false;
|
||||||
|
if (this.mContainer.getTouchState() == 3) {
|
||||||
|
this.mContainer.setTouchState(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.handleKeyUpEvent(var1, var2.getEventTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartInput(EditorInfo var1) {
|
||||||
|
if (this.mContainer != null) {
|
||||||
|
this.mContainer.onStartInput(var1);
|
||||||
|
this.initInputView();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartInputView() {
|
||||||
|
this.mKeyDownReceived = false;
|
||||||
|
if (this.mContainer != null) {
|
||||||
|
this.mContainer.onStartInputView();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mDoubleClickDetector.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onTouch(View var1, MotionEvent var2) {
|
||||||
|
Object var3 = var1.getTag();
|
||||||
|
if (var3 != null && "Go".equals(var3)) {
|
||||||
|
this.fakeKeyIndex(0, 2);
|
||||||
|
} else {
|
||||||
|
switch(var2.getAction()) {
|
||||||
|
case 0:
|
||||||
|
this.moveSelectorToPoint(var2.getX(), var2.getY());
|
||||||
|
this.fakeClickDown();
|
||||||
|
this.beginLongClickCountdown();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!this.clickConsumed) {
|
||||||
|
this.clickConsumed = true;
|
||||||
|
if (this.isDoubleClick()) {
|
||||||
|
this.mContainer.onKeyLongPress();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fakeClickUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fakeLongClickUp();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onVoiceResult(String var1) {
|
||||||
|
this.mInputListener.onEntry(6, 0, var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
if (!this.clickConsumed) {
|
||||||
|
this.clickConsumed = true;
|
||||||
|
this.fakeLongClickDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyboardContainer(LeanbackKeyboardContainer var1) {
|
||||||
|
this.mContainer = var1;
|
||||||
|
var1.getView().addOnLayoutChangeListener(this.mOnLayoutChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpaceTracker(TouchNavSpaceTracker var1) {
|
||||||
|
this.mSpaceTracker = var1;
|
||||||
|
var1.setLPFEnabled(true);
|
||||||
|
var1.setKeyEventListener(this.mTouchEventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAddonKeyboard() {
|
||||||
|
this.mContainer.updateAddonKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSuggestions(ArrayList<String> var1) {
|
||||||
|
if (this.mContainer != null) {
|
||||||
|
this.mContainer.updateSuggestions(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DoubleClickDetector {
|
||||||
|
final long DOUBLE_CLICK_TIMEOUT_MS;
|
||||||
|
boolean mFirstClickShiftLocked;
|
||||||
|
long mFirstClickTime;
|
||||||
|
|
||||||
|
private DoubleClickDetector() {
|
||||||
|
this.DOUBLE_CLICK_TIMEOUT_MS = 200L;
|
||||||
|
this.mFirstClickTime = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEvent(long var1) {
|
||||||
|
if (var1 - this.mFirstClickTime > 200L) {
|
||||||
|
this.mFirstClickTime = var1;
|
||||||
|
this.mFirstClickShiftLocked = LeanbackKeyboardController.this.mContainer.isCapsLockOn();
|
||||||
|
LeanbackKeyboardController.this.commitKey();
|
||||||
|
} else {
|
||||||
|
LeanbackKeyboardController.this.mContainer.onShiftDoubleClick(this.mFirstClickShiftLocked);
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.mFirstClickTime = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface InputListener {
|
||||||
|
int ENTRY_TYPE_ACTION = 5;
|
||||||
|
int ENTRY_TYPE_BACKSPACE = 1;
|
||||||
|
int ENTRY_TYPE_DISMISS = 7;
|
||||||
|
int ENTRY_TYPE_LEFT = 3;
|
||||||
|
int ENTRY_TYPE_RIGHT = 4;
|
||||||
|
int ENTRY_TYPE_STRING = 0;
|
||||||
|
int ENTRY_TYPE_SUGGESTION = 2;
|
||||||
|
int ENTRY_TYPE_VOICE = 6;
|
||||||
|
int ENTRY_TYPE_VOICE_DISMISS = 8;
|
||||||
|
|
||||||
|
void onEntry(int var1, int var2, CharSequence var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class KeyChange {
|
||||||
|
public PointF position;
|
||||||
|
public long time;
|
||||||
|
|
||||||
|
public KeyChange(long var1, PointF var3) {
|
||||||
|
this.time = var1;
|
||||||
|
this.position = var3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TouchEventListener implements TouchNavSpaceTracker.KeyEventListener {
|
||||||
|
private TouchEventListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyDown(int var1, KeyEvent var2) {
|
||||||
|
if (LeanbackKeyboardController.this.isEnterKey(var1)) {
|
||||||
|
LeanbackKeyboardController.this.mKeyDownReceived = true;
|
||||||
|
if (var2.getRepeatCount() == 0) {
|
||||||
|
LeanbackKeyboardController.this.mContainer.setTouchState(3);
|
||||||
|
LeanbackKeyboardController.this.mSpaceTracker.blockMovementUntil(var2.getEventTime() + 500L);
|
||||||
|
LeanbackKeyboardController.this.performBestSnap(var2.getEventTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LeanbackKeyboardController.this.handleKeyDownEvent(var1, var2.getRepeatCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyLongPress(int var1, KeyEvent var2) {
|
||||||
|
return LeanbackKeyboardController.this.handleKeyLongPress(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyUp(int var1, KeyEvent var2) {
|
||||||
|
if (LeanbackKeyboardController.this.isEnterKey(var1)) {
|
||||||
|
if (!LeanbackKeyboardController.this.mKeyDownReceived || LeanbackKeyboardController.this.mLongPressHandled) {
|
||||||
|
LeanbackKeyboardController.this.mLongPressHandled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeanbackKeyboardController.this.mKeyDownReceived = false;
|
||||||
|
if (LeanbackKeyboardController.this.mContainer.getTouchState() == 3) {
|
||||||
|
LeanbackKeyboardController.this.mContainer.setTouchState(1);
|
||||||
|
LeanbackKeyboardController.this.mSpaceTracker.unblockMovement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LeanbackKeyboardController.this.handleKeyUpEvent(var1, var2.getEventTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,508 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.Bitmap.Config;
|
||||||
|
import android.graphics.Paint.Align;
|
||||||
|
import android.inputmethodservice.Keyboard;
|
||||||
|
import android.inputmethodservice.Keyboard.Key;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.MeasureSpec;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.liskovsoft.leankeykeyboard.R;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LeanbackKeyboardView extends FrameLayout {
|
||||||
|
public static final int ASCII_PERIOD = 46;
|
||||||
|
public static final int ASCII_SPACE = 32;
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
public static final int KEYCODE_CAPS_LOCK = -6;
|
||||||
|
public static final int KEYCODE_DELETE = -5;
|
||||||
|
public static final int KEYCODE_DISMISS_MINI_KEYBOARD = -8;
|
||||||
|
public static final int KEYCODE_LEFT = -3;
|
||||||
|
public static final int KEYCODE_RIGHT = -4;
|
||||||
|
public static final int KEYCODE_SHIFT = -1;
|
||||||
|
public static final int KEYCODE_SYM_TOGGLE = -2;
|
||||||
|
public static final int KEYCODE_VOICE = -7;
|
||||||
|
private static final int NOT_A_KEY = -1;
|
||||||
|
public static final int SHIFT_LOCKED = 2;
|
||||||
|
public static final int SHIFT_OFF = 0;
|
||||||
|
public static final int SHIFT_ON = 1;
|
||||||
|
private static final String TAG = "LbKbView";
|
||||||
|
private int mBaseMiniKbIndex = -1;
|
||||||
|
private final int mClickAnimDur;
|
||||||
|
private final float mClickedScale;
|
||||||
|
private int mColCount;
|
||||||
|
private View mCurrentFocusView;
|
||||||
|
private boolean mFocusClicked;
|
||||||
|
private int mFocusIndex;
|
||||||
|
private final float mFocusedScale;
|
||||||
|
private final int mInactiveMiniKbAlpha;
|
||||||
|
private ImageView[] mKeyImageViews;
|
||||||
|
private int mKeyTextColor;
|
||||||
|
private int mKeyTextSize;
|
||||||
|
private Keyboard mKeyboard;
|
||||||
|
private LeanbackKeyboardView.KeyHolder[] mKeys;
|
||||||
|
private boolean mMiniKeyboardOnScreen;
|
||||||
|
private int mModeChangeTextSize;
|
||||||
|
private Rect mPadding;
|
||||||
|
private Paint mPaint;
|
||||||
|
private int mRowCount;
|
||||||
|
private int mShiftState;
|
||||||
|
private final int mUnfocusStartDelay;
|
||||||
|
|
||||||
|
public LeanbackKeyboardView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
Resources res = context.getResources();
|
||||||
|
TypedArray styledAttrs = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LeanbackKeyboardView, 0, 0);
|
||||||
|
this.mRowCount = styledAttrs.getInteger(R.styleable.LeanbackKeyboardView_rowCount, -1);
|
||||||
|
this.mColCount = styledAttrs.getInteger(R.styleable.LeanbackKeyboardView_columnCount, -1);
|
||||||
|
this.mKeyTextSize = (int) res.getDimension(R.dimen.key_font_size);
|
||||||
|
this.mPaint = new Paint();
|
||||||
|
this.mPaint.setAntiAlias(true);
|
||||||
|
this.mPaint.setTextSize((float) this.mKeyTextSize);
|
||||||
|
this.mPaint.setTextAlign(Align.CENTER);
|
||||||
|
this.mPaint.setAlpha(255);
|
||||||
|
this.mPadding = new Rect(0, 0, 0, 0);
|
||||||
|
this.mModeChangeTextSize = (int) res.getDimension(R.dimen.function_key_mode_change_font_size);
|
||||||
|
this.mKeyTextColor = res.getColor(R.color.key_text_default);
|
||||||
|
this.mFocusIndex = -1;
|
||||||
|
this.mShiftState = 0;
|
||||||
|
this.mFocusedScale = res.getFraction(R.fraction.focused_scale, 1, 1);
|
||||||
|
this.mClickedScale = res.getFraction(R.fraction.clicked_scale, 1, 1);
|
||||||
|
this.mClickAnimDur = res.getInteger(R.integer.clicked_anim_duration);
|
||||||
|
this.mUnfocusStartDelay = res.getInteger(R.integer.unfocused_anim_delay);
|
||||||
|
this.mInactiveMiniKbAlpha = res.getInteger(R.integer.inactive_mini_kb_alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharSequence adjustCase(LeanbackKeyboardView.KeyHolder keyHolder) {
|
||||||
|
CharSequence label = keyHolder.key.label;
|
||||||
|
CharSequence result = label;
|
||||||
|
if (label != null && label.length() < 3) {
|
||||||
|
boolean flag;
|
||||||
|
if (keyHolder.isInMiniKb && keyHolder.isInvertible) {
|
||||||
|
flag = true;
|
||||||
|
} else {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mKeyboard.isShifted() ^ flag) {
|
||||||
|
result = label.toString().toUpperCase();
|
||||||
|
} else {
|
||||||
|
result = label.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
keyHolder.key.label = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(16)
|
||||||
|
private ImageView createKeyImageView(int keyIndex) {
|
||||||
|
Rect var8 = this.mPadding;
|
||||||
|
int var2 = this.getPaddingLeft();
|
||||||
|
int var3 = this.getPaddingTop();
|
||||||
|
LeanbackKeyboardView.KeyHolder var6 = this.mKeys[keyIndex];
|
||||||
|
Key var7 = var6.key;
|
||||||
|
this.adjustCase(var6);
|
||||||
|
String var5;
|
||||||
|
if (var7.label == null) {
|
||||||
|
var5 = null;
|
||||||
|
} else {
|
||||||
|
var5 = var7.label.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Log.isLoggable("LbKbView", Log.DEBUG)) {
|
||||||
|
Log.d("LbKbView", "LABEL: " + var7.label + "->" + var5);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap var9 = Bitmap.createBitmap(var7.width, var7.height, Config.ARGB_8888);
|
||||||
|
Canvas var10 = new Canvas(var9);
|
||||||
|
Paint var11 = this.mPaint;
|
||||||
|
var11.setColor(this.mKeyTextColor);
|
||||||
|
var10.drawARGB(0, 0, 0, 0);
|
||||||
|
if (var7.icon != null) {
|
||||||
|
if (var7.codes[0] == -1) {
|
||||||
|
switch (this.mShiftState) {
|
||||||
|
case 0:
|
||||||
|
var7.icon = this.getContext().getResources().getDrawable(R.drawable.ic_ime_shift_off);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
var7.icon = this.getContext().getResources().getDrawable(R.drawable.ic_ime_shift_on);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
var7.icon = this.getContext().getResources().getDrawable(R.drawable.ic_ime_shift_lock_on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyIndex = (var7.width - var8.left - var8.right - var7.icon.getIntrinsicWidth()) / 2 + var8.left;
|
||||||
|
int var4 = (var7.height - var8.top - var8.bottom - var7.icon.getIntrinsicHeight()) / 2 + var8.top;
|
||||||
|
var10.translate((float) keyIndex, (float) var4);
|
||||||
|
var7.icon.setBounds(0, 0, var7.icon.getIntrinsicWidth(), var7.icon.getIntrinsicHeight());
|
||||||
|
var7.icon.draw(var10);
|
||||||
|
var10.translate((float) (-keyIndex), (float) (-var4));
|
||||||
|
} else if (var5 != null) {
|
||||||
|
if (var5.length() > 1) {
|
||||||
|
var11.setTextSize((float) this.mModeChangeTextSize);
|
||||||
|
var11.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
|
||||||
|
} else {
|
||||||
|
var11.setTextSize((float) this.mKeyTextSize);
|
||||||
|
var11.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
var10.drawText(var5, (float) ((var7.width - var8.left - var8.right) / 2 + var8.left), (float) ((var7.height - var8.top - var8.bottom) /
|
||||||
|
2) + (var11.getTextSize() - var11.descent()) / 2.0F + (float) var8.top, var11);
|
||||||
|
var11.setShadowLayer(0.0F, 0.0F, 0.0F, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageView var12 = new ImageView(this.getContext());
|
||||||
|
var12.setImageBitmap(var9);
|
||||||
|
var12.setContentDescription(var5);
|
||||||
|
this.addView(var12, new LayoutParams(-2, -2));
|
||||||
|
var12.setX((float) (var7.x + var2));
|
||||||
|
var12.setY((float) (var7.y + var3));
|
||||||
|
if (this.mMiniKeyboardOnScreen && !var6.isInMiniKb) {
|
||||||
|
keyIndex = this.mInactiveMiniKbAlpha;
|
||||||
|
} else {
|
||||||
|
keyIndex = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
var12.setImageAlpha(keyIndex);
|
||||||
|
var12.setVisibility(View.VISIBLE);
|
||||||
|
return var12;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createKeyImageViews(LeanbackKeyboardView.KeyHolder[] keys) {
|
||||||
|
int var3 = keys.length;
|
||||||
|
int var2;
|
||||||
|
if (this.mKeyImageViews != null) {
|
||||||
|
ImageView[] var5 = this.mKeyImageViews;
|
||||||
|
int var4 = var5.length;
|
||||||
|
|
||||||
|
for (var2 = 0; var2 < var4; ++var2) {
|
||||||
|
this.removeView(var5[var2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mKeyImageViews = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var2 = 0; var2 < var3; ++var2) {
|
||||||
|
if (this.mKeyImageViews == null) {
|
||||||
|
this.mKeyImageViews = new ImageView[var3];
|
||||||
|
} else if (this.mKeyImageViews[var2] != null) {
|
||||||
|
this.removeView(this.mKeyImageViews[var2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mKeyImageViews[var2] = this.createKeyImageView(var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeMessages() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setKeys(List<Key> keys) {
|
||||||
|
this.mKeys = new LeanbackKeyboardView.KeyHolder[keys.size()];
|
||||||
|
Iterator var4 = keys.iterator();
|
||||||
|
|
||||||
|
for (int var2 = 0; var2 < this.mKeys.length && var4.hasNext(); ++var2) {
|
||||||
|
Key var3 = (Key) var4.next();
|
||||||
|
this.mKeys[var2] = new LeanbackKeyboardView.KeyHolder(var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean dismissMiniKeyboard() {
|
||||||
|
boolean var1 = false;
|
||||||
|
if (this.mMiniKeyboardOnScreen) {
|
||||||
|
this.mMiniKeyboardOnScreen = false;
|
||||||
|
this.setKeys(this.mKeyboard.getKeys());
|
||||||
|
this.invalidateAllKeys();
|
||||||
|
var1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBaseMiniKbIndex() {
|
||||||
|
return this.mBaseMiniKbIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColCount() {
|
||||||
|
return this.mColCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key getFocusedKey() {
|
||||||
|
return this.mFocusIndex == -1 ? null : this.mKeys[this.mFocusIndex].key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key getKey(int index) {
|
||||||
|
return this.mKeys != null && this.mKeys.length != 0 && index >= 0 && index <= this.mKeys.length ? this.mKeys[index].key : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard getKeyboard() {
|
||||||
|
return this.mKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNearestIndex(float x, float y) {
|
||||||
|
int var7;
|
||||||
|
if (this.mKeys != null && this.mKeys.length != 0) {
|
||||||
|
float var3 = (float) this.getPaddingLeft();
|
||||||
|
float var4 = (float) this.getPaddingTop();
|
||||||
|
float var5 = (float) (this.getMeasuredHeight() - this.getPaddingTop() - this.getPaddingBottom());
|
||||||
|
float var6 = (float) (this.getMeasuredWidth() - this.getPaddingLeft() - this.getPaddingRight());
|
||||||
|
int var9 = this.getRowCount();
|
||||||
|
int var10 = this.getColCount();
|
||||||
|
int var8 = (int) ((y - var4) / var5 * (float) var9);
|
||||||
|
if (var8 < 0) {
|
||||||
|
var7 = 0;
|
||||||
|
} else {
|
||||||
|
var7 = var8;
|
||||||
|
if (var8 >= var9) {
|
||||||
|
var7 = var9 - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var9 = (int) ((x - var3) / var6 * (float) var10);
|
||||||
|
if (var9 < 0) {
|
||||||
|
var8 = 0;
|
||||||
|
} else {
|
||||||
|
var8 = var9;
|
||||||
|
if (var9 >= var10) {
|
||||||
|
var8 = var10 - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var8 += this.mColCount * var7;
|
||||||
|
var7 = var8;
|
||||||
|
if (var8 > 46) {
|
||||||
|
var7 = var8;
|
||||||
|
if (var8 < 53) {
|
||||||
|
var7 = 46;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var8 = var7;
|
||||||
|
if (var7 >= 53) {
|
||||||
|
var8 = var7 - 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var8 < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var7 = var8;
|
||||||
|
if (var8 >= this.mKeys.length) {
|
||||||
|
return this.mKeys.length - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var7 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return var7;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRowCount() {
|
||||||
|
return this.mRowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShiftState() {
|
||||||
|
return this.mShiftState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateAllKeys() {
|
||||||
|
this.createKeyImageViews(this.mKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateKey(int keyIndex) {
|
||||||
|
if (this.mKeys != null && keyIndex >= 0 && keyIndex < this.mKeys.length) {
|
||||||
|
if (this.mKeyImageViews[keyIndex] != null) {
|
||||||
|
this.removeView(this.mKeyImageViews[keyIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mKeyImageViews[keyIndex] = this.createKeyImageView(keyIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMiniKeyboardOnScreen() {
|
||||||
|
return this.mMiniKeyboardOnScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShifted() {
|
||||||
|
return this.mShiftState == 1 || this.mShiftState == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onKeyLongPress() {
|
||||||
|
int var1 = this.mKeys[this.mFocusIndex].key.popupResId;
|
||||||
|
if (var1 != 0) {
|
||||||
|
this.dismissMiniKeyboard();
|
||||||
|
this.mMiniKeyboardOnScreen = true;
|
||||||
|
List var6 = (new Keyboard(this.getContext(), var1)).getKeys();
|
||||||
|
int var3 = var6.size();
|
||||||
|
var1 = this.mFocusIndex;
|
||||||
|
int var2 = this.mFocusIndex / this.mColCount;
|
||||||
|
int var4 = (this.mFocusIndex + var3) / this.mColCount;
|
||||||
|
if (var2 != var4) {
|
||||||
|
var1 = this.mColCount * var4 - var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mBaseMiniKbIndex = var1;
|
||||||
|
|
||||||
|
for (var2 = 0; var2 < var3; ++var2) {
|
||||||
|
Key var7 = (Key) var6.get(var2);
|
||||||
|
var7.x = this.mKeys[var1 + var2].key.x;
|
||||||
|
var7.y = this.mKeys[var1 + var2].key.y;
|
||||||
|
var7.edgeFlags = this.mKeys[var1 + var2].key.edgeFlags;
|
||||||
|
this.mKeys[var1 + var2].key = var7;
|
||||||
|
this.mKeys[var1 + var2].isInMiniKb = true;
|
||||||
|
LeanbackKeyboardView.KeyHolder var8 = this.mKeys[var1 + var2];
|
||||||
|
boolean var5;
|
||||||
|
if (var2 == 0) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var8.isInvertible = var5;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.invalidateAllKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
if (this.mKeyboard == null) {
|
||||||
|
this.setMeasuredDimension(this.getPaddingLeft() + this.getPaddingRight(), this.getPaddingTop() + this.getPaddingBottom());
|
||||||
|
} else {
|
||||||
|
int var3 = this.mKeyboard.getMinWidth() + this.getPaddingLeft() + this.getPaddingRight();
|
||||||
|
heightMeasureSpec = var3;
|
||||||
|
if (MeasureSpec.getSize(widthMeasureSpec) < var3 + 10) {
|
||||||
|
heightMeasureSpec = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setMeasuredDimension(heightMeasureSpec, this.mKeyboard.getHeight() + this.getPaddingTop() + this.getPaddingBottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFocus(int row, int col, boolean clicked) {
|
||||||
|
this.setFocus(this.mColCount * row + col, clicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFocus(int index, boolean clicked) {
|
||||||
|
this.setFocus(index, clicked, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFocus(int index, boolean clicked, boolean showFocusScale) {
|
||||||
|
float var4 = 1.0F;
|
||||||
|
if (this.mKeyImageViews != null && this.mKeyImageViews.length != 0) {
|
||||||
|
int var5;
|
||||||
|
label49:
|
||||||
|
{
|
||||||
|
if (index >= 0) {
|
||||||
|
var5 = index;
|
||||||
|
if (index < this.mKeyImageViews.length) {
|
||||||
|
break label49;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var5 = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var5 != this.mFocusIndex || clicked != this.mFocusClicked) {
|
||||||
|
if (var5 != this.mFocusIndex) {
|
||||||
|
if (this.mFocusIndex != -1) {
|
||||||
|
LeanbackUtils.sendAccessibilityEvent(this.mKeyImageViews[this.mFocusIndex], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var5 != -1) {
|
||||||
|
LeanbackUtils.sendAccessibilityEvent(this.mKeyImageViews[var5], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mCurrentFocusView != null) {
|
||||||
|
this.mCurrentFocusView.animate().scaleX(1.0F).scaleY(1.0F).setInterpolator(LeanbackKeyboardContainer.sMovementInterpolator)
|
||||||
|
.setStartDelay((long) this.mUnfocusStartDelay);
|
||||||
|
this.mCurrentFocusView.animate().setDuration((long) this.mClickAnimDur).setInterpolator(LeanbackKeyboardContainer
|
||||||
|
.sMovementInterpolator).setStartDelay((long) this.mUnfocusStartDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var5 != -1) {
|
||||||
|
if (clicked) {
|
||||||
|
var4 = this.mClickedScale;
|
||||||
|
} else if (showFocusScale) {
|
||||||
|
var4 = this.mFocusedScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mCurrentFocusView = this.mKeyImageViews[var5];
|
||||||
|
this.mCurrentFocusView.animate().scaleX(var4).scaleY(var4).setInterpolator(LeanbackKeyboardContainer.sMovementInterpolator)
|
||||||
|
.setDuration((long) this.mClickAnimDur).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mFocusIndex = var5;
|
||||||
|
this.mFocusClicked = clicked;
|
||||||
|
if (-1 != var5 && !this.mKeys[var5].isInMiniKb) {
|
||||||
|
this.dismissMiniKeyboard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyboard(Keyboard keyboard) {
|
||||||
|
this.removeMessages();
|
||||||
|
this.mKeyboard = keyboard;
|
||||||
|
this.setKeys(this.mKeyboard.getKeys());
|
||||||
|
int var2 = this.mShiftState;
|
||||||
|
this.mShiftState = -1;
|
||||||
|
this.setShiftState(var2);
|
||||||
|
this.requestLayout();
|
||||||
|
this.invalidateAllKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShiftState(int state) {
|
||||||
|
if (this.mShiftState != state) {
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
this.mKeyboard.setShifted(false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
this.mKeyboard.setShifted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mShiftState = state;
|
||||||
|
this.invalidateAllKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyHolder {
|
||||||
|
public boolean isInMiniKb = false;
|
||||||
|
public boolean isInvertible = false;
|
||||||
|
public Key key;
|
||||||
|
|
||||||
|
public KeyHolder(Key var2) {
|
||||||
|
this.key = var2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class LeanbackLocales {
|
||||||
|
public static final Locale ALBANIANIAN;
|
||||||
|
public static final Locale AZERBAIJANI;
|
||||||
|
public static final Locale[] AZERTY;
|
||||||
|
public static final Locale BASQUE_SPANISH;
|
||||||
|
public static final Locale BELGIAN_DUTCH;
|
||||||
|
public static final Locale BRITISH_ENGLISH = new Locale("en", "GB");
|
||||||
|
public static final Locale CANADIAN_FRENCH;
|
||||||
|
public static final Locale CATALAN;
|
||||||
|
public static final Locale CROATIAN;
|
||||||
|
public static final Locale CZECH;
|
||||||
|
public static final Locale DANISH;
|
||||||
|
public static final Locale ENGLISH;
|
||||||
|
public static final Locale ESTONIAN;
|
||||||
|
public static final Locale FINNISH;
|
||||||
|
public static final Locale FRENCH;
|
||||||
|
public static final Locale GALIC_SPANISH;
|
||||||
|
public static final Locale GERMAN;
|
||||||
|
public static final Locale HUNGARIAN;
|
||||||
|
public static final Locale INDIAN_ENGLISH;
|
||||||
|
public static final Locale NORWEGIAN;
|
||||||
|
public static final Locale OTHER_SPANISH;
|
||||||
|
public static final Locale[] QWERTY_AZ;
|
||||||
|
public static final Locale[] QWERTY_CA;
|
||||||
|
public static final Locale[] QWERTY_DA;
|
||||||
|
public static final Locale[] QWERTY_ES_EU;
|
||||||
|
public static final Locale[] QWERTY_ES_US;
|
||||||
|
public static final Locale[] QWERTY_ET;
|
||||||
|
public static final Locale[] QWERTY_FI;
|
||||||
|
public static final Locale[] QWERTY_GB;
|
||||||
|
public static final Locale[] QWERTY_IN;
|
||||||
|
public static final Locale[] QWERTY_NB;
|
||||||
|
public static final Locale[] QWERTY_SV;
|
||||||
|
public static final Locale[] QWERTY_US;
|
||||||
|
public static final Locale[] QWERTZ;
|
||||||
|
public static final Locale[] QWERTZ_CH;
|
||||||
|
public static final Locale SERBIAN;
|
||||||
|
public static final Locale SLOVENIAN;
|
||||||
|
public static final Locale SPAIN_SPANISH;
|
||||||
|
public static final Locale SWEDISH;
|
||||||
|
public static final Locale SWISS_FRENCH;
|
||||||
|
public static final Locale SWISS_GERMAN;
|
||||||
|
public static final Locale SWISS_ITALIAN;
|
||||||
|
|
||||||
|
static {
|
||||||
|
QWERTY_GB = new Locale[]{BRITISH_ENGLISH};
|
||||||
|
INDIAN_ENGLISH = new Locale("en", "IN");
|
||||||
|
QWERTY_IN = new Locale[]{INDIAN_ENGLISH};
|
||||||
|
SPAIN_SPANISH = new Locale("es", "ES");
|
||||||
|
GALIC_SPANISH = new Locale("gl", "ES");
|
||||||
|
BASQUE_SPANISH = new Locale("eu", "ES");
|
||||||
|
QWERTY_ES_EU = new Locale[]{SPAIN_SPANISH, GALIC_SPANISH, BASQUE_SPANISH};
|
||||||
|
OTHER_SPANISH = new Locale("es", "");
|
||||||
|
QWERTY_ES_US = new Locale[]{OTHER_SPANISH};
|
||||||
|
AZERBAIJANI = new Locale("az", "");
|
||||||
|
QWERTY_AZ = new Locale[]{AZERBAIJANI};
|
||||||
|
CATALAN = new Locale("ca", "");
|
||||||
|
QWERTY_CA = new Locale[]{CATALAN};
|
||||||
|
DANISH = new Locale("da", "");
|
||||||
|
QWERTY_DA = new Locale[]{DANISH};
|
||||||
|
ESTONIAN = new Locale("et", "");
|
||||||
|
QWERTY_ET = new Locale[]{ESTONIAN};
|
||||||
|
FINNISH = new Locale("fi", "");
|
||||||
|
QWERTY_FI = new Locale[]{FINNISH};
|
||||||
|
NORWEGIAN = new Locale("nb", "");
|
||||||
|
QWERTY_NB = new Locale[]{NORWEGIAN};
|
||||||
|
SWEDISH = new Locale("sv", "");
|
||||||
|
QWERTY_SV = new Locale[]{SWEDISH};
|
||||||
|
ENGLISH = Locale.ENGLISH;
|
||||||
|
CANADIAN_FRENCH = Locale.CANADA_FRENCH;
|
||||||
|
QWERTY_US = new Locale[]{ENGLISH, CANADIAN_FRENCH};
|
||||||
|
SWISS_GERMAN = new Locale("de", "CH");
|
||||||
|
SWISS_ITALIAN = new Locale("it", "CH");
|
||||||
|
QWERTZ_CH = new Locale[]{SWISS_GERMAN, SWISS_ITALIAN};
|
||||||
|
GERMAN = new Locale("de", "");
|
||||||
|
CROATIAN = new Locale("hr", "");
|
||||||
|
CZECH = new Locale("cs", "");
|
||||||
|
SWISS_FRENCH = new Locale("fr", "CH");
|
||||||
|
HUNGARIAN = new Locale("hu", "");
|
||||||
|
SERBIAN = new Locale("sr", "");
|
||||||
|
SLOVENIAN = new Locale("sl", "");
|
||||||
|
ALBANIANIAN = new Locale("sq", "");
|
||||||
|
QWERTZ = new Locale[]{GERMAN, CROATIAN, CZECH, SWISS_FRENCH, SWISS_ITALIAN, HUNGARIAN, SERBIAN, SLOVENIAN, ALBANIANIAN};
|
||||||
|
FRENCH = Locale.FRENCH;
|
||||||
|
BELGIAN_DUTCH = new Locale("nl", "BE");
|
||||||
|
AZERTY = new Locale[]{FRENCH, BELGIAN_DUTCH};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.inputmethod.CompletionInfo;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import com.liskovsoft.leankeykeyboard.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class LeanbackSuggestionsFactory {
|
||||||
|
private static final boolean DEBUG = Log.isLoggable("LbSuggestionsFactory", Log.DEBUG);
|
||||||
|
private static final int MODE_AUTO_COMPLETE = 2;
|
||||||
|
private static final int MODE_DEFAULT = 0;
|
||||||
|
private static final int MODE_DOMAIN = 1;
|
||||||
|
private static final String TAG = "LbSuggestionsFactory";
|
||||||
|
private InputMethodService mContext;
|
||||||
|
private int mMode;
|
||||||
|
private int mNumSuggestions;
|
||||||
|
private final ArrayList<String> mSuggestions = new ArrayList();
|
||||||
|
|
||||||
|
public LeanbackSuggestionsFactory(InputMethodService var1, int var2) {
|
||||||
|
this.mContext = var1;
|
||||||
|
this.mNumSuggestions = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSuggestions() {
|
||||||
|
this.mSuggestions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createSuggestions() {
|
||||||
|
this.clearSuggestions();
|
||||||
|
if (this.mMode == 1) {
|
||||||
|
String[] var3 = this.mContext.getResources().getStringArray(R.array.common_domains);
|
||||||
|
int var2 = var3.length;
|
||||||
|
|
||||||
|
for(int var1 = 0; var1 < var2; ++var1) {
|
||||||
|
String var4 = var3[var1];
|
||||||
|
this.mSuggestions.add(var4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getSuggestions() {
|
||||||
|
return this.mSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDisplayCompletions(CompletionInfo[] infos) {
|
||||||
|
this.createSuggestions();
|
||||||
|
int len;
|
||||||
|
if (infos == null) {
|
||||||
|
len = 0;
|
||||||
|
} else {
|
||||||
|
len = infos.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < len && this.mSuggestions.size() < this.mNumSuggestions && !TextUtils.isEmpty(infos[i].getText()); ++i) {
|
||||||
|
this.mSuggestions.add(i, infos[i].getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Log.isLoggable("LbSuggestionsFactory", Log.DEBUG)) {
|
||||||
|
for(len = 0; len < this.mSuggestions.size(); ++len) {
|
||||||
|
Log.d("LbSuggestionsFactory", "completion " + len + ": " + (String)this.mSuggestions.get(len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartInput(EditorInfo info) {
|
||||||
|
this.mMode = 0;
|
||||||
|
if ((info.inputType & 65536) != 0) {
|
||||||
|
this.mMode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(LeanbackUtils.getInputTypeClass(info)) {
|
||||||
|
case 1:
|
||||||
|
switch(LeanbackUtils.getInputTypeVariation(info)) {
|
||||||
|
case 32:
|
||||||
|
case 208:
|
||||||
|
this.mMode = 1;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldSuggestionsAmend() {
|
||||||
|
return this.mMode == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.google.android.leanback.ime;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
|
public class LeanbackUtils {
|
||||||
|
private static final int ACCESSIBILITY_DELAY_MS = 250;
|
||||||
|
private static final Handler sAccessibilityHandler = new Handler();
|
||||||
|
|
||||||
|
public static int getImeAction(EditorInfo var0) {
|
||||||
|
return var0.imeOptions & 1073742079;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInputTypeClass(EditorInfo var0) {
|
||||||
|
return var0.inputType & 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInputTypeVariation(EditorInfo var0) {
|
||||||
|
return var0.inputType & 4080;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAlphabet(int var0) {
|
||||||
|
return Character.isLetter(var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendAccessibilityEvent(final View var0, boolean var1) {
|
||||||
|
if (var0 != null && var1) {
|
||||||
|
sAccessibilityHandler.removeCallbacksAndMessages((Object)null);
|
||||||
|
sAccessibilityHandler.postDelayed(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
var0.sendAccessibilityEvent(16384);
|
||||||
|
}
|
||||||
|
}, 250L);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package com.google.android.leanback.ime.voice;
|
||||||
|
|
||||||
|
import android.animation.TimeAnimator;
|
||||||
|
import android.animation.TimeAnimator.TimeListener;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Paint.Style;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import com.liskovsoft.leankeykeyboard.R;
|
||||||
|
|
||||||
|
public class BitmapSoundLevelView extends View {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
private static final int MIC_LEVEL_GUIDELINE_OFFSET = 13;
|
||||||
|
private static final int MIC_PRIMARY_LEVEL_IMAGE_OFFSET = 3;
|
||||||
|
private static final String TAG = "BitmapSoundLevelsView";
|
||||||
|
private TimeAnimator mAnimator;
|
||||||
|
private final int mCenterTranslationX;
|
||||||
|
private final int mCenterTranslationY;
|
||||||
|
private int mCurrentVolume;
|
||||||
|
private Rect mDestRect;
|
||||||
|
private final int mDisableBackgroundColor;
|
||||||
|
private final Paint mEmptyPaint;
|
||||||
|
private final int mEnableBackgroundColor;
|
||||||
|
private SpeechLevelSource mLevelSource;
|
||||||
|
private final int mMinimumLevelSize;
|
||||||
|
private Paint mPaint;
|
||||||
|
private int mPeakLevel;
|
||||||
|
private int mPeakLevelCountDown;
|
||||||
|
private final Bitmap mPrimaryLevel;
|
||||||
|
private final Bitmap mTrailLevel;
|
||||||
|
|
||||||
|
public BitmapSoundLevelView(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapSoundLevelView(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(16)
|
||||||
|
public BitmapSoundLevelView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
this.mEmptyPaint = new Paint();
|
||||||
|
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.BitmapSoundLevelView, defStyleAttr, 0);
|
||||||
|
this.mEnableBackgroundColor = styledAttrs.getColor(R.styleable.BitmapSoundLevelView_enabledBackgroundColor, Color.parseColor("#66FFFFFF"));
|
||||||
|
this.mDisableBackgroundColor = styledAttrs.getColor(R.styleable.BitmapSoundLevelView_disabledBackgroundColor, -1);
|
||||||
|
boolean primaryLevelEnabled = false;
|
||||||
|
boolean peakLevelEnabled = false;
|
||||||
|
int primaryLevelId = 0;
|
||||||
|
if (styledAttrs.hasValue(R.styleable.BitmapSoundLevelView_primaryLevels)) {
|
||||||
|
primaryLevelId = styledAttrs.getResourceId(R.styleable.BitmapSoundLevelView_primaryLevels, R.drawable.vs_reactive_dark);
|
||||||
|
primaryLevelEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int trailLevelId = 0;
|
||||||
|
if (styledAttrs.hasValue(R.styleable.BitmapSoundLevelView_trailLevels)) {
|
||||||
|
trailLevelId = styledAttrs.getResourceId(R.styleable.BitmapSoundLevelView_trailLevels, R.drawable.vs_reactive_light);
|
||||||
|
peakLevelEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mCenterTranslationX = styledAttrs.getDimensionPixelOffset(R.styleable.BitmapSoundLevelView_levelsCenterX, 0);
|
||||||
|
this.mCenterTranslationY = styledAttrs.getDimensionPixelOffset(R.styleable.BitmapSoundLevelView_levelsCenterY, 0);
|
||||||
|
this.mMinimumLevelSize = styledAttrs.getDimensionPixelOffset(R.styleable.BitmapSoundLevelView_minLevelRadius, 0);
|
||||||
|
styledAttrs.recycle();
|
||||||
|
if (primaryLevelEnabled) {
|
||||||
|
this.mPrimaryLevel = BitmapFactory.decodeResource(this.getResources(), primaryLevelId);
|
||||||
|
} else {
|
||||||
|
this.mPrimaryLevel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peakLevelEnabled) {
|
||||||
|
this.mTrailLevel = BitmapFactory.decodeResource(this.getResources(), trailLevelId);
|
||||||
|
} else {
|
||||||
|
this.mTrailLevel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mPaint = new Paint();
|
||||||
|
this.mDestRect = new Rect();
|
||||||
|
this.mEmptyPaint.setFilterBitmap(true);
|
||||||
|
this.mLevelSource = new SpeechLevelSource();
|
||||||
|
this.mLevelSource.setSpeechLevel(0);
|
||||||
|
this.mAnimator = new TimeAnimator();
|
||||||
|
this.mAnimator.setRepeatCount(-1);
|
||||||
|
this.mAnimator.setTimeListener(new TimeListener() {
|
||||||
|
public void onTimeUpdate(TimeAnimator animator, long l, long l1) {
|
||||||
|
BitmapSoundLevelView.this.invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(16)
|
||||||
|
private void startAnimator() {
|
||||||
|
if (!this.mAnimator.isStarted()) {
|
||||||
|
this.mAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(16)
|
||||||
|
private void stopAnimator() {
|
||||||
|
this.mAnimator.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAnimatorState() {
|
||||||
|
if (this.isEnabled()) {
|
||||||
|
this.startAnimator();
|
||||||
|
} else {
|
||||||
|
this.stopAnimator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
this.updateAnimatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
this.stopAnimator();
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
if (this.isEnabled()) {
|
||||||
|
canvas.drawColor(this.mEnableBackgroundColor);
|
||||||
|
final int level = this.mLevelSource.getSpeechLevel();
|
||||||
|
if (level > this.mPeakLevel) {
|
||||||
|
this.mPeakLevel = level;
|
||||||
|
this.mPeakLevelCountDown = 25;
|
||||||
|
} else if (this.mPeakLevelCountDown == 0) {
|
||||||
|
this.mPeakLevel = Math.max(0, this.mPeakLevel - 2);
|
||||||
|
} else {
|
||||||
|
--this.mPeakLevelCountDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level > this.mCurrentVolume) {
|
||||||
|
this.mCurrentVolume += (level - this.mCurrentVolume) / 4;
|
||||||
|
} else {
|
||||||
|
this.mCurrentVolume = (int) ((float) this.mCurrentVolume * 0.95F);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int centerX = this.mCenterTranslationX + this.getWidth() / 2;
|
||||||
|
final int centerY = this.mCenterTranslationY + this.getWidth() / 2;
|
||||||
|
int size;
|
||||||
|
if (this.mTrailLevel != null) {
|
||||||
|
size = (centerX - this.mMinimumLevelSize) * this.mPeakLevel / 100 + this.mMinimumLevelSize;
|
||||||
|
this.mDestRect.set(centerX - size, centerY - size, centerX + size, centerY + size);
|
||||||
|
canvas.drawBitmap(this.mTrailLevel, (Rect) null, this.mDestRect, this.mEmptyPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mPrimaryLevel != null) {
|
||||||
|
size = (centerX - this.mMinimumLevelSize) * this.mCurrentVolume / 100 + this.mMinimumLevelSize;
|
||||||
|
this.mDestRect.set(centerX - size, centerY - size, centerX + size, centerY + size);
|
||||||
|
canvas.drawBitmap(this.mPrimaryLevel, (Rect) null, this.mDestRect, this.mEmptyPaint);
|
||||||
|
this.mPaint.setColor(this.getResources().getColor(R.color.search_mic_background));
|
||||||
|
this.mPaint.setStyle(Style.FILL);
|
||||||
|
canvas.drawCircle((float) centerX, (float) centerY, (float) (this.mMinimumLevelSize - 3), this.mPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mTrailLevel != null && this.mPrimaryLevel != null) {
|
||||||
|
this.mPaint.setColor(this.getResources().getColor(R.color.search_mic_levels_guideline));
|
||||||
|
this.mPaint.setStyle(Style.STROKE);
|
||||||
|
canvas.drawCircle((float) centerX, (float) centerY, (float) (centerX - 13), this.mPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
canvas.drawColor(this.mDisableBackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onWindowFocusChanged(boolean var1) {
|
||||||
|
super.onWindowFocusChanged(var1);
|
||||||
|
if (var1) {
|
||||||
|
this.updateAnimatorState();
|
||||||
|
} else {
|
||||||
|
this.stopAnimator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean var1) {
|
||||||
|
super.setEnabled(var1);
|
||||||
|
this.updateAnimatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevelSource(SpeechLevelSource var1) {
|
||||||
|
this.mLevelSource = var1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package com.google.android.leanback.ime.voice;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.Parcelable.Creator;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.BaseSavedState;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import com.google.android.leanback.ime.LeanbackUtils;
|
||||||
|
import com.liskovsoft.leankeykeyboard.R;
|
||||||
|
|
||||||
|
public class RecognizerView extends RelativeLayout {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
private static final String TAG = "RecognizerView";
|
||||||
|
private RecognizerView.Callback mCallback;
|
||||||
|
private boolean mEnabled;
|
||||||
|
protected ImageView mMicButton;
|
||||||
|
private BitmapSoundLevelView mSoundLevels;
|
||||||
|
private RecognizerView.State mState;
|
||||||
|
|
||||||
|
public RecognizerView(Context var1) {
|
||||||
|
super(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecognizerView(Context var1, AttributeSet var2) {
|
||||||
|
super(var1, var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecognizerView(Context var1, AttributeSet var2, int var3) {
|
||||||
|
super(var1, var2, var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState(RecognizerView.State var1) {
|
||||||
|
this.mState = var1;
|
||||||
|
this.refreshUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getMicButton() {
|
||||||
|
return this.mMicButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
this.refreshUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick() {
|
||||||
|
switch (this.mState) {
|
||||||
|
case MIC_INITIALIZING:
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
case LISTENING:
|
||||||
|
this.mCallback.onCancelRecordingClicked();
|
||||||
|
return;
|
||||||
|
case RECORDING:
|
||||||
|
this.mCallback.onStopRecordingClicked();
|
||||||
|
return;
|
||||||
|
case RECOGNIZING:
|
||||||
|
this.mCallback.onCancelRecordingClicked();
|
||||||
|
return;
|
||||||
|
case NOT_LISTENING:
|
||||||
|
this.mCallback.onStartRecordingClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingSuperCall")
|
||||||
|
@Override
|
||||||
|
public void onFinishInflate() {
|
||||||
|
LayoutInflater.from(this.getContext()).inflate(R.layout.recognizer_view, this, true);
|
||||||
|
this.mSoundLevels = (BitmapSoundLevelView) this.findViewById(R.id.microphone);
|
||||||
|
this.mMicButton = (ImageView) this.findViewById(R.id.recognizer_mic_button);
|
||||||
|
this.mState = RecognizerView.State.NOT_LISTENING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRestoreInstanceState(Parcelable var1) {
|
||||||
|
if (!(var1 instanceof RecognizerView.SavedState)) {
|
||||||
|
super.onRestoreInstanceState(var1);
|
||||||
|
} else {
|
||||||
|
RecognizerView.SavedState var2 = (RecognizerView.SavedState) var1;
|
||||||
|
super.onRestoreInstanceState(var2.getSuperState());
|
||||||
|
this.mState = var2.mState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parcelable onSaveInstanceState() {
|
||||||
|
RecognizerView.SavedState var1 = new RecognizerView.SavedState(super.onSaveInstanceState());
|
||||||
|
var1.mState = this.mState;
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void refreshUi() {
|
||||||
|
if (this.mEnabled) {
|
||||||
|
switch (this.mState) {
|
||||||
|
case MIC_INITIALIZING:
|
||||||
|
this.mMicButton.setImageResource(R.drawable.vs_micbtn_on_selector);
|
||||||
|
this.mSoundLevels.setEnabled(false);
|
||||||
|
return;
|
||||||
|
case LISTENING:
|
||||||
|
this.mMicButton.setImageResource(R.drawable.vs_micbtn_on_selector);
|
||||||
|
this.mSoundLevels.setEnabled(true);
|
||||||
|
return;
|
||||||
|
case RECORDING:
|
||||||
|
this.mMicButton.setImageResource(R.drawable.vs_micbtn_rec_selector);
|
||||||
|
this.mSoundLevels.setEnabled(true);
|
||||||
|
return;
|
||||||
|
case RECOGNIZING:
|
||||||
|
this.mMicButton.setImageResource(R.drawable.vs_micbtn_off_selector);
|
||||||
|
this.mSoundLevels.setEnabled(false);
|
||||||
|
return;
|
||||||
|
case NOT_LISTENING:
|
||||||
|
this.mMicButton.setImageResource(R.drawable.vs_micbtn_off_selector);
|
||||||
|
this.mSoundLevels.setEnabled(false);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(RecognizerView.Callback callback) {
|
||||||
|
this.mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMicEnabled(boolean enabled) {
|
||||||
|
this.mEnabled = enabled;
|
||||||
|
if (enabled) {
|
||||||
|
this.mMicButton.setAlpha(1.0F);
|
||||||
|
this.mMicButton.setImageResource(R.drawable.ic_voice_available);
|
||||||
|
} else {
|
||||||
|
this.mMicButton.setAlpha(0.1F);
|
||||||
|
this.mMicButton.setImageResource(R.drawable.ic_voice_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMicFocused(boolean focused) {
|
||||||
|
if (this.mEnabled) {
|
||||||
|
if (focused) {
|
||||||
|
this.mMicButton.setImageResource(R.drawable.ic_voice_focus);
|
||||||
|
} else {
|
||||||
|
this.mMicButton.setImageResource(R.drawable.ic_voice_available);
|
||||||
|
}
|
||||||
|
|
||||||
|
LeanbackUtils.sendAccessibilityEvent(this.mMicButton, focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeechLevelSource(SpeechLevelSource var1) {
|
||||||
|
this.mSoundLevels.setLevelSource(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInitializingMic() {
|
||||||
|
this.updateState(RecognizerView.State.MIC_INITIALIZING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showListening() {
|
||||||
|
this.updateState(RecognizerView.State.LISTENING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showNotListening() {
|
||||||
|
this.updateState(RecognizerView.State.NOT_LISTENING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showRecognizing() {
|
||||||
|
this.updateState(RecognizerView.State.RECOGNIZING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showRecording() {
|
||||||
|
this.updateState(RecognizerView.State.RECORDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void onCancelRecordingClicked();
|
||||||
|
|
||||||
|
void onStartRecordingClicked();
|
||||||
|
|
||||||
|
void onStopRecordingClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SavedState extends BaseSavedState {
|
||||||
|
public static final Creator<RecognizerView.SavedState> CREATOR = new Creator<RecognizerView.SavedState>() {
|
||||||
|
public RecognizerView.SavedState createFromParcel(Parcel var1) {
|
||||||
|
return new RecognizerView.SavedState(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecognizerView.SavedState[] newArray(int var1) {
|
||||||
|
return new RecognizerView.SavedState[var1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RecognizerView.State mState;
|
||||||
|
|
||||||
|
private SavedState(Parcel var1) {
|
||||||
|
super(var1);
|
||||||
|
this.mState = RecognizerView.State.valueOf(var1.readString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedState(Parcelable var1) {
|
||||||
|
super(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel var1, int var2) {
|
||||||
|
super.writeToParcel(var1, var2);
|
||||||
|
var1.writeString(this.mState.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static enum State {
|
||||||
|
LISTENING, MIC_INITIALIZING, NOT_LISTENING, RECOGNIZING, RECORDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.google.android.leanback.ime.voice;
|
||||||
|
|
||||||
|
public class SpeechLevelSource {
|
||||||
|
private volatile int mSpeechLevel;
|
||||||
|
|
||||||
|
public int getSpeechLevel() {
|
||||||
|
return this.mSpeechLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return this.mSpeechLevel > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.mSpeechLevel = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeechLevel(int var1) {
|
||||||
|
if (var1 >= 0 && var1 <= 100) {
|
||||||
|
this.mSpeechLevel = var1;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package com.google.android.pano.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.VelocityTracker;
|
||||||
|
import android.view.InputDevice.MotionRange;
|
||||||
|
|
||||||
|
public class TouchNavMotionTracker {
|
||||||
|
private static final float MAXIMUM_FLING_VELOCITY = 1270.0F;
|
||||||
|
private static final float MINIMUM_FLING_VELOCITY = 200.0F;
|
||||||
|
private float mCurrX;
|
||||||
|
private float mCurrY;
|
||||||
|
private MotionEvent mDownEvent;
|
||||||
|
private final float mMaxFlingVelocityX;
|
||||||
|
private final float mMaxFlingVelocityY;
|
||||||
|
private final float mMinFlingVelocityX;
|
||||||
|
private final float mMinFlingVelocityY;
|
||||||
|
private final float mMinScrollX;
|
||||||
|
private final float mMinScrollY;
|
||||||
|
private float mPrevX;
|
||||||
|
private float mPrevY;
|
||||||
|
private final float mResolutionX;
|
||||||
|
private final float mResolutionY;
|
||||||
|
private float mScrollX;
|
||||||
|
private float mScrollY;
|
||||||
|
private float mVelX;
|
||||||
|
private float mVelY;
|
||||||
|
private VelocityTracker mVelocityTracker;
|
||||||
|
|
||||||
|
public TouchNavMotionTracker(float resolutionX, float resolutionY, float minScrollDist) {
|
||||||
|
if (resolutionX <= 0.0F) {
|
||||||
|
resolutionX = 6.3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mResolutionX = resolutionX;
|
||||||
|
if (resolutionY <= 0.0F) {
|
||||||
|
resolutionY = 6.3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mResolutionY = resolutionY;
|
||||||
|
this.mMaxFlingVelocityX = this.mResolutionX * 1270.0F;
|
||||||
|
this.mMaxFlingVelocityY = this.mResolutionY * 1270.0F;
|
||||||
|
this.mMinFlingVelocityX = this.mResolutionX * 200.0F;
|
||||||
|
this.mMinFlingVelocityY = this.mResolutionY * 200.0F;
|
||||||
|
this.mMinScrollX = this.mResolutionX * minScrollDist;
|
||||||
|
this.mMinScrollY = this.mResolutionY * minScrollDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public static TouchNavMotionTracker buildTrackerForDevice(final InputDevice device, final float minScrollDist) {
|
||||||
|
MotionRange range = device.getMotionRange(0);
|
||||||
|
float resolution;
|
||||||
|
if (range == null) {
|
||||||
|
resolution = 0.0F;
|
||||||
|
} else {
|
||||||
|
resolution = range.getResolution();
|
||||||
|
}
|
||||||
|
|
||||||
|
float resolutionX = resolution;
|
||||||
|
if (resolution <= 0.0F) {
|
||||||
|
resolutionX = 6.3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionRange range2 = device.getMotionRange(1);
|
||||||
|
if (range2 == null) {
|
||||||
|
resolution = 0.0F;
|
||||||
|
} else {
|
||||||
|
resolution = range2.getResolution();
|
||||||
|
}
|
||||||
|
|
||||||
|
float resolutionY = resolution;
|
||||||
|
if (resolution <= 0.0F) {
|
||||||
|
resolutionY = 6.3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TouchNavMotionTracker(resolutionX, resolutionY, minScrollDist);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMovement(MotionEvent var1) {
|
||||||
|
if (this.mVelocityTracker == null) {
|
||||||
|
this.mVelocityTracker = VelocityTracker.obtain();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mVelocityTracker.addMovement(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
if (this.mDownEvent != null) {
|
||||||
|
this.mDownEvent.recycle();
|
||||||
|
this.mDownEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mVelocityTracker != null) {
|
||||||
|
this.mVelocityTracker.recycle();
|
||||||
|
this.mVelocityTracker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean computeVelocity() {
|
||||||
|
this.mVelocityTracker.computeCurrentVelocity(1000);
|
||||||
|
this.mVelX = Math.min(this.mMaxFlingVelocityX, this.mVelocityTracker.getXVelocity());
|
||||||
|
this.mVelY = Math.min(this.mMaxFlingVelocityY, this.mVelocityTracker.getYVelocity());
|
||||||
|
return Math.abs(this.mVelX) > this.mMinFlingVelocityX || Math.abs(this.mVelY) > this.mMinFlingVelocityY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MotionEvent getDownEvent() {
|
||||||
|
return this.mDownEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPhysicalX(float var1) {
|
||||||
|
return var1 / this.mResolutionX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPhysicalY(float var1) {
|
||||||
|
return var1 / this.mResolutionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getScrollX() {
|
||||||
|
return this.mScrollX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getScrollY() {
|
||||||
|
return this.mScrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getXResolution() {
|
||||||
|
return this.mResolutionX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getXVel() {
|
||||||
|
return this.mVelX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getYResolution() {
|
||||||
|
return this.mResolutionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getYVel() {
|
||||||
|
return this.mVelY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDownEvent(MotionEvent var1) {
|
||||||
|
if (this.mDownEvent != null && var1 != this.mDownEvent) {
|
||||||
|
this.mDownEvent.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mDownEvent = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setNewValues(float var1, float var2) {
|
||||||
|
this.mCurrX = var1;
|
||||||
|
this.mCurrY = var2;
|
||||||
|
this.mScrollX = this.mCurrX - this.mPrevX;
|
||||||
|
this.mScrollY = this.mCurrY - this.mPrevY;
|
||||||
|
return Math.abs(this.mScrollX) > this.mMinScrollX || Math.abs(this.mScrollY) > this.mMinScrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePrevValues() {
|
||||||
|
this.mPrevX = this.mCurrX;
|
||||||
|
this.mPrevY = this.mCurrY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,593 @@
|
|||||||
|
package com.google.android.pano.util;
|
||||||
|
|
||||||
|
import android.animation.TimeInterpolator;
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
|
import android.view.animation.AccelerateInterpolator;
|
||||||
|
|
||||||
|
public class TouchNavSpaceTracker {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
public static final float DEFAULT_DAMPED_SENSITIVITY = 0.5F;
|
||||||
|
public static final long DEFAULT_DAMPENING_DURATION_MS = 200L;
|
||||||
|
public static final long DEFAULT_DAMPING_DURATION_MS = 200L;
|
||||||
|
public static final float DEFAULT_HORIZONTAL_SIZE_MM = 120.0F;
|
||||||
|
public static final float DEFAULT_LPF_COEFF = 0.25F;
|
||||||
|
public static final float DEFAULT_MAX_FLICK_DISTANCE_MM = 40.0F;
|
||||||
|
public static final long DEFAULT_MAX_FLICK_DURATION_MS = 250L;
|
||||||
|
public static final float DEFAULT_MIN_FLICK_DISTANCE_MM = 4.0F;
|
||||||
|
public static final float DEFAULT_SENSITIVITY = 1.0F;
|
||||||
|
public static final float DEFAULT_VERTICAL_SIZE_MM = 50.0F;
|
||||||
|
private static final int[] DIRECTIONS = new int[]{1, 3, 2, 6, 4, 12, 8, 9, 1};
|
||||||
|
private static final float[] DIRECTION_BOUNDARIES = new float[]{-2.7488935F, -1.9634954F, -1.1780972F, -0.3926991F, 0.3926991F, 1.1780972F, 1.9634954F, 2.7488935F};
|
||||||
|
public static final int DIRECTION_DOWN = 2;
|
||||||
|
public static final int DIRECTION_DOWN_LEFT = 3;
|
||||||
|
public static final int DIRECTION_DOWN_RIGHT = 6;
|
||||||
|
public static final int DIRECTION_LEFT = 1;
|
||||||
|
public static final int DIRECTION_RIGHT = 4;
|
||||||
|
public static final int DIRECTION_UP = 8;
|
||||||
|
public static final int DIRECTION_UP_LEFT = 9;
|
||||||
|
public static final int DIRECTION_UP_RIGHT = 12;
|
||||||
|
private static final int MSG_LONG_CLICK = 0;
|
||||||
|
private static final String TAG = "TouchNavSpaceTracker";
|
||||||
|
private float mDampedSensitivity;
|
||||||
|
private float mDampingDuration;
|
||||||
|
private float mFlickMaxDistance;
|
||||||
|
private long mFlickMaxDuration;
|
||||||
|
private float mFlickMaxSquared;
|
||||||
|
private float mFlickMinDistance;
|
||||||
|
private float mFlickMinSquared;
|
||||||
|
private Handler mHandler;
|
||||||
|
protected TouchNavSpaceTracker.KeyEventListener mKeyEventListener;
|
||||||
|
private float mLPFCurrX;
|
||||||
|
private float mLPFCurrY;
|
||||||
|
private boolean mLPFEnabled;
|
||||||
|
private long mMovementBlockTime;
|
||||||
|
private float mPhysicalHeight;
|
||||||
|
private PointF mPhysicalPosition;
|
||||||
|
private float mPhysicalWidth;
|
||||||
|
private float mPixelHeight;
|
||||||
|
protected TouchNavSpaceTracker.TouchEventListener mPixelListener;
|
||||||
|
private float mPixelWidth;
|
||||||
|
private float mPixelsPerMm;
|
||||||
|
private PointF mPrevPhysPosition;
|
||||||
|
private float mSensitivity;
|
||||||
|
private TimeInterpolator mSensitivityInterpolator;
|
||||||
|
protected final SparseArray<TouchNavMotionTracker> mTouchParams;
|
||||||
|
private float mUnscaledFlickMaxDistance;
|
||||||
|
private float mUnscaledFlickMinDistance;
|
||||||
|
private boolean mWasBlocked;
|
||||||
|
|
||||||
|
public TouchNavSpaceTracker() {
|
||||||
|
this((TouchNavSpaceTracker.KeyEventListener)null, (TouchNavSpaceTracker.TouchEventListener)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TouchNavSpaceTracker(TouchNavSpaceTracker.KeyEventListener var1, TouchNavSpaceTracker.TouchEventListener var2) {
|
||||||
|
this.mPrevPhysPosition = new PointF(Float.MIN_VALUE, Float.MIN_VALUE);
|
||||||
|
this.mPhysicalPosition = new PointF(Float.MIN_VALUE, Float.MIN_VALUE);
|
||||||
|
this.mWasBlocked = false;
|
||||||
|
this.mSensitivityInterpolator = new AccelerateInterpolator();
|
||||||
|
this.mDampingDuration = 200.0F;
|
||||||
|
this.mDampedSensitivity = 0.5F;
|
||||||
|
this.mSensitivity = 1.0F;
|
||||||
|
this.mUnscaledFlickMinDistance = 4.0F;
|
||||||
|
this.mUnscaledFlickMaxDistance = 40.0F;
|
||||||
|
this.mFlickMinDistance = this.mSensitivity * 4.0F;
|
||||||
|
this.mFlickMaxDistance = this.mSensitivity * 40.0F;
|
||||||
|
this.mFlickMinSquared = this.mFlickMinDistance * this.mFlickMinDistance;
|
||||||
|
this.mFlickMaxSquared = this.mFlickMaxDistance * this.mFlickMaxDistance;
|
||||||
|
this.mFlickMaxDuration = 250L;
|
||||||
|
this.mLPFEnabled = false;
|
||||||
|
this.mHandler = new Handler() {
|
||||||
|
public void handleMessage(Message var1) {
|
||||||
|
switch(var1.what) {
|
||||||
|
case 0:
|
||||||
|
if (TouchNavSpaceTracker.this.mKeyEventListener != null) {
|
||||||
|
TouchNavSpaceTracker.this.mKeyEventListener.onKeyLongPress(var1.arg1, (KeyEvent)var1.obj);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.mKeyEventListener = var1;
|
||||||
|
this.mPixelListener = var2;
|
||||||
|
this.mTouchParams = new SparseArray(1);
|
||||||
|
this.mPhysicalWidth = 120.0F;
|
||||||
|
this.mPhysicalHeight = 50.0F;
|
||||||
|
this.mPixelWidth = 0.0F;
|
||||||
|
this.mPixelHeight = 0.0F;
|
||||||
|
this.mPixelsPerMm = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateSensitivity(MotionEvent var1, MotionEvent var2) {
|
||||||
|
long var4 = var1.getEventTime() - var2.getEventTime();
|
||||||
|
float var3;
|
||||||
|
if (var1.getEventTime() < this.mMovementBlockTime) {
|
||||||
|
var3 = 0.0F;
|
||||||
|
this.mWasBlocked = true;
|
||||||
|
} else if ((float)var4 < this.mDampingDuration) {
|
||||||
|
var3 = this.mSensitivityInterpolator.getInterpolation((float)var4 / this.mDampingDuration);
|
||||||
|
var3 = this.mDampedSensitivity + (this.mSensitivity - this.mDampedSensitivity) * var3;
|
||||||
|
} else {
|
||||||
|
var3 = this.mSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var3 != 0.0F && this.mWasBlocked) {
|
||||||
|
this.mWasBlocked = false;
|
||||||
|
this.setPhysicalPosition(this.mPhysicalPosition.x, this.mPhysicalPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForLongClick(int var1, KeyEvent var2) {
|
||||||
|
if (var1 == 23) {
|
||||||
|
Message var3 = this.mHandler.obtainMessage(0);
|
||||||
|
var3.arg1 = var1;
|
||||||
|
var3.obj = var2;
|
||||||
|
if (!this.mHandler.hasMessages(0)) {
|
||||||
|
this.mHandler.sendMessageDelayed(var3, (long)ViewConfiguration.getLongPressTimeout());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clampPosition() {
|
||||||
|
if (this.mPhysicalPosition.x < 0.0F) {
|
||||||
|
this.setPhysicalPosition(0.0F, this.mPhysicalPosition.y);
|
||||||
|
} else if (this.mPhysicalPosition.x > this.mPhysicalWidth) {
|
||||||
|
this.setPhysicalPosition(this.mPhysicalWidth, this.mPhysicalPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mPhysicalPosition.y < 0.0F) {
|
||||||
|
this.setPhysicalPosition(this.mPhysicalPosition.x, 0.0F);
|
||||||
|
} else if (this.mPhysicalPosition.y > this.mPhysicalHeight) {
|
||||||
|
this.setPhysicalPosition(this.mPhysicalPosition.x, this.mPhysicalHeight);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDpadDirection(float var1, float var2) {
|
||||||
|
var1 = (float)Math.atan2((double)(-var2), (double)var1);
|
||||||
|
|
||||||
|
int var3;
|
||||||
|
for(var3 = 0; var3 < DIRECTION_BOUNDARIES.length && var1 >= DIRECTION_BOUNDARIES[var3]; ++var3) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DIRECTIONS[var3];
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getPhysicalX(float var1) {
|
||||||
|
return this.mPixelWidth <= 0.0F ? 0.0F : this.mPhysicalWidth * var1 / this.mPixelWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getPhysicalY(float var1) {
|
||||||
|
return this.mPixelHeight <= 0.0F ? 0.0F : this.mPhysicalHeight * var1 / this.mPixelHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getPixelX(float var1) {
|
||||||
|
return this.mPixelWidth * var1 / this.mPhysicalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getPixelY(float var1) {
|
||||||
|
return this.mPixelHeight * var1 / this.mPhysicalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPrimaryDpadDirection(float var1, float var2) {
|
||||||
|
if (Math.abs(var1) > Math.abs(var2)) {
|
||||||
|
return var1 > 0.0F ? 4 : 1;
|
||||||
|
} else {
|
||||||
|
return var2 > 0.0F ? 2 : 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getScaledValue(float var1, float var2) {
|
||||||
|
return var1 * var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TouchNavMotionTracker getTrackerForDevice(InputDevice var1) {
|
||||||
|
TouchNavMotionTracker var3 = (TouchNavMotionTracker)this.mTouchParams.get(var1.getId());
|
||||||
|
TouchNavMotionTracker var2 = var3;
|
||||||
|
if (var3 == null) {
|
||||||
|
var2 = TouchNavMotionTracker.buildTrackerForDevice(var1, 0.1F);
|
||||||
|
this.mTouchParams.put(var1.getId(), var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPhysicalSizeInternal(float var1, float var2) {
|
||||||
|
this.mPhysicalWidth = var1;
|
||||||
|
this.mPhysicalHeight = var2;
|
||||||
|
if (this.mPhysicalPosition.x > this.mPhysicalWidth) {
|
||||||
|
this.mPhysicalPosition.x = this.mPhysicalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mPhysicalPosition.y > this.mPhysicalHeight) {
|
||||||
|
this.mPhysicalPosition.y = this.mPhysicalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePhysicalSize() {
|
||||||
|
if (this.mPixelWidth > 0.0F && this.mPixelHeight > 0.0F && this.mPixelsPerMm > 0.0F) {
|
||||||
|
this.setPhysicalSizeInternal(this.mPixelWidth / this.mPixelsPerMm, this.mPixelHeight / this.mPixelsPerMm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void blockMovementUntil(long var1) {
|
||||||
|
this.mMovementBlockTime = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureDamping(float var1, long var2) {
|
||||||
|
this.mDampedSensitivity = var1;
|
||||||
|
this.mDampingDuration = (float)var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configureFlicks(float var1, float var2, long var3) {
|
||||||
|
this.mUnscaledFlickMinDistance = var1;
|
||||||
|
this.mUnscaledFlickMaxDistance = var2;
|
||||||
|
this.mFlickMinDistance = this.mSensitivity * var1;
|
||||||
|
this.mFlickMaxDistance = this.mSensitivity * var2;
|
||||||
|
this.mFlickMinSquared = this.mFlickMinDistance * this.mFlickMinDistance;
|
||||||
|
this.mFlickMaxSquared = this.mFlickMaxDistance * this.mFlickMaxDistance;
|
||||||
|
this.mFlickMaxDuration = var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointF getCurrentPhysicalPosition() {
|
||||||
|
return new PointF(this.mPhysicalPosition.x, this.mPhysicalPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointF getCurrentPixelPosition() {
|
||||||
|
return new PointF(this.getPixelX(this.mPhysicalPosition.x), this.getPixelY(this.mPhysicalPosition.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onGenericMotionEvent(MotionEvent var1) {
|
||||||
|
if (var1 != null && (var1.getSource() & 2097152) == 2097152) {
|
||||||
|
InputDevice var13 = var1.getDevice();
|
||||||
|
if (var13 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TouchNavMotionTracker var19 = this.getTrackerForDevice(var13);
|
||||||
|
int var10 = var1.getActionMasked();
|
||||||
|
var19.addMovement(var1);
|
||||||
|
boolean var6;
|
||||||
|
if ((var10 & 255) == 6) {
|
||||||
|
var6 = true;
|
||||||
|
} else {
|
||||||
|
var6 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int var7;
|
||||||
|
if (var6) {
|
||||||
|
var7 = var1.getActionIndex();
|
||||||
|
} else {
|
||||||
|
var7 = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float var3 = 0.0F;
|
||||||
|
float var2 = 0.0F;
|
||||||
|
int var9 = var1.getPointerCount();
|
||||||
|
|
||||||
|
for(int var8 = 0; var8 < var9; ++var8) {
|
||||||
|
if (var7 != var8) {
|
||||||
|
var3 += var1.getX(var8);
|
||||||
|
var2 += var1.getY(var8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int var17;
|
||||||
|
if (var6) {
|
||||||
|
var17 = var9 - 1;
|
||||||
|
} else {
|
||||||
|
var17 = var9;
|
||||||
|
}
|
||||||
|
|
||||||
|
float var4 = var3 / (float)var17;
|
||||||
|
float var5 = var2 / (float)var17;
|
||||||
|
TouchNavSpaceTracker.PhysicalMotionEvent var14 = new TouchNavSpaceTracker.PhysicalMotionEvent(var1.getDeviceId(), var19.getPhysicalX(var4), var19.getPhysicalX(var5), var1.getEventTime());
|
||||||
|
boolean var18 = false;
|
||||||
|
boolean var12 = false;
|
||||||
|
boolean var11;
|
||||||
|
MotionEvent var15;
|
||||||
|
TouchNavSpaceTracker.PhysicalMotionEvent var16;
|
||||||
|
switch(var10 & 255) {
|
||||||
|
case 0:
|
||||||
|
if (this.mLPFEnabled) {
|
||||||
|
this.mLPFCurrX = var4;
|
||||||
|
this.mLPFCurrY = var5;
|
||||||
|
}
|
||||||
|
|
||||||
|
var19.setNewValues(var4, var5);
|
||||||
|
var19.updatePrevValues();
|
||||||
|
var19.setDownEvent(MotionEvent.obtain(var1));
|
||||||
|
if (this.mPixelListener != null) {
|
||||||
|
return false | this.mPixelListener.onDown(var14);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
var15 = var19.getDownEvent();
|
||||||
|
if (var15 == null) {
|
||||||
|
Log.w("TouchNavSpaceTracker", "Up event without down event");
|
||||||
|
return false | this.mPixelListener.onUp(var14, this.getPixelX(this.mPhysicalPosition.x), this.getPixelY(this.mPhysicalPosition.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
var16 = new TouchNavSpaceTracker.PhysicalMotionEvent(var1.getDeviceId(), var19.getPhysicalX(var15.getX()), var19.getPhysicalY(var15.getY()), var15.getEventTime());
|
||||||
|
var6 = var18;
|
||||||
|
if (var19.computeVelocity()) {
|
||||||
|
var6 = var18;
|
||||||
|
if (this.mPixelListener != null) {
|
||||||
|
var2 = this.getPixelX(var19.getPhysicalX(var19.getXVel()));
|
||||||
|
var3 = this.getPixelY(var19.getPhysicalY(var19.getYVel()));
|
||||||
|
var18 = false | this.mPixelListener.onFling(var16, var14, var2, var3);
|
||||||
|
var6 = var18;
|
||||||
|
if (var14.getTime() - var16.getTime() < this.mFlickMaxDuration) {
|
||||||
|
var2 = var14.getX() - var16.getX();
|
||||||
|
var3 = var14.getY() - var16.getY();
|
||||||
|
var4 = var2 * var2 + var3 * var3;
|
||||||
|
var6 = var18;
|
||||||
|
if (var4 > this.mFlickMinSquared) {
|
||||||
|
var6 = var18;
|
||||||
|
if (var4 < this.mFlickMaxSquared) {
|
||||||
|
this.mPixelListener.onFlick(var16, var14, this.getDpadDirection(var2, var3), this.getPrimaryDpadDirection(var2, var3));
|
||||||
|
var6 = var18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var2 = this.getPixelX(this.mPhysicalPosition.x);
|
||||||
|
var3 = this.getPixelY(this.mPhysicalPosition.y);
|
||||||
|
var11 = this.mPixelListener.onUp(var14, var2, var3);
|
||||||
|
var19.clear();
|
||||||
|
return var6 | var11;
|
||||||
|
case 2:
|
||||||
|
if (var19.getDownEvent() == null) {
|
||||||
|
var19.setDownEvent(MotionEvent.obtain(var1));
|
||||||
|
if (this.mLPFEnabled) {
|
||||||
|
this.mLPFCurrX = var4;
|
||||||
|
this.mLPFCurrY = var5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = var4;
|
||||||
|
var2 = var5;
|
||||||
|
if (this.mLPFEnabled) {
|
||||||
|
this.mLPFCurrX = this.mLPFCurrX * 0.75F + 0.25F * var4;
|
||||||
|
this.mLPFCurrY = this.mLPFCurrY * 0.75F + 0.25F * var5;
|
||||||
|
var3 = this.mLPFCurrX;
|
||||||
|
var2 = this.mLPFCurrY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var19.setNewValues(var3, var2)) {
|
||||||
|
var2 = var19.getPhysicalX(var19.getScrollX());
|
||||||
|
var3 = var19.getPhysicalY(var19.getScrollY());
|
||||||
|
var4 = this.calculateSensitivity(var1, var19.getDownEvent());
|
||||||
|
this.mPhysicalPosition.x = this.mPrevPhysPosition.x + this.getScaledValue(var2, var4);
|
||||||
|
this.mPhysicalPosition.y = this.mPrevPhysPosition.y + this.getScaledValue(var3, var4);
|
||||||
|
this.clampPosition();
|
||||||
|
if (!this.mPhysicalPosition.equals(this.mPrevPhysPosition)) {
|
||||||
|
var11 = var12;
|
||||||
|
if (this.mPixelListener != null) {
|
||||||
|
var11 = var12;
|
||||||
|
if (this.mPixelHeight > 0.0F) {
|
||||||
|
var11 = var12;
|
||||||
|
if (this.mPixelWidth > 0.0F) {
|
||||||
|
var15 = var19.getDownEvent();
|
||||||
|
var16 = new TouchNavSpaceTracker.PhysicalMotionEvent(var1.getDeviceId(), var19.getPhysicalX(var15.getX()), var19.getPhysicalY(var15.getY()), var15.getEventTime());
|
||||||
|
var2 = this.getPixelX(this.mPhysicalPosition.x);
|
||||||
|
var3 = this.getPixelY(this.mPhysicalPosition.y);
|
||||||
|
var11 = false | this.mPixelListener.onMove(var16, var14, var2, var3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mPrevPhysPosition.set(this.mPhysicalPosition);
|
||||||
|
} else {
|
||||||
|
var11 = false | true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var19.updatePrevValues();
|
||||||
|
return var11;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false | true;
|
||||||
|
case 3:
|
||||||
|
var19.clear();
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyDown(int var1, KeyEvent var2) {
|
||||||
|
if (var2 != null && var2.getDevice() != null && (var2.getDevice().getSources() & 2097152) == 2097152) {
|
||||||
|
if (var2.getRepeatCount() == 0) {
|
||||||
|
this.checkForLongClick(var1, var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mKeyEventListener != null) {
|
||||||
|
return this.mKeyEventListener.onKeyDown(var1, var2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyUp(int var1, KeyEvent var2) {
|
||||||
|
if (var2 != null && var2.getDevice() != null && (var2.getDevice().getSources() & 2097152) == 2097152) {
|
||||||
|
if (var1 == 23) {
|
||||||
|
this.mHandler.removeMessages(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mKeyEventListener != null) {
|
||||||
|
return this.mKeyEventListener.onKeyUp(var1, var2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
this.mHandler.removeMessages(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyEventListener(TouchNavSpaceTracker.KeyEventListener var1) {
|
||||||
|
this.mKeyEventListener = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLPFEnabled(boolean var1) {
|
||||||
|
this.mLPFEnabled = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalDensity(float var1) {
|
||||||
|
this.mPixelsPerMm = var1;
|
||||||
|
if (var1 > 0.0F) {
|
||||||
|
this.updatePhysicalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalPosition(float var1, float var2) {
|
||||||
|
this.mPhysicalPosition.x = var1;
|
||||||
|
this.mPhysicalPosition.y = var2;
|
||||||
|
this.mPrevPhysPosition.x = var1;
|
||||||
|
this.mPrevPhysPosition.y = var2;
|
||||||
|
this.clampPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalSize(float var1, float var2) {
|
||||||
|
if (this.mPixelsPerMm <= 0.0F) {
|
||||||
|
this.setPhysicalSizeInternal(var1, var2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixelPosition(float var1, float var2) {
|
||||||
|
this.setPhysicalPosition(this.getPhysicalX(var1), this.getPhysicalY(var2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixelSize(float var1, float var2) {
|
||||||
|
this.mPixelHeight = var2;
|
||||||
|
this.mPixelWidth = var1;
|
||||||
|
this.updatePhysicalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSensitivity(float var1) {
|
||||||
|
this.mSensitivity = var1;
|
||||||
|
this.configureFlicks(this.mUnscaledFlickMinDistance, this.mUnscaledFlickMaxDistance, this.mFlickMaxDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTouchEventListener(TouchNavSpaceTracker.TouchEventListener var1) {
|
||||||
|
this.mPixelListener = var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unblockMovement() {
|
||||||
|
this.mMovementBlockTime = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface KeyEventListener {
|
||||||
|
boolean onKeyDown(int var1, KeyEvent var2);
|
||||||
|
|
||||||
|
boolean onKeyLongPress(int var1, KeyEvent var2);
|
||||||
|
|
||||||
|
boolean onKeyUp(int var1, KeyEvent var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PhysicalMotionEvent {
|
||||||
|
private final int mDeviceId;
|
||||||
|
private final long mTime;
|
||||||
|
// $FF: renamed from: mX float
|
||||||
|
private final float field_6;
|
||||||
|
// $FF: renamed from: mY float
|
||||||
|
private final float field_7;
|
||||||
|
|
||||||
|
public PhysicalMotionEvent(int var1, float var2, float var3, long var4) {
|
||||||
|
this.mDeviceId = var1;
|
||||||
|
this.field_6 = var2;
|
||||||
|
this.field_7 = var3;
|
||||||
|
this.mTime = var4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final InputDevice getDevice() {
|
||||||
|
return InputDevice.getDevice(this.getDeviceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getDeviceId() {
|
||||||
|
return this.mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long getTime() {
|
||||||
|
return this.mTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float getX() {
|
||||||
|
return this.field_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float getY() {
|
||||||
|
return this.field_7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SimpleTouchEventListener implements TouchNavSpaceTracker.KeyEventListener, TouchNavSpaceTracker.TouchEventListener {
|
||||||
|
public boolean onDown(TouchNavSpaceTracker.PhysicalMotionEvent var1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onFlick(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, int var3, int var4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onFling(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, float var3, float var4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyDown(int var1, KeyEvent var2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyLongPress(int var1, KeyEvent var2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyUp(int var1, KeyEvent var2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onMove(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, float var3, float var4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onUp(TouchNavSpaceTracker.PhysicalMotionEvent var1, float var2, float var3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface TouchEventListener {
|
||||||
|
boolean onDown(TouchNavSpaceTracker.PhysicalMotionEvent var1);
|
||||||
|
|
||||||
|
boolean onFlick(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, int var3, int var4);
|
||||||
|
|
||||||
|
boolean onFling(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, float var3, float var4);
|
||||||
|
|
||||||
|
boolean onMove(TouchNavSpaceTracker.PhysicalMotionEvent var1, TouchNavSpaceTracker.PhysicalMotionEvent var2, float var3, float var4);
|
||||||
|
|
||||||
|
boolean onUp(TouchNavSpaceTracker.PhysicalMotionEvent var1, float var2, float var3);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
package com.google.leanback.ime;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.CompletionInfo;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
import com.google.android.leanback.ime.LeanbackKeyboardController;
|
||||||
|
import com.google.android.leanback.ime.LeanbackSuggestionsFactory;
|
||||||
|
import com.google.android.leanback.ime.LeanbackUtils;
|
||||||
|
|
||||||
|
public class LeanbackImeService extends InputMethodService {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
public static final String IME_CLOSE = "com.google.android.athome.action.IME_CLOSE";
|
||||||
|
public static final String IME_OPEN = "com.google.android.athome.action.IME_OPEN";
|
||||||
|
public static final int MAX_SUGGESTIONS = 10;
|
||||||
|
static final int MODE_FREE_MOVEMENT = 1;
|
||||||
|
static final int MODE_TRACKPAD_NAVIGATION = 0;
|
||||||
|
private static final int MSG_SUGGESTIONS_CLEAR = 123;
|
||||||
|
private static final int SUGGESTIONS_CLEAR_DELAY = 1000;
|
||||||
|
private static final String TAG = "LbImeService";
|
||||||
|
private boolean mEnterSpaceBeforeCommitting;
|
||||||
|
private final Handler mHandler = new Handler() {
|
||||||
|
public void handleMessage(Message var1) {
|
||||||
|
if (var1.what == 123 && LeanbackImeService.this.mShouldClearSuggestions) {
|
||||||
|
LeanbackImeService.this.mSuggestionsFactory.clearSuggestions();
|
||||||
|
LeanbackImeService.this.mKeyboardController.updateSuggestions(LeanbackImeService.this.mSuggestionsFactory.getSuggestions());
|
||||||
|
LeanbackImeService.this.mShouldClearSuggestions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private LeanbackKeyboardController.InputListener mInputListener = new LeanbackKeyboardController.InputListener() {
|
||||||
|
public void onEntry(int var1, int var2, CharSequence var3) {
|
||||||
|
LeanbackImeService.this.handleTextEntry(var1, var2, var3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private View mInputView;
|
||||||
|
private LeanbackKeyboardController mKeyboardController;
|
||||||
|
private boolean mShouldClearSuggestions = true;
|
||||||
|
private LeanbackSuggestionsFactory mSuggestionsFactory;
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public LeanbackImeService() {
|
||||||
|
if (!this.enableHardwareAcceleration()) {
|
||||||
|
Log.w("LbImeService", "Could not enable hardware acceleration");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSuggestionsDelayed() {
|
||||||
|
if (!this.mSuggestionsFactory.shouldSuggestionsAmend()) {
|
||||||
|
this.mHandler.removeMessages(123);
|
||||||
|
this.mShouldClearSuggestions = true;
|
||||||
|
this.mHandler.sendEmptyMessageDelayed(123, 1000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAmpersandLocation(InputConnection var1) {
|
||||||
|
String var4 = this.getEditorText(var1);
|
||||||
|
int var3 = var4.indexOf(64);
|
||||||
|
int var2 = var3;
|
||||||
|
if (var3 < 0) {
|
||||||
|
var2 = var4.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCharLengthAfterCursor(InputConnection var1) {
|
||||||
|
int var2 = 0;
|
||||||
|
CharSequence var3 = var1.getTextAfterCursor(1000, 0);
|
||||||
|
if (var3 != null) {
|
||||||
|
var2 = var3.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCharLengthBeforeCursor(InputConnection var1) {
|
||||||
|
int var2 = 0;
|
||||||
|
CharSequence var3 = var1.getTextBeforeCursor(1000, 0);
|
||||||
|
if (var3 != null) {
|
||||||
|
var2 = var3.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEditorText(InputConnection var1) {
|
||||||
|
StringBuilder var2 = new StringBuilder();
|
||||||
|
CharSequence var3 = var1.getTextBeforeCursor(1000, 0);
|
||||||
|
CharSequence var4 = var1.getTextAfterCursor(1000, 0);
|
||||||
|
if (var3 != null) {
|
||||||
|
var2.append(var3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var4 != null) {
|
||||||
|
var2.append(var4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTextEntry(int var1, int var2, CharSequence var3) {
|
||||||
|
InputConnection var5 = this.getCurrentInputConnection();
|
||||||
|
boolean var4 = true;
|
||||||
|
if (var5 != null) {
|
||||||
|
boolean var6;
|
||||||
|
switch (var1) {
|
||||||
|
case 0:
|
||||||
|
this.clearSuggestionsDelayed();
|
||||||
|
if (this.mEnterSpaceBeforeCommitting && this.mKeyboardController.enableAutoEnterSpace()) {
|
||||||
|
if (LeanbackUtils.isAlphabet(var2)) {
|
||||||
|
var5.commitText(" ", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mEnterSpaceBeforeCommitting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var5.commitText(var3, 1);
|
||||||
|
var6 = var4;
|
||||||
|
if (var2 == 46) {
|
||||||
|
this.mEnterSpaceBeforeCommitting = true;
|
||||||
|
var6 = var4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this.clearSuggestionsDelayed();
|
||||||
|
var5.deleteSurroundingText(1, 0);
|
||||||
|
this.mEnterSpaceBeforeCommitting = false;
|
||||||
|
var6 = var4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 6:
|
||||||
|
this.clearSuggestionsDelayed();
|
||||||
|
if (!this.mSuggestionsFactory.shouldSuggestionsAmend()) {
|
||||||
|
var5.deleteSurroundingText(this.getCharLengthBeforeCursor(var5), this.getCharLengthAfterCursor(var5));
|
||||||
|
} else {
|
||||||
|
var1 = this.getAmpersandLocation(var5);
|
||||||
|
var5.setSelection(var1, var1);
|
||||||
|
var5.deleteSurroundingText(0, this.getCharLengthAfterCursor(var5));
|
||||||
|
}
|
||||||
|
|
||||||
|
var5.commitText(var3, 1);
|
||||||
|
this.mEnterSpaceBeforeCommitting = true;
|
||||||
|
case 5:
|
||||||
|
this.sendDefaultEditorAction(false);
|
||||||
|
var6 = false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
var3 = var5.getTextBeforeCursor(1000, 0);
|
||||||
|
if (var3 == null) {
|
||||||
|
var2 = 0;
|
||||||
|
} else {
|
||||||
|
var2 = var3.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var1 == 3) {
|
||||||
|
var1 = var2;
|
||||||
|
if (var2 > 0) {
|
||||||
|
var1 = var2 - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var3 = var5.getTextAfterCursor(1000, 0);
|
||||||
|
var1 = var2;
|
||||||
|
if (var3 != null) {
|
||||||
|
var1 = var2;
|
||||||
|
if (var3.length() > 0) {
|
||||||
|
var1 = var2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var5.setSelection(var1, var1);
|
||||||
|
var6 = var4;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
var5.performEditorAction(1);
|
||||||
|
var6 = false;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
var5.performEditorAction(2);
|
||||||
|
var6 = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var6 = var4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mKeyboardController.areSuggestionsEnabled() && var6) {
|
||||||
|
this.mKeyboardController.updateSuggestions(this.mSuggestionsFactory.getSuggestions());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public View onCreateInputView() {
|
||||||
|
this.mInputView = this.mKeyboardController.getView();
|
||||||
|
this.mInputView.requestFocus();
|
||||||
|
return this.mInputView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDisplayCompletions(CompletionInfo[] var1) {
|
||||||
|
if (this.mKeyboardController.areSuggestionsEnabled()) {
|
||||||
|
this.mShouldClearSuggestions = false;
|
||||||
|
this.mHandler.removeMessages(123);
|
||||||
|
this.mSuggestionsFactory.onDisplayCompletions(var1);
|
||||||
|
this.mKeyboardController.updateSuggestions(this.mSuggestionsFactory.getSuggestions());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEvaluateFullscreenMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingSuperCall")
|
||||||
|
@Override
|
||||||
|
public boolean onEvaluateInputViewShown() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFinishInputView(boolean var1) {
|
||||||
|
super.onFinishInputView(var1);
|
||||||
|
this.sendBroadcast(new Intent("com.google.android.athome.action.IME_CLOSE"));
|
||||||
|
this.mSuggestionsFactory.clearSuggestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onGenericMotionEvent(MotionEvent var1) {
|
||||||
|
return this.isInputViewShown() && (var1.getSource() & 2097152) == 2097152 && this.mKeyboardController.onGenericMotionEvent(var1) ? true :
|
||||||
|
super.onGenericMotionEvent(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onHideIme() {
|
||||||
|
this.requestHideSelf(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onInitializeInterface() {
|
||||||
|
this.mKeyboardController = new LeanbackKeyboardController(this, this.mInputListener);
|
||||||
|
this.mEnterSpaceBeforeCommitting = false;
|
||||||
|
this.mSuggestionsFactory = new LeanbackSuggestionsFactory(this, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyDown(int var1, KeyEvent var2) {
|
||||||
|
return this.isInputViewShown() && this.mKeyboardController.onKeyDown(var1, var2) ? true : super.onKeyDown(var1, var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyUp(int var1, KeyEvent var2) {
|
||||||
|
return this.isInputViewShown() && this.mKeyboardController.onKeyUp(var1, var2) ? true : super.onKeyUp(var1, var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onShowInputRequested(int var1, boolean var2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int onStartCommand(Intent var1, int var2, int var3) {
|
||||||
|
if (var1 != null) {
|
||||||
|
super.onStartCommand(var1, var2, var3);
|
||||||
|
if (var1.getBooleanExtra("restart", false)) {
|
||||||
|
Log.e("LeanbackImeService", "Service->onStartCommand: trying to restart service");
|
||||||
|
LeanbackKeyboardController var4 = this.mKeyboardController;
|
||||||
|
if (var4 != null) {
|
||||||
|
var4.updateAddonKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartInput(EditorInfo var1, boolean var2) {
|
||||||
|
super.onStartInput(var1, var2);
|
||||||
|
this.mEnterSpaceBeforeCommitting = false;
|
||||||
|
this.mSuggestionsFactory.onStartInput(var1);
|
||||||
|
this.mKeyboardController.onStartInput(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartInputView(EditorInfo var1, boolean var2) {
|
||||||
|
super.onStartInputView(var1, var2);
|
||||||
|
this.mKeyboardController.onStartInputView();
|
||||||
|
this.sendBroadcast(new Intent("com.google.android.athome.action.IME_OPEN"));
|
||||||
|
if (this.mKeyboardController.areSuggestionsEnabled()) {
|
||||||
|
this.mSuggestionsFactory.createSuggestions();
|
||||||
|
this.mKeyboardController.updateSuggestions(this.mSuggestionsFactory.getSuggestions());
|
||||||
|
InputConnection var4 = this.getCurrentInputConnection();
|
||||||
|
if (var4 != null) {
|
||||||
|
String var3 = this.getEditorText(var4);
|
||||||
|
var4.deleteSurroundingText(this.getCharLengthBeforeCursor(var4), this.getCharLengthAfterCursor(var4));
|
||||||
|
var4.commitText(var3, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// Decompiled by Procyon v0.5.30
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.liskovsoft.inputchooser;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
public class LaunchAppActivity extends Activity
|
||||||
|
{
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
|
private void addIntentFlags(final Intent intent) {
|
||||||
|
intent.addFlags(67108864);
|
||||||
|
intent.addFlags(536870912);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
|
private ActivityInfo getCurrentActivityInfo() {
|
||||||
|
try {
|
||||||
|
return this.getPackageManager().getActivityInfo(this.getComponentName(), 129);
|
||||||
|
}
|
||||||
|
catch (NameNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
this.makeLongToast(ex.getLocalizedMessage(), 10);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchApp() {
|
||||||
|
final Intent intent = this.makeIntent(this.getCurrentActivityInfo());
|
||||||
|
this.addIntentFlags(intent);
|
||||||
|
this.startIntent(intent);
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent makeIntent(final ActivityInfo activityInfo) {
|
||||||
|
final Bundle metaData = activityInfo.metaData;
|
||||||
|
final Intent intent = new Intent();
|
||||||
|
if (metaData.getString("intent") != null) {
|
||||||
|
intent.setAction(metaData.getString("intent"));
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
intent.setComponent(new ComponentName(metaData.getString("package"), metaData.getString("class")));
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeLongToast(final String s, int i) {
|
||||||
|
int n;
|
||||||
|
for (n = i / 2, i = 0; i < n; ++i) {
|
||||||
|
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startIntent(final Intent intent) {
|
||||||
|
try {
|
||||||
|
this.startActivity(intent);
|
||||||
|
}
|
||||||
|
catch (ActivityNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
this.makeLongToast(ex.getLocalizedMessage(), 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onCreate(final Bundle bundle) {
|
||||||
|
super.onCreate(bundle);
|
||||||
|
this.launchApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.liskovsoft.inputchooser;
|
||||||
|
import android.app.*;
|
||||||
|
import android.content.*;
|
||||||
|
import android.content.res.*;
|
||||||
|
import android.net.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.inputmethod.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class RestartServiceReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
sendMessageToService(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessageToService(Context context) {
|
||||||
|
Log.e("RestartServiceReceiver", "Sending message to the service");
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setComponent(new ComponentName(getPackageName(context), "com.google.leanback.ime.LeanbackImeService"));
|
||||||
|
intent.putExtra("restart", true);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onStartCommand(Intent intent) {
|
||||||
|
if (intent.getBooleanExtra("restart", false)) {
|
||||||
|
System.out.println("Restarting service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void killThisPackageProcess(Context context) {
|
||||||
|
Log.e("RestartServiceReceiver", "Attempting to kill org.liskovsoft.androidtv.rukeyboard process");
|
||||||
|
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
activityManager.killBackgroundProcesses(getPackageName(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restartService(Context context) {
|
||||||
|
// START YOUR SERVICE HERE
|
||||||
|
Log.e("RestartServiceReceiver", "Restarting Service");
|
||||||
|
//final Class<?> serviceClass = classForName("com.google.leanback.ime.LeanbackImeService");
|
||||||
|
//Intent serviceIntent = new Intent(context.getApplicationContext(), serviceClass);
|
||||||
|
Intent serviceIntent = new Intent();
|
||||||
|
serviceIntent.setComponent(new ComponentName(getPackageName(context), "com.google.leanback.ime.LeanbackImeService"));
|
||||||
|
context.stopService(serviceIntent);
|
||||||
|
context.startService(serviceIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> classForName(String clazz) {
|
||||||
|
Class<?> serviceClass;
|
||||||
|
try {
|
||||||
|
serviceClass = Class.forName(clazz);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return serviceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchLocale(Context ctx) {
|
||||||
|
Log.e("RestartServiceReceiver", "Trying to switch locale back and forward");
|
||||||
|
Locale savedLocale = Locale.getDefault();
|
||||||
|
trySwitchLocale(ctx, new Locale("ru"));
|
||||||
|
trySwitchLocale(ctx, savedLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trySwitchLocale(Context ctx, Locale locale) {
|
||||||
|
Locale.setDefault(locale);
|
||||||
|
Configuration config = ctx.getResources().getConfiguration();
|
||||||
|
config.locale = locale;
|
||||||
|
ctx.getResources().updateConfiguration(config,
|
||||||
|
ctx.getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPackageName(Context ctx) {
|
||||||
|
return ctx.getPackageName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.liskovsoft.keyboardaddons;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.inputmethodservice.Keyboard;
|
||||||
|
import com.anysoftkeyboard.keyboards.KeyboardAddOnAndBuilder;
|
||||||
|
import com.anysoftkeyboard.keyboards.KeyboardFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KeyboardManager {
|
||||||
|
private final Keyboard mEnglishKeyboard;
|
||||||
|
private final Context mContext;
|
||||||
|
private final List<KeyboardAddOnAndBuilder> mKeyboardBuilders;
|
||||||
|
private final List<Keyboard> mAllKeyboards;
|
||||||
|
private final KeyboardFactory mKeyboardFactory;
|
||||||
|
private int mKeyboardIndex = 0;
|
||||||
|
|
||||||
|
public KeyboardManager(Context ctx, int defaultKeyboard1) {
|
||||||
|
this(ctx, new Keyboard(ctx, defaultKeyboard1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyboardManager(Context ctx, Keyboard englishKeyboard) {
|
||||||
|
mContext = ctx;
|
||||||
|
mEnglishKeyboard = englishKeyboard;
|
||||||
|
mKeyboardFactory = new KeyboardFactory();
|
||||||
|
|
||||||
|
mKeyboardBuilders = mKeyboardFactory.getAllAvailableKeyboards(mContext);
|
||||||
|
mAllKeyboards = buildAllKeyboards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Keyboard> buildAllKeyboards() {
|
||||||
|
List<Keyboard> keyboards = new ArrayList<>();
|
||||||
|
keyboards.add(mEnglishKeyboard);
|
||||||
|
if (!mKeyboardBuilders.isEmpty()) {
|
||||||
|
for (KeyboardAddOnAndBuilder builder : mKeyboardBuilders) {
|
||||||
|
keyboards.add(builder.createKeyboard());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyboards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard getNextKeyboard() {
|
||||||
|
++mKeyboardIndex;
|
||||||
|
mKeyboardIndex = mKeyboardIndex < mAllKeyboards.size() ? mKeyboardIndex : 0;
|
||||||
|
|
||||||
|
Keyboard kbd = mAllKeyboards.get(mKeyboardIndex);
|
||||||
|
if (kbd == null) {
|
||||||
|
throw new UnsupportedOperationException(String.format("Keyboard %s not initialized", mKeyboardIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return kbd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 360 B |
|
After Width: | Height: | Size: 975 B |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/ic_ime_delete.png
Normal file
|
After Width: | Height: | Size: 874 B |
|
After Width: | Height: | Size: 311 B |
|
After Width: | Height: | Size: 306 B |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 748 B |
|
After Width: | Height: | Size: 541 B |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/ic_ime_space.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/ic_ime_symbols.png
Normal file
|
After Width: | Height: | Size: 957 B |
|
After Width: | Height: | Size: 6.7 KiB |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/ic_voice_focus.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/ic_voice_off.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
BIN
leankeykeyboard/src/main/res/drawable-hdpi-v4/key_selector.9.png
Normal file
|
After Width: | Height: | Size: 475 B |
|
After Width: | Height: | Size: 545 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 342 B |
|
After Width: | Height: | Size: 717 B |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/ic_ime_delete.png
Normal file
|
After Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 265 B |
|
After Width: | Height: | Size: 548 B |
|
After Width: | Height: | Size: 532 B |
|
After Width: | Height: | Size: 400 B |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/ic_ime_space.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/ic_ime_symbols.png
Normal file
|
After Width: | Height: | Size: 642 B |
|
After Width: | Height: | Size: 4.1 KiB |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/ic_voice_focus.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/ic_voice_off.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
BIN
leankeykeyboard/src/main/res/drawable-mdpi-v4/key_selector.9.png
Normal file
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 399 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
BIN
leankeykeyboard/src/main/res/drawable-xhdpi-v4/banner_app.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 536 B |
|
After Width: | Height: | Size: 1.4 KiB |
BIN
leankeykeyboard/src/main/res/drawable-xhdpi-v4/ic_ime_delete.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 376 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 729 B |
|
After Width: | Height: | Size: 901 B |
|
After Width: | Height: | Size: 584 B |
BIN
leankeykeyboard/src/main/res/drawable-xhdpi-v4/ic_ime_space.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
leankeykeyboard/src/main/res/drawable-xhdpi-v4/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
BIN
leankeykeyboard/src/main/res/drawable-xhdpi-v4/ic_voice_off.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 670 B |
|
After Width: | Height: | Size: 711 B |
|
After Width: | Height: | Size: 5.6 KiB |