Friday, February 3, 2012

Sending SMS on Android and tracking it

Lets look at how to send an SMS. It's pretty simple though. A few lines of code and your SMS is gone. To make it more convenient and meaningful, we should also be able to track and let the user know when the SMS is sent and when it is delivered. For a start, I won't be looking into how to trap the error messages here and log or show it to the user. May be, when I get some more time, I will update this post. For now, lets track our SMS.
        Intent sentIntent = new Intent(INTENT_ACTION_SENT);
        PendingIntent pendingSentIntent = PendingIntent.getBroadcast(this,
                REQUEST_CODE_ACTION_SENT, sentIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        Intent deliveryIntent = new Intent(INTENT_ACTION_DELIVERY);
        PendingIntent pendingDeliveryIntent = PendingIntent.getBroadcast(this,
                REQUEST_CODE_ACTION_DELIVERY, deliveryIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        SmsManager smsManager = SmsManager.getDefault();

        // Second parameter is the service center number. Use null if you want
        // to use the default number
        smsManager.sendTextMessage(number, null, message, pendingSentIntent,
                pendingDeliveryIntent);

In the above code snipped, you can see that we are passing 2 pending intents to the SMSManager, one of which will be fired when the SMS is sent, and the other, when the SMS is delivered. It would also let you know the error type if the sending or delivery fails, so that you can take action for the errors. INTENT_ACTION_SENT and INTENT_ACTION_DELIVERY are string constants, which are just some random actions required to setup of the PendingIntents and receive them back.

Setting up the SMS is super easy. How do we track or listen to the updates, which happen through the PendingIntents? Well, those pending intents could be for starting an Activity, a Service or sending out a Broadcast. As you can see, here, I have used a Broadcast, to keep it simple. So, in our activity, we would need to register BroadcastReceivers for the same actions.
        IntentFilter filter = new IntentFilter(INTENT_ACTION_SENT);
        filter.addAction(INTENT_ACTION_DELIVERY);

        registerReceiver(smsSentDeliveredReceiver, filter);

Now, the onReceive() method will be fired, when those events happen, and thus you can notify the user about when the message is sent and delivered.
        String action = intent.getAction();
        Log.i(TAG, "Received: " + action);

        if (action.equals(INTENT_ACTION_SENT)) {
            Log.i(TAG, "Message: Sent");
            Toast.makeText(this, "Message sent", Toast.LENGTH_LONG).show();
        } else if (action.equals(INTENT_ACTION_DELIVERY)) {
            Log.i(TAG, "Message: Delivered");
            Toast.makeText(this, "Message delivered", Toast.LENGTH_LONG).show();
        }

You can find the sample app and the source code here. Give it a run.

Note: Using this example, you cannot send SMS to real numbers from an emulator. The SMS will be sent, but it will never be delivered.

Thursday, January 12, 2012

Download Videos from Youtube Trick

First things first. Is it legal to download videos from Youtube?

As much I have understood from the Terms of Service, it's not a straight yes or no. Basically, Youtube doesn't have a publicly available API or service that would allow users to download the videos. Of course, you can download back the videos that you had originally uploaded. But who would do that anyway?

However, there are workarounds and tricks with which you can actually download any video from the website. There are many softwares/add-ons that easily do this job. Here is a snapshot from the published "Terms of Service".

You shall not download any Content unless you see a “download” or similar link displayed by YouTube on the Service for that Content. You shall not copy, reproduce, make available online or electronically transmit, publish, adapt, distribute, transmit, broadcast, display, sell, license, or otherwise exploit any Content for any other purposes without the prior written consent of YouTube or the respective licensors of the Content. YouTube and its licensors reserve all rights not expressly granted in and to the Service and the Content.
From Youtube Terms of Service

It says, that you cannot exploit the content/content-owners by selling the videos or re-distributing it, thereby, making a profit against your sales. That's obviously illegal for any kind of content, unless of course, the license makes that content freely re-distributable. So, if you download the videos using those workarounds and tricks, only for your personal use (ex, offline viewing), you probably aren't breaking any rules. What about those add-ons/softwares that do this? Well, it's not illegal for them, since they are just distributing the software. So, they are off the hook.

For personal use, it seems ok to download videos off from Youtube. So? let's see how hard or difficult it is to get this working.Actually, it's quite simple.

Step 1: You should have the VIDEO ID of the video that you want to download.

Step 2: You need to make a call to this api, to get the details for that video. The fmt parameter is for getting the specified format of the videos. See the "Quality and Codecs" section on this page on Wikipedia.
URL => http://www.youtube.com/get_video_info?video_id=VIDEO_ID&fmt=6
Step 3: Process the response. You will get a plain string response. That response contains all the direct URLs to various formats of the video. Look for the key "url_encoded_fmt_stream_map" and the value for this key is what you need from this response. You will need to filter out all the URLs from here, and get hold of the URL to the format you want to download. Here are some sample URLs.
http://o-o.preferred.bharti-bom1.v8.lscache7.c.youtube.com/videoplayback?sparams=id,expire,ip,ipbits,itag,source,ratebypass,cp&fexp=904510,914501,908302,902315,916201,905267&itag=45&ip=203.0.0.0&signature=095A9503E49931B0B849257048E73EB7388F515A.C13F4074ED92A82DF94BBD73E808E917369C224D&sver=3&ratebypass=yes&source=youtube&expire=1326412828&key=yt1&ipbits=8&cp=U0hRS1RMUF9HUkNOMV9MRlRJOjZ2TTNoOG9ialZD&id=c418f3a4b0f1b751&quality=hd720&fallback_host=tc.v8.cache7.c.youtube.com&type=video/webm; codecs="vp8.0, vorbis"&itag=45
http://o-o.preferred.bharti-bom1.v11.lscache3.c.youtube.com/videoplayback?sparams=id,expire,ip,ipbits,itag,source,ratebypass,cp&fexp=904510,914501,908302,902315,916201,905267&itag=22&ip=203.0.0.0&signature=2C46F096073FDACEFD3B4895EDBC3CA1162682AD.3BCAE9E76BBE98D3FB687AF16ECC595E35AD8173&sver=3&ratebypass=yes&source=youtube&expire=1326412828&key=yt1&ipbits=8&cp=U0hRS1RMUF9HUkNOMV9MRlRJOjZ2TTNoOG9ialZD&id=c418f3a4b0f1b751&quality=hd720&fallback_host=tc.v11.cache3.c.youtube.com&type=video/mp4; codecs="avc1.64001F, mp4a.40.2"&itag=22
 If you notice the URLs, there is a type parameter, where you can determine which URL is for which format (type=video/mp4, type=video/webm etc).

Step 4: To finally be able to download the video, you need to strip off a few values from these URLs, just to make sure your calls don't fail due to long URLs. What I have noticed is that if you strip off (everything after the quality param) the last parts of the URLs, everything's just fine. So, the final URL would be something like:
http://o-o.preferred.bharti-bom1.v11.lscache3.c.youtube.com/videoplayback?sparams=id,expire,ip,ipbits,itag,source,ratebypass,cp&fexp=904510,914501,908302,902315,916201,905267&itag=22&ip=203.0.0.0&signature=2C46F096073FDACEFD3B4895EDBC3CA1162682AD.3BCAE9E76BBE98D3FB687AF16ECC595E35AD8173&sver=3&ratebypass=yes&source=youtube&expire=1326412828&key=yt1&ipbits=8&cp=U0hRS1RMUF9HUkNOMV9MRlRJOjZ2TTNoOG9ialZD&id=c418f3a4b0f1b751&quality=hd720

Step 5: Save the file with the proper extension. That's it. You are done.

If you ask how I got to know about this trick? Well, everything's already out there on the web. A lot of people have already blogged about it before. But, I had to dig for it for almost 2 days. So, I hope someone finds it useful. So, if you want to make your own Youtube Downloader, now you know "How to Download Youtube Videos programmatically". 

Note: This method might stop working as and when Google/Youtube blocks this loophole. In the past, Youtube has been known to block a few other workarounds that had existed.

Wednesday, December 28, 2011

Bluetooth on Android : Part I

This is a part of a series of posts in which I will put forward a full working app what uses bluetooth on your Android device to discover, connect, pair, send and receive files. The source code would be tagged with each part of this series.

Part 1: The app should be able to discover and list out the devices (paired/unpaired).

To start off with learning about bluetooth on Android, visit the official documentation. The documentation is quite lucid and clear. Here I will try to explain parts of my code.

The first activity (HomeActivity), for now, will have a single button "Discover Devices", which will take you to the activity(DiscoverDevicesActivity) where you can see the list of devices that are visible by your device.

This activity does a few things, quite a few if-else conditions.
  • First, you need to check if your device supports bluetooth. If you don't have the hardware capability on your phone, you won't be able to run this application. Eh!! Most of the phones would obviously have bluetooth. Ummm...Yes... But the emulators don't. Arrggghhh!!!!
  • Once you are sure that your device has bluetooth capability, the next thing to check if it is enabled or not. If it's enabled, move on to the next step, else you will need to turn it on first.
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
  • Once, you click on "Yes" on the confirmation dialog that you will see, bluetooth radio will be switched on for you. On the screen, the "Scan" button will become active. Once you tap the "Scan" button, the ListView will show the list of devices that your device can discover.
That's all for the first part of the series. But, there's a little more to understand abut discovery.

The process of discovery is asynchronous. The list view, currently shows two kinds of devices.
  1. Devices which your phone already knows about (Paired)
  2. Devices which are discovered (Which are not paired with your device)
Getting the already paired devices is simple. The BluetoothAdapter will give you details about such devices.
// Check already discovered devices
Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice device : devices) {
       adapter.addDevice(device);
}
adapter.notifyDataSetChanged();
Now comes the actual discovery part. For this, you will need to register a broadcast receiver which will be called whenever a new device is found. After registering the receiver, you need to trigger the discovery by calling the startDiscovery() method of the BluetoothAdapter.
// Scan for new devices
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(devicesReceiver, filter);

bluetoothAdapter.startDiscovery();
On the receiver's onReceive() method, we pick up the details about the new device found, and add it to our ListView.
public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      // When discovery finds a device
      if (BluetoothDevice.ACTION_FOUND.equals(action)) {
          Log.i(TAG, "Device found");
               
          // Get the BluetoothDevice object from the Intent
          BluetoothDevice device = intent
                  .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
          // Add the name and address to an array adapter to show in a ListView
          adapter.addDevice(device);
          adapter.notifyDataSetChanged();
}
The DevicesAdapter is a custom adapter for the ListView which we will be updating in the subsequent posts to show more information about the devices.

As always, you should unregister your receiver, once your activity is paused. Also, in addition to this, you should also cancel the discovery if at all you have started by calling the cancelDiscovery() method of the BluetoothAdapter.

The part 1 of the project can be checked out by fetching the source code and checking out the v1.0 tag from the repository. The complete source code can be found here.

Alternately, if you want to download the source of Part 1 as a zip, use this link to Part 1.

Tuesday, November 1, 2011

Drag and Drop (Honeycomb/ICS)

Drag and drop is already available with Honeycomb and up. In this example, we will see how to implement a simple Drag and Drop Operation.

We have a ListView with some items. The other half of the screen would have a drop area which has a single TextView. We will see, how to drag items from the ListView, drop them into the drop area, which will update the TextView with the list item's title, and also, that particular item would be removed from the ListView.

We will trigger the drag operation, when you long tap on an item in the ListView. After the drag operation is started, you will see a floating view of the selected item, which you can move around the screen. When you approach the drop area, the background of the drop area would change, which means that you can now drop your item. Once you leave the item, the TextView inside the drop area will show the text of the selected item.





This should also work on Ice Cream Sandwich. You can find the whole source code here. The code is quite self-explanatory. But, if you have any queries, let me know through the comment form. There are a few comments in the code to help you.

Wednesday, October 19, 2011

Simple ViewPager for Android

You would have seen many applications recently, which make use of the new and awesome ViewPager that allows views to be horizontally scrollable. The new Android Market app also implements a flavor of the ViewPager, although it's a little more complex that what we will see here. This class is not available directly for you to use. Check this blog for some insight. It would required you to download a compatibility library from Google, add it to your project wherever you would like to use it, and go about paging views.

And yes, it's very simple to implement. I have coded up a little sample where it shows you how to use a ViewPager with a simple PagerAdapter. More or less, it works like the ListViews. Although there is not much documentation for this, it's quite easy to set up everything.

Firstly, you would need to add the ViewPager into your layout file.

Listing of main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:text="Page 1" android:id="@+id/textViewHeader"
        android:layout_width="fill_parent" android:layout_height="wrap_content"
        android:gravity="center" android:padding="10dip" android:textStyle="bold"></TextView>
    <android.support.v4.view.ViewPager
        android:layout_width="fill_parent" android:layout_height="fill_parent"
        android:id="@+id/viewPager" />
</LinearLayout>
In this sample, we are using an extra TextView which would show the current page you are on.

Setting up the ViewPager
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this);
viewPager.setAdapter(adapter);
This code is almost identical to what we would do for a ListView or Gallery. The only difference is that the adapter here, extends a PagerAdapter instead.

The PagerAdapter

The PagerAdapter has a few methods that you would implement. For this example, I have four different views, one for each page. 2 are ListViews, a TextView and a Button, not in that order. Here are the methods that you would need to implement. Look at the MyPageAdapter class for more.

@Override
public void destroyItem(View view, int arg1, Object object) {
         ((ViewPager) view).removeView((View)object);
}
@Override
public int getCount() {
          return views.size();
}
@Override
public Object instantiateItem(View view, int position) {
           View view = views.get(position);
           ((ViewPager) view).addView(view);
           return view;
}
@Override
public boolean isViewFromObject(View view, Object object) {
           return view == object;
}
 And you are done!!! Sweet.... You can find the whole source code here.