You are here

MSDN Blogs

Subscribe to MSDN Blogs feed
Get the latest information, insights, announcements, and news from Microsoft experts and developers in the MSDN blogs.
Updated: 1 hour 42 min ago

Create animations using XAML

Thu, 05/12/2016 - 09:39

Ivor Berry, Content Developer

Here’s a quick intro on creating simple animations in XAML, based off of the Hello World” app for C#/XAML, however this can be applied to any XAML app.

This example animates the opacity of two different objects in our app, but this method of animation can be used on any number of transforms. The nice thing is that you can nest all the DoubleAnimation definitions you want under a single Storyboard, and they will all happen when that Storyboard is invoked. Specifically, DoubleAnimation animates the specified property of the specified target. What properties can be animated depends on the target type, so check out the API definition for that target if you have any questions.

For reference, in this snippet I’ve hooked up the trigger of clicking the button to invoking the animation. If you look at the sample linked above, you’ll see the inputButton declared:


<Button x:Name="inputButton" Content="Say "Hello"" Click="Button_Click"/>

Notice the event handler at the end: Click=”Button_Click”. This is how we tell our app to go run that function in the code-behind file. When using this snippet, my function Button_Click looks like this:


private void Button_Click(object sender, RoutedEventArgs e)
{
 greetingOutput.Text = "Hello, " + nameInput.Text + "!";
 ButtonClicked.Begin();
}

The only thing I added was the ButtonClicked.Begin(). Now if you take a look at the animation snippet, which is the focus of this post, you’ll see I named the storyboard ButtonClicked. So, to invoke this animation, all I need to do is tell it to begin.


<StackPanel x:Name="contentPanel">
 <StackPanel.Resources>
  <Storyboard x:Name="ButtonClicked">
   <DoubleAnimation Storyboard.TargetName="greetingOutput"
    Storyboard.TargetProperty="Opacity"
    From="1"
    To="0"
    Duration="0:0:5"/>
   <DoubleAnimation Storyboard.TargetName="inputButton"
    Storyboard.TargetProperty="Opacity"
    To=".5"
    BeginTime="0:0:5"
    Duration="0:0:2"/>
   </Storyboard>
 </StackPanel.Resources>
...
<StackPanel>

When started in the code-behind, the greetingOutput textblock fades out, followed by the inputButton (the one labeled ‘Say “Hello”‘) dimming.

Now you have animations! Feel free to play with the timing, or animate different properties as you like.

Helpful links

Project and Project Server May 2016 Public Updates

Thu, 05/12/2016 - 09:39

I posted the release blog post yesterday for the May Public updates covering Project 2010, Project 2013 and Project 2013 as well as Project Server 2010, Project Server 2013 and Project Server 2016.  See Project Server 2010, 2013 and 2016 May 2016 PU Announcement for full details.  I updated the list of fixes show on that blog too (upper right on the home page) – and added the first PU for Project Server 2016 (there was actually a release in April – but nothing for Project – just SharePoint).

My pick of the fixes:

  • Project Server 2016 – we sorted out the ‘quota’ issue that could make your site go read-only.
  • Project Server 2013 – we fixed a timesheet OData problem where the wrong TASK_UID could be surfaced
  • Project 2016 – sorted out some task’s % Complete issues where they would get stuck at 99%
  • Project 2013 – improvement to task start dates when the first assignment is updated

UX trends 2016 : Les cards

Thu, 05/12/2016 - 08:56

On en a beaucoup parlé lorsque Google a popularisé ce pattern de design dans sa charte Material Design. Aujourd’hui, la disposition en cartes devient une évidence et commence à être adopté en masse à travers tout le medium applicatif et web comme un élément élégant, universel et surtout très pratique pour concevoir ses interfaces. A la fois mobile dans la compacité et Web par l’approche orienté contenu, certains UI designers ne jurent plus que par ce nouveau gimmick.

Voyons en ce cas par l’exemple comment fonctionne une card…

Google et les cards

Une définition

La définition originale faite par Google est déjà assez claire: Il s’agit d’une planche matérialisée servant de point d’entrée à un contenu plus détaillé. Globalement, c’est donc un élément de navigation, pointant vers un contenu en Drill Down. A cela s’ajoute souvent un caractère informatif (ce qui va être afficher ensuite) qui le rapproche d’un widget ou d’une vignette, et un caractère visuel fort qui en fait un élément de composition spécifique.

Bien sûr, les cartes sont également une réminiscence (un peu skeuomorphique) d’un item connu et aimé du réel, puisque nous avons un rapport affectif évident avec les cartes du monde normal: Cartes à jouer, cartes de visite, cartes à collectionner, cartes de crédit, etc…

Mood carding

 

Quand mettre des cards

