37

I am developing a widget for turning on/off camera led of phone.

I have made a widget that can work like toggle button (on/off).

Behavior is like follows : Sometimes the led light remains on when i enable the widget. But it doesnot turn on/off the camera led but it changes the icon.

I am not able to figure out whats the actual problem.

The same thing works fine in Activity (Torch Light Application).

Can anyone please explain me how can i solve my problem ?

Where i am going wrong ?

You can look at the code below that i have done so far

onUpdate method

@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 method is as follows :

@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 method

private void processOffClick() {

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
            mCamera.release();      
            mCamera = null;
        }
    }

processOnClick method

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;
    }
}
Kartik Domadiya
  • 29,868
  • 19
  • 93
  • 104
  • Please refer to this article for a valid solution: http://stackoverflow.com/questions/3878294/camera-parameters-flash-mode-torch-replacement-for-android-2-1 – HandlerExploit Sep 27 '11 at 16:24
  • 1
    @HandlerExploit : That didn help me buddy.. I have reffered it many times before.. What i am asking is totally different. – Kartik Domadiya Sep 28 '11 at 12:34
  • If I am reading what you are doing correctly... you seem to be trying to control the led from the app widget provider, Is this correct? If so you need to pull that code out and put it in its own class and call that class from your app widget provider – rf43 Sep 28 '11 at 19:47
  • @DDoSAttack: Yes you are correct. I am trying to control the led from the app widget provider. But i think i am implementing wrong things in AppWidgetProvider. Handling click events in onReceive method is not proper i think ! – Kartik Domadiya Sep 29 '11 at 04:24
  • 2
    That is correct. The AppWidgetProvider is very limited in what it can do. You access the layout widgets via RemoteViews http://developer.android.com/reference/android/widget/RemoteViews.html thus my recommendation to create a separate class to handle all the AppWidget UI updates... here is a simple example I posted today: http://stackoverflow.com/questions/7560265/getting-a-string-to-appwidget-via-getextras-or-shared-prefrences/7588272#7588272 – rf43 Sep 29 '11 at 05:43

4 Answers4

40

After a long time, I got free to solve this problem.

Here is what I did.

FlashlightWidgetProvider class :

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);

        }
}

and BroadcastReceiver for 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();
                                    }
                            }
                    }
            }
    }

Permission required in Manifest.xml file :

<uses-permission android:name="android.permission.CAMERA"></uses-permission>

Also register receivers in Manifest.xml file :

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

Important Note : This code works perfect if your phone has FLASH_MODE_TORCH supported.

I have tested in Samsung Galaxy Ace 2.2.1 & 2.3.3. The code is not working because that device has no FLASH_MODE_TORCH.

Works fine in HTC Salsa, Wildfire..

If anyone can test and post results here, it would be best.

j0k
  • 22,600
  • 28
  • 79
  • 90
Kartik Domadiya
  • 29,868
  • 19
  • 93
  • 104
  • This helped a lot also to check if flash is on simply do this:`String x = params.getFlashMode();`... If x is `torch` then it's `on` if x is `off` then it's `off`. – Muhammad Ali May 11 '15 at 01:35
  • The widget stops working after a few hours.. or instantly if i change the date. Can you help me with this? – Muhammad Ali May 13 '15 at 02:40
7

The best technique for handling clicks from a RemoteViews is to create a PendingIntent that calls a service, and perform the "stuff" you want in the service, including any additional RemoteViews updates for your widget. You can send along the relevant data in the intent extras. The service calls stopSelf() at the end, so it shuts off.

You cannot maintain any state in an BroadcastReceiver; the system runs those on any available thread, and doesn't maintain any reference to your instance after calling onReceive(). Your mCamera variable is not guaranteed to be maintained between invocations of your BroadcastReceiver.

If you really need to maintain state, you must do it in the service, and don't use stopSelf() (until an appropriate time).

You do not need a UI thread to use the Camera class, unless you are doing image preview, which requires a SurfaceHolder (and implies a UI). You must, however, have an event loop active, or Camera will not post callbacks to you, which is a problem since Camera is primarily asynchronous. You can do this within a service (see HandlerThread) and keep your service running until it's time to release() everything. Whichever thread calls Camera.open() will receive callbacks.

Did everyone actually read the section on App Widgets? http://developer.android.com/guide/topics/appwidgets/index.html

Using the AppWidgetProvider Class section says pretty much what I am saying here.

escape-llc
  • 1,295
  • 1
  • 12
  • 25
2

I had a similar situation where I need to run certain android code on the UI thread ... which is only available in an Activity. My solution - an activity with a completely transparent layout. So you just see your home screen (albeit unresponsive) while you complete your actions, which in your case should be pretty quick.

Yossi
  • 1,226
  • 1
  • 16
  • 31
  • The thing you explained is the same as above answer. I dont want to do perform the task like this. Is there any proper solution ? – Kartik Domadiya Oct 12 '11 at 04:02
  • 1
    I think you misunderstand. What you are trying to do CANNOT work. The context of a Widget has a very short life cycle, so I believe your code may get killed midway. There are very few things you may actually execute in the context of a Widget. Everything else must be triggered by an Intent. – Yossi Oct 29 '11 at 02:48
1

I have one solution that is not great but works. Have the widget call an Activity, and in the Activity turn the flash on, and later close the Activity. The same to turn it off. If this works in the Activity then this solution will work. It's not elegant but works. I tried it.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130
titusfx
  • 1,896
  • 27
  • 36
  • Thanks for the answer. But i dont want to have widget this way. – Kartik Domadiya Oct 06 '11 at 10:10
  • See my answer above! That's how the app widget system is intended to be used. I've written plenty of widgets, all of which work that way. The sooner you embrace it, the sooner you can be done! – escape-llc Oct 25 '11 at 16:44