Виджет для включения / выключения фонарика камеры в 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, то это решение будет работать. Это не элегантно, но работает. Я попробовал это.

Другие вопросы по тегам