blob: 29a8f67e4cfed0a5e2ffd304327a7ebd60323897 [file] [log] [blame]
Adrian Roosb83777b2014-06-30 15:11:53 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.policy;
18
19import android.content.Context;
Adrian Roosb83777b2014-06-30 15:11:53 +020020import android.hardware.camera2.CameraAccessException;
Adrian Roosb83777b2014-06-30 15:11:53 +020021import android.hardware.camera2.CameraCharacteristics;
Adrian Roosb83777b2014-06-30 15:11:53 +020022import android.hardware.camera2.CameraManager;
Adrian Roosb83777b2014-06-30 15:11:53 +020023import android.os.Handler;
24import android.os.HandlerThread;
25import android.os.Process;
Adrian Roosfab0d812015-03-10 14:43:47 +010026import android.text.TextUtils;
Adrian Roosb83777b2014-06-30 15:11:53 +020027import android.util.Log;
Adrian Roosb83777b2014-06-30 15:11:53 +020028
29import java.lang.ref.WeakReference;
30import java.util.ArrayList;
31
32/**
33 * Manages the flashlight.
34 */
35public class FlashlightController {
36
37 private static final String TAG = "FlashlightController";
Adrian Roos930aaa72014-07-22 15:40:59 +020038 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
39
Adrian Roos930aaa72014-07-22 15:40:59 +020040 private static final int DISPATCH_ERROR = 0;
Adrian Roosfab0d812015-03-10 14:43:47 +010041 private static final int DISPATCH_CHANGED = 1;
Adrian Roos930aaa72014-07-22 15:40:59 +020042 private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
Adrian Roosb83777b2014-06-30 15:11:53 +020043
44 private final CameraManager mCameraManager;
45 /** Call {@link #ensureHandler()} before using */
46 private Handler mHandler;
47
48 /** Lock on mListeners when accessing */
49 private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
50
51 /** Lock on {@code this} when accessing */
52 private boolean mFlashlightEnabled;
53
Adrian Roosfab0d812015-03-10 14:43:47 +010054 private final String mCameraId;
55 private boolean mTorchAvailable;
Adrian Roosb83777b2014-06-30 15:11:53 +020056
57 public FlashlightController(Context mContext) {
58 mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
Adrian Roos930aaa72014-07-22 15:40:59 +020059
Adrian Roosfab0d812015-03-10 14:43:47 +010060 String cameraId = null;
Adrian Roos930aaa72014-07-22 15:40:59 +020061 try {
Adrian Roosfab0d812015-03-10 14:43:47 +010062 cameraId = getCameraId();
John Spurlock553b48b2014-07-24 18:12:58 -040063 } catch (Throwable e) {
Adrian Roos930aaa72014-07-22 15:40:59 +020064 Log.e(TAG, "Couldn't initialize.", e);
65 return;
Adrian Roosfab0d812015-03-10 14:43:47 +010066 } finally {
67 mCameraId = cameraId;
Adrian Roos930aaa72014-07-22 15:40:59 +020068 }
69
Adrian Roos034a6c92014-08-05 16:49:34 +020070 if (mCameraId != null) {
71 ensureHandler();
Adrian Roosfab0d812015-03-10 14:43:47 +010072 mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
Adrian Roos034a6c92014-08-05 16:49:34 +020073 }
Adrian Roosb83777b2014-06-30 15:11:53 +020074 }
75
Adrian Roosfab0d812015-03-10 14:43:47 +010076 public void setFlashlight(boolean enabled) {
77 boolean pendingError = false;
Adrian Roos1e1d6ac2014-07-22 17:18:55 +020078 synchronized (this) {
Adrian Roosfab0d812015-03-10 14:43:47 +010079 if (mFlashlightEnabled != enabled) {
80 mFlashlightEnabled = enabled;
81 try {
82 mCameraManager.setTorchMode(mCameraId, enabled);
83 } catch (CameraAccessException e) {
84 Log.e(TAG, "Couldn't set torch mode", e);
85 mFlashlightEnabled = false;
86 pendingError = true;
87 }
88 }
Adrian Roos1e1d6ac2014-07-22 17:18:55 +020089 }
Adrian Roosfab0d812015-03-10 14:43:47 +010090 dispatchModeChanged(mFlashlightEnabled);
91 if (pendingError) {
92 dispatchError();
Adrian Roos1e1d6ac2014-07-22 17:18:55 +020093 }
94 }
95
Jason Monk3266f612015-07-31 11:37:11 -040096 public synchronized boolean isEnabled() {
97 return mFlashlightEnabled;
98 }
99
Adrian Roos930aaa72014-07-22 15:40:59 +0200100 public synchronized boolean isAvailable() {
Adrian Roosfab0d812015-03-10 14:43:47 +0100101 return mTorchAvailable;
Adrian Roosb83777b2014-06-30 15:11:53 +0200102 }
103
104 public void addListener(FlashlightListener l) {
105 synchronized (mListeners) {
106 cleanUpListenersLocked(l);
107 mListeners.add(new WeakReference<>(l));
108 }
109 }
110
111 public void removeListener(FlashlightListener l) {
112 synchronized (mListeners) {
113 cleanUpListenersLocked(l);
114 }
115 }
116
117 private synchronized void ensureHandler() {
118 if (mHandler == null) {
119 HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
120 thread.start();
121 mHandler = new Handler(thread.getLooper());
122 }
123 }
124
Adrian Roosb83777b2014-06-30 15:11:53 +0200125 private String getCameraId() throws CameraAccessException {
126 String[] ids = mCameraManager.getCameraIdList();
127 for (String id : ids) {
128 CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
129 Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
130 Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
131 if (flashAvailable != null && flashAvailable
132 && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
133 return id;
134 }
135 }
136 return null;
137 }
138
Adrian Roosfab0d812015-03-10 14:43:47 +0100139 private void dispatchModeChanged(boolean enabled) {
140 dispatchListeners(DISPATCH_CHANGED, enabled);
Adrian Roosb83777b2014-06-30 15:11:53 +0200141 }
142
143 private void dispatchError() {
Adrian Roosfab0d812015-03-10 14:43:47 +0100144 dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
Adrian Roosb83777b2014-06-30 15:11:53 +0200145 }
146
Adrian Roos930aaa72014-07-22 15:40:59 +0200147 private void dispatchAvailabilityChanged(boolean available) {
148 dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
149 }
150
151 private void dispatchListeners(int message, boolean argument) {
Adrian Roosb83777b2014-06-30 15:11:53 +0200152 synchronized (mListeners) {
153 final int N = mListeners.size();
154 boolean cleanup = false;
155 for (int i = 0; i < N; i++) {
156 FlashlightListener l = mListeners.get(i).get();
157 if (l != null) {
Adrian Roos930aaa72014-07-22 15:40:59 +0200158 if (message == DISPATCH_ERROR) {
Adrian Roosb83777b2014-06-30 15:11:53 +0200159 l.onFlashlightError();
Adrian Roosfab0d812015-03-10 14:43:47 +0100160 } else if (message == DISPATCH_CHANGED) {
161 l.onFlashlightChanged(argument);
Adrian Roos930aaa72014-07-22 15:40:59 +0200162 } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
163 l.onFlashlightAvailabilityChanged(argument);
Adrian Roosb83777b2014-06-30 15:11:53 +0200164 }
165 } else {
166 cleanup = true;
167 }
168 }
169 if (cleanup) {
170 cleanUpListenersLocked(null);
171 }
172 }
173 }
174
175 private void cleanUpListenersLocked(FlashlightListener listener) {
176 for (int i = mListeners.size() - 1; i >= 0; i--) {
177 FlashlightListener found = mListeners.get(i).get();
178 if (found == null || found == listener) {
179 mListeners.remove(i);
180 }
181 }
182 }
183
Adrian Roosfab0d812015-03-10 14:43:47 +0100184 private final CameraManager.TorchCallback mTorchCallback =
185 new CameraManager.TorchCallback() {
Adrian Roosb83777b2014-06-30 15:11:53 +0200186
187 @Override
Adrian Roosfab0d812015-03-10 14:43:47 +0100188 public void onTorchModeUnavailable(String cameraId) {
189 if (TextUtils.equals(cameraId, mCameraId)) {
Adrian Roos930aaa72014-07-22 15:40:59 +0200190 setCameraAvailable(false);
191 }
192 }
193
Adrian Roosfab0d812015-03-10 14:43:47 +0100194 @Override
195 public void onTorchModeChanged(String cameraId, boolean enabled) {
196 if (TextUtils.equals(cameraId, mCameraId)) {
197 setCameraAvailable(true);
198 setTorchMode(enabled);
199 }
200 }
201
Adrian Roos930aaa72014-07-22 15:40:59 +0200202 private void setCameraAvailable(boolean available) {
203 boolean changed;
204 synchronized (FlashlightController.this) {
Adrian Roosfab0d812015-03-10 14:43:47 +0100205 changed = mTorchAvailable != available;
206 mTorchAvailable = available;
Adrian Roos930aaa72014-07-22 15:40:59 +0200207 }
208 if (changed) {
209 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
210 dispatchAvailabilityChanged(available);
211 }
212 }
Adrian Roosfab0d812015-03-10 14:43:47 +0100213
214 private void setTorchMode(boolean enabled) {
215 boolean changed;
216 synchronized (FlashlightController.this) {
217 changed = mFlashlightEnabled != enabled;
218 mFlashlightEnabled = enabled;
219 }
220 if (changed) {
221 if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
222 dispatchModeChanged(enabled);
223 }
224 }
Adrian Roos930aaa72014-07-22 15:40:59 +0200225 };
226
Adrian Roosb83777b2014-06-30 15:11:53 +0200227 public interface FlashlightListener {
228
229 /**
Adrian Roosfab0d812015-03-10 14:43:47 +0100230 * Called when the flashlight was turned off or on.
231 * @param enabled true if the flashlight is currently turned on.
Adrian Roosb83777b2014-06-30 15:11:53 +0200232 */
Adrian Roosfab0d812015-03-10 14:43:47 +0100233 void onFlashlightChanged(boolean enabled);
234
Adrian Roosb83777b2014-06-30 15:11:53 +0200235
236 /**
237 * Called when there is an error that turns the flashlight off.
238 */
239 void onFlashlightError();
Adrian Roos930aaa72014-07-22 15:40:59 +0200240
241 /**
242 * Called when there is a change in availability of the flashlight functionality
243 * @param available true if the flashlight is currently available.
244 */
245 void onFlashlightAvailabilityChanged(boolean available);
Adrian Roosb83777b2014-06-30 15:11:53 +0200246 }
247}