Android/App개발2020. 4. 6. 14:45

https://medium.com/google-exoplayer/playback-notifications-with-exoplayer-a2f1a18cf93b

 

PlayerNotificationManager를 통해 미디어재생 시 알림센터에 정보를 표시하고 제어할 수 있다.

1. PlayerNotificationManager인스턴스 생성
2. PlayerNotificationManager.MediaDescriptionAdapter 로 재생중인 미디어 아이템 정보를 제공
3. 플레이어에 연결, 리소스가 소거되었을 때는 연결해제

PlayerNotificationManager 인스턴스 생성
 activity나 fragment의 onCreate에서 생성한다. 

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ...
  playerNotificationManager = new PlayerNotificationManager(
      this,
      new DescriptionAdapter(), 
      CHANNEL_ID,
      NOTIFICATION_ID);
}

채널아이디, 알림아이디, 그리고 재생중인 미디어 정보를 제공하기 위해 MediaDescriptionAdapter를 확장한 DescriptionAdapter를 파라메터로 넘긴다.

private class DescriptionAdapter implements
    PlayerNotificationManager.MediaDescriptionAdapter {
    
  @Override
  public String getCurrentContentTitle(Player player) {
    int window = player.getCurrentWindowIndex();
    return getTitle(window);
  }

  @Nullable
  @Override
  public String getCurrentContentText(Player player) {
    int window = player.getCurrentWindowIndex();
    return getDescription(window);
  }

  @Nullable
  @Override
  public Bitmap getCurrentLargeIcon(Player player,
      PlayerNotificationManager.BitmapCallback callback) {
    int window = player.getCurrentWindowIndex();
    Bitmap largeIcon = getLargeIcon(window);
    if (largeIcon == null && getLargeIconUri(window) != null) {
      // load bitmap async
      loadBitmap(getLargeIconUri(window), callback); 
      return getPlaceholderBitmap();
    }
    return largeIcon;
  }

  @Nullable
  @Override
  public PendingIntent createCurrentContentIntent(Player player) {
    int window = player.getCurrentWindowIndex();
    return createPendingIntent(window);
  }
}

플레이어에 연결은 다음과 같이 한다.

player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
playerNotificationManager.setPlayer(player);

플레이어가 해제되기 전에 먼저 연결을 끊어야 한다.

playerNotificationManager.setPlayer(null);
player.release();
player = null;


커스텀하기

 앱의 테마에 맞도록 알림을 커스텀하는 다양한 방법이 존재한다. 재생컨트롤의 동작여부를 설정하고 알림속성을 설정할 수 있도록 매니져가 기능을 제공한다. 이 속성들은 기본값을 가지고 있고 쉽게 바꿀수 있다.

재생컨트롤 액션들

 기본 컨트롤액션을 제공한다. 재생/일시정지, FF/REW, Next/Previous, Stop등이 있으며 생략도 가능하다

// omit skip previous and next actions
playerNotificationManager.setUseNavigationActions(false);
// omit fast forward action by setting the increment to zero
playerNotificationManager.setFastForwardIncrementMs(0);
// omit rewind action by setting the increment to zero
playerNotificationManager.setRewindIncrementMs(0);
// omit the stop action
playerNotificationManager.setStopAction(null);

커스텀액션도 CustionActionReceiver를 확장하여 구현하여 PlayerNotificationManager 생성자의 5번째 파라메터로 넣어서 구현 가능하다.

알림 속성

 알림매니저는 UI와 알림의 동작의 setter를 제공한다. 이는 NotificationCompat.Builder의 속성에 상응한다.

manager.setOngoing(false);
manager.setColor(Color.BLACK);
manager.setColorized(true);
manager.setUseChronometer(false);
manager.setSmallIcon(R.drawable.exo_notification_small_icon);
manager.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE);
manager.setVisibility(NotificationCompat.VISIBILITY_PRIVATE);

MediaSession

 최종적으로 Google Assistant등을 지원하기 위해 MediaSession API를 사용하고 있다면 media style 알림의 장점을 모두 취하기 위해 session에 token을 셋팅할수 있다.

 playerNotificationManager.setMediaSessionToken(token);




 

Playback Notifications with ExoPlayer