Les cards sont à mon sens une résurgence de l’éditorialisation du print, à savoir une mise en colonnes ordonnée et hiérarchisée d’un sommaire de contenus. C’est donc très utile pour définir le poids des éléments sous-jacents, en terme d’importance, de mise en avant, de qualité de contenu… Cela permet également de profiter techniquement des colonage natifs utilisés en web via des frameworks tels que Bootstrap. Que du bénef donc, à condition de respecter les tenants et aboutissants : Avoir suffisamment de contenu, disposer d’une grille régulière, et surtout avoir de quoi alimenter les cartes avec du visuel.

WIRED tire son layout de sa parution papier

Les cartes sont également très exploitées dans le cadre d’informations ponctuelles, comme dans les notifications, où elles jouent un rôle d’identifiant visuel de l’application qui les gère, de raccourci vers un contenu particulier, ainsi que d’un extrait de ce contenu.

Le revers des cartes

Parmi les défauts régulièrement mis en avant dans l’utilisation des cartes, on trouve essentiellement de mauvais usages, comme de disposer des cartes uniformément, contenant des placeholders vides ou n’ayant pas de sens.

Un autre souci vient du côté répétitif et lassant de ce genre de disposition. Les cartes utilisées à mauvais escient, plus comme un pattern de développement qu’un usage, retombent souvent dans l’ornière de la redondance visuelle.

Exemple 1 : Twitter

Bien sûr, dans les cas de mono-colonne et de disposition en flux, pour exprimer l’empilement et la spontanéité du contenu, les cartes sont les bienvenues. Souples à manipuler, elles ne sont pas forcément utiles ici pour aller naviguer vers plus d’interactions, bien que cela soit une des options, mais pour bien séparer les différentes contributions. Les actions les plus fréquentes, comme répondre, retweetter, etc…sont placées sur la carte, tandis que les options complémentaires sont disposées sur la popup de contenu. D’ailleurs, le fait que le contenu primordial s’affiche en popup (donc par-dessus) suggère que le contenu initial est bien dans le flux général.

Carte twitter

Exemple 2: Pinterest

Bien entendu, des sites et applications comme Pinterest ont largement contribué à l’essor des cards, grâce au caractère dynamique de l’ensemble (tuiles irrégulières, composition asymétrique, etc…) et au fort aspect visuel. Parfois un peu trop fort d’ailleurs, tant le visuel prend le pas sur le textuel et noie un peu l’information. Cependant, le fait de disposer de plusieurs gabarits différents de cartes rend l’ensemble imprévisible et surprenant, donc inépuisable puisque utilisé en conjonction d’un scroll infini.

Les cards ont ici une volonté interactive minimaliste, puisque le sous-niveau est bien plus loquace. Ce dernier propose à son tour un tableau de cartes. On remarquera sur cette popup une navigation multi-axes probante.

Les cartes de Pinterest

Exemple 3: Dribble

Dribble, le site des créatifs, utilise des cartes dans le contexte de la galerie. Beaucoup de contenu, un raccourci vers le travail de l’artiste (avec une grande section de commentaires), et un roll-over affichant le titre de l’oeuvre. Efficace et précis pour mettre le visuel en avant. Le modèle permet aussi les images animées qui viennent apporter de la plus value visuelle, mais complique un peu le roll-over principal. Notez que certaines infos, en l’occurrence le nom et l’identifiant de l’auteur, pourtant attachées à la vignette, sont disposées à l’extérieur, dans les gouttières. Ce qui explique l’épaisseur anormale de cet espace, permettant toutefois de reposer l’œil. Notez également que les contenus dans la carte et hors la carte sont alignés sur la même grille, suggérant l’appartenance commune.

Les cartes par Dribble

Exemple 4: The Gardian (application Windows)

Encore une fois, lorsque l’on dispose à la fois de beaucoup de contenus et de bons supports visuels, les cartes deviennent une disposition naturelle. C’est le cas pour la presse, naturellement, comme dans cet exemple du Gardian et de son application Windows 10. On notera la facilité avec laquelle s’intègre la publicité dans un layout en cartes. Les informations redondantes sont traitées en gris light, une manière d’atténuer le côté répétitif de la disposition.

Application The Gardian

Exemple 5: Les notifications

Annoncé lors de la Build Conference 2016, l’Anniversary Update de Windows laisse entrevoir un système de notifications enrichies, des espaces dédiés et des widgets utilisant bien entendu, un modèle étendu de cartes. Celles-ci devraient permettre un accès encore plus direct aux fonctions, un punaisage actif et du contenu temps réel enrichi. Nous en reparlerons donc dans très peu de temps.

Notifications Windows 10

Les liens :

Le Material Design de Google

Le site Dribble

