はじめに
セカンドライフ技術系 Advent Calendar 2014 - Adventarの2個目のエントリです。 libopenmetaverseでフレンドリストを表示してみる - なんとなくからの続きです。 前回、フレンドリストの作成をしました。今回はそのフレンドリストを使って、プロフィールを表示するということについて見て行きたいと思います。
概要
フレンドリストの対象項目(青で囲んだところ)を押下したら、ダイアログで対象のアバターのプロフィールを表示させるということを説明していきたいと思います。今回はプロフィールの表示です。
プロフィールダイアログの作成
使用するlibopenmetaverseのメンバーの説明とか
libopenmetaverseにおいて、アバターのプロフィールを取得するには、OpenMetaverse.AvatarManager.RequestAvatarProperties(UUID)
メソッドを使用します。
public void RequestAvatarProperties( UUID avatarid )
OpenMetaverse.AvatarManager.AvatarPropertiesReply
イベントでAvatarProperties構造体に格納されたデータを取得できます。
AvatarProperties構造体は
Member | Description |
---|---|
AboutText | |
AllowPublish | Should this profile be published on the web |
BornOn | |
CharterMember | |
FirstLifeImage | First Life image ID |
FirstLifeText | First Life about text |
Flags | Flags of the profile |
FromOSD(OSD) | |
GetOSD() | |
Identified | |
MaturePublish | Is this a mature profile |
Online | Avatar Online Status |
Partner | |
ProfileImage | Profile image ID |
ProfileURL | Web URL for this profile |
Transacted |
と定義されています。
http://lib.openmetaverse.org/docs/trunk/?topic=html/T_OpenMetaverse_Avatar_AvatarProperties.htm
また、グループや興味などのデータも同様に
OpenMetaverse.AvatarManager.AvatarGroupsReply
イベント
OpenMetaverse.AvatarManager.AvatarInterestsReply
イベント
において値を取得することができます。
画像を取得することについては、
LibOpenMetaverseとCSJ2KでSIMのMinimap画像を表示してみる - なんとなく
においても触れているように
OpenMetaverse.AssetManager.RequestImage()
メソッドを使用します。
AvatarProperties構造体のProfileImageを使用して、プロフィール画像を取得できます。
http://lib.openmetaverse.org/docs/trunk/?topic=html/T_OpenMetaverse_TextureDownloadCallback.htm
実装
省きながらではあるけど、実際にどういうふうに実装しているかについて見て行きたいと思います。ソースコードとその説明です。
ソース
OnCreateDialogをそのまま
public override Dialog OnCreateDialog (Bundle savedInstanceState) { var builder = new AlertDialog.Builder (Activity); LayoutInflater layout = (LayoutInflater)Activity.GetSystemService (Context.LayoutInflaterService); View view = layout.Inflate (Resource.Layout.profileDialogContent, null); AvatarsPropertiesDone = new AutoResetEvent (false); //profileデータの取得 Client.Avatars.RequestAvatarProperties (AvatarID); AvatarsPropertiesDone.WaitOne (); //Partnerのデータ取得 if (AvatarProfileContent.PartnerID.Count != 0) { DisplayNamesDone = new AutoResetEvent (false); Client.Avatars.GetDisplayNames (AvatarProfileContent.PartnerID, PartnerDisplayNamesCallback); DisplayNamesDone.WaitOne (); Client.Avatars.RequestAvatarNames (PartnerBadIDs); } //avatarImageの取得 if (AvatarProfileContent.ProfileImageID != UUID.Zero) { BeginDownloadingImage (AvatarProfileContent, view.FindViewById<ImageView> (Resource.Id.imageView1), view.FindViewById<ProgressBar> (Resource.Id.progressBar1), Activity); } else { view.FindViewById<ImageView> (Resource.Id.imageView1).SetImageBitmap (BitmapFactory.DecodeResource (res, Resource.Drawable.Icon)); view.FindViewById<ImageView> (Resource.Id.imageView1).Visibility = ViewStates.Visible; view.FindViewById<ProgressBar> (Resource.Id.progressBar1).Visibility = ViewStates.Gone; } view.FindViewById<TextView> (Resource.Id.textViewBorndate).Text = AvatarProfileContent.BornOn; if (string.IsNullOrEmpty (AvatarProfileContent.PartnerName)) { view.FindViewById<TextView> (Resource.Id.textViewPartner).Text = "None"; } else { view.FindViewById<TextView> (Resource.Id.textViewPartner).Text = AvatarProfileContent.PartnerName; } view.FindViewById<TextView> (Resource.Id.textViewDescription).Text = AvatarProfileContent.AboutText; view.FindViewById<TextView> (Resource.Id.textViewURI).Text = AvatarProfileContent.ProfileURL; builder.SetView (view); if (ShownAvatarData.IsDefaultDisplayName) { builder.SetTitle (ShownAvatarData.AvatarDisplayName); } else { builder.SetTitle (ShownAvatarData.AvatarName); } builder.SetPositiveButton ("閉じる", (sender, args) => { this.Dismiss(); }); return builder.Create(); }
イベントとかコールバックとか
Client.Avatars.AvatarPropertiesReply += (object sender, AvatarPropertiesReplyEventArgs e) => { List<UUID> partnerID = new List<UUID>(); partnerID.Add(e.Properties.Partner); AvatarProfileContent = new AvatarProfile(ShownAvatarData.AvatarID,ShownAvatarData.AvatarName,ShownAvatarData.AvatarDisplayName, e.Properties.AboutText,e.Properties.BornOn,e.Properties.ProfileURL,e.Properties.ProfileImage,partnerID); AvatarsPropertiesDone.Set(); }; Client.Avatars.UUIDNameReply += (object sender, UUIDNameReplyEventArgs e) => { foreach (KeyValuePair<UUID,string> kvp in e.Names) { PartnerIDName = new AvatarData(kvp.Key,kvp.Value,null,false); } }; DisplayNamesCallback += new AvatarManager.DisplayNamesCallback (PartnerDisplayNamesCallback); TextureDownloadCallback += new TextureDownloadCallback(ProfileTextureDownloadCallback);
コールバック実態
private static void PartnerDisplayNamesCallback(bool success,AgentDisplayName[] DisplayNames,UUID[] BadIDs) { foreach(AgentDisplayName displayname in DisplayNames) { PartnerIDName = new AvatarData (displayname.ID, displayname.LegacyFullName, displayname.DisplayName,displayname.IsDefaultDisplayName); } foreach (UUID s in BadIDs) { PartnerBadIDs.Add(s); } DisplayNamesDone.Set (); } private static void ProfileTextureDownloadCallback(TextureRequestState state, AssetTexture assetTexture) { ManagedImage mImage; if (state == TextureRequestState.Finished) { OpenJPEG.DecodeToImage (assetTexture.AssetData, out mImage, out ProfileImage); textureDone.Set(); } }
非同期で画像取得とか
private static async void BeginDownloadingImage (AvatarProfile avatarProfile, ImageView imageView,ProgressBar progressBar,Activity context) { Bitmap imageBitmap = null; //progress context.RunOnUiThread (() =>imageView.SetImageBitmap (BitmapFactory.DecodeResource(res,Resource.Drawable.Icon))); imageBitmap = await DownloadingProfileImageAsync (avatarProfile); context.RunOnUiThread (() =>{ imageView.SetImageBitmap (imageBitmap); //ぐるぐる回っているのをやめて、取得したBitmapを表示させる imageView.Visibility = ViewStates.Visible; progressBar.Visibility = ViewStates.Gone; }); } private static Bitmap DownloadingProfileImage(AvatarProfile avatarProfileContent) { Bitmap imageBitmap = null; AutoResetEvent downloadDone = new AutoResetEvent (false); var task1 = Task.Factory.StartNew (() => { textureDone = new AutoResetEvent (false); Client.Assets.RequestImage (avatarProfileContent.ProfileImageID, ImageType.Baked, ProfileTextureDownloadCallback); textureDone.WaitOne (); imageBitmap = GetCroppedBitmap (Bitmap.CreateScaledBitmap (ProfileImage, 128, 128, false)); downloadDone.Set(); }); downloadDone.WaitOne (); return imageBitmap; } private static Task<Bitmap> DownloadingProfileImageAsync(AvatarProfile avatarProfile) { return Task.Run(() => DownloadingProfileImage(avatarProfile)); }
実装説明
- フレンドリストの項目を押下した際にDialogFragmentを作成し、同時にAvatarのUUIDをDialogFragmentに渡します。
- RequestAvatarProperties メソッドを使用して、AvatarPropertiesを取得します。
- 取得したAvatarProperties.ProfileImageより、非同期でプロフィール画像をRequestImageメソッドを使用して取得します。
- 取得したAvatarPropertiesより、BornOn,AboutText,Partner,URLを表示します。
- 取得できたプロフィール画像を表示します。
フレンドリストからプロフィールダイアログへの値渡しのソースは、Xamarin.Android関連なので省いてしまいました。値渡しについてはParcelableインターフェイスを実装したクラスを使って、DialogFragment作成前にインスタンスに保存して、作成後に取り出しています。
まとめ
libopenmetaverseに関して、結構説明したような気がします。でも、実は重要な部分であるチャットとか3Dについては説明していないです。おそらく、来年のアドベントカレンダーでできるかもしれません。
来年は個人的にはtextviewerをリリースしたいです。でも、同じことを去年の記事でも書いてました。体調ややる気と相談になり個人的要因がとても強いです。
textviewerがリリースできれば、移植しているlibopenmetaverseのAndroid版について本家にmergeを相談したいと思ってます。Xamarin.iOSでもlibopenmetaverseは動いているようですので、現行はデスクトップ向けですが、ポータブルな環境でも使えるライブラリとして利用できるようにしたいなぁという妄想もあります。