Displaying a notification with playback controls is a best practice for media apps on Android. For audio playback in the background it’s…

medium.com

 

Posted by 삼스

댓글을 달아 주세요

카테고리 없음2020. 2. 19. 13:26

CEF

CEF

Chromium Embedded Framework (CEF). 
chromium 기반의 브라우저를 내포하는 앱을 만들 수 있는 프레임워크이다.

소개

BSD-license이고 구글 크로미엄프로젝트 기반으로 2008년에 마샬그린브랫에의해 시작된 오픈소스 프로젝트이다.
크로미엄프로젝트와 다르게 구글 크롭앱개발에 주로 집중하였고 써드파티앰의 내장된 브라우저 사용 케이스에 집중하였다.
CEF는 크로미텀의 하부와 코드의 복잡함으로부터 고수준의 안정화된 API, 특정목적의 배포, 그리고 바이너리 배포를 통해 벗어날수 있게 해준다.
CEF기본 구현은 사용자로부터 조금의 또는 전혀 통합하는 작업을 필요로 하지 않는다.
현재 1억개 이상의 인스턴스가 사용되고 있다. 그 일부가 위키페이지에 정리되어 있다.

사용케이스 몇가지는 다음과 같다.
* 기존 네이티브 애플리케이션에 HTML5호환 웹브라우저를 내장
* 가벼운 네이트브 쉘애플리케이션으로 기본적으로 웹기술에 기반하여 개발된 UI를 호스팅
* 자체적인 드로잉프레이뭐크를 사용하는 오프스크린 웹컨텐츠를 렌더링
* 웹속성이나 애플리케이션의 자동화된 테스트를 수행

CEF는 수많은 언어와 운영체제를 지원하며 새로운 또는 기존애플리케이션에 쉽게 통합이 가능하다. 성능과 쉬운 사용을 목표로 설계되었다.
기반프레임워크는 C, C++로 네이티브라이브러리로 제공되고 애플리케이션과 크로미엄과 상세한 구현으로부터 분리해준다.
이는 브라우저와 커스텀플러그인, 프로토콜, 자바스크립트 객체 그리고 자바스크립트 확장 등을 포함하는 앱간에 밀접한 통합을 제공한다.
애플리케이션은 리로스로딩을 제어하거나, 네비게이션, 메뉴, 프린트 등을 하면서 구글 크롬웹브라우저와 동일한 성능과 HTML5기술을 사용할 수 있다.

수많은 개인과 단체가 시간과 자원을 CEF개발에 기연하고 있지만 더 많은 커뮤니티로부터의 참여는 항상 환영한다.


CEF Tutorial

CEF3로 간단한 앱을 작성하는 방법을 설명한다. CEF3 사용에 대해서는 GeneralUsage위키페이지를 참고하라.

시작하기

CEF개발을 시작하기 쉽게 해주는 샘플프로젝트를 제공한다. cef-project 웹사이트에 방문하고 단계별로 설명을 따라해보기 바란다.


커스텀URL 로딩하기

샘플프로젝트는 구글 홈페이지를 로딩한다. 다른 url로 변경하려면 커맨드라인에서 다음과같이 수행한다.

# Load the local file “c:\example\example.html
cefsimple.exe --url=file://c:/example/example.html

샘플프로젝트에서 cefsimple/simple_app.cc에서 다음라인을 수정하고 다시 컴파일하면 바로 진입한다.

// Load the local file “c:\example\example.html

if (url.empty())
  url = "file://c:/example/example.html";



애플리케이션 요소들

모든 CEF애플리케이션은 다음의 기본 콤포넌트들로 구성된다.

1. CEF dynamic library(libcef.dll 윈도우용, libcef.so 리눅스용, Chromium Embedded Framework.framework 맥용)
2. 지원 파일들(*.pak, *.bin, ...)
3. 리소스 (html/js/css, string, ...)
4. 클라이언트 실행파일

CEF dynamic library, 지원파일들, 리소스는 모든 CEF 애플리케이션에 존재한다. 바이너리 배포판의 Debug / Release 또는 Resources 디렉토리에 포함된다. 이러한 파일 중 필요한 파일과 제외 할 수있는 파일에 대한 자세한 내용은 이진 배포에 포함 된 README.txt 파일을 참조하십시오. 각 플랫폼에서 필요한 애플리케이션 레이아웃에 대한 자세한 설명은 아래를 참조하십시오.

