Daydream을 지원하는 앱은 어트랙트 모드에서 안드로이드 UI 툴킷 전체를 사용할 수 있습니다. 즉, 레이아웃, 애니메이션, 3D, 사용자 지정 뷰 등을 포함하여 앱이 현재 가지고 있는 컴포넌트를 쉽게 사용할 수 있고, 보다 잔잔한 프리젠테이션을 위해서 해당 컴포넌트들을 쉽게 조합할 수 있습니다. 또한 터치스크린 입력을 사용할 수 있기 때문에 원한다면 여러가지 인터랙티브한 경험을 제공할 수도 있습니다.
Daydream은 본인의 앱을 홍보할 수 있는 기회를 다소 제공합니다. 앱의 복잡한 면을 감추고 시각적으로 흥미있는 부분을 살려서 사용자가 앱 전체에 흥미를 느낄 수 있도록 합니다. 비디오 게임의 시선 끌기 모드 (attract mode) 처럼 말이죠.
그림 1. Google 세상보기(Google Currents)에서는 부드럽고 지속적으로 움직이는 뉴스 화면에서 화제거리를 스크롤한다.
구글 세상보기(Google Currents)는 이러한 방법으로 접근한 좋은 예입니다. Daydream으로서 시각적으로 흥미있는 화제거리를 선택하여 화면 슬라이딩으로 보여 줍니다. 화제거리를 터치하면 세상보기는 화면 전체로 해당 내용을 보여주며, 다시 터치하면 전체 실행 중인 앱에서 읽을 수 있습니다.
(서브클래스 구현 호출을 잊지 말 것)
DreamService 에서 사용하는 주요 메소드
- setContentView(): Daydream을 위한 배경 설정. XML 리소스 ID나 뷰 인스턴스, 자신이 구현한 사용자 지정 뷰일 수도 있음.
- setInteractive(boolean):디폴트로 Daydream은 사용자가 스크린을 터치하면 종료됨. 전형적인 화면 보호기처럼. 사용자가 터치하고 뷰에서 상호 작용하기 원하면 setInteractive(true)를 호출
- setScreenBright(boolean): 디폴트로 Daydream에서는 가장 밝은 화면으로 설정되기에, 적절치 않은 경우도 있음 (예: 어두운 방). 따라서 false로 설정하면 밝기가 아주 낮은 수준으로 설정됨
마지막으로, 시스템에 Daydream을 홍보하려면, AndroidManifest.xml에 <service>를 생성하세요.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app"> <uses-sdk android:targetSdkVersion="17" android:minSdkVersion="17" />
<application> <service android:name=".ExampleDaydream" android:exported="true" android:label="@string/my_daydream_name"> <intent-filter> <action android:name="android.service.dreams.DreamService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.service.dream" android:resource="@xml/dream_info" /> </service> </application> </manifest> |
<meta-data> 태그는 선택사항으로, Daydream에 대한 Activity 설정을 명시하는 XML 리소스를 가리킵니다. 사용자는 Settings 앱에서 해당 Daydream 이름 옆에 있는 설정(settings) 아이콘을 눌러서 확인할 수 있습니다.
<!-- res/xml/dream_info.xml --> <?xml version="1.0" encoding="utf-8"?> <dream xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.example.app/.ExampleDreamSettingsActivity" /> |
다음은 여러분이 시작해 볼 수 있는 전형적인 화면 보호기 예제로, 움직이는 로고를 보여줍니다. TimeAnimator 를 사용하여 구현한 매끄럽고 부드러운 60Hz 애니메이션을 제공합니다.
그림 2. Will one of them hit the corner?
public class BouncerDaydream extends DreamService { @Override public void onDreamingStarted() { super.onDreamingStarted();
// Our content view will take care of animating its children. final Bouncer bouncer = new Bouncer(this); bouncer.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); bouncer.setSpeed(200); // pixels/sec
// Add some views that will be bounced around. // Here I'm using ImageViews but they could be any kind of // View or ViewGroup, constructed in Java or inflated from // resources. for (int i=0; i<5; i++) { final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); final ImageView image = new ImageView(this); image.setImageResource(R.drawable.android); image.setBackgroundColor(0xFF004000); bouncer.addView(image, lp); }
setContentView(bouncer); } }
public class Bouncer extends FrameLayout implements TimeAnimator.TimeListener { private float mMaxSpeed; private final TimeAnimator mAnimator; private int mWidth, mHeight;
public Bouncer(Context context) { this(context, null); }
public Bouncer(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public Bouncer(Context context, AttributeSet attrs, int flags) { super(context, attrs, flags); mAnimator = new TimeAnimator(); mAnimator.setTimeListener(this); }
/** * Start the bouncing as soon as we’re on screen. */ @Override public void onAttachedToWindow() { super.onAttachedToWindow(); mAnimator.start(); }
/** * Stop animations when the view hierarchy is torn down. */ @Override public void onDetachedFromWindow() { mAnimator.cancel(); super.onDetachedFromWindow(); }
/** * Whenever a view is added, place it randomly. */ @Override public void addView(View v, ViewGroup.LayoutParams lp) { super.addView(v, lp); setupView(v); }
/** * Reposition all children when the container size changes. */ @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; for (int i=0; i<getChildCount(); i++) { setupView(getChildAt(i)); } }
/** * Bouncing view setup: random placement, random velocity. */ private void setupView(View v) { final PointF p = new PointF(); final float a = (float) (Math.random()*360); p.x = mMaxSpeed * (float)(Math.cos(a)); p.y = mMaxSpeed * (float)(Math.sin(a)); v.setTag(p); v.setX((float) (Math.random() * (mWidth - v.getWidth()))); v.setY((float) (Math.random() * (mHeight - v.getHeight()))); }
/** * Every TimeAnimator frame, nudge each bouncing view along. */ public void onTimeUpdate(TimeAnimator animation, long elapsed, long dt_ms) { final float dt = dt_ms / 1000f; // seconds for (int i=0; i<getChildCount(); i++) { final View view = getChildAt(i); final PointF v = (PointF) view.getTag();
// step view for velocity * time view.setX(view.getX() + v.x * dt); view.setY(view.getY() + v.y * dt);
// handle reflections final float l = view.getX(); final float t = view.getY(); final float r = l + view.getWidth(); final float b = t + view.getHeight(); boolean flipX = false, flipY = false; if (r > mWidth) { view.setX(view.getX() - 2 * (r - mWidth)); flipX = true; } else if (l < 0) { view.setX(-l); flipX = true; } if (b > mHeight) { view.setY(view.getY() - 2 * (b - mHeight)); flipY = true; } else if (t < 0) { view.setY(-t); flipY = true; } if (flipX) v.x *= -1; if (flipY) v.y *= -1; } }
public void setSpeed(float s) { mMaxSpeed = s; } }
|
이 예제 코드로 본인이 원하는 것을 작업해서 화면에 넣어 줄 필요(간단한 그래픽이나 에러 메시지처럼) 없이 사용자에게 손쉽게 보여 줄 수 있습니다. 또한 더욱 복잡한 Daydream 프로젝트를 시작하기에도 좋은 샘플입니다.
기타 주의 사항
> 우선, 시스템에 피해를 주지 말라
Daydream은 디바이스가 충전될 때 실행된다. 하지만 Daydream이 지나치게 많은 CPU를 소모하게 되는 경우 충전이 매우 느리거나 전혀 진행되지 않을 수도 있다. 디바이스가 충전되고 있지 않음을 감지하면 시스템은 Daydream을 멈추게 만든다. 따라서 적절한 시간에 충전할수 있도록 코드에서 충분한 전력을 남겨 두도록 한다.
> 화면 잠금을 준수하라
Daydream은 키가드(keyguard) 작동상에서 실행된다. 다시 말해, 민감한 콘텐츠를 보여줄 수 있는 경우라면, 사용자가 해당 콘텐츠를 제어할 수 있는 사용자 툴을 제공할 필요가 있다. 예를 들어, 포토 테이블과 포토 프레임에서는 사용자가 디스플레이될 사진을 앨범에서 선택할 수 있도록 해야한다. (당황스러운 슬라이드쇼를 피함)
> 화면 밝기
Daydream에서 사용되는 장소를 판단하고 이에 따라 setScreenBright()를 사용하여 화면 밝기를 조정하라. 그리고 필요에 따라서 어둡거나 밟은 색상을 사용하라. 침대 머리맡 시계는 탁상 시계보다 어두워야 한다. 두 가지 모두에 쓰일 것이라고 판단되면 사용자에게 선택권을 주어야 한다.
> 상태바 숨김 여부
많은 사용자들이 배터리 충전이나 날짜/시간 정보에 즉각적으로 접근할 수 있기를 원할 것이다. 따라서 setFullscreen() 사용을 피해야 한다. 특히 Daydream이 예술적인 측면보다 정보를 더 많이 가지고 있을 때 더욱 그렇다. Daydream은 요란스럽지 않으면서도 시간 정보와 충전 상태를 보여 줄 수 있게 “lights out”모드로 상태바를 시작할 것이다 (View.SYSTEM_UI_FLAG_LOW_PROFILE).
> 언제 설정을 사용하나
일반적으로 어느정도 재량권을 가지고 Daydream 설정에 제어도구나 다이얼을 추가할 수 있다. 사실 이것은 개인화 기능이기에 사용자 편의대로 수정할 수 있도록 권장되어야 한다. 하지만 때로 심미적인 환경에서 더욱 설득력있는 경험이 나올 수 있기 때문에 소수의 세련되고 아름다운 구성 중에서 선택할 수 있도록 사용자에게 선택권을 주도록 하라 (비행기 조종석의 제어권을 모두 내어주기 보다).
> 하나 이상이 될 수도 있다
설정에서 사용자에게 완전히 상이한 디스플레이 모드 사이에서 선택하도록 한다면, Daydream을 다중 DreamService 구현으로 나누라. 예를 들어 안드로이드 4.2에서 포토 갤러리는 포토 테이블과 포토 프레임 DayDream을 모두 제공한다.
> 개발용 액티비티를 사용하라
대부분 안드로이드 개발 툴은 기존 안드로이드 앱 개발 및 디버깅에 최적화되어 있다. DreamService와 액티비티가 매우 흡사하기 때문에 DreamService과 동일한 콘텐츠 뷰를 호스트하는 테스트용 액티비티를 만드는 것이 유용할 수 있다. 다른 안드로이드 프로젝트처럼 IDE에서 코드를 런치하여 쉽게 테스트 할 수 있다.
자, 이제 충분히 설명 드린 것 같습니다. 여러분의 앱에 Daydream 지원을 개발할 도구가 있습니다. 여러분이 즐겁게 개발하면 사용자들도 즐길 수 있을 것입니다. 그리고 Google Play에 반짝이는 새 APK를 업로드할 때, 앱 설명을 추가하여 Daydream을 찾는 사용자들이 검색할 수 있도록 해주세요!