M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.inputmethodservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* AbstractInputMethodService provides a abstract base class for input methods.
* Normal input method implementations will not derive from this directly,
* instead building on top of {@link InputMethodService} or another more
* complete base class. Be sure to read {@link InputMethod} for more
* information on the basics of writing input methods.
*
* <p>This class combines a Service (representing the input method component
* to the system with the InputMethod interface that input methods must
* implement. This base class takes care of reporting your InputMethod from
* the service when clients bind to it, but provides no standard implementation
* of the InputMethod interface itself. Derived classes must implement that
* interface.
*/
public abstract class AbstractInputMethodService extends Service
implements KeyEvent.Callback {
private InputMethod mInputMethod;
final KeyEvent.DispatcherState mDispatcherState
= new KeyEvent.DispatcherState();
/**
* Base class for derived classes to implement their {@link InputMethod}
* interface. This takes care of basic maintenance of the input method,
* but most behavior must be implemented in a derived class.
*/
public abstract class AbstractInputMethodImpl implements InputMethod {
/**
* Instantiate a new client session for the input method, by calling
* back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
* AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
*/
public void createSession(SessionCallback callback) {
callback.sessionCreated(onCreateInputMethodSessionInterface());
}
/**
* Take care of enabling or disabling an existing session by calling its
* {@link AbstractInputMethodSessionImpl#revokeSelf()
* AbstractInputMethodSessionImpl.setEnabled()} method.
*/
public void setSessionEnabled(InputMethodSession session, boolean enabled) {
((AbstractInputMethodSessionImpl)session).setEnabled(enabled);
}
/**
* Take care of killing an existing session by calling its
* {@link AbstractInputMethodSessionImpl#revokeSelf()
* AbstractInputMethodSessionImpl.revokeSelf()} method.
*/
public void revokeSession(InputMethodSession session) {
((AbstractInputMethodSessionImpl)session).revokeSelf();
}
}
/**
* Base class for derived classes to implement their {@link InputMethodSession}
* interface. This takes care of basic maintenance of the session,
* but most behavior must be implemented in a derived class.
*/
public abstract class AbstractInputMethodSessionImpl implements InputMethodSession {
boolean mEnabled = true;
boolean mRevoked;
/**
* Check whether this session has been enabled by the system. If not
* enabled, you should not execute any calls on to it.
*/
public boolean isEnabled() {
return mEnabled;
}
/**
* Check whether this session has been revoked by the system. Revoked
* session is also always disabled, so there is generally no need to
* explicitly check for this.
*/
public boolean isRevoked() {
return mRevoked;
}
/**
* Change the enabled state of the session. This only works if the
* session has not been revoked.
*/
public void setEnabled(boolean enabled) {
if (!mRevoked) {
mEnabled = enabled;
}
}
/**
* Revoke the session from the client. This disabled the session, and
* prevents it from ever being enabled again.
*/
public void revokeSelf() {
mRevoked = true;
mEnabled = false;
}
/**
* Take care of dispatching incoming key events to the appropriate
* callbacks on the service, and tell the client when this is done.
*/
public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
boolean handled = event.dispatch(AbstractInputMethodService.this,
mDispatcherState, this);
if (callback != null) {
callback.finishedEvent(seq, handled);
}
}
/**
* Take care of dispatching incoming trackball events to the appropriate
* callbacks on the service, and tell the client when this is done.
*/
public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
boolean handled = onTrackballEvent(event);
if (callback != null) {
callback.finishedEvent(seq, handled);
}
}
}
/**
* Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
* for used for processing events from the target application.
* Normally you will not need to use this directly, but
* just use the standard high-level event callbacks like {@link #onKeyDown}.
*/
public KeyEvent.DispatcherState getKeyDispatcherState() {
return mDispatcherState;
}
/**
* Called by the framework during initialization, when the InputMethod
* interface for this service needs to be created.
*/
public abstract AbstractInputMethodImpl onCreateInputMethodInterface();
/**
* Called by the framework when a new InputMethodSession interface is
* needed for a new client of the input method.
*/
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
/**
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
* calls on your input method.
*/
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
}
@Override
final public IBinder onBind(Intent intent) {
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
return new IInputMethodWrapper(this, mInputMethod);
}
public boolean onTrackballEvent(MotionEvent event) {
return false;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.inputmethodservice;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Button;
/**
* Specialization of {@link Button} that ignores the window not being focused.
*/
class ExtractButton extends Button {
public ExtractButton(Context context) {
super(context, null);
}
public ExtractButton(Context context, AttributeSet attrs) {
super(context, attrs, com.android.internal.R.attr.buttonStyle);
}
public ExtractButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Pretend like the window this view is in always has focus, so it will
* highlight when selected.
*/
@Override public boolean hasWindowFocus() {
return this.isEnabled() ? true : false;
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.inputmethodservice;
import android.content.Context;
import android.util.AttributeSet;
import android.view.inputmethod.ExtractedText;
import android.widget.EditText;
/***
* Specialization of {@link EditText} for showing and interacting with the
* extracted text in a full-screen input method.
*/
public class ExtractEditText extends EditText {
private InputMethodService mIME;
private int mSettingExtractedText;
public ExtractEditText(Context context) {
super(context, null);
}
public ExtractEditText(Context context, AttributeSet attrs) {
super(context, attrs, com.android.internal.R.attr.editTextStyle);
}
public ExtractEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
void setIME(InputMethodService ime) {
mIME = ime;
}
/**
* Start making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
* the new selection to the client
*/
public void startInternalChanges() {
mSettingExtractedText += 1;
}
/**
* Finish making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
* the new selection to the client
*/
public void finishInternalChanges() {
mSettingExtractedText -= 1;
}
/**
* Implement just to keep track of when we are setting text from the
* client (vs. seeing changes in ourself from the user).
*/
@Override public void setExtractedText(ExtractedText text) {
try {
mSettingExtractedText++;
super.setExtractedText(text);
} finally {
mSettingExtractedText--;
}
}
/**
* Report to the underlying text editor about selection changes.
*/
@Override protected void onSelectionChanged(int selStart, int selEnd) {
if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) {
mIME.onExtractedSelectionChanged(selStart, selEnd);
}
}
/**
* Redirect clicks to the IME for handling there. First allows any
* on click handler to run, though.
*/
@Override public boolean performClick() {
if (!super.performClick() && mIME != null) {
mIME.onExtractedTextClicked();
return true;
}
return false;
}
@Override public boolean onTextContextMenuItem(int id) {
// Horrible hack: select word option has to be handled by original view to work.
if (mIME != null && id != android.R.id.startSelectingText) {
if (mIME.onExtractTextContextMenuItem(id)) {
return true;
}
}
return super.onTextContextMenuItem(id);
}
/**
* We are always considered to be an input method target.
*/
@Override
public boolean isInputMethodTarget() {
return true;
}
/**
* Return true if the edit text is currently showing a scroll bar.
*/
public boolean hasVerticalScrollBar() {
return computeVerticalScrollRange() > computeVerticalScrollExtent();
}
/**
* Pretend like the window this view is in always has focus, so its
* highlight and cursor will be displayed.
*/
@Override public boolean hasWindowFocus() {
return this.isEnabled();
}
/**
* Pretend like this view always has focus, so its
* highlight and cursor will be displayed.
*/
@Override public boolean isFocused() {
return this.isEnabled();
}
/**
* Pretend like this view always has focus, so its
* highlight and cursor will be displayed.
*/
@Override public boolean hasFocus() {
return this.isEnabled();
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.inputmethodservice;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.EditorInfo;
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
private static final boolean DEBUG = false;
private static final int DO_FINISH_INPUT = 60;
private static final int DO_DISPLAY_COMPLETIONS = 65;
private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
private static final int DO_DISPATCH_KEY_EVENT = 70;
private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
private static final int DO_UPDATE_SELECTION = 90;
private static final int DO_UPDATE_CURSOR = 95;
private static final int DO_APP_PRIVATE_COMMAND = 100;
private static final int DO_TOGGLE_SOFT_INPUT = 105;
private static final int DO_FINISH_SESSION = 110;
HandlerCaller mCaller;
InputMethodSession mInputMethodSession;
// NOTE: we should have a cache of these.
static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
final IInputMethodCallback mCb;
InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
mCb = cb;
}
public void finishedEvent(int seq, boolean handled) {
try {
mCb.finishedEvent(seq, handled);
} catch (RemoteException e) {
}
}
}
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession) {
mCaller = new HandlerCaller(context, this);
mInputMethodSession = inputMethodSession;
}
public InputMethodSession getInternalInputMethodSession() {
return mInputMethodSession;
}
public void executeMessage(Message msg) {
switch (msg.what) {
case DO_FINISH_INPUT:
mInputMethodSession.finishInput();
return;
case DO_DISPLAY_COMPLETIONS:
mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
return;
case DO_UPDATE_EXTRACTED_TEXT:
mInputMethodSession.updateExtractedText(msg.arg1,
(ExtractedText)msg.obj);
return;
case DO_DISPATCH_KEY_EVENT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
mInputMethodSession.dispatchKeyEvent(msg.arg1,
(KeyEvent)args.arg1,
new InputMethodEventCallbackWrapper(
(IInputMethodCallback)args.arg2));
mCaller.recycleArgs(args);
return;
}
case DO_DISPATCH_TRACKBALL_EVENT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
mInputMethodSession.dispatchTrackballEvent(msg.arg1,
(MotionEvent)args.arg1,
new InputMethodEventCallbackWrapper(
(IInputMethodCallback)args.arg2));
mCaller.recycleArgs(args);
return;
}
case DO_UPDATE_SELECTION: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
mInputMethodSession.updateSelection(args.argi1, args.argi2,
args.argi3, args.argi4, args.argi5, args.argi6);
mCaller.recycleArgs(args);
return;
}
case DO_UPDATE_CURSOR: {
mInputMethodSession.updateCursor((Rect)msg.obj);
return;
}
case DO_APP_PRIVATE_COMMAND: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
mInputMethodSession.appPrivateCommand((String)args.arg1,
(Bundle)args.arg2);
mCaller.recycleArgs(args);
return;
}
case DO_TOGGLE_SOFT_INPUT: {
mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
return;
}
case DO_FINISH_SESSION: {
mInputMethodSession = null;
return;
}
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
public void finishInput() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
}
public void displayCompletions(CompletionInfo[] completions) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(
DO_DISPLAY_COMPLETIONS, completions));
}
public void updateExtractedText(int token, ExtractedText text) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
DO_UPDATE_EXTRACTED_TEXT, token, text));
}
public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq,
event, callback));
}
public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq,
event, callback));
}
public void updateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
oldSelStart, oldSelEnd, newSelStart, newSelEnd,
candidatesStart, candidatesEnd));
}
public void updateCursor(Rect newCursor) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR,
newCursor));
}
public void appPrivateCommand(String action, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
}
public void toggleSoftInput(int showFlags, int hideFlags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
}
public void finishSession() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
}
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.inputmethodservice;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputConnectionWrapper;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Implements the internal IInputMethod interface to convert incoming calls
* on to it back to calls on the public InputMethod interface, scheduling
* them on the main thread of the process.
*/
class IInputMethodWrapper extends IInputMethod.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
private static final boolean DEBUG = false;
private static final int DO_DUMP = 1;
private static final int DO_ATTACH_TOKEN = 10;
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
private static final int DO_RESTART_INPUT = 34;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
private static final int DO_REVOKE_SESSION = 50;
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
final WeakReference<AbstractInputMethodService> mTarget;
final HandlerCaller mCaller;
final WeakReference<InputMethod> mInputMethod;
static class Notifier {
boolean notified;
}
// NOTE: we should have a cache of these.
static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
final IInputMethodCallback mCb;
InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) {
mContext = context;
mCb = cb;
}
public void sessionCreated(InputMethodSession session) {
try {
if (session != null) {
IInputMethodSessionWrapper wrap =
new IInputMethodSessionWrapper(mContext, session);
mCb.sessionCreated(wrap);
} else {
mCb.sessionCreated(null);
}
} catch (RemoteException e) {
}
}
}
public IInputMethodWrapper(AbstractInputMethodService context,
InputMethod inputMethod) {
mTarget = new WeakReference<AbstractInputMethodService>(context);
mCaller = new HandlerCaller(context.getApplicationContext(), this);
mInputMethod = new WeakReference<InputMethod>(inputMethod);
}
public InputMethod getInternalInputMethod() {
return mInputMethod.get();
}
public void executeMessage(Message msg) {
InputMethod inputMethod = mInputMethod.get();
// Need a valid reference to the inputMethod for everything except a dump.
if (inputMethod == null && msg.what != DO_DUMP) {
Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
return;
}
switch (msg.what) {
case DO_DUMP: {
AbstractInputMethodService target = mTarget.get();
if (target == null) {
return;
}
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
try {
target.dump((FileDescriptor)args.arg1,
(PrintWriter)args.arg2, (String[])args.arg3);
} catch (RuntimeException e) {
((PrintWriter)args.arg2).println("Exception: " + e);
}
synchronized (args.arg4) {
((CountDownLatch)args.arg4).countDown();
}
mCaller.recycleArgs(args);
return;
}
case DO_ATTACH_TOKEN: {
inputMethod.attachToken((IBinder)msg.obj);
return;
}
case DO_SET_INPUT_CONTEXT: {
inputMethod.bindInput((InputBinding)msg.obj);
return;
}
case DO_UNSET_INPUT_CONTEXT:
inputMethod.unbindInput();
return;
case DO_START_INPUT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null;
inputMethod.startInput(ic, (EditorInfo)args.arg2);
mCaller.recycleArgs(args);
return;
}
case DO_RESTART_INPUT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null;
inputMethod.restartInput(ic, (EditorInfo)args.arg2);
mCaller.recycleArgs(args);
return;
}
case DO_CREATE_SESSION: {
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (IInputMethodCallback)msg.obj));
return;
}
case DO_SET_SESSION_ENABLED:
inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0);
return;
case DO_REVOKE_SESSION:
inputMethod.revokeSession((InputMethodSession)msg.obj);
return;
case DO_SHOW_SOFT_INPUT:
inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
case DO_HIDE_SOFT_INPUT:
inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
AbstractInputMethodService target = mTarget.get();
if (target == null) {
return;
}
if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
CountDownLatch latch = new CountDownLatch(1);
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
fd, fout, args, latch));
try {
if (!latch.await(5, TimeUnit.SECONDS)) {
fout.println("Timeout waiting for dump");
}
} catch (InterruptedException e) {
fout.println("Interrupted waiting for dump");
}
}
public void attachToken(IBinder token) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
}
public void bindInput(InputBinding binding) {
InputConnection ic = new InputConnectionWrapper(
IInputContext.Stub.asInterface(binding.getConnectionToken()));
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
public void unbindInput() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
}
public void startInput(IInputContext inputContext, EditorInfo attribute) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
inputContext, attribute));
}
public void restartInput(IInputContext inputContext, EditorInfo attribute) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
inputContext, attribute));
}
public void createSession(IInputMethodCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
}
public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
try {
InputMethodSession ls = ((IInputMethodSessionWrapper)
session).getInternalInputMethodSession();
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
} catch (ClassCastException e) {
Log.w(TAG, "Incoming session not of correct type: " + session, e);
}
}
public void revokeSession(IInputMethodSession session) {
try {
InputMethodSession ls = ((IInputMethodSessionWrapper)
session).getInternalInputMethodSession();
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
} catch (ClassCastException e) {
Log.w(TAG, "Incoming session not of correct type: " + session, e);
}
}
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
flags, resultReceiver));
}
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
flags, resultReceiver));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,816 @@
/*
* Copyright (C) 2008-2009 Google Inc.
*
* 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 android.inputmethodservice;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
import android.util.DisplayMetrics;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
* consists of rows of keys.
* <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
* <pre>
* &lt;Keyboard
* android:keyWidth="%10p"
* android:keyHeight="50px"
* android:horizontalGap="2px"
* android:verticalGap="2px" &gt;
* &lt;Row android:keyWidth="32px" &gt;
* &lt;Key android:keyLabel="A" /&gt;
* ...
* &lt;/Row&gt;
* ...
* &lt;/Keyboard&gt;
* </pre>
* @attr ref android.R.styleable#Keyboard_keyWidth
* @attr ref android.R.styleable#Keyboard_keyHeight
* @attr ref android.R.styleable#Keyboard_horizontalGap
* @attr ref android.R.styleable#Keyboard_verticalGap
*/
public class Keyboard {
static final String TAG = "Keyboard";
// Keyboard XML Tags
private static final String TAG_KEYBOARD = "Keyboard";
private static final String TAG_ROW = "Row";
private static final String TAG_KEY = "Key";
public static final int EDGE_LEFT = 0x01;
public static final int EDGE_RIGHT = 0x02;
public static final int EDGE_TOP = 0x04;
public static final int EDGE_BOTTOM = 0x08;
public static final int KEYCODE_SHIFT = -1;
public static final int KEYCODE_MODE_CHANGE = -2;
public static final int KEYCODE_CANCEL = -3;
public static final int KEYCODE_DONE = -4;
public static final int KEYCODE_DELETE = -5;
public static final int KEYCODE_ALT = -6;
/** Keyboard label **/
private CharSequence mLabel;
/** Horizontal gap default for all rows */
private int mDefaultHorizontalGap;
/** Default key width */
private int mDefaultWidth;
/** Default key height */
private int mDefaultHeight;
/** Default gap between rows */
private int mDefaultVerticalGap;
/** Is the keyboard in the shifted state */
private boolean mShifted;
/** Key instance for the shift key, if present */
private Key mShiftKey;
/** Key index for the shift key, if present */
private int mShiftKeyIndex = -1;
/** Current key width, while loading the keyboard */
private int mKeyWidth;
/** Current key height, while loading the keyboard */
private int mKeyHeight;
/** Total height of the keyboard, including the padding and keys */
private int mTotalHeight;
/**
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
* right side.
*/
private int mTotalWidth;
/** List of keys in this keyboard */
private List<Key> mKeys;
/** List of modifier keys such as Shift & Alt, if any */
private List<Key> mModifierKeys;
/** Width of the screen available to fit the keyboard */
private int mDisplayWidth;
/** Height of the screen */
private int mDisplayHeight;
/** Keyboard mode, or zero, if none. */
private int mKeyboardMode;
// Variables for pre-computing nearest keys.
private static final int GRID_WIDTH = 10;
private static final int GRID_HEIGHT = 5;
private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
private int mCellWidth;
private int mCellHeight;
private int[][] mGridNeighbors;
private int mProximityThreshold;
/** Number of key widths from current touch point to search for nearest keys. */
private static float SEARCH_DISTANCE = 1.8f;
/**
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
* Some of the key size defaults can be overridden per row from what the {@link Keyboard}
* defines.
* @attr ref android.R.styleable#Keyboard_keyWidth
* @attr ref android.R.styleable#Keyboard_keyHeight
* @attr ref android.R.styleable#Keyboard_horizontalGap
* @attr ref android.R.styleable#Keyboard_verticalGap
* @attr ref android.R.styleable#Keyboard_Row_rowEdgeFlags
* @attr ref android.R.styleable#Keyboard_Row_keyboardMode
*/
public static class Row {
/** Default width of a key in this row. */
public int defaultWidth;
/** Default height of a key in this row. */
public int defaultHeight;
/** Default horizontal gap between keys in this row. */
public int defaultHorizontalGap;
/** Vertical gap following this row. */
public int verticalGap;
/**
* Edge flags for this row of keys. Possible values that can be assigned are
* {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
*/
public int rowEdgeFlags;
/** The keyboard mode for this row */
public int mode;
private Keyboard parent;
public Row(Keyboard parent) {
this.parent = parent;
}
public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
this.parent = parent;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard);
defaultWidth = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyWidth,
parent.mDisplayWidth, parent.mDefaultWidth);
defaultHeight = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyHeight,
parent.mDisplayHeight, parent.mDefaultHeight);
defaultHorizontalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_horizontalGap,
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
verticalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_verticalGap,
parent.mDisplayHeight, parent.mDefaultVerticalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard_Row);
rowEdgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Row_rowEdgeFlags, 0);
mode = a.getResourceId(com.android.internal.R.styleable.Keyboard_Row_keyboardMode,
0);
}
}
/**
* Class for describing the position and characteristics of a single key in the keyboard.
*
* @attr ref android.R.styleable#Keyboard_keyWidth
* @attr ref android.R.styleable#Keyboard_keyHeight
* @attr ref android.R.styleable#Keyboard_horizontalGap
* @attr ref android.R.styleable#Keyboard_Key_codes
* @attr ref android.R.styleable#Keyboard_Key_keyIcon
* @attr ref android.R.styleable#Keyboard_Key_keyLabel
* @attr ref android.R.styleable#Keyboard_Key_iconPreview
* @attr ref android.R.styleable#Keyboard_Key_isSticky
* @attr ref android.R.styleable#Keyboard_Key_isRepeatable
* @attr ref android.R.styleable#Keyboard_Key_isModifier
* @attr ref android.R.styleable#Keyboard_Key_popupKeyboard
* @attr ref android.R.styleable#Keyboard_Key_popupCharacters
* @attr ref android.R.styleable#Keyboard_Key_keyOutputText
* @attr ref android.R.styleable#Keyboard_Key_keyEdgeFlags
*/
public static class Key {
/**
* All the key codes (unicode or custom code) that this key could generate, zero'th
* being the most important.
*/
public int[] codes;
/** Label to display */
public CharSequence label;
/** Icon to display instead of a label. Icon takes precedence over a label */
public Drawable icon;
/** Preview version of the icon, for the preview popup */
public Drawable iconPreview;
/** Width of the key, not including the gap */
public int width;
/** Height of the key, not including the gap */
public int height;
/** The horizontal gap before this key */
public int gap;
/** Whether this key is sticky, i.e., a toggle key */
public boolean sticky;
/** X coordinate of the key in the keyboard layout */
public int x;
/** Y coordinate of the key in the keyboard layout */
public int y;
/** The current pressed state of this key */
public boolean pressed;
/** If this is a sticky key, is it on? */
public boolean on;
/** Text to output when pressed. This can be multiple characters, like ".com" */
public CharSequence text;
/** Popup characters */
public CharSequence popupCharacters;
/**
* Flags that specify the anchoring to edges of the keyboard for detecting touch events
* that are just out of the boundary of the key. This is a bit mask of
* {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and
* {@link Keyboard#EDGE_BOTTOM}.
*/
public int edgeFlags;
/** Whether this is a modifier key, such as Shift or Alt */
public boolean modifier;
/** The keyboard that this key belongs to */
private Keyboard keyboard;
/**
* If this key pops up a mini keyboard, this is the resource id for the XML layout for that
* keyboard.
*/
public int popupResId;
/** Whether this key repeats itself when held down */
public boolean repeatable;
private final static int[] KEY_STATE_NORMAL_ON = {
android.R.attr.state_checkable,
android.R.attr.state_checked
};
private final static int[] KEY_STATE_PRESSED_ON = {
android.R.attr.state_pressed,
android.R.attr.state_checkable,
android.R.attr.state_checked
};
private final static int[] KEY_STATE_NORMAL_OFF = {
android.R.attr.state_checkable
};
private final static int[] KEY_STATE_PRESSED_OFF = {
android.R.attr.state_pressed,
android.R.attr.state_checkable
};
private final static int[] KEY_STATE_NORMAL = {
};
private final static int[] KEY_STATE_PRESSED = {
android.R.attr.state_pressed
};
/** Create an empty key with no attributes. */
public Key(Row parent) {
keyboard = parent.parent;
height = parent.defaultHeight;
width = parent.defaultWidth;
gap = parent.defaultHorizontalGap;
edgeFlags = parent.rowEdgeFlags;
}
/** Create a key with the given top-left coordinate and extract its attributes from
* the XML parser.
* @param res resources associated with the caller's context
* @param parent the row that this key belongs to. The row must already be attached to
* a {@link Keyboard}.
* @param x the x coordinate of the top-left
* @param y the y coordinate of the top-left
* @param parser the XML parser containing the attributes for this key
*/
public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
this(parent);
this.x = x;
this.y = y;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard);
width = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyWidth,
keyboard.mDisplayWidth, parent.defaultWidth);
height = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyHeight,
keyboard.mDisplayHeight, parent.defaultHeight);
gap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_horizontalGap,
keyboard.mDisplayWidth, parent.defaultHorizontalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard_Key);
this.x += gap;
TypedValue codesValue = new TypedValue();
a.getValue(com.android.internal.R.styleable.Keyboard_Key_codes,
codesValue);
if (codesValue.type == TypedValue.TYPE_INT_DEC
|| codesValue.type == TypedValue.TYPE_INT_HEX) {
codes = new int[] { codesValue.data };
} else if (codesValue.type == TypedValue.TYPE_STRING) {
codes = parseCSV(codesValue.string.toString());
}
iconPreview = a.getDrawable(com.android.internal.R.styleable.Keyboard_Key_iconPreview);
if (iconPreview != null) {
iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
iconPreview.getIntrinsicHeight());
}
popupCharacters = a.getText(
com.android.internal.R.styleable.Keyboard_Key_popupCharacters);
popupResId = a.getResourceId(
com.android.internal.R.styleable.Keyboard_Key_popupKeyboard, 0);
repeatable = a.getBoolean(
com.android.internal.R.styleable.Keyboard_Key_isRepeatable, false);
modifier = a.getBoolean(
com.android.internal.R.styleable.Keyboard_Key_isModifier, false);
sticky = a.getBoolean(
com.android.internal.R.styleable.Keyboard_Key_isSticky, false);
edgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Key_keyEdgeFlags, 0);
edgeFlags |= parent.rowEdgeFlags;
icon = a.getDrawable(
com.android.internal.R.styleable.Keyboard_Key_keyIcon);
if (icon != null) {
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
}
label = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyLabel);
text = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyOutputText);
if (codes == null && !TextUtils.isEmpty(label)) {
codes = new int[] { label.charAt(0) };
}
a.recycle();
}
/**
* Informs the key that it has been pressed, in case it needs to change its appearance or
* state.
* @see #onReleased(boolean)
*/
public void onPressed() {
pressed = !pressed;
}
/**
* Changes the pressed state of the key. If it is a sticky key, it will also change the
* toggled state of the key if the finger was release inside.
* @param inside whether the finger was released inside the key
* @see #onPressed()
*/
public void onReleased(boolean inside) {
pressed = !pressed;
if (sticky) {
on = !on;
}
}
int[] parseCSV(String value) {
int count = 0;
int lastIndex = 0;
if (value.length() > 0) {
count++;
while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
count++;
}
}
int[] values = new int[count];
count = 0;
StringTokenizer st = new StringTokenizer(value, ",");
while (st.hasMoreTokens()) {
try {
values[count++] = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
Log.e(TAG, "Error parsing keycodes " + value);
}
}
return values;
}
/**
* Detects if a point falls inside this key.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return whether or not the point falls inside the key. If the key is attached to an edge,
* it will assume that all points between the key and the edge are considered to be inside
* the key.
*/
public boolean isInside(int x, int y) {
boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
if ((x >= this.x || (leftEdge && x <= this.x + this.width))
&& (x < this.x + this.width || (rightEdge && x >= this.x))
&& (y >= this.y || (topEdge && y <= this.y + this.height))
&& (y < this.y + this.height || (bottomEdge && y >= this.y))) {
return true;
} else {
return false;
}
}
/**
* Returns the square of the distance between the center of the key and the given point.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return the square of the distance of the point from the center of the key
*/
public int squaredDistanceFrom(int x, int y) {
int xDist = this.x + width / 2 - x;
int yDist = this.y + height / 2 - y;
return xDist * xDist + yDist * yDist;
}
/**
* Returns the drawable state for the key, based on the current state and type of the key.
* @return the drawable state of the key.
* @see android.graphics.drawable.StateListDrawable#setState(int[])
*/
public int[] getCurrentDrawableState() {
int[] states = KEY_STATE_NORMAL;
if (on) {
if (pressed) {
states = KEY_STATE_PRESSED_ON;
} else {
states = KEY_STATE_NORMAL_ON;
}
} else {
if (sticky) {
if (pressed) {
states = KEY_STATE_PRESSED_OFF;
} else {
states = KEY_STATE_NORMAL_OFF;
}
} else {
if (pressed) {
states = KEY_STATE_PRESSED;
}
}
}
return states;
}
}
/**
* Creates a keyboard from the given xml key layout file.
* @param context the application or service context
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
*/
public Keyboard(Context context, int xmlLayoutResId) {
this(context, xmlLayoutResId, 0);
}
/**
* Creates a keyboard from the given xml key layout file. Weeds out rows
* that have a keyboard mode defined but don't match the specified mode.
* @param context the application or service context
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
* @param modeId keyboard mode identifier
*/
public Keyboard(Context context, int xmlLayoutResId, int modeId) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
mDisplayWidth = dm.widthPixels;
mDisplayHeight = dm.heightPixels;
//Log.v(TAG, "keyboard's display metrics:" + dm);
mDefaultHorizontalGap = 0;
mDefaultWidth = mDisplayWidth / 10;
mDefaultVerticalGap = 0;
mDefaultHeight = mDefaultWidth;
mKeys = new ArrayList<Key>();
mModifierKeys = new ArrayList<Key>();
mKeyboardMode = modeId;
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
}
/**
* <p>Creates a blank keyboard from the given resource file and populates it with the specified
* characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
* </p>
* <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
* possible in each row.</p>
* @param context the application or service context
* @param layoutTemplateResId the layout template file, containing no keys.
* @param characters the list of characters to display on the keyboard. One key will be created
* for each character.
* @param columns the number of columns of keys to display. If this number is greater than the
* number of keys that can fit in a row, it will be ignored. If this number is -1, the
* keyboard will fit as many keys as possible in each row.
*/
public Keyboard(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
this(context, layoutTemplateResId);
int x = 0;
int y = 0;
int column = 0;
mTotalWidth = 0;
Row row = new Row(this);
row.defaultHeight = mDefaultHeight;
row.defaultWidth = mDefaultWidth;
row.defaultHorizontalGap = mDefaultHorizontalGap;
row.verticalGap = mDefaultVerticalGap;
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
for (int i = 0; i < characters.length(); i++) {
char c = characters.charAt(i);
if (column >= maxColumns
|| x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
x = 0;
y += mDefaultVerticalGap + mDefaultHeight;
column = 0;
}
final Key key = new Key(row);
key.x = x;
key.y = y;
key.label = String.valueOf(c);
key.codes = new int[] { c };
column++;
x += key.width + key.gap;
mKeys.add(key);
if (x > mTotalWidth) {
mTotalWidth = x;
}
}
mTotalHeight = y + mDefaultHeight;
}
public List<Key> getKeys() {
return mKeys;
}
public List<Key> getModifierKeys() {
return mModifierKeys;
}
protected int getHorizontalGap() {
return mDefaultHorizontalGap;
}
protected void setHorizontalGap(int gap) {
mDefaultHorizontalGap = gap;
}
protected int getVerticalGap() {
return mDefaultVerticalGap;
}
protected void setVerticalGap(int gap) {
mDefaultVerticalGap = gap;
}
protected int getKeyHeight() {
return mDefaultHeight;
}
protected void setKeyHeight(int height) {
mDefaultHeight = height;
}
protected int getKeyWidth() {
return mDefaultWidth;
}
protected void setKeyWidth(int width) {
mDefaultWidth = width;
}
/**
* Returns the total height of the keyboard
* @return the total height of the keyboard
*/
public int getHeight() {
return mTotalHeight;
}
public int getMinWidth() {
return mTotalWidth;
}
public boolean setShifted(boolean shiftState) {
if (mShiftKey != null) {
mShiftKey.on = shiftState;
}
if (mShifted != shiftState) {
mShifted = shiftState;
return true;
}
return false;
}
public boolean isShifted() {
return mShifted;
}
public int getShiftKeyIndex() {
return mShiftKeyIndex;
}
private void computeNearestNeighbors() {
// Round-up so we don't have any pixels outside the grid
mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
mGridNeighbors = new int[GRID_SIZE][];
int[] indices = new int[mKeys.size()];
final int gridWidth = GRID_WIDTH * mCellWidth;
final int gridHeight = GRID_HEIGHT * mCellHeight;
for (int x = 0; x < gridWidth; x += mCellWidth) {
for (int y = 0; y < gridHeight; y += mCellHeight) {
int count = 0;
for (int i = 0; i < mKeys.size(); i++) {
final Key key = mKeys.get(i);
if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
< mProximityThreshold ||
key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
indices[count++] = i;
}
}
int [] cell = new int[count];
System.arraycopy(indices, 0, cell, 0, count);
mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
}
}
}
/**
* Returns the indices of the keys that are closest to the given point.
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @return the array of integer indices for the nearest keys to the given point. If the given
* point is out of range, then an array of size zero is returned.
*/
public int[] getNearestKeys(int x, int y) {
if (mGridNeighbors == null) computeNearestNeighbors();
if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
if (index < GRID_SIZE) {
return mGridNeighbors[index];
}
}
return new int[0];
}
protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
return new Row(res, this, parser);
}
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
XmlResourceParser parser) {
return new Key(res, parent, x, y, parser);
}
private void loadKeyboard(Context context, XmlResourceParser parser) {
boolean inKey = false;
boolean inRow = false;
boolean leftMostKey = false;
int row = 0;
int x = 0;
int y = 0;
Key key = null;
Row currentRow = null;
Resources res = context.getResources();
boolean skipRow = false;
try {
int event;
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
if (event == XmlResourceParser.START_TAG) {
String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
inRow = true;
x = 0;
currentRow = createRowFromXml(res, parser);
skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
if (skipRow) {
skipToEndOfRow(parser);
inRow = false;
}
} else if (TAG_KEY.equals(tag)) {
inKey = true;
key = createKeyFromXml(res, currentRow, x, y, parser);
mKeys.add(key);
if (key.codes[0] == KEYCODE_SHIFT) {
mShiftKey = key;
mShiftKeyIndex = mKeys.size()-1;
mModifierKeys.add(key);
} else if (key.codes[0] == KEYCODE_ALT) {
mModifierKeys.add(key);
}
} else if (TAG_KEYBOARD.equals(tag)) {
parseKeyboardAttributes(res, parser);
}
} else if (event == XmlResourceParser.END_TAG) {
if (inKey) {
inKey = false;
x += key.gap + key.width;
if (x > mTotalWidth) {
mTotalWidth = x;
}
} else if (inRow) {
inRow = false;
y += currentRow.verticalGap;
y += currentRow.defaultHeight;
row++;
} else {
// TODO: error or extend?
}
}
}
} catch (Exception e) {
Log.e(TAG, "Parse error:" + e);
e.printStackTrace();
}
mTotalHeight = y - mDefaultVerticalGap;
}
private void skipToEndOfRow(XmlResourceParser parser)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
if (event == XmlResourceParser.END_TAG
&& parser.getName().equals(TAG_ROW)) {
break;
}
}
}
private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard);
mDefaultWidth = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyWidth,
mDisplayWidth, mDisplayWidth / 10);
mDefaultHeight = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyHeight,
mDisplayHeight, 50);
mDefaultHorizontalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_horizontalGap,
mDisplayWidth, 0);
mDefaultVerticalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_verticalGap,
mDisplayHeight, 0);
mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
a.recycle();
}
static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
TypedValue value = a.peekValue(index);
if (value == null) return defValue;
if (value.type == TypedValue.TYPE_DIMENSION) {
return a.getDimensionPixelOffset(index, defValue);
} else if (value.type == TypedValue.TYPE_FRACTION) {
// Round it to avoid values like 47.9999 from getting truncated
return Math.round(a.getFraction(index, base, base, defValue));
}
return defValue;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.inputmethodservice;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.IBinder;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.WindowManager;
/**
* A SoftInputWindow is a Dialog that is intended to be used for a top-level input
* method window. It will be displayed along the edge of the screen, moving
* the application user interface away from it so that the focused item is
* always visible.
*/
class SoftInputWindow extends Dialog {
final KeyEvent.DispatcherState mDispatcherState;
public void setToken(IBinder token) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.token = token;
getWindow().setAttributes(lp);
}
/**
* Create a DockWindow that uses a custom style.
*
* @param context The Context in which the DockWindow should run. In
* particular, it uses the window manager and theme from this context
* to present its UI.
* @param theme A style resource describing the theme to use for the window.
* See <a href="{@docRoot}reference/available-resources.html#stylesandthemes">Style
* and Theme Resources</a> for more information about defining and
* using styles. This theme is applied on top of the current theme in
* <var>context</var>. If 0, the default dialog theme will be used.
*/
public SoftInputWindow(Context context, int theme,
KeyEvent.DispatcherState dispatcherState) {
super(context, theme);
mDispatcherState = dispatcherState;
initDockWindow();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mDispatcherState.reset();
}
/**
* Get the size of the DockWindow.
*
* @return If the DockWindow sticks to the top or bottom of the screen, the
* return value is the height of the DockWindow, and its width is
* equal to the width of the screen; If the DockWindow sticks to the
* left or right of the screen, the return value is the width of the
* DockWindow, and its height is equal to the height of the screen.
*/
public int getSize() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
return lp.height;
} else {
return lp.width;
}
}
/**
* Set the size of the DockWindow.
*
* @param size If the DockWindow sticks to the top or bottom of the screen,
* <var>size</var> is the height of the DockWindow, and its width is
* equal to the width of the screen; If the DockWindow sticks to the
* left or right of the screen, <var>size</var> is the width of the
* DockWindow, and its height is equal to the height of the screen.
*/
public void setSize(int size) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
lp.width = -1;
lp.height = size;
} else {
lp.width = size;
lp.height = -1;
}
getWindow().setAttributes(lp);
}
/**
* Set which boundary of the screen the DockWindow sticks to.
*
* @param gravity The boundary of the screen to stick. See {#link
* android.view.Gravity.LEFT}, {#link android.view.Gravity.TOP},
* {#link android.view.Gravity.BOTTOM}, {#link
* android.view.Gravity.RIGHT}.
*/
public void setGravity(int gravity) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
boolean oldIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
lp.gravity = gravity;
boolean newIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
if (oldIsVertical != newIsVertical) {
int tmp = lp.width;
lp.width = lp.height;
lp.height = tmp;
getWindow().setAttributes(lp);
}
}
private void initDockWindow() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
lp.setTitle("InputMethod");
lp.gravity = Gravity.BOTTOM;
lp.width = -1;
// Let the input method window's orientation follow sensor based rotation
// Turn this off for now, it is very problematic.
//lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
getWindow().setAttributes(lp);
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
}

View File

@@ -0,0 +1,8 @@
<html>
<body>
Base classes for writing input methods. These APIs are not for use by
normal applications, they are a framework specifically for writing input
method components. Implementations will typically derive from
{@link android.inputmethodservice.InputMethodService}.
</body>
</html>