아키텍쳐

다음은 이 튜터리얼의 기본적인 중요한 아이템들을 요약한다.

* CEF는 멀티프로세스를 사용한다. 메인 애플리케이션 프로세스는 '브라우저' 프로세스로 불린다. 서브프로세스들은 렌더러, 플러그인, GPU등으로 생성해되게 될것이다.
* 윈도우와 리눅스에서는 메인과 서브프로세스가 같이 실행될 수 있다. OS-X는 실행과 서브프로세스의 번들이 분리되어야 한다.
* 대부분의 프로세스는 다중스레드를 갖는다. 
* 일부 콜백과 함수들은 개개 프로세스 또는 개개의 스레드를 사용할 수 있다. 콜백이나 함수를 처음 사용하는 경우 헤더파일의 코멘트를 반드시 읽어볼것을 권장한다.

소스코드

cefsimple애플리케이션은 CEF를 초기화하고 하나의 브라우저팝업윈도우를 생성한다. 애플리케이션은 모든 브라우저윈도우가 닫히면 종료된다. 프로그램 흐름은 다음과 같다.

1. OS는 브라우저프로세스를 시작한다. 진입포인트는 main또는 wWinMain
2. 진입포인트 함수는
  프로세스레벨의 콜백을 처리하는 SimpleApp의 인스턴스를 생성한다.
  CEF를 초기화 하고 CEF message loop를 시작한다.
3. CEF가 초기화 되면 SimpleApp::OnContextInitialized가 호출된다. 이 메서드는
  SimpleHandler의 싱글턴 인스턴스를 생성한다.
  CefBrowserHost::CreateBrowser를 사용하여 브라우저윈도우를 생성한다.
4. 모든 브라우저는 SimpleHandler인스턴스를 공유하며 이는 브라우저의 행위를 커스텀하고 관련된 콜백을 처리한다(life span, 로딩상태, 타이틀 표시등...)
5. 브라우저하나가 닫히면 SimpleHandler::OnBeforeClose가 호출된다. 모든 브라우저가 닫히면 CEF message loop가 끝나며 애플리케이션이 종료된다.


진입포인트 함수

이 함수는 CEF와 운영체제 관련된 객체들을 초기화해야 한다. 예를 들어 리눅스에서는 X11 에러헨들러를 인스톨해야 하고, OS-X에서는 필요한 코코아객체들의 할당이 필요하다.

* 윈도우 : cefsimple/cefsimple_win.cc
* 리눅스 : cefsimple/cefsimple_linux.cc
* OS-X 
  브라우저 프로세스 : cefsimple/cefsimple_mac.mm
  서브 프로세스 : cefsimple/process_helper.mac.cc


SimpleApp

프로세스레벨의 콜백을 처리해야 한다. interface/method로 노출되며 멀티프로세스에 의해 공유되고 각 프로세스만 에서 호출된다. 
예를 들어 CefBrowserProcessHandler인터페이스는 브라우저 프로세스에서만 호출된다. 
CefRenderProcessHandler인터페이스(샘플에서는 없음)는 렌더프로세스에서만 호출된다.
GetBrowserProcessHandler는 반드시 this를 리턴해야 한다. SimpleApp은 CefApp과 CefBrowserProcessHandler를 구현해야 하기 때문이다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

* 공유된 구현 : cefsimple/simple_app.h, cefsimple/simple_app.cc

SimpleHandler

브라우저레벨의 콜백을 처리해야 한다. 브라우저 프로세스로부터 실행된다. 샘플에서우리는 CefClient인스턴스를 모든 브라우저에서 사용했다. 하지만 당신의 애플리케이션은 각각 전용의 인스턴스를 사용해도 된다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

* 공유된 구현 : cefsimple/simple_handler.h, cefsimple/simple_handler.cc
* 윈도우 : cefsimple/simple_handler_win.cc
* 리눅스 : cefsimple/simple_handler_linux.cc
* OS-X : cefsimple/simple_handler_mac.mm

