Customize "share picture via" menu on Android (Android Intent Filters)

来源:互联网 发布:mac 中文输入法 消失了 编辑:程序博客网 时间:2024/06/06 00:37

 http://eggie5.com/8-hook-share-picture-via-menu-android

You know in the android gallery when you view a picture there is the share menu that allows you to upload your picture to various services. It is the job of the app to handle the upload - for example on my phone (depending on the apps you have installed) facebook, picasa, blogger all have entries in that list. This article will describe the process to get your app in that list and post the picture over HTTP.

Basically there are 3 steps to get this working:

  1. Register your app w/ the android platform to show your app in the platform share menu. (see Intents)
  2. Get the image from the Album app using android APIs (see ContentResolver)
  3. Do actual HTTP post request

1. Add hook to AndroidManifest.xml

If you intend to get your app in the share menu you must register that intent (pun intended) w/ the andoid system. This is done via and intent-filter in theAndroidManifest.xml file. 

In order to use the internet to make and HTTP request we must also register that w/ Android via the use-permission directive in the manifest file. See below example:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.eggie5" android:versionCode="1" android:versionName="1.0">    <application android:icon="@drawable/icon" android:label="@string/app_name">        <activity android:name=".post_to_eggie5" android:label="@string/app_name">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>            <intent-filter>                <action android:name="android.intent.action.SEND" />                <category android:name="android.intent.category.DEFAULT" />                <data android:mimeType="image/*" />            </intent-filter>        </activity>    </application>    <uses-permission android:name="android.permission.INTERNET" /></manifest>


 

2. Get image from Gallery

Getting an image from the camera isn't as straight forward as it seems. It's not just open the file at this path - you must use the ContentResolver/ContentProvider API in android - which the gallery app exposes. See code example:

public void onCreate(Bundle savedInstanceState){    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    Intent intent = getIntent();    Bundle extras = intent.getExtras();    String action = intent.getAction();    // if this is from the share menu    if (Intent.ACTION_SEND.equals(action))    {        if (extras.containsKey(Intent.EXTRA_STREAM))        {            try            {                // Get resource path from intent callee                Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);                // Query gallery for camera picture via                // Android ContentResolver interface                ContentResolver cr = getContentResolver();                InputStream is = cr.openInputStream(uri);                // Get binary bytes for encode                byte[] data = getBytesFromFile(is);                // base 64 encode for text transmission (HTTP)                byte[] encoded_data = Base64.encodeBase64(data);                String data_string = new String(encoded_data); // convert to string                SendRequest(data_string);                return;            } catch (Exception e)            {                Log.e(this.getClass().getName(), e.toString());            }        } else if (extras.containsKey(Intent.EXTRA_TEXT))        {            return;        }    }}


 

First we get a path to the image (uri) from the share menu of the gallery when we clicked on share. It's not however just a path to the image - it's a contenturi which is android platform specific way to refrence a resource. It looks somehting like thiscontent://com.example.gallery/3. With a URI you can then query gallery for the resource. The key here isgetContentResolver() method - it will get you a handle to android internal DRM system which the cameria/gallery exposes(uses). Then in order to sent the image over HTTP we must convert it to plaintext encoding (base64) from binary. We do this by gettings it's bits and converting to base64 string representation (details are beyond the scope of this article - see source for details).

3. HTTP Post Request

Rather than choose a high level library for the HTTP action I chose to implement it using sockets as an exercise (more fun - we can really see what's happening).

First we build the xml post body string. We then create the request and send it. Note: most people would do this using multipart/mime but I choose just to encode the image into the xml photo node.

private void SendRequest(String data_string){    try    {        String xmldata = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"                + "<photo><photo>" + data_string                + "</photo><caption>via android - " + new Date().toString()                + "</caption></photo>";        // Create socket        String hostname = "eggie5.com";        String path = "/photos";        int port = 80;        InetAddress addr = InetAddress.getByName(hostname);        Socket sock = new Socket(addr, port);        // Send header        BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(                sock.getOutputStream(), "UTF-8"));        wr.write("POST " + path + " HTTP/1.1\r\n");        wr.write("Host: eggie5.com\r\n");        wr.write("Content-Length: " + xmldata.length() + "\r\n");        wr.write("Content-Type: text/xml; charset=\"utf-8\"\r\n");        wr.write("Accept: text/xml\r\n");        wr.write("\r\n");        // Send data        wr.write(xmldata);        wr.flush();        // Response        BufferedReader rd = new BufferedReader(new InputStreamReader(                sock.getInputStream()));        String line;        while ((line = rd.readLine()) != null)        {            Log.v(this.getClass().getName(), line);        }    } catch (Exception e)    {        Log.e(this.getClass().getName(), "Upload failed", e);    }}


 

Then on the server side - I must decode the base64 stream back into binary which I did in ruby:

Base64.decode64(incoming_file)

See full source on github: https://github.com/eggie5/android-share-menu

Next Steps:

When the image is uploading the UI kinda locks up - the next step would be to have some type of progress bar like the facebook or picasa app does.

Also usually the images are way to big for the front page of this site (where they go) - should implement some type of resize on the phone. This would also make the upload faster.

 

 

ps

Uros Mesaric3 comments collapsed CollapseExpand

Tnx for code. Just what I needed.

  • eggie5 2 comments collapsed CollapseExpand

    glad i could help!

  • Rakesh Raut7391 comment collapsed CollapseExpand

    thanxxxxxx alot exactly the same thing i want.....

  • Stephen Sykes1 comment collapsed CollapseExpand

    Added thread via AsyncTask for server call and progress dialog. Updated code available at my github fork:
    https://github.com/sasykes/and...

  • Dia_mor 2 comments collapsed CollapseExpand

    finally code that works sending data to a server!!! thanks!! ibe been trying tens of examples and this is the first one successfull!!

  • eggie5 1 comment collapsed CollapseExpand

    GOOD

  • Michealkl1 comment collapsed CollapseExpand

    Thanks for the code. How if I want to resize the photo to any size(VGA, 1M...) on the phone before upload to server?

  • Praveentubachi2 comments collapsed CollapseExpand

    Hi all,
    I found this link useful and yes,
    thanks for this good example. In this example we get the URI
    which gives the path for the resource in the gallery, that URI isn't the
    path pointing towards the location of the resource(Image) in sdcard(If
    it wer to have existed in sdcard). I would like to know how you the path
    which gives the location for the resource. Please Help. Thanks in
    advance.

  • eggie5 1 comment collapsed CollapseExpand

    Well the Gallery app "owns" the images and is implemented in such a way that you must use the ContentResolver API to get access to them. I suppose, of course, that there is a file path to them somewhere on the SD card, but those details aren't exposed.

  • Ashley McConnell5 comments collapsed CollapseExpand

    Slightly off topic, but do you happen to know if you can start the "Share Via" activity from your own activity?

  • eggie5 4 comments collapsed CollapseExpand

    uh, not sure what you mean...

  • Ashley McConnell3 comments collapsed CollapseExpand

    Hi Eggie5,  

    After an hour searching I found the answer here: - http://stackoverflow.com/quest... (2 mins after posting above :))I mean I would like to take a screenshot of my app, save it to the SD card then call the Share Via dialog (like you get when you take a pic on the camera and it allows you to send it to email / tweet / facebook etc.)Hope this helps,All the best,Ash

  • eggie5 2 comments collapsed CollapseExpand

    Are you programaticly want to invoke the share via dialog from your activity? How do you take a screenshot? I'm curious...

  • Ashley McConnell1 comment collapsed CollapseExpand

    I suspect it's only possible to take a screenshot with an OpenGL app, but this is how: - http://stackoverflow.com/quest... HTH!

  • Joevolcano5 comments collapsed CollapseExpand

    I used this code and I still cant get my app to show up inthe share menu on the emulator or the droid x after selecting some image ans clicking on share... it just goes right to an mms message... frustrating....

  • Joevolcano4 comments collapsed CollapseExpand

    I think it is working for sharing 1 image but not multi

  • eggie5 3 comments collapsed CollapseExpand

    I designed it to only upload 1 photo at a time. 

    To upload multiple you might have to change something in the manifest file. If you figure it out, let me know.

  • Joevolcano2 comments collapsed CollapseExpand

    The following settings I have in my AndroidManifest.xml will handle single and multiple share in the emulator.
          
      <activity android:label="@string/app_name" android:name=".ApplicationName">
    <intent-filter>
    <action android:name="android.intent.action.MAIN">
    <category android:name="android.intent.category.LAUNCHER">
    </category></action></intent-filter>
    <intent-filter>
    <action android:name="android.intent.action.SEND_MULTIPLE">
    <action android:name="android.intent.action.SEND">
    <category android:name="android.intent.category.DEFAULT">
    <data android:mimetype="image/*">
    </data></category></action></action></intent-filter>
           </activity>

    This my oncreate method to access the multiple files. it just logs the uri

        public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);                Intent intent = getIntent();            Bundle extras = intent.getExtras();            String action = intent.getAction();            // if this is from the share menu            if (Intent.ACTION_SEND_MULTIPLE.equals(action))            {                   if (extras.containsKey(Intent.EXTRA_STREAM))                 {                                       ArrayList<uri> theFileList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);                     try                     {                         for(Uri uri: theFileList)         {         Log.i("INFO", uri.getPath());                 }         return;                     }          catch (Exception e)                     {                         Log.e(this.getClass().getName(), e.toString());         }             }         }    }</uri>

  • eggie5 1 comment collapsed CollapseExpand

    cool thanks for the update - maybe I should add it to the code I posted...

  • Anzar Zulfiqar2 comments collapsed CollapseExpand

    cant we give the priority to ur intent filter in this case, so that when the menu is displayed by the android system, our application should be the first option

  • eggie5 1 comment collapsed CollapseExpand

    It just seems alphabetical on my phone.... I don't know any android settings to change the order...

  • Dia_mor 2 comments collapsed CollapseExpand

    hi there. can you show the server side? is it php?

  • eggie5 1 comment collapsed CollapseExpand

    Hi,
    The server side can be anything - this particular site is running ruby. The server just needs to accept an xml post request with base64 encoded photo data in the <photo> tag. The server would then need to base64 decode it and save to disk.

    Here is a snippet of my ruby server code:

    #decode binary data
    @temp_file = Base64.decode64(incoming_file)

    #save to disk
    #FileUtils.mkdir_p(full_directory_name)
    #File.open(full_filename, "wb") {|f| f.write(@temp_file.read)}

    #or save to amazon s3
    @s3 = Rightscale::S3.new(ENV['AMAZON_ACCESS_KEY_ID'], ENV['AMAZON_SECRET_ACCESS_KEY'])
    b=@s3.bucket("#{my_bucket}#{Rails.env}")
    b.put("#{photo_path}/#{self.id}", @temp_file, {}, "public-read-write")</photo>

  • Melpo 2 comments collapsed CollapseExpand

    Hm, eclipse is complaining about Base64, SendRequest and getBytesFromFile. Where can I import these from?

  • eggie5 1 comment collapsed CollapseExpand

    if you look at my github example project the library is included, https://github.com/eggie5/andr... or just search org.apache.commons.codec.binary.Base64 and get the jar off the internet and include it in whatever build system/IDE you use.

  • Marius 2 comments collapsed CollapseExpand

    This very example is something I've been looking for. Is it possible for somebody to make a ready-complied version for me? This would be very useful for calling from App Inventor apps.

    marius@evang.no

  • eggie5 1 comment collapsed CollapseExpand

    never used app inventor before... sorry...

  • Erikswed1043 comments collapsed CollapseExpand

    Thanks for the code snipe
    Oh my bad of course i messed up the xml.
    and also i had to covert the uri to absolute path something.
    Can i ask , when i press the post_to_eggie5 in the share menu , screen goes black with the text post_to_eggie5 at the top.
    How do i disable that?

  • eggie5 1 comment collapsed CollapseExpand

    updated - if your logcat is still broke sounds like an issue w/ your eclipse/android sdk setup...

  • eggie5 1 comment collapsed CollapseExpand

    hi, actually I didn't finish this code yet - I got too busy w/ finals at school. I plan on finishing it this week - I'll let you know...

  • Erikswed1041 comment collapsed CollapseExpand

    hmm
    Im new to Android and created a new android project in Eclipse
    but get "unable to instantiate activity error from the Logcat log.

    Do I have to put something in that addAttachment(Strin... method??
    hmm

    I did put an image into my virtual sdcard and try sharing clicking but program/activity crash

     

  • 原创粉丝点击