initial commit

This commit is contained in:
Yuriy Liskov
2018-01-20 23:25:33 +02:00
commit 305b1d4e33
220 changed files with 9408 additions and 0 deletions

22
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View File

Binary file not shown.

View 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
View 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
View 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
View File

@@ -0,0 +1 @@
/build

View 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
View 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

View File

@@ -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());
}
}

View 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>

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
//}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,6 @@
package com.anysoftkeyboard.utils;
public class BuildConfig {
public final static boolean TESTING_BUILD = true;
public final static boolean DEBUG = true;
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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) {
;
}
}
}

View File

@@ -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, "&", "&amp;");
str = replaceString(str, "<", "&lt;");
str = replaceString(str, ">", "&gt;");
str = replaceString(str, "\"", "&quot;");
str = replaceString(str, "'", "&apos;");
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());
// }
}

View File

@@ -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);
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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};
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Some files were not shown because too many files have changed in this diff Show More