빌드 순서

빌드 순서는 플랫폼에 의존한다. binary 배포와 함께 포함된 CMake파일을 찾아서 살펴보면 모든과정에 대한 이해가 가능할것이다. 모든 플랫폼에 공통된 빌드 순서는 일반적으로 다음과 같이 요약될 수 있다.

1. libcef_dll_wrapper static library를 컴파일
2. application 소스 컴파일, libcef dynamic library와 libceff_dll_wrapper static library 링크
3. library와 리소스들을 출력디렉토리에 복사


윈도우 빌드순서

1. libcef_dll_wrapper static library를 컴파일
2. cefsimple.ext 컴파일 및 링크
- 필요한 소스코드 : cefsimple_win.cc, simple_app.cc, simple_handler.cc, simple_handler_win.cc.
- 필요한 라이브러리 : comctl32.lib, shlwapi.lib, rcprt4.lib, libcef_dll_wrapper.lib, libcef.lib, cef_sandbox.lib
cef_sandbox.lib(sandbox지원시)은 VS 2015 update3으로 빌드된 정적라이브러리이다. 다른 버전의 VS는 컴파일이 안될수도 있다. sandbox를 지원하지 않으면 문제가 없으며 cefsimple_win.cc의 코멘트를 확인하여 비활성화 하면 된다.
- 리소스 파일 : cefsimple.rc
- 매니페스트 파일 : cefsimple.exe.manifest, compatibility.manifest
3. Resource 폴더의 모든 파일 대상폴더로 복사
4. Debug/Release 폴더의 모든 파일 대상폴더로 복사

결과 폴더의 구조는 2526 브랜치정도가 보일거다.

Application/
    cefsimple.exe  <= cefsimple application executable
    libcef.dll <= main CEF library
    icudtl.dat <= unicode support data
    libEGL.dll, libGLESv2.dll, ... <= accelerated compositing support libraries
    cef.pak, devtools_resources.pak, ... <= non-localized resources and strings
    natives_blob.bin, snapshot_blob.bin <= V8 initial snapshot
    locales/
        en-US.pak, ... <= locale-specific resources and strings


Posted by 삼스

댓글을 달아 주세요

Android/App개발2020. 2. 12. 14:45

https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84

 

코틀린 표준 함수들 일부는 어디에 사용하는지 명확하게 알지 못한다. 이에 대해 그 차이점을 명확하게 설명하고 어떤걸 사용해야 하는지 소개하고자 한다.

범위함수(Scoping functions)

run, with, T.run, T.let, T.also 그리고 T.apply에 대해 집중할거다. 

다음은 run함수의 범위지정기능을 설명하는 간단한 코드이다.

불러오는 중입니다...

 

fun test() {
  var mood = "I am sad"

  run {
    val mood = "I am happy"
    println(mood)   // I am happy
  }

  println(mood) // I am sad
}



test함수 내의 분리된 범위에서 mood는 재정의 되고 "I am happy"가 프린트된다. run 범위내에 제한된다.
이게 범위지정함수인데 별로 유용해보이지 않지만 범위지정으로써의 장점이 있다. 먼가를 반환할 수 있다.

아래코드를 보면 show()를 두번 호출하지 않고 두개의 view에 적용이 가능하다.

run {
  if (firstTimeVIew) introView
  else normalView
}.show()



범위지정함수의 3가지 속성

범위지정함수를 좀더 흥미있게 만들기 위해 3가지 속성으로 나누고 이를 통해 각각을 구별할 수 있다.

1. Normal vs. extension function

with, T.run 은 상당히 유사하다.

with(webview.settings) {
  javascriptEnabled = true
  databaseEnabled = true
}

webview.settings.run {
  javascriptEnabled = true
  databaseEnabled = true 
}



두가지 차이점은 일반함수냐 확장함수냐이다. 그래서 각각의 장점은 멀까?

webview.settings가 null일 수 있다고 생각해보자

// 별로다
with(webview.settings) {
  this?.javascriptEnabled = true
  this?.databaseEnabled = true
}

// 이게 더 좋아
webview.settings?.run {
  javascriptEnabled = true
  databaseEnabled = true
}


