Accessing Native Features with DependencyService


윈도우 10 앱 개발(Windows 10 UWP app)에 박문찬 MVP입니다. 자마린에서는 각 디바이스 별로 기능을 구현해야하는 경우 DependencyService를 이용해야 합니다. 그래서, 자마린 홈 페이지를 참고해서 Text-To-Speech 만들어 보겠습니다.



1. 참고

Accessing Native Features with DependencyService



2. 시작하기


DependencyService를 사용하면 앱이 공유 코드에서 플랫폼 관련 기능을 호출 할 수 있습니다. 이 기능을 통해 Xamarin.Forms 앱은 기본 앱이 할 수있는 모든 것을 할 수 있습니다.


여기서 예제로 사용하는 Text-To-Speech의 경우 기본적으로 자마린에서 해당 기능을 제공하지는 않습니다만, 기본 인터페이스를 정의하고, 그 인터페이스를 각 플랫폼에서 구현한다면, 구현하지 못하는 기능이 없다는 것입니다.



구현하기 위해 필요한 사항은 아래와 같습니다.

* Interface - 필수 항목으로 공유 프로젝트에 구현하려는 기능에 대한 인터페이스를 생성합니다.

* Implementation Per Platform - 인터페이스를 구현한 클래스를 각 플랫폼마다 생성합니다.

* Registration - 각 플랫폼마다 구현된 클래스는 DependencyService를 이용해서 사용할 수 있도록 미리 등록되어야 합니다.

* Call to DependencyService - DependencyService를 이용해서 구현된 클래스를 사용합니다.

3. 신규 프로젝트 생성과 인터페이스 생성


File -> New -> Project -> Blank App


이름은 TTSSample로 지정했습니다. 생성시 UWP의 경우 Windows 10 (10.0; Build 10586)을 선택합니다.



프로젝트 생성 후 Windows, WinPhone 프로젝트는 Remove로 지워버리고, iOS 프로젝트는 unload project 했습니다.




TTSSample (portable) 공용 프로젝트에 Interfaces라는 이름의 폴더와 인터페이스 파일을 추가합니다.



namespace TTSSample.Interfaces
{
    public interface ITextToSpeech
    {
        void Speak(string text); //note that interface members are public by default
    }
}



4. 각 플랫폼 별로 구현하기


각 플랫폼 별로 구현할 때 꼭 기본 생성자가 존재해야 한다고 어디선가 본 것 같습니다.


iOS Implementation - iOS가 없어서 넘어갑니다.



Android Implementation


안드로이드 프로젝트 루트에 TextToSpeechImplementation.cs라는 클래스를 추가하고 아래와 같이 코딩을 합니다.


using System.Collections.Generic;
using Android.Speech.Tts;
using Java.Lang;
using TTSSample.Droid;
using TTSSample.Interfaces;
using Xamarin.Forms;


[assembly: Dependency(typeof(TextToSpeechImplementation))]

namespace TTSSample.Droid
{
    public class TextToSpeechImplementation : Object, ITextToSpeech, TextToSpeech.IOnInitListener
    {
        private readonly TextToSpeech _speaker;
        private string _toSpeak;

        //기본 생성자

        public TextToSpeechImplementation()
        {
            var ctx = Forms.Context; // useful for many Android SDK features
            _speaker = new TextToSpeech(ctx, this);
        }

        #region IOnInitListener implementation

        public void OnInit(OperationResult status)
        {
            if (!status.Equals(OperationResult.Success)) return;
            var p = new Dictionary<string, string>();
            _speaker.Speak(_toSpeak, QueueMode.Flush, p);
        }

        #endregion

        public void Speak(string text)
        {
            _toSpeak = text;
            var p = new Dictionary<string, string>();
            _speaker.Speak(_toSpeak, QueueMode.Flush, p);
        }

    }
}


중요 부분은 굵은 글씨로 표시되어 있습니다.


[assembly: Dependency(typeof(TextToSpeechImplementation))] : DependencyService에 이 클래스를 등록해주는 기능으로 ITextToSpeech라는 인터페이스를 구현해 놓은 것이라는 걸 알려주는 것입니다.


실제 Speak라는 메서드에서 text를 보이스로 변환해서 출력해 줍니다.

