Click here to Skip to main content
13,860,571 members
Click here to Skip to main content
Add your own
alternative version

Stats

15.9K views
368 downloads
7 bookmarked
Posted 5 Dec 2015
Licenced CPOL

App for consuming Android flashlight with a widget

, 5 Dec 2015
Rate this:
Please Sign up or sign in to vote.
This article discusses Android programming and aims at teaching you about Android programming for Camera and Widgets.

Introduction and Background

Here it goes, I got a new cell phone this month. I like it very much, but one thing that it doesn't come with is the toolkit for basic utilities. One of them is the flashlight application that triggers the device to activate or deactivate the flashlight as required. Android does support you to have Camera API consumed in your application (after all, flashlight is provided to for photos or videos and not as a floodlight). So, I was thinking that I should get an application from Play Store right? No! Although it would provide me with an "elegant" solution to my flashlight problem, but what about the army of advertisement I will have to go through, or the unclear UI of the application. So I thought I should build an application for myself to be able to trigger the flashlight on or off. 

Since I made it for myself, why not teach you guys too, so that you can use the same application, or build on top of it and share it with me so that I can also use your skills. :-) The source code will be available and the application is being published to Google's Play Store. 

In this article I will discuss Android programming for Camera API and a small overview of Widget programming in Android applications. Although this type of application is very basic and straight-forward, at least it will provide you with a very basic idea of using Camera API in devices and to manage those background services using widgets on your home screen. 

It is not that much hard to create a similar application, but still in many ways, it may be tough job be able to create a widget that responds to user interaction for triggering the service that we are going to use to activate or deactivate the flashlight. In the coming sections you will learn how to create a similar application keeping the "simple UI" in mind and how you can program a widget to keep the application concept even more simple. 

Pre-requisites

You are not required to have an advanced understanding of Android APIs or how Linux works, later permissions and rest of the stuff will be talked about in the article. But I will try to make sure that I am as much clear as a beginner in this concept would want me to be. 

Writing the Android application

In this section I will talk about building the application. I will slice it in two different sections, one for building the service for flashlight application and the next section comes for building the Widget for our application to allow users to be able to trigger the service from any screen. 

During the both two sections, I will keep the "level of my English" pretty much simple for everyone to be able to understand what I mean to say in every paragraph. 

At this stage I will recommend creating a new Android application. There have been many great tutorials on "How to create an Android project" and you may consider reading one of them to learn how to create a new project, because I won't be covering the "basic" topic in this article. 

Building the back-end service

First of all, we need to define the service that needs to be executed each time. Android APIs allow us to use the hardware components to an extent. The extent is over as soon as we try to use a component to its sub-components. The flashlight comes with Camera in an Android device, which means that you cannot use the flashlight separately, you have to be using Camera. That is when hacking begins, and you start using Camera but only to use the flashlight and not the image or video capturing .

The object that takes our interest is Camera object from android.hardware package. The object allows us to create connections to provided cameras on device, check if there are any camera, and also allows us to create our own special applications that make use of cameras, such as the ones being used while creating Instagram-inspired applications. So we will also use the same object to trigger the request to Android OS to let our application use the camera for a while. 

Setting the permissions for our application

Like all other processes in a Linux environment, we first need to set up the permissions for our application to be able to use. Without permissions, our application is going to end up having RuntimeExceptions. Thus, before going any further, open your application's manifest and write the following permissions to it.

<uses-permission

        android:name="android.permission.FLASHLIGHT"

        android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"

        android:protectionLevel="normal"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.hardware.camera.flash"/>

<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />

The above code is just the set of permissions needed. We also need to able to use the flash, thus we set them up separately. One thing to note is that although everything that comes with a camera is controlled by camera, however, our application needs to have permission to use each and every component including flashlight. Camera won't allow your application to natively communicate with the components if your manifest does not share the permissions details; thus a RuntimeException.

The <uses-feature> element allows us to indicate that our application requires these features and is of no use without them. Of course our application would be of no use, if there is no flashlight found in the device. The attribute android:required allows us to set a condition for our application to be installed, or ignore depending on the existence of that hardware feature. You can set it to either true or false, denoting that the feature is required. 

That is it for permissions, we just need to use the Camera object and then ask it to turn on the flashlight for us, as long as we want. 

Creating the MainActivity

In Android programming, MainActivity is a conventional name to be used for the first activity, Android Studio enforces (or well, simply, uses) this name for the very first activity. So, in our MainActivity we are going to create a button that enables us to trigger the Camera object. The actual code that does that would be shown later, in this section only the activity will be discussed. 

First of all, create a button, that can be clicked to trigger the service. 


Figure 1: Android Studio displaying the rendering of MainActivity, with a Button object and some text.

