Виджет для включения/выключения фонарика камеры в 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;
    }
}

Ответ 1

По прошествии долгого времени я смог решить эту проблему.

Вот что я сделал.

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..

Если кто-нибудь может проверить и опубликовать результаты здесь, было бы лучше.

Ответ 2

Технология лучше всего для обработки кликов с 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 Class говорит о многом, о чем я говорю здесь.

Ответ 3

У меня была аналогичная ситуация, когда мне нужно было запустить определенный код Android в потоке пользовательского интерфейса... который доступен только в Activity. Мое решение - это деятельность с полностью прозрачной компоновкой. Таким образом, вы просто видите свой домашний экран (хотя и не отвечаете), пока выполняете свои действия, что в вашем случае должно быть довольно быстрым.

Ответ 4

У меня есть одно решение, которое не велико, но работает. Попросите виджет вызвать действие, а в действии включите вспышку, а затем закройте Activity. То же самое, чтобы отключить его. Если это работает в Activity, это решение будет работать. Это не изящно, но работает. Я попробовал.