Facebook Multi-Friend-Selector for Flash AS3

UPDATE Nov 2010: This requires the old REST API Facebook_library_v3.4_flash.swc, which has now been deprecated in favour of the Graph API. Please feel free to fork this for use with the new GraphAPI SWC. There is a little more info on this in the comments. I’d love to update it myself, but not sure when that might happen at the moment :-/

One of the drawbacks of the current Facebook Actionscript API is that it doesn’t come bundled with UI components, it’s simply a data API. Fortunately you can use the JavaScript Client Library’s FB.UI.FBMLPopupDialog() to render FBML overlaying your SWF (if you don’t mind using wmode=”transparent”). But still, when it comes to the FBML fb:multi-friend-selector, if you want to do anything but send out invites to your Facebook app (via a browser-redirecting POST) , you’re out of luck.

Ideally, the fb:multi-friend-selector would allow the setting of a callback which would return the UIDs of the selected friends. It would then be down to the developer to choose what to do with them.

So, I decided to recreate the fb:multi-friend-selector directly in Flash. It will allow you to input an array of uid strings and later return a FacebookUserCollection featuring the selected users. Unfortunately I haven’t created this to be a fully resizable component, it simply does what it says on the tin. Hopefully, you may find that this gets you out of a sticky situation once you realise the shortcomings of the FBML fb:multi-friend-selector.

Don't be fooled, this MultiFriendSelector is not FBML, it's Flash :)

You can download it from the milkisevil-toolbox on github. You’ll need to add the “lib/milkisevil/FacebookComponents.swc” to your project and create a new instance of the “com.milkisevil.ui.facebook.MultiFriendSelector” class.

Here’s a rough guide to how you might want to instantiate the MultiFriendSelector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Don't forget to make the following imports where appropriate
import com.milkisevil.ui.facebook.MultiFriendSelector;
import com.milkisevil.events.StatusEventEnhanced;

// And the following inside your class
private var multiFriendSelector:MultiFriendSelector;

private function showFriendSelector():void
{
    multiFriendSelector = new MultiFriendSelector( facebook, 16 );
    multiFriendSelector.title = 'Your friends';
    multiFriendSelector.subtitle = 'Irritate the hell out of your friends!';
    multiFriendSelector.addEventListener( MultiFriendSelector.STATUS_EVENT, multiFriendSelectorStatus );
    addChild( multiFriendSelector );
    multiFriendSelector.getFriends();      
}

private function hideFriendSelector():void
{
    removeChild( multiFriendSelector );
    multiFriendSelector = null;
}

private function multiFriendSelectorStatus(event:StatusEventEnhanced):void
{
    trace('exec multiFriendSelectorStatus: ' + event.code);

    switch(event.code)
    {
        case MultiFriendSelector.CLOSE:
            hideFriendSelector();
            break;
       
        case MultiFriendSelector.SUBMIT:
            var selectedUsers:FacebookUserCollection = multiFriendSelector.getSelected();
            hideFriendSelector();
           
            var uidList:Array = [];
           
            for(var i:int = 0; i<selectedUsers.length; i++)
            {
                var facebookUser:FacebookUser = selectedUsers.getItemAt(i) as FacebookUser;
                uidList.push( facebookUser.uid );
            }
           
            // Now do some custom stuff with those uids
            myCustomMethod( uidList );
           
            break;
    }
}

Tags: ,