Pay attention to the button itself, the Button would trigger the service, here is the XML code (if it is of any interest)

<Button

        style="?android:attr/buttonStyleSmall"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/turn_on_string"

        android:id="@+id/turn_on_button"

        android:layout_alignParentBottom="true"

        android:layout_centerHorizontal="true"/>

Handling user interaction in the Activity

You must have noticed that I have not yet attached any "onClick" attribute to the Button object. That is not "by design", I forgot to add that event there :laugh: and it struck my mind when I was pasting the code here.

Anyways, I did that in the code-behind and attached the click listeners to the button. The button listener, in turn triggers the back-end service of our application to activate (or deactivate) the flashlight in our device. The following code does the trick for that, 

// Find the button from the view.
final Button button = (Button)findViewById(R.id.turn_on_button);

// Attach the event handler
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        // Create an intent (Empty at the point)
        Intent serviceIntent = new Intent(getApplicationContext(), FlashlightIntentService.class);
        if (button.getText().toString().equals(getString(R.string.turn_on_string))) {
            // Turn on the flashlight
            serviceIntent.setAction(FlashlightIntentService.ACTION_TURN_ON);
            startService(serviceIntent);

            button.setText(R.string.turn_off_string);
        } else {
            // Turn off the flashlight
            serviceIntent.setAction(FlashlightIntentService.ACTION_TURN_OFF);
            startService(serviceIntent);

            button.setText(R.string.turn_on_string);
        }
    }
});

One thing I may have missed is the background service, I want to talk about it later. But, if you want to know where does FlashlightIntentService.ACTION_TURN_ON comes from, please look at the actions of the service. The above program simply attaches an event handler to the button, the button can then be used by the user to trigger this service. You may also see that we are using a service, by the function, startService(serviceIntent)

I tried to keep the application as much concise and compact as I could, that is why I created a separated service to handle the requests instead of hardcoding every command in the MainActivity. It will also let me update the service to add any further features to the service. This lets me manage the UI interaction in the UI thread, and to perform the background actions in a background thread. 

Building the background service — FlashlightIntentService

All of our logic relies on this back-end service of our application. I have implemented all of the "if-then-else" concepts in this one service and provided two actions, one for each, on or off. The backend service needs to be executed on a separate thread, because we want to use it in our widget also. So even if there is no instance of MainActivity we still need our users to be able to use the service. At this moment, create a new IntentService in your application, name it what you want. I named the IntentService as FlashlightIntentService. You should consider using the same name to avoid any conflict. 

What actions will the service perform?

Our service just perform the following two actions in our application.

  1. Turns the flashlight on.
  2. Turns the flashlight off.

Simple, so we need two actions in our service to denote the command that needs to be executed. In Android, we define these actions as static constant (final) strings. These strings can then be used by different activities to create Intents, and our service can check what is the current action for the intent that triggered our service. Have a look at how the actions are defined, 

// ACTIONS
public static final String ACTION_TURN_ON = "com.wordpress.afzaalahmadzeeshan.flashlight_app.TURN_ON";
public static final String ACTION_TURN_OFF = "com.wordpress.afzaalahmadzeeshan.flashlight_app.TURN_OFF";

These two can now be accessed by any activity, since they are public. We can create our intents and then use setAction(String) to set an action for that intent. In Android programming, actions define what the activity (or user) actually wants to do. In the topic above, these actions are being used to define the behaviour of that button above. 

Tip: For more on IntentService vs Service, please read this Stack Overflow thread.

Establishing the "secure" connection to camera

When the service starts, we also want to establish the connection to our camera device. In Android, there may be many factors that we may need to take care about while working with devices provided by the Android device itself. Such as Camera, camera may be in use by another application, it may also be not available or permission may not be available to the application and so on. 

In these cases, we use a try...catch block to establish the connection and also to process if there are some troubles while establishing the connection. Most common problem is, "Failed to connect to camera service". The problem means that some other application is currently using the camera and you cannot use it right now. This also is a result of "memory leak", as an application may have ended but forgot to release the camera resource, that is why, always clean up resources before you end your application's activity. 

However, the following function does it, it tries to connect to the service and in case of any error, it notifies the user. 

private void fillFields() {
    // Initialize the fields
    canWork = this.getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
    // Get the IDs if any.
    ids = AppWidgetManager.getInstance(this)
                .getAppWidgetIds(new ComponentName(this, FlashLightWidget.class));

    // If flash feature is available
    if(canWork) {
        // Get the camera instance
        if(camera == null) {
            // Only if the camera object is null. Otherwise,
            // we may have captured the camera instance in last visit.
            try {
                camera = Camera.open(); // Try to open the camera

                // Set the properties.
                Camera.Parameters p = camera.getParameters();

                // Set it to flash mode
                p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                camera.setParameters(p);
            } catch (Exception ignored) {
                notifyUser();
            }
        }
    } else {
        // If flash is not available, notify the user
        notifyUser();
    }
}