다른 설정 해줄 내용은 없습니다.


Universal Windows Platform Implementation

UWP 프로젝트에 TextToSpeechImplementation.cs 클래스를 루트에 추가하고 아래와 같이 입력 합니다.

using System;
using System.Threading.Tasks;
using Windows.Media.SpeechSynthesis;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using TTSSample.Interfaces;
using TTSSample.UWP;
using Xamarin.Forms;

[assembly: Dependency(typeof(TextToSpeechImplementation))]
namespace TTSSample.UWP
{
    public class TextToSpeechImplementation : ITextToSpeech
    {
        /// <summary>
        ///     미디어 엘리먼트
        /// </summary>
        private readonly MediaElement _mediaElement;
        /// <summary>
        ///     신디사이저
        /// </summary>
        private readonly SpeechSynthesizer _synth;
        /// <summary>
        ///     기본 생성자가 반드시 필요
        /// </summary>
        public TextToSpeechImplementation()
        {
            _mediaElement = new MediaElement {AudioCategory = AudioCategory.Speech};
            _synth = new SpeechSynthesizer();
        }
        /// <summary>
        ///     구현되어야할 Speak라는 메서드
        /// </summary>
        /// <param name="text"></param>
        public async void Speak(string text)
        {
            var stream = await _synth.SynthesizeTextToStreamAsync(text);
            if (stream == null) return;
            if (_mediaElement.CurrentState != MediaElementState.Stopped)
                _mediaElement.Stop();
            //음성 출력 종료 후 스트림을 Dispose 시키기 위해서 추가한 내용
            var taskCompleted = new TaskCompletionSource<bool>();
            RoutedEventHandler endOfPlayHandler = (s, e) =>
            {
                stream.Dispose();
                taskCompleted.SetResult(true);
            };
            _mediaElement.MediaEnded += endOfPlayHandler;
            _mediaElement.SetSource(stream, stream.ContentType);
            _mediaElement.Play();
            await taskCompleted.Task;
            _mediaElement.MediaEnded -= endOfPlayHandler;
        }
    }
}


[assembly: Dependency(typeof(TextToSpeechImplementation))] : DependencyService에 이 클래스를 등록해주는 기능으로 ITextToSpeech라는 인터페이스를 구현해 놓은 것이라고 표시를 하며 Speak라는 메서드에서 text를 보이스로 변환해서 출력해 줍니다.



5. 공통 프로젝트 작업


MainPage.xaml

<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TTSSample"
             x:Class="TTSSample.MainPage">

    <StackLayout>
        <Label Text="Welcome to Xamarin Forms!"
               VerticalOptions="Center"
               HorizontalOptions="Center" />

        <Button Text="TextToSpeech" Clicked="Button_OnClicked" />
    </StackLayout>

</ContentPage>


MainPage.xaml.cs

using System;
using TTSSample.Interfaces;
using Xamarin.Forms;

namespace TTSSample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_OnClicked(object sender, EventArgs e)
        {
            DependencyService.Get<ITextToSpeech>().Speak("Hello from Xamarin Forms");
        }
    }
}


사용은 위와 같이 간단하게 사용할 수 있습니다.



6. 결과 확인


UWP 프로젝트를 윈폰 에뮬레이터를 이용해서 출력한 경우 입니다.


주의 사항! 윈폰 에뮬레이터로 실행하기 위해서는 CPU를 x86으로 변경하시고 실행하시기 바랍니다.

또한, 앱에서 사용하는 볼륨을 최고로 올리셔야 또렷하게 들립니다.

UWP 앱을 데스크탑 모드로 실행한 경우 화면 입니다. 목소리 좋고 잘됩니다. -0-




안드로이드 프로젝트를 선택하고 마쉬멜로 에물레이터로 돌린 결과 입니다. 음..목소리가 좀 그렇습니다만. 잘 됩니다.







7. 마무리


직접 만들어보니, 원하는 기능을 구현하는 것이 크게 어려울 것 같지는 않습니다. UWP 쪽은 자료를 찾기가 쉬운데, 안드로이드의 기능을 c#에서 사용하는 자료가 얼만큼 있고, 찾기가 쉬운지가 관건일 것 같습니다. 


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

티스토리 툴바