Le site Pinterest

Le site Twitter

L’application The Gardian

WIndows 10 Anniversary Update

Les guidelines applicatives de Microsoft

 

On a Cloud: With Pressboard Media

Thu, 05/12/2016 - 08:04

I have had the opportunity over the past several months helping Pressboard Media. Their team has been very welcome to my ideas and contributions I have been able to make. I was lucky to book them in to present at the Vancouver Azure meetup, which I have been running for the past 6 years.

Data and algorithms are turning marketing into a science.

Pressboard is a story marketplace that uses data to connect brands with big publishers and influential bloggers around content. In this session Tiam Korki will outline some of the challenges he faced working with data at Pressboard to enable their rapid growth processing a few GB to a few TB of data and how Azure and its stable of data tools enabled his startup to overcome these challenges and grow to $1M+ in revenue in their second year.

Tiam Korki is an architect and product manager with 20 years of experience designing and building large scale enterprise applications using Microsoft technologies. He’s a serial entrepreneur on his third AdTech startup, dealing with some of the most challenging data problems that are facing organizations of all sizes, deriving insights from customer interactions to the fledgling IoT landscape.

Here is the original event.

Visual Studio Codeで採用している「Electron」バージョン1.0リリース

Thu, 05/12/2016 - 07:24

皆さん、こんにちは。テクニカルエバンジェリスト戸倉彩です。

今回は、「Visual Studio Code」のソフトウェア自体で採用されているテクノロジーの話を少ししたいと思います。

■Electron (読み方:エレクトロン)とは
Electronとは、HTML, CSS, JavaScriptのWebテクノロジーを利用して、WEBアプリケーションをWindows, OS X, Linuxに対応したデスクトップアプリケーションを作れるフレームワーク。オープンソースとして公開されています。
※以前は、Atom-Shell(読み方:アトムシェル)と呼ばれていました。

