1 /* 2 * Copyright (C) 2018 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 17 package com.android.settings.flashlight; 18 19 import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; 20 21 import static androidx.slice.builders.ListBuilder.ICON_IMAGE; 22 23 import android.annotation.ColorInt; 24 import android.app.PendingIntent; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.hardware.camera2.CameraAccessException; 29 import android.hardware.camera2.CameraCharacteristics; 30 import android.hardware.camera2.CameraManager; 31 import android.net.Uri; 32 import android.provider.Settings; 33 import android.provider.Settings.Secure; 34 import android.util.Log; 35 36 import androidx.core.graphics.drawable.IconCompat; 37 import androidx.slice.Slice; 38 import androidx.slice.builders.ListBuilder; 39 import androidx.slice.builders.ListBuilder.RowBuilder; 40 import androidx.slice.builders.SliceAction; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.settings.R; 44 import com.android.settings.Utils; 45 import com.android.settings.slices.CustomSliceRegistry; 46 import com.android.settings.slices.CustomSliceable; 47 48 49 /** 50 * Utility class to build a Flashlight Slice, and handle all associated actions. 51 */ 52 public class FlashlightSlice implements CustomSliceable { 53 54 private static final String TAG = "FlashlightSlice"; 55 56 /** 57 * Action broadcasting a change on whether flashlight is on or off. 58 */ 59 private static final String ACTION_FLASHLIGHT_CHANGED = 60 "com.android.settings.flashlight.action.FLASHLIGHT_CHANGED"; 61 62 private final Context mContext; 63 FlashlightSlice(Context context)64 public FlashlightSlice(Context context) { 65 mContext = context; 66 } 67 68 @Override getSlice()69 public Slice getSlice() { 70 if (!isFlashlightAvailable(mContext)) { 71 return null; 72 } 73 final PendingIntent toggleAction = getBroadcastIntent(mContext); 74 @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); 75 final IconCompat icon = 76 IconCompat.createWithResource(mContext, R.drawable.ic_signal_flashlight); 77 return new ListBuilder(mContext, CustomSliceRegistry.FLASHLIGHT_SLICE_URI, 78 ListBuilder.INFINITY) 79 .setAccentColor(color) 80 .addRow(new RowBuilder() 81 .setTitle(mContext.getText(R.string.power_flashlight)) 82 .setTitleItem(icon, ICON_IMAGE) 83 .setPrimaryAction( 84 SliceAction.createToggle(toggleAction, null, 85 isFlashlightEnabled(mContext)))) 86 .build(); 87 } 88 89 @Override getUri()90 public Uri getUri() { 91 return CustomSliceRegistry.FLASHLIGHT_SLICE_URI; 92 } 93 94 @Override getIntentFilter()95 public IntentFilter getIntentFilter() { 96 return new IntentFilter(ACTION_FLASHLIGHT_CHANGED); 97 } 98 99 @Override onNotifyChange(Intent intent)100 public void onNotifyChange(Intent intent) { 101 try { 102 final String cameraId = getCameraId(mContext); 103 if (cameraId != null) { 104 final boolean state = intent.getBooleanExtra( 105 EXTRA_TOGGLE_STATE, isFlashlightEnabled(mContext)); 106 final CameraManager cameraManager = mContext.getSystemService(CameraManager.class); 107 cameraManager.setTorchMode(cameraId, state); 108 } 109 } catch (CameraAccessException e) { 110 Log.e(TAG, "Camera couldn't set torch mode.", e); 111 } 112 mContext.getContentResolver().notifyChange(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, null); 113 } 114 115 @Override getIntent()116 public Intent getIntent() { 117 return null; 118 } 119 120 @Override getSliceHighlightMenuRes()121 public int getSliceHighlightMenuRes() { 122 // no landing page in Settings 123 return NO_RES; 124 } 125 getCameraId(Context context)126 private static String getCameraId(Context context) throws CameraAccessException { 127 final CameraManager cameraManager = context.getSystemService(CameraManager.class); 128 final String[] ids = cameraManager.getCameraIdList(); 129 for (String id : ids) { 130 CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); 131 Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 132 Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); 133 if (flashAvailable != null && flashAvailable 134 && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { 135 return id; 136 } 137 } 138 return null; 139 } 140 141 @VisibleForTesting isFlashlightAvailable(Context context)142 static boolean isFlashlightAvailable(Context context) { 143 int defaultAvailability = 0; 144 try { 145 // check if there is a flash unit 146 if (getCameraId(context) != null) { 147 defaultAvailability = 1; 148 } 149 } catch (CameraAccessException e) { 150 Log.e(TAG, "Error getting camera id.", e); 151 } 152 return Secure.getInt(context.getContentResolver(), 153 Secure.FLASHLIGHT_AVAILABLE, defaultAvailability) == 1; 154 } 155 isFlashlightEnabled(Context context)156 private static boolean isFlashlightEnabled(Context context) { 157 return Settings.Secure.getInt( 158 context.getContentResolver(), Secure.FLASHLIGHT_ENABLED, 0) == 1; 159 } 160 } 161