Виджет для включения / выключения фонарика камеры в Android
Я разрабатываю виджет для включения / выключения светодиода камеры телефона.
Я сделал виджет, который может работать как кнопка переключения (вкл / выкл).
Поведение выглядит следующим образом: Иногда светодиодный индикатор остается включенным, когда я включаю виджет. Но он не включает / выключает светодиод камеры, а меняет значок.
Я не могу понять, в чем проблема.
То же самое отлично работает в Activity (приложение Torch Light).
Может кто-нибудь, пожалуйста, объясните мне, как я могу решить мою проблему?
Куда я иду не так?
Вы можете посмотреть на код ниже, что я сделал до сих пор
onUpdate
метод
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
//super.onUpdate(context, appWidgetManager, appWidgetIds);
remoteViews = new RemoteViews( context.getPackageName(), R.layout.widgetlayout);
watchWidget = new ComponentName( context, FlashLightWidget.class );
Intent intentClick = new Intent(context,FlashLightWidget.class);
intentClick.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, ""+appWidgetIds[0]);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetIds[0],intentClick, 0);
remoteViews.setOnClickPendingIntent(R.id.myToggleWidget, pendingIntent);
appWidgetManager.updateAppWidget( watchWidget, remoteViews );
ctx=context;
}
onReceive
Метод заключается в следующем:
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
remoteViews = new RemoteViews( context.getPackageName(), R.layout.widgetlayout);
if (intent.getAction()==null) {
Bundle extras = intent.getExtras();
if(extras!=null) {
if(status)
{
status=false;
remoteViews.setImageViewResource(R.id.myToggleWidget, R.drawable.shutdown1);
processOnClick();
Toast.makeText(context,"Status==false-onclick",Toast.LENGTH_SHORT).show();
}
else
{
status = true;
remoteViews.setImageViewResource(R.id.myToggleWidget, R.drawable.shutdown2);
processOffClick();
Toast.makeText(context,"Status==true--Ofclick",Toast.LENGTH_SHORT).show();
}
}
watchWidget = new ComponentName( context, FlashLightWidget.class );
(AppWidgetManager.getInstance(context)).updateAppWidget( watchWidget, remoteViews );
}
}
}
processOffClick
метод
private void processOffClick() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
processOnClick
метод
private void processOnClick() {
if(mCamera==null)
{
try {
mCamera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
}
if (mCamera != null) {
Parameters params = mCamera.getParameters();
List<String> flashModes = params.getSupportedFlashModes();
if (flashModes == null) {
return;
} else {
params.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(params);
mCamera.startPreview();
String flashMode = params.getFlashMode();
if (!Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_TORCH)) {
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(params);
}
}
}
} else if (mCamera == null) {
//Toast.makeText(ctx, "Camera not found", Toast.LENGTH_LONG).show();
return;
}
}
4 ответа
Спустя долгое время я получил возможность решить эту проблему.
Вот что я сделал.
FlashlightWidgetProvider
учебный класс:
public class FlashlightWidgetProvider extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent receiver = new Intent(context, FlashlightWidgetReceiver.class);
receiver.setAction("COM_FLASHLIGHT");
receiver.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, 0);
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
и BroadcastReceiver для FlashlightWidgetReceiver:
public class FlashlightWidgetReceiver extends BroadcastReceiver {
private static boolean isLightOn = false;
private static Camera camera;
@Override
public void onReceive(Context context, Intent intent) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
if(isLightOn) {
views.setImageViewResource(R.id.button, R.drawable.off);
} else {
views.setImageViewResource(R.id.button, R.drawable.on);
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, FlashlightWidgetProvider.class),
views);
if (isLightOn) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
isLightOn = false;
}
} else {
// Open the default i.e. the first rear facing camera.
camera = Camera.open();
if(camera == null) {
Toast.makeText(context, R.string.no_camera, Toast.LENGTH_SHORT).show();
} else {
// Set the torch flash mode
Parameters param = camera.getParameters();
param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
try {
camera.setParameters(param);
camera.startPreview();
isLightOn = true;
} catch (Exception e) {
Toast.makeText(context, R.string.no_flash, Toast.LENGTH_SHORT).show();
}
}
}
}
}
Разрешение требуется в Manifest.xml
файл:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
Также зарегистрируйте получателей в Manifest.xml
файл:
<receiver android:name=".FlashlightWidgetProvider" android:icon="@drawable/on" android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/flashlight_appwidget_info" />
</receiver>
<receiver android:name="FlashlightWidgetReceiver">
<intent-filter>
<action android:name="COM_FLASHLIGHT"></action>
</intent-filter>
</receiver>
Важное примечание: этот код отлично работает, если ваш телефон имеет FLASH_MODE_TORCH
поддерживается.
Я тестировал в Samsung Galaxy Ace 2.2.1 и 2.3.3. Код не работает, потому что это устройство не имеет FLASH_MODE_TORCH.
Прекрасно работает в HTC Salsa, Wildfire..
Если кто-то может проверить и опубликовать результаты здесь, было бы лучше.
Лучшая техника для обработки кликов с RemoteViews
это создать PendingIntent
который вызывает службу, и выполняет "материал", который вы хотите в службе, включая любые дополнительные RemoteViews
обновления для вашего виджета. Вы можете отправить соответствующие данные в намерениях дополнений. Служба звонков stopSelf()
в конце, так что он отключается.
Вы не можете поддерживать любое состояние в BroadcastReceiver
; система запускает их в любом доступном потоке и не поддерживает никаких ссылок на ваш экземпляр после вызова onReceive()
, Ваш mCamera
Не гарантируется, что переменная будет поддерживаться между вызовами вашего BroadcastReceiver
,
Если вам действительно нужно поддерживать состояние, вы должны сделать это в службе, а не использовать stopSelf()
(до подходящего времени).
Вам не нужен поток пользовательского интерфейса, чтобы использовать Camera
класс, если вы не делаете предварительный просмотр изображения, который требует SurfaceHolder
(и подразумевает пользовательский интерфейс). Однако вы должны иметь активную петлю событий, или Camera
не будет публиковать обратные вызовы вам, что является проблемой, так как Camera
в основном асинхронный. Вы можете сделать это в службе (см. HandlerThread
) и продолжайте работу службы, пока не наступит время release()
все. Какой бы поток ни вызывал Camera.open()
будет получать обратные вызовы.
Все ли читали раздел по виджетам приложений? http://developer.android.com/guide/topics/appwidgets/index.html
Использование раздела класса AppWidgetProvider говорит о том, что я здесь говорю.
У меня была похожая ситуация, когда мне нужно было запустить определенный код Android в потоке пользовательского интерфейса... который доступен только в Activity. Мое решение - деятельность с полностью прозрачным макетом. Таким образом, вы просто видите свой домашний экран (хотя и не реагируете) во время выполнения своих действий, что в вашем случае должно быть довольно быстрым.
У меня есть одно решение, которое не очень хорошо, но работает. Сделайте, чтобы виджет вызвал Activity, и в Activity включите вспышку, а затем закройте Activity. То же самое, чтобы выключить его. Если это работает в Activity, то это решение будет работать. Это не элегантно, но работает. Я попробовал это.