/* * 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.graphics.Rect; import android.os.IBinder; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import java.lang.Math; /** * 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; private final Rect mBounds = new Rect(); 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 Style * and Theme Resources for more information about defining and * using styles. This theme is applied on top of the current theme in * context. 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(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { getWindow().getDecorView().getHitRect(mBounds); final MotionEvent event = clipMotionEvent(ev, mBounds); return super.dispatchTouchEvent(event); } /** * 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, * size 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, size 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); } private static MotionEvent clipMotionEvent(MotionEvent me, Rect bounds) { final int pointerCount = me.getPointerCount(); boolean shouldClip = false; for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { final int x = (int)me.getX(pointerIndex); final int y = (int)me.getY(pointerIndex); if (!bounds.contains(x, y)) { shouldClip = true; break; } } if (!shouldClip) return me; if (pointerCount == 1) { final int x = (int)me.getX(); final int y = (int)me.getY(); me.setLocation( Math.max(bounds.left, Math.min(x, bounds.right - 1)), Math.max(bounds.top, Math.min(y, bounds.bottom - 1))); return me; } final int[] pointerIds = new int[pointerCount]; final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount]; for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { pointerIds[pointerIndex] = me.getPointerId(pointerIndex); final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); me.getPointerCoords(pointerIndex, coords); pointerCoords[pointerIndex] = coords; final int x = (int)coords.x; final int y = (int)coords.y; if (!bounds.contains(x, y)) { coords.x = Math.max(bounds.left, Math.min(x, bounds.right - 1)); coords.y = Math.max(bounds.top, Math.min(y, bounds.bottom - 1)); } } return MotionEvent.obtain( me.getDownTime(), me.getEventTime(), me.getAction(), pointerCount, pointerIds, pointerCoords, me.getMetaState(), me.getXPrecision(), me.getYPrecision(), me.getDeviceId(), me.getEdgeFlags(), me.getSource(), me.getFlags()); } }