Java tutorial
/* * Copyright (C) 2015 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 com.android.switchaccess; import android.accessibilityservice.AccessibilityService; import android.annotation.TargetApi; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.support.v4.os.BuildCompat; import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import com.android.switchaccess.treebuilding.MainTreeBuilder; import com.android.utils.LogUtils; import com.android.utils.SharedPreferencesUtils; import com.android.utils.widget.SimpleOverlay; import com.google.android.marvin.talkback.TalkBackService; /** * Enable a user to perform touch gestures using keyboards with only a small number of keys. These * keyboards may be adapted to match a user's capabilities, for example with very large buttons, * proximity sensors around the head, or sip/puff switches. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class SwitchAccessService extends AccessibilityService implements SharedPreferences.OnSharedPreferenceChangeListener, ActionProcessor.UiChangedListener { private ActionProcessor mActionProcessor; private UiChangeDetector mEventProcessor; private Analytics mAnalytics; private WakeLock mWakeLock; private static SwitchAccessService sInstance = null; private OverlayController mOverlayController; private OptionManager mOptionManager; private AutoScanController mAutoScanController; private KeyboardEventManager mKeyboardEventManager; private MainTreeBuilder mMainTreeBuilder; @Override public boolean onUnbind(Intent intent) { if (mAutoScanController != null) { mAutoScanController.stopScan(); } return super.onUnbind(intent); } @Override public void onDestroy() { mAnalytics.stop(); mOptionManager.shutdown(); mOverlayController.shutdown(); mMainTreeBuilder.shutdown(); sInstance = null; super.onDestroy(); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { mEventProcessor.onAccessibilityEvent(event); } @Override public void onInterrupt() { /* TODO Will this ever be called? */ } /** * @return The active SwitchingControl instance or {@code null} if not available. */ public static SwitchAccessService getInstance() { return sInstance; } /** * Intended to mimic the behavior of onKeyEvent if this were the only service running. * It will be called from onKeyEvent, both from this service and from others in this apk * (TalkBack). This method must not block, since it will block onKeyEvent as well. * @param keyEvent A key event * @return {@code true} if the event is handled, {@code false} otherwise. */ public boolean onKeyEventShared(KeyEvent keyEvent) { if (mKeyboardEventManager.onKeyEvent(keyEvent, mActionProcessor, mAnalytics)) { mWakeLock.acquire(); mWakeLock.release(); return true; } return false; } /* * FULL_WAKE_LOCK has been deprecated, but the replacement only works for applications. The docs * actually say that using wake locks from a service is a legitimate use case. */ @SuppressWarnings("deprecation") @Override protected void onServiceConnected() { sInstance = this; mOverlayController = new OverlayController(new SimpleOverlay(this)); mOverlayController.configureOverlay(); mOptionManager = new OptionManager(mOverlayController); mMainTreeBuilder = new MainTreeBuilder(this); mAutoScanController = new AutoScanController(mOptionManager, new Handler(), this); mKeyboardEventManager = new KeyboardEventManager(this, mOptionManager, mAutoScanController); mAnalytics = new Analytics(); mAnalytics.start(); PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "SwitchAccess"); SharedPreferencesUtils.getSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); mActionProcessor = new ActionProcessor(this); mEventProcessor = new UiChangeDetector(mActionProcessor); } @Override protected boolean onKeyEvent(KeyEvent keyEvent) { boolean keyHandled = onKeyEventShared(keyEvent); if (keyHandled) { /* * This is inefficient but is needed when we pull down the notification shade. * It also only works because the key event handling is delayed to see if the * UI needs to stabilize. * TODO Refactor so we only re-index immediately after events if we're scanning */ rebuildOptionScanTree(); } if (!BuildCompat.isAtLeastN()) { TalkBackService talkBackService = TalkBackService.getInstance(); if (talkBackService != null) { keyHandled = talkBackService.onKeyEventShared(keyEvent) || keyHandled; } } return keyHandled; } @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { LogUtils.log(this, Log.DEBUG, "A shared preference changed: %s", key); mKeyboardEventManager.reloadPreferences(this); } @Override public void onUiChangedAndIsNowStable() { rebuildOptionScanTree(); } public OptionManager getOptionManager() { return mOptionManager; } private void rebuildOptionScanTree() { OptionScanNode globalContextMenuTree = mMainTreeBuilder .buildContextMenu(GlobalActionNode.getGlobalActionList(this)); mOptionManager.clearFocusIfNewTree(mMainTreeBuilder.addWindowListToTree( SwitchAccessWindowInfo.convertZOrderWindowList(getWindows()), globalContextMenuTree)); } }