이 경우는 T.run 사용이 더 유용하다.


2. This vs. it 인자

strVal?.run {
  println("The length of this String is $length")
}

strVal?.let {
  println("The length of this String is $it.length")
}


T.run의 경우 block: T.()의 형태를 가지며 따라서 범위내에서 this로 참조가 가능하다. this의 경우 일반적으로 생략이 가능하므로 $this.length가 아니라 $length가 가능하다.

T.let의 경우 block: (T)의 형태를 가지며 인자를 따로 명시하지 않으면 it으로 접근이 가능하다.


위 예를 보면 T.run이 좀더 암시적이어서 우세해 보이나 T.let의 장점도 존재한다.

* let은 함수/멤버 변수를 외부 클래스 함수/멤버와 구별하기가 더 좋다.
* 파라메터로 this가 전달되면 생략이 불가한데 it이 더 짧고 명확하다
* let은 변수명을 it이 아니라 다른 이름으로 부여가 가능하다.

value?.let { myname -> 
  println("The name is $myname");
}


3. this 또는 다른 타입의 반환

value?.let {
  println("The length of val is ${it.length}")
}

value?.also {
  println("The length of val is ${it.length}")
}


두가지의 차이점은 let은 임의 타입을 반환하고 also는 동일한 this타입을 반환한다.
둘다 체이닝함수에 유용한다. T.let은 연산된 결과를 다음 체인에 전달하고 T.also는 동일한 객체를 전달한다.

val original = "abc"

original.let {
  prinln("The original string is $it") // 원래 문자열
  it.reversed() // 리버스된 문자열 반환
}.let {
  println("Ths reversed string is $it") 
  it.length. // 문자열길이 반환
}.let {
  println("The length is $it") // 전달받은 타입은 숫자
}

original.let {
  prinln("The original string is $it")
  it.reversed()
}.also {
  println("Ths reversed string is $it") // "abc" 프린트
  it.length
}.also {  
  println("The length is $it")  // "abc" 프린트
}

original.also {
  prinln("The original string is $it")
}.also {
  println("Ths reversed string is ${it.reversed()}") // 리버스된 문자열 프린트
}.also {  
  println("The length is ${it.length}")  // 문자열 길이 프린트
}


위 샘플을 보면 also가 더이상 무슨 소용이 있나 싶을 수 있지만 잘생각해 보면 좋은 장점이 있다.

1. 동일 객체에 대한 일련의 처리를 명확하게 구분할 수 있게 해준다.
2. 체이닝 빌드 연산을 만듦으로 객체를 스스로 조작하는데 파워풀한 기능을 제공한다.

두가지를 합쳐서 한번은 변환하고 이어서 그 객체를 조작하는 체이닝을 만들 수 있다.

// 일반적인 접근
fun makeDir(path: String): File {
  val result = File(path)
  result.mkdirs()
  return result
}

// 향상된 접근
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }


모든 속성 살펴보기

T.apply로 3가지 속성을 살펴보는것으로 더 많은것을 알 수 있다.

1. 함수의 확장이 필요한가?
2. 인자로서 this를 전달이 필요한가?
3. this를 반환이 필요한가?

// 일반적인 접근
fun createInstance(args: Bundle) : MyFragment {
  val fragment = MyFragment()
  fragment.arguments = args
  return fragment
}

// 향상된 접근
fun createInstance(args: Bundle) = MyFragment().apply { argument = args }
fun createInstance(args: Bundle) = MyFragment().also { it.argument = args }



this를 반환하기 때문에 체이닝이 가능하다.

// 일반적인 접근
fun createIntent(intentData: String, intentAction: String) : MyFragment {
  val intent = Intent()
  intent.action = intentAction
  intent.data = Uri.parse(intentData)
  return intent
}

fun createIntent(intentData: String, intentAction: String) = Intent()
    .apply { action = intentAction }
    .apply { data = intentData }

fun createIntent(intentData: String, intentAction: String) = Intent()
    .also { it.action = intentAction }
    .also { it.data = intentData }

위에서 설명한 표준함수들을 선택하는데 있어 선택장애자를 위한 표는 다음과 같다.

 




Posted by 삼스

댓글을 달아 주세요