blob: 9a21a1e378e6df908648eb0f9510963d997478e1 [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 Monk72d7bc42016-02-29 10:03:13 -050096 public boolean hasFlashlight() {
97 return mCameraId != null;
98 }
99
Jason Monk3266f612015-07-31 11:37:11 -0400100 public synchronized boolean isEnabled() {
101 return mFlashlightEnabled;
102 }
103
Adrian Roos930aaa72014-07-22 15:40:59 +0200104 public synchronized boolean isAvailable() {
Adrian Roosfab0d812015-03-10 14:43:47 +0100105 return mTorchAvailable;
Adrian Roosb83777b2014-06-30 15:11:53 +0200106 }
107
108 public void addListener(FlashlightListener l) {
109 synchronized (mListeners) {
110 cleanUpListenersLocked(l);
111 mListeners.add(new WeakReference<>(l));
112 }
113 }
114
115 public void removeListener(FlashlightListener l) {
116 synchronized (mListeners) {
117 cleanUpListenersLocked(l);
118 }
119 }
120
121 private synchronized void ensureHandler() {
122 if (mHandler == null) {
123 HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
124 thread.start();
125 mHandler = new Handler(thread.getLooper());
126 }
127 }
128
Adrian Roosb83777b2014-06-30 15:11:53 +0200129 private String getCameraId() throws CameraAccessException {
130 String[] ids = mCameraManager.getCameraIdList();
131 for (String id : ids) {
132 CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
133 Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
134 Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
135 if (flashAvailable != null && flashAvailable
136 && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
137 return id;
138 }
139 }
140 return null;
141 }
142
Adrian Roosfab0d812015-03-10 14:43:47 +0100143 private void dispatchModeChanged(boolean enabled) {
144 dispatchListeners(DISPATCH_CHANGED, enabled);
Adrian Roosb83777b2014-06-30 15:11:53 +0200145 }
146
147 private void dispatchError() {
Adrian Roosfab0d812015-03-10 14:43:47 +0100148 dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
Adrian Roosb83777b2014-06-30 15:11:53 +0200149 }
150
Adrian Roos930aaa72014-07-22 15:40:59 +0200151 private void dispatchAvailabilityChanged(boolean available) {
152 dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
153 }
154
155 private void dispatchListeners(int message, boolean argument) {
Adrian Roosb83777b2014-06-30 15:11:53 +0200156 synchronized (mListeners) {
157 final int N = mListeners.size();
158 boolean cleanup = false;
159 for (int i = 0; i < N; i++) {
160 FlashlightListener l = mListeners.get(i).get();
161 if (l != null) {
Adrian Roos930aaa72014-07-22 15:40:59 +0200162 if (message == DISPATCH_ERROR) {
Adrian Roosb83777b2014-06-30 15:11:53 +0200163 l.onFlashlightError();
Adrian Roosfab0d812015-03-10 14:43:47 +0100164 } else if (message == DISPATCH_CHANGED) {
165 l.onFlashlightChanged(argument);
Adrian Roos930aaa72014-07-22 15:40:59 +0200166 } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
167 l.onFlashlightAvailabilityChanged(argument);
Adrian Roosb83777b2014-06-30 15:11:53 +0200168 }
169 } else {
170 cleanup = true;
171 }
172 }
173 if (cleanup) {
174 cleanUpListenersLocked(null);
175 }
176 }
177 }
178
179 private void cleanUpListenersLocked(FlashlightListener listener) {
180 for (int i = mListeners.size() - 1; i >= 0; i--) {
181 FlashlightListener found = mListeners.get(i).get();
182 if (found == null || found == listener) {
183 mListeners.remove(i);
184 }
185 }
186 }
187
Adrian Roosfab0d812015-03-10 14:43:47 +0100188 private final CameraManager.TorchCallback mTorchCallback =
189 new CameraManager.TorchCallback() {
Adrian Roosb83777b2014-06-30 15:11:53 +0200190
191 @Override
Adrian Roosfab0d812015-03-10 14:43:47 +0100192 public void onTorchModeUnavailable(String cameraId) {
193 if (TextUtils.equals(cameraId, mCameraId)) {
Adrian Roos930aaa72014-07-22 15:40:59 +0200194 setCameraAvailable(false);
195 }
196 }
197
Adrian Roosfab0d812015-03-10 14:43:47 +0100198 @Override
199 public void onTorchModeChanged(String cameraId, boolean enabled) {
200 if (TextUtils.equals(cameraId, mCameraId)) {
201 setCameraAvailable(true);
202 setTorchMode(enabled);
203 }
204 }
205
Adrian Roos930aaa72014-07-22 15:40:59 +0200206 private void setCameraAvailable(boolean available) {
207 boolean changed;
208 synchronized (FlashlightController.this) {
Adrian Roosfab0d812015-03-10 14:43:47 +0100209 changed = mTorchAvailable != available;
210 mTorchAvailable = available;
Adrian Roos930aaa72014-07-22 15:40:59 +0200211 }
212 if (changed) {
213 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
214 dispatchAvailabilityChanged(available);
215 }
216 }
Adrian Roosfab0d812015-03-10 14:43:47 +0100217
218 private void setTorchMode(boolean enabled) {
219 boolean changed;
220 synchronized (FlashlightController.this) {
221 changed = mFlashlightEnabled != enabled;
222 mFlashlightEnabled = enabled;
223 }
224 if (changed) {
225 if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
226 dispatchModeChanged(enabled);
227 }
228 }
Adrian Roos930aaa72014-07-22 15:40:59 +0200229 };
230
Adrian Roosb83777b2014-06-30 15:11:53 +0200231 public interface FlashlightListener {
232
233 /**
Adrian Roosfab0d812015-03-10 14:43:47 +0100234 * Called when the flashlight was turned off or on.
235 * @param enabled true if the flashlight is currently turned on.
Adrian Roosb83777b2014-06-30 15:11:53 +0200236 */
Adrian Roosfab0d812015-03-10 14:43:47 +0100237 void onFlashlightChanged(boolean enabled);
238
Adrian Roosb83777b2014-06-30 15:11:53 +0200239
240 /**
241 * Called when there is an error that turns the flashlight off.
242 */
243 void onFlashlightError();
Adrian Roos930aaa72014-07-22 15:40:59 +0200244
245 /**
246 * Called when there is a change in availability of the flashlight functionality
247 * @param available true if the flashlight is currently available.
248 */
249 void onFlashlightAvailabilityChanged(boolean available);
Adrian Roosb83777b2014-06-30 15:11:53 +0200250 }
251}