▼Electron公式サイト(http://electron.atom.io/)

▼GitHub上に公開されているElectron (https://github.com/electron/electron)

Visual Studio Codeは、アーキテクチャ的にはWEBテクノロジー、ネイティブ対応、言語固有の組み合わせを意識して開発されています。これまで、マイクロソフトでは、開発コード「Monaco」によってVisual Studio Online Monacoや、Internet ExploreのF12ツールなど、HTMLベースのエディタ機能をクラウド上で展開してきました。後に、このElectronを使うことによって、クロスプラットフォームに対応したVisual Studio Codeというアプリが誕生しました。

例えば、OS X版のVisual Studio Codeのパッケージ内を見ると、「Frameworks」フォルダ内にElectronのフレームワーク関連のファイルが格納されており、Electronが採用されていることを確認することができます。

5月11日、GitHub社はクロスプラットフォームのデスクトップアプリ開発フレームワーク「Electron」の最新バージョン1.0をリリースしました。Webアプリケーションを、デスクトップアプリケーションで作ろうとすると開発言語やライブラリが異なったり、各OSに対応させるのは非常に大変です。Electronはそんな課題を解決してくれるフレームワークです。個人でも使えるので、気になる方は試してみてください。


(引用元:http://electron.atom.io/blog/2016/05/11/electron-1-0)

★5/25(水)に開催予定のde:code 2016イベントのDevelopmentトラックのVisual Studio Code』セッションの中でも、GitHub池田尚史氏 @ikeike443 と一緒に「Electron」の話も少し触れたいと思います。お楽しみ。

Have a nice Code

Xbox one에서 윈도우 10 UWP 앱 개발

Thu, 05/12/2016 - 00:34

지난 Build 2016에서 전해진 소식중에 게임 개발자들이 관심을 가진 내용이 바로 Xbox에 관한 내용이 었습니다. Xbox를 Dev Mode로 전환하고 Visual Studio로 개발한 앱이 원격으로 실행되는 데모를 보고 내 게임을 Xbox 용으로 출시할 수 있겠다는 기대였습니다.

Xbox one을 구해서 직접 UWP 로 만든 게임을 Visual Studio 통해서 Xbox에서 돌려봤습니다. 직접 해본 결과 아래와 같은 주의사항과 오해가 생길 수 있는 부분을 집고 넘어가는게 좋을 것 같습니다.

주의사항
  1. UWP on Xbox 는 아직 프리뷰 상태입니다. Windows 10 Anniversary Update가 정식으로 나와야 안정화 되서 뭔가 만들어 볼 만 할 것 같습니다.
  2. UWP 앱으로 만들면 게임 앱이라도 Xbox에서 앱 메뉴에서 검색되고 설치됩니다. 게임 메뉴에서 다른 대작 게임들과 어깨를 나란히 하고 싶으면 ID@XBOX 프로그램으로 시작해야 합니다. UWP 앱과 ID@BOX 게임은 모든면에서 전혀 다릅니다.
  3. UWP 앱으로 게임을 만들면 PC에서보다 오히려 느릴 수 있습니다. UWP 앱은 Xbox의 성능을 충분히 끌어다 쓰지 못하는 구조 입니다. 따라서 3D 게임과 같이 충분한 하드웨어 성능을 요구하는 게임은 ID@BOX 프로그램으로 게임을 출시해야 합니다. 이 부분은 계속 개선이 되겠지만 현재는 그렇습니다. 참조 문서 System resources for UWP apps and games on Xbox One
환경설정
  1. 윈도우 개발자 센터 가입
  2. 윈도우 참가자 프로그램 (Windows Insider Program) 가입
  3. Windows 10이 설치된 개발 PC와 Xbox one
  4. 개발 PC와 Xbox one을 유선 네트워크로 묶어준다. 용량 큰 패키지가 왔다갔다 하기 때문에 무선은 느려서 못 쓸 지경
  5. Visual Studio 2015 Update 2. 설치할 때 Universal Windows App Development Tools 을 반드시 설치 할 것
  6. Windows 10 SDK Preview Build 14295를 윈도우 참가자 프로그램 (Windows Insider Program)에서 다운로드 및 설치. 이 글을 쓰는 현재 시점에는 14295 빌드를 구할수 없고 14332 버전이 있습니다.

Xbox one을 Dev Mode 로 전환하기

Xbox One Developer Mode activation 문서(영문이지만 스크린 캡쳐 위주)를 따라하면 어렵지 않게 Dev Mode로 전환할 수 있습니다. Dev Mode와 Retail Mode가 서로 전환이 가능한데 Dev Mode에서는 기존 Xbox 게임을 할 수 없습니다. 반대로 Dev Mode를 Retail Mode로 전환하면 Dev Mode에 설치되었던 앱과 데이터가 지워집니다.

Dev Mode Activation 이라는 앱을 설치해서 모드를 왔다갔다 하는 형식입니다. Dev Mode Activation 이라는 앱을 설치하고 전환하면 약 3.5G 정도의 업데이트가 약 20분 정도 진행됩니다. 중간에 Dev Center에 Activation Code를 입력하기도 합니다.

Dev Mode는 이렇게 생겼습니다.

OS Version : March 2016 (10.0.14290.1014) Devi Kit type : Universal Windows App Devkit 앱 테스트

개발 PC에서 앱을 만들어서 테스트 해봅니다. 간단하게는 빈 UWP(Universal Windows Platform)앱을 만들어서 해봐도 됩니다. (Create your first application 참조) 저는 게임이 진짜 잘 안돌아 가는지 알고 싶어서 Unity로 만든 3D 게임을 UWP 앱으로 포팅해서 테스트 했습니다. 게임은 Unity 자습서에 있는 샘플 게임인 Survival Shooter tutorial  을 Windows 10 빌드를 만들어서 사용했습니다. 이 과정은 동영상을 통해서 확인 하실 수 있습니다.

Visual Studio 2015에서 Xbox로 앱을 배포하고 디버깅을 하는 방법은 Remote Machine으로 연결하여 Remote Debugging 으로 테스트 합니다. 그 방법은 아래와 같습니다.

  1. Visual Studio 상단 빌드를 x64 / Remote Machine 으로 설정합니다.
  2. 프로젝트에서 오른쪽 클릭해서 Properties를 선택하고 Debug 탭으로 이동합니다.
  3. Start Options > Remote Machine 의 Find 버튼을 눌러서 Xbox one 의 IP를 입력합니다. Xbox one의 Dev Home 에서 IP를 확인 할 수 있습니다.
  4. Remote Machine 버튼을 누르거나 F5를 눌러 앱을 실행합니다. 빌드가 성공하면 만들어진 패키지를 Xbox로 전송하고 Xbox에 설치 합니다. 그리고 Visual Studio가 디버그 모드로 변경되면서 Xbox에 설치된 앱이 실행됩니다. 이 상태에서는 브레이크 포인트를 잡는 등 Visual Studio 2015의 디버깅 기능을 충분히 활용 가능합니다.

알려진 문제

아직 프리뷰입니다. 프리뷰 버전으로 뭔가 해볼때 정신건강에 이상을 초래할 수 있습니다. 따라서 알려진 문제를 꼼꼼히 보고 대처하고 안되면 빨리 포기하는게 프리뷰를 대하는 자세라고 생각합니다. 제가 격은 몇가지 문제도 함께 공유 합니다.

Dev Home이 실행되다가 죽는다.

조금 당황했었습니다. Dev Home에 들어가야 다시 Retail Mode로 돌아갈 수 있는데 Dev Home이 안들어가지니 이 Xbox는 생명이 다한게 아닌가 했습니다. 리부팅으로도 해결이 안되면 My Apps and Games 메뉴에 들어가서 설치된 개발중인 앱을 Uninstall 합니다. 앱이 배포되다가 잘 못되면 이런현상이 일어납니다.

Visual Studio에서 F5 실행했는데 Xbox one 에서 0x8xxxxxxx 라는 메시지의 오류가 발생

Xbox를 리부팅 합니다.

Visual Studio에서 아래와 같은 에러가 나면 DEP0700 : Registration of the app failed. Deployment Register operation with target volume C: on Package 201404-MiniShooter_1.0.0.0_x64__3k3d86b2gzjkr from: (AppxManifest.xml) failed with error 0x80070002. See http://go.microsoft.com/fwlink/?LinkId=235160 for help diagnosing app deployment issues. (0x80073cf9)

Visual Studio 에서 Package.appxmanifest 를 열면 Packaging 탭에 Choose Certificate… 이라는 버튼이 있습니다. 그 버튼을 눌러 새로운 인증서를 만듭니다. 원인도 잘 모르지만 우선 꼼수로 해결이 됩니다.

결론

Xbox one이 설치되는 위치는 대걔 거실 큰 TV 옆이고 사용자들도 소파에 기대어 컨트롤러를 들고 사용하게 됩니다. 이런 상황에 맞는 다른 UI와 UX가 필요할 것 같습니다. Xbox one의 시스템 성능을 최대한 사용하려는 게임은 ID@XBOX 프로그램으로 게임을 출시하길 바랍니다.

참고자료
  1. UWP on Xbox 페이지
  2. XBOX ONE에서 Windows 10 UWP앱을 돌려보자. by megayuchi

Skype for Business App SDK 紹介

Wed, 05/11/2016 - 23:56

Skype for Business API 開発

こんにちは。

今回は、最近ダウンロード開始された Skype for Business App SDK について、実際の code を使って紹介します。

補足 : ここでは Objective-C を使用しますが、Swift でも書けます。ただし、後述の Helper クラスは Objective-C しかないので、Swift で Helper と同様の処理を頑張って記述する必要があります。

Skype for Business App SDK で可能なこと

以前紹介した Skype Web SDK は Skype for Business REST API (UCWA, User API) を使った JavaScript API (Web 用の API) でしたが、ここで紹介する Skype for Business App SDK は iOS, Android 用の SDK です。

ただし、利用シナリオが限定されているので注意してください。
Skype Web SDK では Skype for Business の一般シナリオである B2B での利用を想定していましたが、この Skype for Business App SDK は B2C での利用を想定しています
Skype for Business を持っていない一般ユーザーが、Skype for Business を持っている裏側のアドバイザー、医者、ヘルプデスクなど (スタッフ) と接続して連携するような使い方で、以前、似たシナリオで Skype URI や Skype Button を使った開発手法を紹介しましたが (「Skype URI で Skype for Business に Call する」を参照)、今回のシナリオでは Skype さえも不要で、iOS や Android で動く一般のアプリに埋め込まれた形で使用できます。(利用者は、それ以外の追加のアプリなどは不要です。)

B2C シナリオの設計(Design)

本投稿では開発に踏み切る方のハードルを上げないよう、以降では極力簡単なサンプルで紹介しますが、実は、この Skype for Business App SDK を使用するには、そこそこちゃんとした設計 (構成) が必要です。

というのは、上述の通り B2C シナリオのため利用者 (User) は匿名で利用しますが、この匿名アクセスを実現するために「Skype Meeting を API (REST, Web) で処理する (Skype for Business)」で紹介した Online Meeting を組み合わせるためです。(組み合わせ方がいろいろ考えられます。)
Skype for Business の Online Meeting では、Skype for Business のライセンスのない匿名ユーザーも会議に参加できますが (会議の設定により、匿名アクセスを拒否したり、ロビーで承認したユーザーのみアクセスさせることなども可能)、Skype for Business App SDK は、この仕組みを使って Skype for Business を持っていない一般ユーザーと Skype for Business を持っているバックエンドのスタッフをつなぎます。(Skype for Business App SDK に Meeting Url を渡すことで、この接続をおこなってくれます。)

From “MSDN – Embed Skype business-to-consumer communications in your mobile app” (Richard Taylor, Microsoft)

さまざまな方法 (シナリオ) が考えられますが、一例を紹介すると、例えば下記のような処理をおこなう一連のアプリ (フロントエンド アプリ、バックエンド アプリ) を提供します。

  1. 顧客からの質問に応答するバックエンド スタッフ (Skype for Business のライセンスを持ったユーザー) は、カスタム アプリ (ISV アプリ) を使って、内部で、「Skype Meeting を API (REST, Web) で処理する (Skype for Business)」で解説した方法で Scheduled Meeting を作成しておきます。(顧客からの問い合わせを待機した状態)
  2. 顧客がモバイル アプリ (スマホ アプリ) を使って問い合わせをおこなうと、アプリのサーバー側では、利用可能なバックエンド スタッフの Scheduled Meeting (上述) を検索して、そのうちの 1 つの Meeting Url をアプリ (スマホ アプリ) に返します。
  3. スマホ アプリ (iOS, Android) は、Skype for Business App SDK を使って、このバックエンド スタッフ (Skype for Business User) と顧客 (Anonymous の Guest User) を接続します。

こうした方法以外に、「Skype Meeting を API (REST, Web) で処理する (Skype for Business)」で解説した Ad-hoc Meeting を使うパターンも考えられます。
認証フローに注意しながら、アプリのニーズに応じて、さまざまな方法で設計できます。

補足 : なお現在、Skype for Business Online (REST) は「Azure AD : Backend Server-Side アプリの開発 (Deamon, Service など)」で解説した方法をサポートしていません。もし将来、この方式がサポートされれば、さらに接続シナリオは増えていくことでしょう。

アプリの構築 – Meeting Url の取得

では、実際に簡単なサンプルを構築してみましょう。
上述の通り、本来なら、サーバー側 (バックエンド) のプログラミングなども必要ですが、サンプルを煩雑にしない目的で、今回は上述の 3 の処理のみをプログラミングします。(1 と 2 は「Skype Meeting を API (REST, Web) で処理する (Skype for Business)」で解説した API で実装されているものと仮定します。)

そこで、このあとのプログラミングのため、あらかじめ Meeting Url を手動で作成して、この Meeting Url を使って、Skype for Business App SDK による Video 接続の iOS アプリ (サンプル) を作成します。
使用する Meeting Url は「Skype Meeting を API (REST, Web) で処理する (Skype for Business)」で解説した Join Url で、「https://meet.lync.com/o365directory/tsmatsuz/3O719UCX」といった形式です。
PC や Mac をお使いの方は、Office 365 の試用版などで Outlook (Web 版の Outlook Web App も可) にログインして Skype 会議 (Skype Meeting) を作成することで、下記の赤の囲みの通り、簡単に Meeting Url が取得できます。

なお、作成された Skype Meeting の既定の設定では、Meeting Url さえ分かっていれば匿名 (Guest) ユーザーで参加可能ですが、Skype Meeting の設定 (管理) 画面でこの権限の確認・変更が可能です。

アプリの構築 – XCode プロジェクトの作成・構成

では、上記で取得した Meeting Url を使って、モバイル アプリを作成してみましょう。

XCode でプロジェクトを新規作成します。(今回は [Single View Project] を作成してみましょう。)

Skype for Business App SDK をダウンロードし、上記で作成した XCode プロジェクトの [Embedded Binary] に、SDK の AppSDKiOS/SkypeForBusiness.framework を追加します。

補足 : BUILD と RUN における SDK 利用の注意点
今回紹介する Video のサンプルは、Mac 上の iOS Simulator では動作 (RUN) しません。必ず、iPhone など実機を接続して動作確認してください。
なお、iOS Simulator で上記の AppSDKiOS/SkypeForBusiness.framework を使用した場合、BUILD も通りません。(“missing required architecture x86_x64″ のエラーが発生します。) もし、開発中、iOS Simulator の構成で BUILD をしたい場合は、一時的に AppSDKiOSSimulator/SkypeForBusiness.framework のほうを使用してください。(上述の通り、動作確認 (RUN) では Simulator は使用できないので注意。)

また今回、SDK に含まれるヘルパークラス (SfBConversationHelper など) を使用するため、Skype for Business App SDK の Helpers/SfBConversationHelper.h、Helpers/SfBConversationHelper.m をプロジェクトのソースに含めておきます。(下図)

さいごに、必要な XCode のフレームワークを追加します。
プロジェクトの [Linked Frameworks and Libraries] を表示して、今回は、AVFoundation.framework と GLKit.framework (このあと GLKitView を使います) を追加します。

アプリの構築 – UI の作成

XCode の Storyboard を使って UI を作成します。
今回のサンプルでは、単に、アプリ起動時に Video 会議に接続して、相手の顔 (Incoming Video) と自分の顔 (Outgoing Video) を表示しますので、ボタン遷移などの Storyboard らしいデザインは不要です。

プロジェクトに含まれる Main.Storyboard で、既定の [View Controller Scene] を表示して、下図の通り、Incoming Video (相手の顔) を表示するための GLKitView (下図の薄い水色の部分) と、その中に Outgoing Video (自分の顔) を表示する UIView (下図の白い部分) を Drag & Drop で挿入します。

このあと、プログラム コード (Objective-C) を使って、この挿入した View や Layout にストリーミングを関連付けることで Video が表示されます。
そこで、あらかじめ、これらの挿入した View を、Control キーを押しながら View のソースコード (ViewController.m) の @interface と @end の間に Drag & Drop して、下記太字の通りサブクラスを作成します。

#import "ViewController.h" #import <GLKit/GLKit.h> @interface ViewController () <SfBConversationHelperDelegate> @property (strong, nonatomic) IBOutlet GLKView *incomingView; @property (strong, nonatomic) IBOutlet UIView *outgoingView; @end @implementation ViewController . . . @end

補足 : 上記ソースコードの通り、GLKit.h のヘッダー参照も追加 (import) しておいてください。

アプリの構築 – プログラミング

以上で準備完了です。
以降は、Skype for Business App SDK を使ってプログラム コードを作成してみましょう。

まず、Skype for Business App SDK を使用するため、前述で挿入したバイナリー (SkypeForBusiness.framework) や挿入したヘルパークラス (SfBConversationHelper) へのヘッダー参照を追加 (import) します。

#import "ViewController.h" #import "SfBConversationHelper.h" #import <GLKit/GLKit.h> #import <SkypeForBusiness/SkypeForBusiness.h> @interface ViewController () <SfBConversationHelperDelegate> . . . @end @implementation ViewController . . . @end

今回は単純に、上述で取得した Meeting Url に匿名 User (Guest User) で接続し、アプリ起動時 (View の表示時) に、上記で挿入した View に Video を表示します。ViewController の表示が完了した段階 (viewDidAppear) でこの処理をおこなうため、下記太字の通りソースコードを追加します。

なお、「https://meet.lync.com/o365directory/tsmatsuz/3O719UCX」は上記であらかじめ取得した Meeting Url で、「Demo Customer」は Meeting に Guest 参加 (Anonymous 参加) した際の参加者の名前です。

#import "ViewController.h" #import "SfBConversationHelper.h" #import <GLKit/GLKit.h> #import <SkypeForBusiness/SkypeForBusiness.h> @interface ViewController () <SfBConversationHelperDelegate> @property (strong, nonatomic) IBOutlet GLKView *incomingView; @property (strong, nonatomic) IBOutlet UIView *outgoingView; @property (strong, nonatomic) SfBConversationHelper *conversationHelper; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)viewDidAppear:(BOOL)animated { [self joinMeeting]; } /** * Joins a meeting */ - (void)joinMeeting { NSError *error = nil; NSString *meetingURLString = @"https://meet.lync.com/o365directory/tsmatsuz/3O719UCX"; NSString *meetingDisplayName = @"Demo Customer"; // Join online meeting SfBApplication *sfb = SfBApplication.sharedApplication; SfBConversation *conversation = [sfb joinMeetingAnonymousWithUri:[NSURL URLWithString:meetingURLString] displayName:meetingDisplayName error:&error]; if (conversation) { // Success : Set observer and show video in UI View [conversation addObserver:self forKeyPath:@"canLeave" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; _conversationHelper = [[SfBConversationHelper alloc] initWithConversation:conversation delegate:self devicesManager:sfb.devicesManager outgoingVideoView:self.outgoingView incomingVideoLayer:(CAEAGLLayer *) self.incomingView.layer userInfo:@"Demo Customer"]; } else { // Fail : Show error details UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Error" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alertController animated:YES completion:nil]; } } @end

今回は Video の処理のため、Video の状態通知の際に呼ばれる、下記の 3 つの Callback Listener も実装します。

#import "ViewController.h" #import "SfBConversationHelper.h" #import <GLKit/GLKit.h> #import <SkypeForBusiness/SkypeForBusiness.h> @interface ViewController () <SfBConversationHelperDelegate> @property (strong, nonatomic) IBOutlet GLKView *incomingView; @property (strong, nonatomic) IBOutlet UIView *outgoingView; @property (strong, nonatomic) SfBConversationHelper *conversationHelper; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)viewDidAppear:(BOOL)animated { [self joinMeeting]; } /** * Joins a meeting */ - (void)joinMeeting { . . . } /** * Callback listner */ - (void)conversationHelper:(SfBConversationHelper *)avHelper didSubscribeToVideo:(SfBParticipantVideo *)video { // Do some task at incoming video } - (void)conversationHelper:(SfBConversationHelper *)avHelper videoService:(SfBVideoService *)videoService didChangeCanStart:(BOOL)canStart { // When video service is ready to start, start the service. if (canStart) { [videoService start:nil]; } } - (void)conversationHelper:(SfBConversationHelper *)avHelper selfAudio:(SfBParticipantAudio *)audio didChangeIsMuted:(BOOL)isMuted { // When the audio status changes, do some task (ex. change mute/unmute button) if (!isMuted) { // Do some task } else { // Do some task } } @end

さいごに、今回、canLeave プロパティの observe もおこなうようにしたので (上記コード参照)、その Handler も追加しておきます。

#import "ViewController.h" #import "SfBConversationHelper.h" #import <GLKit/GLKit.h> #import <SkypeForBusiness/SkypeForBusiness.h> @interface ViewController () <SfBConversationHelperDelegate> @property (strong, nonatomic) IBOutlet GLKView *incomingView; @property (strong, nonatomic) IBOutlet UIView *outgoingView; @property (strong, nonatomic) SfBConversationHelper *conversationHelper; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)viewDidAppear:(BOOL)animated { [self joinMeeting]; } /** * Joins a meeting */ . . . /** * Callback listner */ . . . /** * Monitor canLeave property of a conversation (Handler) */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"canLeave"]) { // Do some task } } @end

今回は実装しませんが、もちろん、Video の Mute / Unmute や、会話の終了などの基本的な操作も可能です。

// Leave from conversation NSError *error = nil; [_conversationHelper.conversation leave:&error]; // Toggle Mute/Unmute audio [_conversationHelper toggleAudioMuted:nil];

以上で完成です。
実行結果は説明不要と思いますが、アプリを起動すると Online Meeting に Anonymous (Guest) で参加されて、下図の通り表示されます。(本田さんにご協力いただきました。。。) なお、上述の通り、Mac 上の Simulator ではなく、実機で動作を確認する必要があるので注意してください。

さいごに、今回作成したソースコード全体を掲載しておきます。今回は Objective-C ですが、Swift で記述した場合、上述の通り、Helper が使用できないため、もっと長くなります。

#import "ViewController.h" #import "SfBConversationHelper.h" #import <GLKit/GLKit.h> #import <SkypeForBusiness/SkypeForBusiness.h> @interface ViewController () <SfBConversationHelperDelegate> @property (strong, nonatomic) IBOutlet GLKView *incomingView; @property (strong, nonatomic) IBOutlet UIView *outgoingView; @property (strong, nonatomic) SfBConversationHelper *conversationHelper; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)viewDidAppear:(BOOL)animated { [self joinMeeting]; } /** * Joins a meeting */ - (void)joinMeeting { NSError *error = nil; NSString *meetingURLString = @"https://meet.lync.com/o365directory/tsmatsuz/3O719UCX"; NSString *meetingDisplayName = @"Demo Customer"; // Join online meeting SfBApplication *sfb = SfBApplication.sharedApplication; SfBConversation *conversation = [sfb joinMeetingAnonymousWithUri:[NSURL URLWithString:meetingURLString] displayName:meetingDisplayName error:&error]; if (conversation) { // Success : Set observer and show video in UI View [conversation addObserver:self forKeyPath:@"canLeave" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; _conversationHelper = [[SfBConversationHelper alloc] initWithConversation:conversation delegate:self devicesManager:sfb.devicesManager outgoingVideoView:self.outgoingView incomingVideoLayer:(CAEAGLLayer *) self.incomingView.layer userInfo:@"Demo Customer"]; } else { // Fail : Show error details UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Error" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alertController animated:YES completion:nil]; } } /** * Callback listner */ - (void)conversationHelper:(SfBConversationHelper *)avHelper didSubscribeToVideo:(SfBParticipantVideo *)video { // Do some task at incoming video //self.incomingView.hidden = NO; } - (void)conversationHelper:(SfBConversationHelper *)avHelper videoService:(SfBVideoService *)videoService didChangeCanStart:(BOOL)canStart { // When video service is ready to start, start the service. if (canStart) { //if (self.outgoingView.hidden) { // self.outgoingView.hidden = NO; //} [videoService start:nil]; } } - (void)conversationHelper:(SfBConversationHelper *)avHelper selfAudio:(SfBParticipantAudio *)audio didChangeIsMuted:(BOOL)isMuted { // When the audio status changes, do some task (ex. change mute/unmute button) if (!isMuted) { // Do some task // [self.muteButton setTitle:@"Unmute" forState:UIControlStateNormal]; } else { // Do some task // [self.muteButton setTitle:@"Mute" forState:UIControlStateNormal]; } } /** * Monitor canLeave property of a conversation (Handler) */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"canLeave"]) { // Do some task //self.endCallButton.enabled = _conversationHelper.conversation.canLeave; } } @end

Pages

Drupal 7 Appliance - Powered by TurnKey Linux