26 Responses to “Facebook Multi-Friend-Selector for Flash AS3”

  1. MultiFriendSelectorAsset is not found.. how to rectify it?

    Can anyone help me?

  2. Hi Archana,
    Did you add the lib/milkisevil/FacebookComponents.swc to your project?

  3. Hi,
    can u tell me why my flash cs4/5 doesn’t load swc? Do i have to use Flash builder or Flex? Is there a way to keep flash instead of flex?

    I also tried to change extension (swc –> zip): in this manner i obtain library.swf and catalog.xml but i suppose that i have to rebuild your classes…

    Can anyone help me pls?

  4. Hi Mark,

    The FacebookComponents.swc only contains the UI assets, so make sure you’ve also included the ‘src’ folder in your class path. Ultimately, you need to add the SWC and the actual src classes to your project.

    It’d surely be easier for others using this if everything was in one SWC. So I’ll keep that in mind for any future releases.

    Let me know if that solves your problem.

  5. i get this error in the MultiFriendSelector.as

    when I do this.

    multiFriendSelector = new MultiFriendSelector;

    I’m not really sure how to call the friend selector

    1046: Type was not found or was not a compile-time constant: Facebook.

    thanks for the help

  6. Hi Andres,

    This currently can only be used with the old REST API Facebook SDK SWC. So if you’re using this with the latest Graph API SWC, it won’t work.

    If you’re up to it, you should be able to rework the classes to support the Graph API. You’ll just need to replace the parts of class MultiFriendSelector that gets your friend’s data from the API (which is what passing an instance of facebook:Facebook via the constructor used to do).

    You’ll also need to change how the MultiFriendSelector dispatches data back to your application class. This means getting rid of FacebookUserCollection and using an Array or Vector instead, as I don’t think the new Graph API SWC contains these classes.

    Sorry I can’t be of more help just now, but feel free to fork this on github as it will definitely quicker than rewriting from scratch.

  7. thanks! I’ll try that see if i can get the job done!

  8. Philip,

    If you can help me briefly explaining how to call the class so i can create the Friend selector.

    All libraries are running for mi now

    I create the variable:

    var multiFriendSelector:MultiFriendSelector;

    no problem… I created the MultiFriendSelectorCall.as file with the above code :

    // Don’t forget to make the following imports where appropriate
    import com.milkisevil.ui.facebook.MultiFriendSelector;
    import com.milkisevil.events.StatusEventEnhanced;

    // And the following inside your class
    private var multiFriendSelector:MultiFriendSelector;

    private function showFriendSelector():void
    {
    multiFriendSelector = new MultiFriendSelector( facebook, 16 );
    multiFriendSelector.title = ‘Your friends’;
    multiFriendSelector.subtitle = ‘Irritate the hell out of your friends!’;

    And I imported it in my main flash : import com.milkisevil.ui.facebook.MultiFriendSelectorCall;

    when I do:

    multiFriendSelector.showFriendSelector();

    It does not call the function.

    Can you show me a quick run on how to call the multi riend selector?

  9. ey man, congrats for the hard work
    can you please eligthen me :)
    i get this error
    1046: Type was not found or was not a compile-time constant: NativeWindowBoundsEvent.

  10. so I converted this to graph api, using the shortcut of complining in the deprecated restAPI just to get the UI components.

    I have 2 problems that hopefully you can help with:

    1) when the Multifriends selector is added to the displaylist, it seems to add 2 copies of itself. i.e Once the list is populated, if I click on the close button – it closes but a second copy the the multifriedselector is present right underneath it.

    2) when I look at the logs it says “Warning failed to place object at depth ” and “:Warning failed to place object at depth 2″ – and has 50 such warnings.
    I suspect it has to do with the tweening classes and the fact that my test code is pure actionscript (no flash).

    Any hints of where to look would be greatly appreciated.

  11. Well,

    I solved the issue of the component being added twice – seems like the showFriendSelector() method from your sample code in this post was being called twice because I had a call to this method in my onLogin method and that was triggered twice during the Facebook login process. I moved the creation of the multifriendselector component into the class constructor to ensure only one instance is created and this seems to have solved that problem.

    I have no idea what the “Failed to place objet at Depth 1″ error is and there is no documentation about this on the web – but the component seems to work.

    Final questions: Do your components require the TweenMax library to be downloaded separately (this is what I had to do to get them to compile with acompc)

  12. I have adapted this utility to work with the new facebook Graph API.

    You can pull the code from :

    git://github.com/letapjar/milkisevil-toolbox.git

    Please note, this version requires 1 new library – the as3commons-reflection library.

    downloadable from:
    http://www.as3commons.org/as3-commons-reflect/

    The MultiFriendSelector will work with either web-based flash apps or desktop apps – simply pass in the third constructor argument of isAir:Boolean – default is ‘true’ meaning the desktop api is used – instantiate with false to use the facebook – web api.

    getSelectedFriends returns and Array of the selected friends – typed as objects with fields uid, name and pic_square

    hope this helps everyone!

  13. Thanks Letapjar!

    I also did a work arround with this library, my results were not as good as I expected but did the work i requiered.

    I’ll check out your adaptation and post results!

    Thanks for contribuiting!

  14. I was able to compile Letapjar’s fork into my app, however I am getting a runtime error. Here’s what it says:

    Type Coercion failed: cannot convert flash.display::MovieClip@1df55301 to fl.containers.ScrollPane.
    at com.milkisevil.ui.facebook::MultiFriendSelector()[/Users/jonnymorrill/Projects/whatsinaword/flash/include/letapjar-milkisevil-toolbox-b806879/src/com/milkisevil/ui/facebook/MultiFriendSelector.as:67]

    Any Ideas? Thanks :)

  15. I’m having the same problem jrmorrill has. It looks like the MultiFriendSelectorAsset content.scrollPane isn’t of type scrollPane but a MovieClip. Any attempt to set it up or convert it to a ScrollPane throws that error. I’m guessing that the “scrollpane” is in some kind of holder of type MovieClip. Can we get an update to that swc or something?

  16. I believe that is correct. the MultiFriendSelector itself extends BaseUI which extends movieclip. It has a child sprite cleverly called “child”). The child sprite is of type MultiFriendSelectorAsset – and this is the component that contains the actual scrollpane.

    I don’t have flash – I program in pure actionscript so I ended up writing my own container code and just used the friend buttons and the logic for handling their various clicks.

    To access the scrollpane you’d need to write in a getter / setter.

  17. Thanks Letapjar, I believe the following line (67 in MultiFriendSelector.as) is what you’re describing:

    scrollPane = child.content.scrollPane;

    so merely doing this doesn’t get a scrollPane and requires a getter/setter for the class MultiFriendSelectorAsset? If so I’ll gladly set that up, but i won’t be able to if it’s in the FacebookComponents.swc which i deduce is it’s location. Perhaps there’s a better way to do this? I’m just clueless to it since i don’t know what MultiFriendSelectorAsset looks like.

  18. I don’t know what is in the facebook components.swc – But that is probably where the visual parts of the components come from.

    Line 66 in the code says:

    scrollPnae = child.content.scrollPane;

    so if you add a getter to MultiFriendSelector for the private variable scrollPane (or just make it a public variable) you should have programatic access tot he scrollPane. That var is of type fl.containers.ScrollPane – so the getter should work. the Mutlifriend selector itself did not give me any runtime errors in a pure actionscript application.

  19. I think we might have gotten our wires crossed. Line 66 (for you) line 67 (for me) is were the error occurs. child.content.scrollPane is not a ScrollPane. So when someone runs the MultiFriendSelector constructor a runtime error is thrown because private var scrollPane is a ScrollPane, but child.content.scrollPane is not. it’s a MovieClip.

    I believe jrmorrill and myself got this error when we did this:

    var mFriendSelect:MultiFriendSelector = new MultiFriendSelector(16,5,false);

    and not when we were trying to do something like this (which i imagine is what you understood):

    mFriendSelect.scrollPane;

    You mentioned that you weren’t getting errors. Are you setting isAir to true? could there be a difference with ‘com.facebook.graph.FacebookDesktop’ and ‘com.facebook.graph.Facebook’?

  20. Hmm,

    I did only test the component in an AIR app – I usually develop on the desktop and then migrate to the web. So yes, I was setting isAir to true.

    However, the actionscript graphAPI contains no visual components (unlike the old facebook API) so it *shouldn’t* matter which of those options is selected.

    Are you certain you are including the FacebookComponents.swc as a library?

    I just tried re-compiling the MultiFriendSelector.as file with mxmlc – it compiled just fine.

    PPerhaps you can put a breakpoint into the program and see what type the child.content.scrollPane shows up as?

  21. Is it possible to post an working example whith Facebook graphAPI?

  22. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    package{
        import com.facebook.graph.FacebookDesktop;
        import com.facebook.graph.*;
        import flash.display.*;
        import flash.text.*;
        import flash.events.*;
        import flash.net.*;
        import flash.geom.*;
        import com.milkisevil.ui.facebook.*;
        import com.milkisevil.events.StatusEventEnhanced;
        //import org.as3commons.collections.*;
        import com.facebook.graph.core.AbstractFacebook;
        import MySimpleButton;
       
        public class CasterTest extends Sprite{
       
        private var btn:MySimpleButton;
        public var out:TextField;
        private var face:Loader;
        private var multiFriendSelector:MultiFriendSelector;
        private var mfsOnStage:Boolean = false;
        private var loggedIn:Boolean = false;

        public function CasterTest():void{
          out = new TextField();
          out.text='\n';
          addChild(out);
          btn = new MySimpleButton("Login");
          btn.x=200;btn.y=100;
          addChild(btn);
          out.multiline=true;
          out.width=200;out.height=400;
          out.x=200;
          FacebookDesktop.init("ENTER YOUR FB APP ID HERE",onInit); //initialize
          FacebookDesktop.manageSession=true;
          //addEventListener(Event.ADDED_TO_STAGE, initFB);
        }
       

        private function onFBLogin(result:Object, fail:Object):void {
                    if (result) {
                    trace("onFBLogin succeeded\n");
                    this.loggedIn=true;
                    trace("Result = "+result.toString());
                    btn.label="Logout"
                    btn.addEventListener(MouseEvent.CLICK,logout);
                    createDialer();
                } else {
                        this.loggedIn=false;
                    trace("onFBLogin: Login Failed\n");
                    btn.addEventListener(MouseEvent.CLICK,login); //addd the listener back to the button
                    trace(fail);               
                }
            }
       
        public function onInit(result:Object, fail:Object):void{
                    if (result) { //already logged in because of existing session
                    trace("onInit, Logged In\n");
                    this.loggedIn=true;
                    btn.label = "Logout"
                    btn.addEventListener(MouseEvent.CLICK,logout);
                    createDialer();
                } else {
                    trace("onInit, Not Logged In - waiting for user to click login button\n");
                    btn.addEventListener(MouseEvent.CLICK,login);
                    //FacebookDesktop.login(onFBLogin);
                }
            }
       
        private function login(evt:MouseEvent) :void{
            FacebookDesktop.login(onFBLogin);
            btn.removeEventListener(MouseEvent.CLICK,login);
        }
           
        private function logout(evt:MouseEvent) :void
        {
            FacebookDesktop.logout(); //add in your canvasl url to properly logout
            btn.label="Login \n Again"
            btn.removeEventListener(MouseEvent.CLICK,logout);
            btn.addEventListener(MouseEvent.CLICK,login);
        }    
       
        private function createDialer():void{
            multiFriendSelector = new MultiFriendSelector( 10 );
            showFriendSelector();
        }
       
        protected function getFriends(success:Object, fail:Object):void{
               
         
          if (success){
            var friendsIds:Array = [];
            var friends:Array = success as Array;
            var flist:String;
            var l:uint=friends.length;
            flist = 'friends list has'+l+' elements :\n';  
                var friend:Object;
                for (var i:uint=0;i&lt;l;i++) {
                    friend = friends[i];
                    flist+=friend.name+&quot;\n&quot;;
                }
           
            //out.appendText(flist);

          }
          if (fail){
            out.appendText(&quot;failed getting friends \n&quot;);
          }
       
        }
       
        private function getPic():void{
               face = new Loader();
               var req:URLRequest = new URLRequest(FacebookDesktop.getImageUrl(FacebookDesktop.getSession().user.id,&quot;small&quot;));

       
            face.load(req);
            face.contentLoaderInfo.addEventListener(Event.COMPLETE, picLoaded)
           //this.label.appendText(&quot;could not fetch user picture url&quot;);
           //addChild(face);
          //face.width=50;face.height=50;
       
        }  
       
        private function picLoaded(e:Event):void{
          var pic:Bitmap = Bitmap(face.content);
          pic.smoothing=false;
          pic.width=30;pic.height=30;
          addChild(pic);

        }
       
        private function showFriendSelector():void
        {
            trace( &quot;showFriendSelectorMethod Called&quot; );
            //multiFriendSelector = new MultiFriendSelector( 10 );
            multiFriendSelector.title = &#039;Friend Dialer&#039;;
            multiFriendSelector.subtitle = &#039;Choose up to 10 friends to Video Call&#039;;
            multiFriendSelector.addEventListener( MultiFriendSelector.STATUS_EVENT, multiFriendSelectorStatus );
            multiFriendSelector.width=500; multiFriendSelector.height=300;
            multiFriendSelector.addEventListener(Event.ADDED_TO_STAGE,logStageAdditions);
            if (!mfsOnStage){
            addChild( multiFriendSelector );
            }
            multiFriendSelector.getFriends();  
         
        }
        private function logStageAdditions(e:Event):void{
         this.mfsOnStage=true;
         trace(&quot;A multifriend selector was added to the stage&quot;);
        }
        private function hideFriendSelector(targ:Object):void
        {
            targ = targ as MultiFriendSelector;
            var parent:DisplayObjectContainer = targ.parent as DisplayObjectContainer;
            targ.parent.removeChild(targ);
            trace(&quot;removed the multifriend selectors from it&#039;s parent&quot;);
            multiFriendSelector = null;
        }

        private function multiFriendSelectorStatus(event:StatusEventEnhanced):void
        {
            trace(&#039;exec multiFriendSelectorStatus: &#039; + event.code);

            switch(event.code)
            {
            case MultiFriendSelector.CLOSE:
                hideFriendSelector(event.target);
                break;
               
            case MultiFriendSelector.SUBMIT:
                var selectedUsers:Array = multiFriendSelector.getSelectedFriends();
                printSelection(selectedUsers);
                hideFriendSelector(event.target);
                //FacebookDesktop.logout();
               /* var uidList:Array = [];
               
                for(var i:int = 0; i&lt;selectedUsers.length; i++)
                {
                    var facebookUser:FacebookUser = selectedUsers.getItemAt(i) as FacebookUser;
                    uidList.push( facebookUser.uid );
                }*/

               
                     
                break;
            }
        }

            //TODO: Remove this method once the MultiFriendSelector is stable and tested.
        //traces out the names of the selected firends -- FOR TESTING PURPOSES ONLY
        private function printSelection(users:Array):void{
       
          var u:Object;
          var names:String = &quot;Selection was: \n&quot;;
          for(var i:int=0;i&lt;users.length;i++){
            u = users[i];
            names+=u.name+&#039; id: &#039;+u.uid+&#039; pic: &#039;+u.pic_square;
            names+=&#039;\n&#039;;
           }
           trace(names);
        }
       
        }//class
       
        }//package
  23. Hello again Letapjar,

    yes i am including FacebookComponents.swc as a library.

    I used DeMonsterDebugger to trace out chlid.content.scrollPane and it told me that child.content.scrollPane is not of type ScrollPane, but an object of type MovieClip with the name “scrollPane”. this must be the problem.

    somewhere child.content is added an movieclip that has the name property set to scrollPane, but not cast as that. for example:

    var _scrollPane:MovieClip = new MovieClip();
    _scrollPane.name = “scrollPane”;

    addChild(_scrollPane);

    i might be wrong about how that was added but perhaps a search in MultiFriendSelectorAsset for “scrollPane” may shed some light on why this is not the appropriate type?

    is there any way i could help in the process? I’m more than happy to assist in the fixing of this issue.

  24. Well,

    It’s not my library to start – so I have no access to the facebook components.swc. All I can say is: 1) the child.content.scrollPane IS typed as a ScrollPane in it’s variable declaration – and 2) the component works – for me anyway – so I dunno where your error is coming from. I realize that doesn’t help much – but prhaps there is some error elsewhere? Have you tried writing a separate unit test? The code I posted erailer in this thread should work out of the box to generate a working multifriend selector.

  25. Great work Philip and Letapjar, love your work.
    Took a little fooling around:
    I found that as3commons.reflect classes had a missing dependency of as3commons.lang classes.
    Also a little bit working out how to translate the text(especially the submit and skip buttons – i have no idea where this MultiFriendSelectorAsset is coming from, but I managed to change the text of the buttons with a little digging aroune).
    But all in all, appreciate your work, thanks guys.

  26. Hmm spoke to soon, was working fine in AIR app, but when i ported it to a web-based app, the pictures aren’t appearing.
    After digging around, it seems that you need to now add a permission file, such as:
    Security.loadPolicyFile(“https://graph.facebook.com/crossdomain.xml”);

Leave a Reply