1cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/* 2cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Copyright (C) 2012 The Android Open Source Project 3cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * 4cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * you may not use this file except in compliance with the License. 6cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * You may obtain a copy of the License at 7cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * 8cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * 10cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Unless required by applicable law or agreed to in writing, software 11cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * See the License for the specific language governing permissions and 14cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * limitations under the License. 15cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */ 16cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 17cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownpackage com.android.server.input; 18cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 19cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.ArrayUtils; 20cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.FastXmlSerializer; 21cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport com.android.internal.util.XmlUtils; 22cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 23cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParser; 24cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlPullParserException; 25cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport org.xmlpull.v1.XmlSerializer; 26cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 27abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagiimport android.annotation.Nullable; 28d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gereckeimport android.view.Surface; 29d6396d67201fb2b64d13070324bb115c9c23b08aJason Gereckeimport android.hardware.input.TouchCalibration; 3039606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile; 31cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Slog; 32cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport android.util.Xml; 33cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 34cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedInputStream; 35cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.BufferedOutputStream; 36cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.File; 37cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileNotFoundException; 38cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.FileOutputStream; 39cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.IOException; 40cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.io.InputStream; 419e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets; 42cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.ArrayList; 43cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Collections; 44cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.HashMap; 45cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Map; 46607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamathimport java.util.Objects; 47cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport java.util.Set; 48cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 49cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownimport libcore.io.IoUtils; 50cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 51cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown/** 52cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Manages persistent state recorded by the input manager service as an XML file. 53cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * Caller must acquire lock on the data store before accessing it. 54cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * 55cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * File format: 56cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <code> 57cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <input-mananger-state> 58cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <input-devices> 59cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" /> 60cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * >input-devices> 61cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * >/input-manager-state> 62cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown * </code> 63cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown */ 64cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brownfinal class PersistentDataStore { 65cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown static final String TAG = "InputManager"; 66cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 67cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // Input device state by descriptor. 68cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private final HashMap<String, InputDeviceState> mInputDevices = 69cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown new HashMap<String, InputDeviceState>(); 70cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private final AtomicFile mAtomicFile; 71cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 72cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // True if the data has been loaded. 73cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private boolean mLoaded; 74cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 75cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // True if there are changes to be saved. 76cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private boolean mDirty; 77cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 78cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public PersistentDataStore() { 79e17b445b6c813f6f9bc93a5e3811128a197ef50bDianne Hackborn mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"), 80e17b445b6c813f6f9bc93a5e3811128a197ef50bDianne Hackborn "input-state"); 81cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 82cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 83cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public void saveIfNeeded() { 84cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (mDirty) { 85cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown save(); 86cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mDirty = false; 87cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 88cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 89cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 90d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { 91d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); 92d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke if (state == null) { 93d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke return TouchCalibration.IDENTITY; 94d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 95d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 96d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke TouchCalibration cal = state.getTouchCalibration(surfaceRotation); 97d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (cal == null) { 98d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return TouchCalibration.IDENTITY; 99d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 100d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return cal; 101d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 102d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 103d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) { 104d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); 105d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 106d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (state.setTouchCalibration(surfaceRotation, calibration)) { 107d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke setDirty(); 108d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke return true; 109d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 110d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 111d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke return false; 112d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 113d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 114cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public String getCurrentKeyboardLayout(String inputDeviceDescriptor) { 115cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); 116cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return state != null ? state.getCurrentKeyboardLayout() : null; 117cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 118cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 119cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor, 120cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown String keyboardLayoutDescriptor) { 121cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); 122cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) { 123cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 124cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 125cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 126cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 127cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 128cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 129cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public String[] getKeyboardLayouts(String inputDeviceDescriptor) { 130cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); 131cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state == null) { 132cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return (String[])ArrayUtils.emptyArray(String.class); 133cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 134cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return state.getKeyboardLayouts(); 135cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 136cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 1372d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa public boolean addKeyboardLayout(String inputDeviceDescriptor, 1382d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa String keyboardLayoutDescriptor) { 139cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); 140cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state.addKeyboardLayout(keyboardLayoutDescriptor)) { 141cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 142cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 143cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 144cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 145cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 146cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 147cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean removeKeyboardLayout(String inputDeviceDescriptor, 148cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown String keyboardLayoutDescriptor) { 149cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); 150cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) { 151cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 152cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 153cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 154cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 155cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 156cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 1572d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) { 158cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); 1592d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (state != null && state.switchKeyboardLayout(direction)) { 160cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 161cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 162cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 163cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 164cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 165cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 166cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) { 167cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown boolean changed = false; 168cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown for (InputDeviceState state : mInputDevices.values()) { 169cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) { 170cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown changed = true; 171cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 172cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 173cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (changed) { 174cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 175cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 176cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 177cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 178cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 179cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 180cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private InputDeviceState getInputDeviceState(String inputDeviceDescriptor, 181cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown boolean createIfAbsent) { 182cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown loadIfNeeded(); 183cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = mInputDevices.get(inputDeviceDescriptor); 184cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (state == null && createIfAbsent) { 185cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown state = new InputDeviceState(); 186cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mInputDevices.put(inputDeviceDescriptor, state); 187cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown setDirty(); 188cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 189cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return state; 190cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 191cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 192cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void loadIfNeeded() { 193cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (!mLoaded) { 194cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown load(); 195cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mLoaded = true; 196cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 197cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 198cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 199cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void setDirty() { 200cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mDirty = true; 201cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 202cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 203cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void clearState() { 204cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mInputDevices.clear(); 205cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 206cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 207cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void load() { 208cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown clearState(); 209cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 210cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final InputStream is; 211cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown try { 212cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown is = mAtomicFile.openRead(); 213cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } catch (FileNotFoundException ex) { 214cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return; 215cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 216cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 217cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown XmlPullParser parser; 218cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown try { 219cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown parser = Xml.newPullParser(); 2209e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name()); 221cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown loadFromXml(parser); 222cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } catch (IOException ex) { 223cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex); 224cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown clearState(); 225cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } catch (XmlPullParserException ex) { 226cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex); 227cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown clearState(); 228cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } finally { 229cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown IoUtils.closeQuietly(is); 230cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 231cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 232cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 233cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void save() { 234cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final FileOutputStream os; 235cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown try { 236cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown os = mAtomicFile.startWrite(); 237cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown boolean success = false; 238cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown try { 239cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown XmlSerializer serializer = new FastXmlSerializer(); 2409e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name()); 241cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown saveToXml(serializer); 242cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.flush(); 243cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown success = true; 244cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } finally { 245cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (success) { 246cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mAtomicFile.finishWrite(os); 247cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } else { 248cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mAtomicFile.failWrite(os); 249cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 250cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 251cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } catch (IOException ex) { 252cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex); 253cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 254cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 255cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 256cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void loadFromXml(XmlPullParser parser) 257cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throws IOException, XmlPullParserException { 258cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown XmlUtils.beginDocument(parser, "input-manager-state"); 259cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final int outerDepth = parser.getDepth(); 260cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown while (XmlUtils.nextElementWithin(parser, outerDepth)) { 261cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (parser.getName().equals("input-devices")) { 262cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown loadInputDevicesFromXml(parser); 263cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 264cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 265cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 266cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 267cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void loadInputDevicesFromXml(XmlPullParser parser) 268cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throws IOException, XmlPullParserException { 269cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final int outerDepth = parser.getDepth(); 270cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown while (XmlUtils.nextElementWithin(parser, outerDepth)) { 271cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (parser.getName().equals("input-device")) { 272cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown String descriptor = parser.getAttributeValue(null, "descriptor"); 273cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (descriptor == null) { 274cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throw new XmlPullParserException( 275cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown "Missing descriptor attribute on input-device."); 276cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 277cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (mInputDevices.containsKey(descriptor)) { 278cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throw new XmlPullParserException("Found duplicate input device."); 279cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 280cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 281cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown InputDeviceState state = new InputDeviceState(); 282cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown state.loadFromXml(parser); 283cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mInputDevices.put(descriptor, state); 284cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 285cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 286cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 287cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 288cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void saveToXml(XmlSerializer serializer) throws IOException { 289cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.startDocument(null, true); 290cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 291cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.startTag(null, "input-manager-state"); 292cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.startTag(null, "input-devices"); 293cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) { 294cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final String descriptor = entry.getKey(); 295cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final InputDeviceState state = entry.getValue(); 296cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.startTag(null, "input-device"); 297cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.attribute(null, "descriptor", descriptor); 298cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown state.saveToXml(serializer); 299cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.endTag(null, "input-device"); 300cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 301cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.endTag(null, "input-devices"); 302cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.endTag(null, "input-manager-state"); 303cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.endDocument(); 304cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 305cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 306cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private static final class InputDeviceState { 307d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke private static final String[] CALIBRATION_NAME = { "x_scale", 308d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" }; 309d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 310d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke private TouchCalibration[] mTouchCalibration = new TouchCalibration[4]; 311abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi @Nullable 312cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private String mCurrentKeyboardLayout; 3132d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa private ArrayList<String> mKeyboardLayouts = new ArrayList<String>(); 314cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 315d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke public TouchCalibration getTouchCalibration(int surfaceRotation) { 316d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke try { 317d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return mTouchCalibration[surfaceRotation]; 318d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } catch (ArrayIndexOutOfBoundsException ex) { 319d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex); 320d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return null; 321d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 322d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 323d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 324d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) { 325d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke try { 326d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (!calibration.equals(mTouchCalibration[surfaceRotation])) { 327d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke mTouchCalibration[surfaceRotation] = calibration; 328d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return true; 329d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 330d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return false; 331d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } catch (ArrayIndexOutOfBoundsException ex) { 332d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex); 333d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke return false; 334d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 335d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 336d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 337abb12ed4f2fe5843d5f23a1b3d29ade4f9da76e0Keisuke Kuroyanagi @Nullable 338cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public String getCurrentKeyboardLayout() { 339cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return mCurrentKeyboardLayout; 340cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 341cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 342cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean setCurrentKeyboardLayout(String keyboardLayout) { 343607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamath if (Objects.equals(mCurrentKeyboardLayout, keyboardLayout)) { 344cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 345cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 346cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown addKeyboardLayout(keyboardLayout); 347cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mCurrentKeyboardLayout = keyboardLayout; 348cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 349cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 350cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 351cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public String[] getKeyboardLayouts() { 3522d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (mKeyboardLayouts.isEmpty()) { 353cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return (String[])ArrayUtils.emptyArray(String.class); 354cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 3552d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]); 356cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 357cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 358cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean addKeyboardLayout(String keyboardLayout) { 3592d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout); 360cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (index >= 0) { 361cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 362cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 3632d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mKeyboardLayouts.add(-index - 1, keyboardLayout); 364cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (mCurrentKeyboardLayout == null) { 365cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mCurrentKeyboardLayout = keyboardLayout; 366cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 367cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 368cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 369cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 370cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean removeKeyboardLayout(String keyboardLayout) { 3712d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout); 372cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (index < 0) { 373cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return false; 374cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 3752d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mKeyboardLayouts.remove(index); 376cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index); 377cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return true; 378cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 379cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 380cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown private void updateCurrentKeyboardLayoutIfRemoved( 381cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown String removedKeyboardLayout, int removedIndex) { 382607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamath if (Objects.equals(mCurrentKeyboardLayout, removedKeyboardLayout)) { 3832d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (!mKeyboardLayouts.isEmpty()) { 384cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown int index = removedIndex; 3852d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (index == mKeyboardLayouts.size()) { 386cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown index = 0; 387cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 3882d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mCurrentKeyboardLayout = mKeyboardLayouts.get(index); 389cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } else { 390cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mCurrentKeyboardLayout = null; 391cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 392cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 393cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 394cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 3952d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa public boolean switchKeyboardLayout(int direction) { 3962d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa final int size = mKeyboardLayouts.size(); 3972d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (size < 2) { 3982d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa return false; 3992d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa } 4002d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout); 4012d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa assert index >= 0; 4022d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (direction > 0) { 4032d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa index = (index + 1) % size; 4042d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa } else { 4052d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa index = (index + size - 1) % size; 406cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 4072d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mCurrentKeyboardLayout = mKeyboardLayouts.get(index); 4082d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa return true; 409cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 410cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 411cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) { 412cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown boolean changed = false; 4132d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa for (int i = mKeyboardLayouts.size(); i-- > 0; ) { 4142d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa String keyboardLayout = mKeyboardLayouts.get(i); 415cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (!availableKeyboardLayouts.contains(keyboardLayout)) { 416cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout); 4172d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mKeyboardLayouts.remove(i); 418cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i); 419cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown changed = true; 420cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 421cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 422cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown return changed; 423cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 424cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 425cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public void loadFromXml(XmlPullParser parser) 426cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throws IOException, XmlPullParserException { 427cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown final int outerDepth = parser.getDepth(); 428cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown while (XmlUtils.nextElementWithin(parser, outerDepth)) { 429cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (parser.getName().equals("keyboard-layout")) { 430cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown String descriptor = parser.getAttributeValue(null, "descriptor"); 431cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (descriptor == null) { 432cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throw new XmlPullParserException( 433cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown "Missing descriptor attribute on keyboard-layout."); 434cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 435d5f7ed9fe9dc3590f6ef9cb7470e29e836a95907Michael Wright String current = parser.getAttributeValue(null, "current"); 4362d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (mKeyboardLayouts.contains(descriptor)) { 4372d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa throw new XmlPullParserException( 4382d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa "Found duplicate keyboard layout."); 4392d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa } 4402d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa 4412d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mKeyboardLayouts.add(descriptor); 442cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (current != null && current.equals("true")) { 443cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (mCurrentKeyboardLayout != null) { 444cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown throw new XmlPullParserException( 445cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown "Found multiple current keyboard layouts."); 446cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 447cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown mCurrentKeyboardLayout = descriptor; 448cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 449d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } else if (parser.getName().equals("calibration")) { 450d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke String format = parser.getAttributeValue(null, "format"); 451d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke String rotation = parser.getAttributeValue(null, "rotation"); 452d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke int r = -1; 453d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 454d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke if (format == null) { 455d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke throw new XmlPullParserException( 456d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke "Missing format attribute on calibration."); 457d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 458d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (!format.equals("affine")) { 459d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke throw new XmlPullParserException( 460d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke "Unsupported format for calibration."); 461d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 462d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (rotation != null) { 463d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke try { 464d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke r = stringToSurfaceRotation(rotation); 465d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } catch (IllegalArgumentException e) { 466d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke throw new XmlPullParserException( 467d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke "Unsupported rotation for calibration."); 468d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 469d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 470d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 471d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke float[] matrix = TouchCalibration.IDENTITY.getAffineTransform(); 472d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke int depth = parser.getDepth(); 473d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke while (XmlUtils.nextElementWithin(parser, depth)) { 474d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke String tag = parser.getName().toLowerCase(); 475d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke String value = parser.nextText(); 476d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 477d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) { 478d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (tag.equals(CALIBRATION_NAME[i])) { 479d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke matrix[i] = Float.parseFloat(value); 480d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke break; 481d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 482d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 483d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 484d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 485d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (r == -1) { 486d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke // Assume calibration applies to all rotations 487d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke for (r = 0; r < mTouchCalibration.length; r++) { 488d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke mTouchCalibration[r] = new TouchCalibration(matrix[0], 489d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); 490d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 491d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } else { 492d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke mTouchCalibration[r] = new TouchCalibration(matrix[0], 493d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); 494d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 495cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 496cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 497cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 498cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // Maintain invariant that layouts are sorted. 4992d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa Collections.sort(mKeyboardLayouts); 500cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 501cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // Maintain invariant that there is always a current keyboard layout unless 502cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown // there are none installed. 5032d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) { 5042d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa mCurrentKeyboardLayout = mKeyboardLayouts.get(0); 505cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 506cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 507cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown 508cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown public void saveToXml(XmlSerializer serializer) throws IOException { 5092d9accb29f8878cd6d92ec78d1956e64322bce5cYohei Yukawa for (String layout : mKeyboardLayouts) { 510cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.startTag(null, "keyboard-layout"); 511cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.attribute(null, "descriptor", layout); 512cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown if (layout.equals(mCurrentKeyboardLayout)) { 513cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.attribute(null, "current", "true"); 514cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 515cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown serializer.endTag(null, "keyboard-layout"); 516cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 517d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke 518d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke for (int i = 0; i < mTouchCalibration.length; i++) { 519d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if (mTouchCalibration[i] != null) { 520d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke String rotation = surfaceRotationToString(i); 521d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke float[] transform = mTouchCalibration[i].getAffineTransform(); 522d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 523d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.startTag(null, "calibration"); 524d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.attribute(null, "format", "affine"); 525d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.attribute(null, "rotation", rotation); 526d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) { 527d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.startTag(null, CALIBRATION_NAME[j]); 528d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.text(Float.toString(transform[j])); 529d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.endTag(null, CALIBRATION_NAME[j]); 530d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 531d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke serializer.endTag(null, "calibration"); 532d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 533d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 534d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 535d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 536d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke private static String surfaceRotationToString(int surfaceRotation) { 537d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke switch (surfaceRotation) { 538d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke case Surface.ROTATION_0: return "0"; 539d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke case Surface.ROTATION_90: return "90"; 540d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke case Surface.ROTATION_180: return "180"; 541d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke case Surface.ROTATION_270: return "270"; 542d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 543d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation); 544d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 545d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke 546d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke private static int stringToSurfaceRotation(String s) { 547d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if ("0".equals(s)) { 548d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return Surface.ROTATION_0; 549d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 550d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if ("90".equals(s)) { 551d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return Surface.ROTATION_90; 552d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 553d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if ("180".equals(s)) { 554d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return Surface.ROTATION_180; 555d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke } 556d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke if ("270".equals(s)) { 557d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke return Surface.ROTATION_270; 558d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke } 559d52207423225bcd99e94276e9d0fc2cb5f905602Jason Gerecke throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'"); 560cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 561cf39bdf3dff5e29447f6ce734b76dc3490385e58Jeff Brown } 562d6396d67201fb2b64d13070324bb115c9c23b08aJason Gerecke} 563