There is one function that may interest you, notifyUser. This function is more like "in case of emergency" function. It displays a notification to the user, if the flashlight is not available, or if camera cannot be connected to the application. In these situations, our application simply notifies the users about the problem. 

private void notifyUser() {
    NotificationCompat.Builder mBuilder =
           new NotificationCompat.Builder(this)
                 .setSmallIcon(R.drawable.ic_flash_on_white_24dp)
                 .setContentTitle("Cannot access flashlight")
                 .setContentText("Camera application may be running in a background application.");

    int mId = 1234;
    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    // mId allows you to update the notification later on.
    mNotificationManager.notify(mId, mBuilder.build());
}

I don't want to talk about this notification builder thing, it is pretty much easy and can easily be understood from Android documentations. Now that we have defined the function that we need to be executed that fills our variables and objects, we have also looked in the "methods" to avoid any unwanted exceptions to be raised as we are using the application in a real-device. We may want to continue to next step to actually handle the requests coming from users. In the next section, you will see how to actually perform actions. 

Handling the Intents and Actions

Every IntentService object has a function onHandleIntent(Intent) which triggers a background worker thread which is responsible for performing the actions on a background thread. It saves your UI thread and doesn't freeze it.

I have already managed everything to be in their own spots (functions!) and now in this function, I can only call them to do the work for me. I can then manage what to do when and when to do what! Pretty much simple, right? Have a look below.

@Override
protected void onHandleIntent(Intent intent) {
    // Fill the fields
    fillFields();

    // Handle the intent
    if(intent.getAction().equals(ACTION_TURN_OFF)) {
        alter(false);
    } else if(intent.getAction().equals(ACTION_TURN_ON)) {
        alter(true);
    }
}

So now you see, how I actually handled the intent. The intent is actually being passed to a worker thread which will now be handling how to process the request based on the connections established to the camera and all other similar stuff. Otherwise, it will just display an error message. 

In the intent handler, you can see there is only one function that accepts a boolean value. Then our thread will work on it. The function is declared and defined as following, 

private void alter(boolean on) {
    if(canWork) {
        if(on) {
            try {
                // Start the "flashlight"
                camera.startPreview();
                FlashLightWidget.lightOn = true;
                notifyWidgets();
            } catch (Exception e) {
                Toast.makeText(FlashlightIntentService.this, e.getMessage(), Toast.LENGTH_SHORT)
                        .show();
            }
        } else {
            try {
                // Stop everything and just release the resource
                camera.stopPreview();
                camera.release();
                camera = null;
                FlashLightWidget.lightOn = false;
                notifyWidgets();
            } catch (Exception e) {
                Toast.makeText(FlashlightIntentService.this, e.getMessage(), Toast.LENGTH_SHORT)
                        .show();
            }
        }
    } else {
        notifyUser();
    }
}

In the above function, I am either triggering the camera to be active, or I am closing it. That depends on the value of "on", a parameter which is passed to the function. 

Well, at this stage, the application just works. If you click on the button it does turn the flashlight on and so on. But, I wanted to have something "even better". I wanted to use a Widget in my device to be able to use it, I mean I don't want to start the activity to get the flashlight on. I wanted to use a widget, that I can press on and the light be started. So, the next section is dedicated to that. 


Figure 2: Using the application through activity. 


Figure 3: The button says, "TURN OFF", because flashlight is currently active. 

This is it for the section of Activity. The activity also relies on the service. This is why, when we will build the widget we won't be calling the activity to start, instead we will make a direct call to the background service and trigger it to activate the flashlight for us. Pretty much simple it is. :-) 

Developing the Android Widget for our application

In this section, you will learn how to create the Android Widget. Of course, the title was just a cover up for teaching you Android basics, but let's just stick to the "Android Flashlight Widget" for now. 

Android Widgets are small screens, or apps, on the homescreen of your device. You can use them to trigger any service, intent, activity or what-ever Android is capable of. Widgets are a shortcut to your application. We will use the same good concept of using the shortcut and will use it to trigger the service. 

Building the UI for Widget

Like all other Android applications or activities, Android widget also has a UI for our users that they can use to interact with your application. In many ways, the widgets are not necessarily required to start your application, that is where your use a service, in our case it is an IntentService. 

Layouts for widgets are also XML files, with a layout, some Android controls that are rendered over screen for users. If you have created a new widget, Android Studio may have created a default layout for you, if you haven't, then create one and edit the layout file by the following XML markup. 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:padding="@dimen/widget_margin">

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:id="@+id/button_switch_widget"

        android:layout_alignParentTop="true"

        android:layout_alignParentLeft="true"

        android:layout_alignParentStart="true"

        android:layout_margin="15dp"/>

