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/

2016년 12월 14일 오프라인 모임을 통해서 추가된 트러블슈팅 내용입니다.



1. Hyper-V기능의 활성화는 Windows 10 Pro 이상 버전에서만 가능합니다. Home버전에서는 사용할 수 없습니다.


->윈도우 버전을 Pro버전 이상으로 변경하신후 진행하셔야합니다.



2. 자마린 탬플릿으로 프로젝트를 생성하기전에 Windows 10 SDK가 설치되어 있어야지만, Windows 10 UWP 앱 개발 프로젝트 템플릿이 추가됩니다. Windows 8.1 SDK 필요 여부는 명확하게 확인하지 않았습니다.


Windows 10 SDK Download 


다운로드를 할 때 주의할 사항은 Windows 10 버전과 맞는 SDK를 설치해야 한다는 것입니다.


Windows 10 Version 1607 (OS Build 14393.x)인 경우에만 최신 SDK를 설치하셔야 하고, 이전 버전이라고 생각되면 해당버전에 맞는 SDK를 설치하셔야 합니다.

가장 좋은 시나리오는 Windows 10 버전을 최신 버전으로 업그레이드를 하고 난 후에 최신 버전 SDK를 설치하는 것입니다.



3. 소소한 팁

한번 에물레이터를 실행해서 가상 네트워크 설정을 한 후 컨넥션(Wi-Fi 접속 경로)이 변경된 경우에도 다시 네트워크 설정을 새로 추가할지를 물어보는데, 이때는 No를 눌러서 기존 가상 네트워크 사용을 유지하도록 하면 됩니다.



4.

the emulator is unable to connect to the device operating system
couldn't auto-detect the guest system ip address.
some functionality might be disabled

->...



5. Build Error

Severity Code Description Project File Line Suppression State
Error  Could not find android.jar for API Level 23. This means the Android SDK platform for API Level 23 is not installed. Either install it in the Android SDK Manager (Tools > Open Android SDK Manager...), or change your Xamarin.Android project to target an API version that is installed.


해결방법 :

https://forums.xamarin.com/discussion/71633/android-api-level-24-is-not-installed

아래 이미지 처럼 23.0.1~3까지 설치를 진행하고, 최신 업데이트를 진행합니다.

그리고, Clean Solution을 한 후 다시 빌드합니다.




----------------------------------------------------------------------------------------------------------------------------


이번 트러블슈팅의 핵심은 에뮬레이터 실행입니다. 장장 2일에 걸쳐 삽질한 내용입니다.


집 PC와 서피스에 모두 Hyper-V 안드로이드 에뮬레이터로 앱 실행을 완료했습니다. ^^/



1. 안드로이드 에뮬레이터 종류


Visual Studio 2015에서 사용할 수 있는 에뮬레이터는 크게 2가지가 있습니다.


1) Android_Accelerated_x86 (Android 6.0 - API 23) 을 선택해서 실행하면 볼 수 있는 에뮬레이터


Hyper-V가 활성화된 상태에서는 실행이 않됩니다. 덕분에 안드로이드 게임실행용 블루스택이나 MEmu 등도 실행이 않됩니다;;;




2) 5" KitKat (4.4) XXHDPI Phone (Android 4.4 - API 19)를 선택했을 때 볼 수 있는 에뮬레이터


Visual Studio Emulator for Android

https://www.visualstudio.com/vs/msft-android-emulator/

위 사이트에서 다운로드 받을 수 있는 것으로 겉 모양은 윈도우폰 에뮬레이터와 동일하고, Hyper-V 환경에서 실행이 됩니다.




2. 트러블...


기존에 PC에 윈도우폰 에뮬레이터가 설치되어 있었는데, 추가로 안드로이드 에뮬레이터 설치하고 Deploy를 하는 과정에서 더 이상 진행이 되지 않아서..삽질을 시작했습니다.


확인해 봐야하는 곳은 3곳입니다. 3곳을 모두 봐야할 수도 있고 한곳만 수정해도 가능할 수 있습니다.


1) Hyper-V Manager


Virtual Machines에 여러개가 실행되어 있는 경우 다 삭제 하고,



Control Panel -> Program and Features 실행 -> Turn Windows features on or off 선택 -> Hyper-V 선택을 풀고, 컴퓨터 재부팅 후 다시 Hyper-V를 선택 후 재부팅 -> Visual Studio 2015에서 에뮬레이터 실행해서 처음부터 다시 합니다.



Virtual Machines을 추가할 때 아래와 같이 물어보면 Yes를 선택해서 추가를 완료합니다.




2) Android SDK Update


음 생각해보니 업데이트랑 관련이 있는지 확실하지 않네요..하지만..그냥 설치하시는 걸로..ㅎㅎ


Tools -> Android -> Android SDK Manager... 를 선택합니다.


7개 설치 및 업데이트, 그 아래 Google APIs, Google APIs ARM EABI v7a System Image, Google APIs Intel x86 Atom System Image가 설치 되어있는지 확인합니다.




3) regedit를 이용해서 SDK 경로 확인


RegEdit 실행 -> HKEY_LOCAL_MACHINE -> SOFTWARE -> WOW6432Node -> Android SDK Tools -> Path 를 수정합니다.


Path에 입력할 값은


Tools -> Options -> Xamarin -> Android Settings -> Android SDK Location의 값을 입력하면 됩니다.



Options에서 Xamarin이 나오지 않으면 Xamarin for Visual Studio가 설치 되어 있어야 합니다.



Output verbosity를 Diagnostic를 선택하면 Output -> Xamarin Diagnostics를 선택하시면 더 많은 정보가 출력됩니다.




4) Visual Studio 2015를 Administrator로 실행하기를 하는 경우에도 가능하다고 하는군요..



3. 결과~



4. 기본 트러블슈팅


Troubleshooting the Visual Studio Emulator for Android



5. 빌드 오류


Severity Code Description Project File Line Suppression State
Error CS5001 Program does not contain a static 'Main' method suitable for an entry point HelloWorldXamarinForms.UWP C:\Sample\HelloWorldXamarinForms\HelloWorldXamarinForms\HelloWorldXamarinForms.UWP\CSC 1 Active


해결 방법

App.xaml 파일을 선택하고 Build Action을 확인해서 ApplicationDefinition으로 선택해야 합니다.



6. Could not connect to the debugger.


Debug창에 위와 같은 메시지가 출력되면서, 앱이 배포가 된 후 바로 종료되고 끝나는 경우로, 디버그 모드가 아닌 경우에는 정상 실행이 됩니다.


해결 방법..

http://dotnetbyexample.blogspot.kr/2016/02/fix-for-could-not-connect-to-debugger.html

블로그 이미지

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/

티스토리 툴바