diff options
author | Vitalii Tomkiv <vitalit@google.com> | 2016-05-23 16:55:22 -0700 |
---|---|---|
committer | Vitalii Tomkiv <vitalit@google.com> | 2016-06-01 14:34:48 -0700 |
commit | 46371473c416415fb6bcb8db85686669c3d65af6 (patch) | |
tree | 624c5dad2d5a07bb8b9ae80ec19b9c105cd7d9ad /car-lib/src/android/car/CarAppFocusManager.java | |
parent | 0ad4c14fef5cc9f9f9eedea9cfe612a5de2519f3 (diff) | |
download | Car-46371473c416415fb6bcb8db85686669c3d65af6.tar.gz |
Car api review: CarAppContextManager renaming to CarAppFocusManager.
Changes include:
- rename to CarAppFocusManager
- allow multiple client listener for listening and setting.
- change name for set / reset to requestAppFocus, abandonAppFocus
- rename all APIs to use focus instead of context
Bug: 28886557
Change-Id: I5e5285c104435dbd2eea43c4b0e43ae5ead81218
Diffstat (limited to 'car-lib/src/android/car/CarAppFocusManager.java')
-rw-r--r-- | car-lib/src/android/car/CarAppFocusManager.java | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java new file mode 100644 index 0000000000..8697dd2a46 --- /dev/null +++ b/car-lib/src/android/car/CarAppFocusManager.java @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2015 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.car; + +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * CarAppFocusManager allows applications to set and listen for the current application focus + * like active navigation or active voice command. Usually only one instance of such application + * should run in the system, and other app setting the flag for the matching app should + * lead into other app to stop. + */ +public final class CarAppFocusManager implements CarManagerBase { + /** + * Listener to get notification for app getting information on application type status changes. + */ + public interface AppFocusChangeListener { + /** + * Application focus has changed. Note that {@link CarAppFocusManager} instance + * causing the change will not get this notification. + * @param appType + * @param active + */ + void onAppFocusChange(int appType, boolean active); + } + + /** + * Listener to get notification for app getting information on app type ownership loss. + */ + public interface AppFocusOwnershipChangeListener { + /** + * Lost ownership for the focus, which happens when other app has set the focus. + * The app losing focus should stop the action associated with the focus. + * For example, navigation app currently running active navigation should stop navigation + * upon getting this for {@link CarAppFocusManager#APP_FOCUS_TYPE_NAVIGATION}. + * @param appType + */ + void onAppFocusOwnershipLoss(int appType); + } + + /** + * Represents navigation focus. + */ + public static final int APP_FOCUS_TYPE_NAVIGATION = 1; + /** + * Represents voice command focus. + */ + public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; + /** + * Update this after adding a new app type. + * @hide + */ + public static final int APP_FOCUS_MAX = 2; + + /** + * A failed focus change request. + */ + public static final int APP_FOCUS_REQUEST_FAILED = 0; + /** + * A successful focus change request. + */ + public static final int APP_FOCUS_REQUEST_GRANTED = 1; + + private final IAppFocus mService; + private final Handler mHandler; + private final Map<AppFocusChangeListener, IAppFocusListenerImpl> mChangeBinders = + new HashMap<>(); + private final Map<AppFocusOwnershipChangeListener, IAppFocusOwnershipListenerImpl> + mOwnershipBinders = new HashMap<>(); + + /** + * @hide + */ + CarAppFocusManager(IBinder service, Looper looper) { + mService = IAppFocus.Stub.asInterface(service); + mHandler = new Handler(looper); + } + + /** + * Register listener to monitor app focus change. + * @param listener + * @param appType Applitcaion type to get notification for. + * @throws CarNotConnectedException + */ + public void registerFocusListener(AppFocusChangeListener listener, int appType) + throws CarNotConnectedException { + if (listener == null) { + throw new IllegalArgumentException("null listener"); + } + IAppFocusListenerImpl binder; + synchronized (this) { + binder = mChangeBinders.get(listener); + if (binder == null) { + binder = new IAppFocusListenerImpl(this, listener); + mChangeBinders.put(listener, binder); + } + binder.addAppType(appType); + } + try { + mService.registerFocusListener(binder, appType); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** + * Unregister listener for application type and stop listening focus change events. + * @param listener + * @param appType + * @throws CarNotConnectedException + */ + public void unregisterFocusListener(AppFocusChangeListener listener, int appType) + throws CarNotConnectedException { + IAppFocusListenerImpl binder; + synchronized (this) { + binder = mChangeBinders.get(listener); + if (binder == null) { + return; + } + } + try { + mService.unregisterFocusListener(binder, appType); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + synchronized (this) { + binder.removeAppType(appType); + if (!binder.hasAppTypes()) { + mChangeBinders.remove(listener); + } + + } + } + + /** + * Unregister listener and stop listening focus change events. + * @param listener + * @throws CarNotConnectedException + */ + public void unregisterFocusListener(AppFocusChangeListener listener) + throws CarNotConnectedException { + IAppFocusListenerImpl binder; + synchronized (this) { + binder = mChangeBinders.remove(listener); + if (binder == null) { + return; + } + } + try { + for (Integer appType : binder.getAppTypes()) { + mService.unregisterFocusListener(binder, appType); + } + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** + * Returns application types currently active in the system. + * @throws CarNotConnectedException + */ + public int[] getActiveAppTypes() throws CarNotConnectedException { + try { + return mService.getActiveAppTypes(); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** + * Checks if listener is associated with active a focus + * @param listener + * @param focus + * @throws CarNotConnectedException + */ + public boolean isOwningFocus(AppFocusOwnershipChangeListener listener, int appType) + throws CarNotConnectedException { + IAppFocusOwnershipListenerImpl binder; + synchronized (this) { + binder = mOwnershipBinders.get(listener); + if (binder == null) { + return false; + } + } + try { + return mService.isOwningFocus(binder, appType); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** + * Requests application focus. + * By requesting this, the application is becoming owner of the focus, and will get + * {@link AppFocusOwnershipChangeListener#onAppFocusOwnershipLoss(int)} + * if ownership is given to other app by calling this. Fore-ground app will have higher priority + * and other app cannot set the same focus while owner is in fore-ground. + * @param ownershipListener + * @param appType + * @return {@link #APP_FOCUS_REQUEST_FAILED} or {@link #APP_FOCUS_REQUEST_GRANTED} + * @throws CarNotConnectedException + * @throws SecurityException If owner cannot be changed. + */ + public int requestAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType) + throws SecurityException, CarNotConnectedException { + if (ownershipListener == null) { + throw new IllegalArgumentException("null listener"); + } + IAppFocusOwnershipListenerImpl binder; + synchronized (this) { + binder = mOwnershipBinders.get(ownershipListener); + if (binder == null) { + binder = new IAppFocusOwnershipListenerImpl(this, ownershipListener); + mOwnershipBinders.put(ownershipListener, binder); + } + binder.addAppType(appType); + } + try { + return mService.requestAppFocus(binder, appType); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** + * Abandon the given focus, i.e. mark it as inactive. This also involves releasing ownership + * for the focus. + * @param ownershipListener + * @param appType + * @throws CarNotConnectedException + */ + public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType) + throws CarNotConnectedException { + if (ownershipListener == null) { + throw new IllegalArgumentException("null listener"); + } + IAppFocusOwnershipListenerImpl binder; + synchronized (this) { + binder = mOwnershipBinders.get(ownershipListener); + if (binder == null) { + return; + } + } + try { + mService.abandonAppFocus(binder, appType); + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + synchronized (this) { + binder.removeAppType(appType); + if (!binder.hasAppTypes()) { + mOwnershipBinders.remove(ownershipListener); + } + } + } + + /** + * Abandon all focuses, i.e. mark them as inactive. This also involves releasing ownership + * for the focus. + * @param ownershipListener + * @throws CarNotConnectedException + */ + public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener) + throws CarNotConnectedException { + IAppFocusOwnershipListenerImpl binder; + synchronized (this) { + binder = mOwnershipBinders.remove(ownershipListener); + if (binder == null) { + return; + } + } + try { + for (Integer appType : binder.getAppTypes()) { + mService.abandonAppFocus(binder, appType); + } + } catch (RemoteException e) { + throw new CarNotConnectedException(e); + } + } + + /** @hide */ + @Override + public void onCarDisconnected() { + // nothing to do + } + + private static class IAppFocusListenerImpl extends IAppFocusListener.Stub { + + private final WeakReference<CarAppFocusManager> mManager; + private final WeakReference<AppFocusChangeListener> mListener; + private final Set<Integer> mAppTypes = new HashSet<>(); + + private IAppFocusListenerImpl(CarAppFocusManager manager, AppFocusChangeListener listener) { + mManager = new WeakReference<>(manager); + mListener = new WeakReference<>(listener); + } + + public void addAppType(int appType) { + mAppTypes.add(appType); + } + + public void removeAppType(int appType) { + mAppTypes.remove(appType); + } + + public Set<Integer> getAppTypes() { + return mAppTypes; + } + + public boolean hasAppTypes() { + return !mAppTypes.isEmpty(); + } + + @Override + public void onAppFocusChange(final int appType, final boolean active) { + final CarAppFocusManager manager = mManager.get(); + final AppFocusChangeListener listener = mListener.get(); + if (manager == null || listener == null) { + return; + } + manager.mHandler.post(new Runnable() { + @Override + public void run() { + listener.onAppFocusChange(appType, active); + } + }); + } + } + + private static class IAppFocusOwnershipListenerImpl extends IAppFocusOwnershipListener.Stub { + + private final WeakReference<CarAppFocusManager> mManager; + private final WeakReference<AppFocusOwnershipChangeListener> mListener; + private final Set<Integer> mAppTypes = new HashSet<>(); + + private IAppFocusOwnershipListenerImpl(CarAppFocusManager manager, + AppFocusOwnershipChangeListener listener) { + mManager = new WeakReference<>(manager); + mListener = new WeakReference<>(listener); + } + + public void addAppType(int appType) { + mAppTypes.add(appType); + } + + public void removeAppType(int appType) { + mAppTypes.remove(appType); + } + + public Set<Integer> getAppTypes() { + return mAppTypes; + } + + public boolean hasAppTypes() { + return !mAppTypes.isEmpty(); + } + + @Override + public void onAppFocusOwnershipLoss(final int appType) { + final CarAppFocusManager manager = mManager.get(); + final AppFocusOwnershipChangeListener listener = mListener.get(); + if (manager == null || listener == null) { + return; + } + manager.mHandler.post(new Runnable() { + @Override + public void run() { + listener.onAppFocusOwnershipLoss(appType); + } + }); + } + } +} |