</RelativeLayout>

The above layout is very simple. It just contains a single ImageView control that is used to preview the image that we are going to use to allow user to interact. I also made sure that the widget is not "resizable" so that it spans over one single screen block grid only. If you are going to show extra details, you can consider adding your widget to be re-sizable to extra grids too. But I don't want to do that, so I just left it. 

Managing the back-end code

The class that works on the back of our widget extends, AppWidgetProvider. This class provides us with basic functions that we may use to perform actions, like to manage what happens if a new widget is created, or what happens if a widget is removed and so on. 

I have just tried to override and provide the logic for one function, the one that updates the application. I have not bothered to maintain or proceed with other functions, like, onEnabled(Context) or onDisabled(Context) etc. I have also provided a custom function, which can be triggered from an external object, like from our service. This is only possible, if we have a static function in our object which can be called without having to have an instance of the object in our scope.

The following function is what handles our application's widget's state through code-behind. 

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    // Construct the RemoteViews object
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.flash_light_widget);
    if(lightOn) {
        // Flash light active
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), 
                                            R.drawable.light_active_45);
        views.setImageViewBitmap(R.id.button_switch_widget, bitmap);

        // Create the intent, set the action
        Intent underlyingIntent = new Intent(context, FlashlightIntentService.class);
        underlyingIntent.setAction(FlashlightIntentService.ACTION_TURN_OFF);

        // Create the pending intent
        PendingIntent turnOffIntent = PendingIntent.getService(context, 1,
                                           underlyingIntent,
                                           PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.button_switch_widget, turnOffIntent);

    } else {
        // Flash light off
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), 
                                            R.drawable.light_deactive_45);
        views.setImageViewBitmap(R.id.button_switch_widget, bitmap);

        // Create the intent, set the action
        Intent underlyingIntent = new Intent(context, FlashlightIntentService.class);
        underlyingIntent.setAction(FlashlightIntentService.ACTION_TURN_ON);

        // Create the pending intent
        PendingIntent turnOnIntent = PendingIntent.getService(context, 1,
                                          underlyingIntent,
                                          PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.button_switch_widget, turnOnIntent);
    }

    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views);
}

The code does everything in our widget side. Widget depends on this code, it updates the widget and also allows user to communicate with the service to trigger the flashlight. I also used a few icons from Google's Material icon set and created one myself, all of them are available to be downloaded from GitHub's repository. What this code does, is simple. It just enables the user to use the widget and trigger intents based on current state. 

If the light is currently on, it will enable user to turn it off, otherwise it would turn it on. That is all that this code does. 

I have used the widget in, like 4 Android devices that I have at home and it works on them. There is a preview of this widget on my device. 



Figure 4: Application Widget being viewed on my own device. Flashlight currently off.

Once you trigger the flashlight using this widget, widget changes the drawable that is being used at the moment on the screen and "it looks like" the flashlight is active. You can tell that by looking at the following screen too.


Figure 5: Flashlight currently active, widget depicts this by changing the drawable resource.

This demonstrates how you can use the application. Of course you cannot see the flashlight because that is not included in the screenshot, but it works. This is it for now. :-) I hope, you would find this article useful. 

Point of Interest — and common tips

In this article you have learnt how to actually create an application that can trigger the flashlight activity in your application. I have made sure to keep the application from terminating un-expectly, but you also need to focus on a few things. First of all, if you are using any resource, remember to close the stream and release the resource before terminating. In the application above, I have managed to do so in the MainActivity's onPause function.

@Override
public void onPause() {
    if(FlashLightWidget.lightOn) {
        // Light is on, turn it off.
        Intent intent = new Intent(this, FlashlightIntentService.class);
        intent.setAction(FlashlightIntentService.ACTION_TURN_OFF);
        startService(intent);
    }
    super.onPause();
}

It doesn't cost much to determine wether the resource has been released or not, but it will always have a good UX if you make sure that you do clean up resources.

Rest is just simple Android application based on native Android APIs. There is one common question, "How to increase or decrease the intensity?" Well Android natively doesn't support this, so you have to write a native application to support it, using NDK. HTC smartphones supported this for their own applications and required root access. Root access is not provided in most devices and you should not rely on root access to provide a service. Either use NDK or do not provide a feature. In my opinion, the feature is not useful! 

I hope you liked the article. If you find something difficult, or easy, please let me know in the comments. :-)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Afzaal Ahmad Zeeshan
Student
Pakistan Pakistan
Computer programmer, author, student, in a relationship and not available!

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190214.1 | Last Updated 5 Dec 2015
Article Copyright 2015 by Afzaal Ahmad Zeeshan
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid