171 lines
6.4 KiB
Java
171 lines
6.4 KiB
Java
/*
|
|
* Copyright (C) 2018 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 com.example.android.multiclientinputmethod;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.app.Service;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.hardware.display.DisplayManager;
|
|
import android.hardware.display.DisplayManager.DisplayListener;
|
|
import android.inputmethodservice.MultiClientInputMethodServiceDelegate;
|
|
import android.os.IBinder;
|
|
import android.util.Log;
|
|
import android.util.SparseIntArray;
|
|
import android.view.Display;
|
|
|
|
/**
|
|
* A {@link Service} that implements multi-client IME protocol.
|
|
*/
|
|
public final class MultiClientInputMethod extends Service implements DisplayListener {
|
|
private static final String TAG = "MultiClientInputMethod";
|
|
private static final boolean DEBUG = false;
|
|
|
|
// last client that had active InputConnection for a given displayId.
|
|
final SparseIntArray mDisplayToLastClientId = new SparseIntArray();
|
|
// Mapping table from the display where IME is attached to the display where IME window will be
|
|
// shown. Assumes that missing display will use the same display for the IME window.
|
|
SparseIntArray mInputDisplayToImeDisplay;
|
|
SoftInputWindowManager mSoftInputWindowManager;
|
|
MultiClientInputMethodServiceDelegate mDelegate;
|
|
|
|
private DisplayManager mDisplayManager;
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "onCreate");
|
|
}
|
|
mInputDisplayToImeDisplay = buildInputDisplayToImeDisplay();
|
|
mDelegate = MultiClientInputMethodServiceDelegate.create(this,
|
|
new MultiClientInputMethodServiceDelegate.ServiceCallback() {
|
|
@Override
|
|
public void initialized() {
|
|
if (DEBUG) {
|
|
Log.i(TAG, "initialized");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addClient(int clientId, int uid, int pid,
|
|
int selfReportedDisplayId) {
|
|
int imeDisplayId = mInputDisplayToImeDisplay.get(selfReportedDisplayId,
|
|
selfReportedDisplayId);
|
|
final ClientCallbackImpl callback = new ClientCallbackImpl(
|
|
MultiClientInputMethod.this, mDelegate,
|
|
mSoftInputWindowManager, clientId, uid, pid, imeDisplayId);
|
|
if (DEBUG) {
|
|
Log.v(TAG, "addClient clientId=" + clientId + " uid=" + uid
|
|
+ " pid=" + pid + " displayId=" + selfReportedDisplayId
|
|
+ " imeDisplayId=" + imeDisplayId);
|
|
}
|
|
|
|
mDelegate.acceptClient(clientId, callback, callback.getDispatcherState(),
|
|
callback.getLooper());
|
|
}
|
|
|
|
@Override
|
|
public void removeClient(int clientId) {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "removeClient clientId=" + clientId);
|
|
}
|
|
}
|
|
});
|
|
mSoftInputWindowManager = new SoftInputWindowManager(this, mDelegate);
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayAdded(int displayId) {
|
|
mInputDisplayToImeDisplay = buildInputDisplayToImeDisplay();
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayRemoved(int displayId) {
|
|
mDisplayToLastClientId.delete(displayId);
|
|
}
|
|
|
|
@Override
|
|
public void onDisplayChanged(int displayId) {
|
|
}
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "onBind intent=" + intent);
|
|
}
|
|
mDisplayManager = getApplicationContext().getSystemService(DisplayManager.class);
|
|
mDisplayManager.registerDisplayListener(this, getMainThreadHandler());
|
|
return mDelegate.onBind(intent);
|
|
}
|
|
|
|
@Override
|
|
public boolean onUnbind(Intent intent) {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "onUnbind intent=" + intent);
|
|
}
|
|
if (mDisplayManager != null) {
|
|
mDisplayManager.unregisterDisplayListener(this);
|
|
}
|
|
return mDelegate.onUnbind(intent);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "onDestroy");
|
|
}
|
|
mDelegate.onDestroy();
|
|
}
|
|
|
|
@NonNull
|
|
private SparseIntArray buildInputDisplayToImeDisplay() {
|
|
Context context = getApplicationContext();
|
|
String config[] = context.getResources().getStringArray(
|
|
R.array.config_inputDisplayToImeDisplay);
|
|
|
|
SparseIntArray inputDisplayToImeDisplay = new SparseIntArray();
|
|
Display[] displays = context.getSystemService(DisplayManager.class).getDisplays();
|
|
for (String item: config) {
|
|
String[] pair = item.split("/");
|
|
if (pair.length != 2) {
|
|
Log.w(TAG, "Skip illegal config: " + item);
|
|
continue;
|
|
}
|
|
int inputDisplay = findDisplayId(displays, pair[0]);
|
|
int imeDisplay = findDisplayId(displays, pair[1]);
|
|
if (inputDisplay != Display.INVALID_DISPLAY && imeDisplay != Display.INVALID_DISPLAY) {
|
|
inputDisplayToImeDisplay.put(inputDisplay, imeDisplay);
|
|
}
|
|
}
|
|
return inputDisplayToImeDisplay;
|
|
}
|
|
|
|
private static int findDisplayId(Display displays[], String regexp) {
|
|
for (Display display: displays) {
|
|
if (display.getUniqueId().matches(regexp)) {
|
|
int displayId = display.getDisplayId();
|
|
if (DEBUG) {
|
|
Log.v(TAG, regexp + " matches displayId=" + displayId);
|
|
}
|
|
return displayId;
|
|
}
|
|
}
|
|
Log.w(TAG, "Can't find the display of " + regexp);
|
|
return Display.INVALID_DISPLAY;
|
|
}
|
|
}
|