Android Wear - Sizuha/devdog GitHub Wiki
https://gist.github.com/JaeWoongOh/f3390a9c82718050813e
http://qiita.com/tnj/items/83c41872125f01588068
Android Wear Device์์:
- Watch๋ฅผ ์ผ๊ณ ์ค์ > ์ ๋ณด > ๋น๋๋ฒํธํญ๋ชฉ์ ์ฐํํ๊ฒ๋๋ฉด ์ค์ ๋ฉ๋ด์ ๊ฐ๋ฐ์ ์ต์ ์ด ์๊ธฐ๊ฒ ๋ฉ๋๋ค.
- ๊ฐ๋ฐ์ ์ต์ ์ผ๋ก ๋ค์ด๊ฐ ๋ณด๋ฉด ๋ธ๋ฃจํฌ์ค ์ค๋ํ๋ก๊น , ADB๋๋ฒ๊น , ๋ธ๋ฃจํฌ์ ๋ํ ๋๋ฒ๊ทธ ํญ๋ชฉ์ด ์๋๋ฐ ๋ชจ๋ ์ฌ์ฉ์ผ๋ก ์ฒดํฌ๋ฅผ ํฉ๋๋ค.
Android Device์ ์ฐ๊ฒฐ๋ PC์์:
$ ./adb forward tcp:4444 localabstract:/adb-hub
$ ./adb connect localhost:4444
connected to localhost:4444
ใใใงใ ใในใ: ๆฅ็ถๆธใฟ ใจใชใใใใจใฏ USB ใง็นใใงใใใฎใจๅใใใใซ LogCat ใฎ่กจ็คบใๅฏ่ฝใซใชใใพใใใใ ใใ Bluetooth ๆฅ็ถใชใฎใงใใใกใคใซใ็ป้ขใฎ่ปข้ใซใฏ็ตๆงๆ้ใๆใใใพใใใใใพใๆฐ่ปฝใซไฝฟใใใฃใฆๆใใงใฏใชใใใใงใใ
์์ง๊น์ง ๋จ๋ ์ผ๋ก Android Wear ์ฑ์ ๋ฐฐํฌํ ์ ์๋ ์๋จ์ด ์๋ค. ๋ฐ๋ผ์, ์ผ๋ฐ ์๋๋ก์ด๋ ์ฑ์ Wear ์ฑ์ ํฌํจ์์ผ์ ํจ๊ป ๋ฐฐํฌํด์ผ๋ง ํ๋ค.
์ฐธ๊ณ
\1. wearable ์ฑ์ apk ํ์ผ์ mobile ์ฑ์ res > raw ํด๋์ ๋ณต์ฌ
\2. mobile ์ฑ์ res > xml > ํด๋์ ์์์ .xml ํ์ผ์ ์์ฑ. (ex. wear_app.xml)
\3. wear_app.xmlํ์ผ์ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ์ด ํธ์ง.
<?xml version="1.0" encoding="utf-8"?>
<wearableApp package="ํด๋น ์ฑ์ ํจํค์ง๋ช
">
<versionCode>1</versionCode> <!-- ๋ฐ๋์ ๋ ์ฑ์ ๋ฒ์ ์ฝ๋๊ฐ ์ผ์นํด์ผ ํจ -->
<versionName>1.0</versionName> <!-- ๋ ์ฑ์ ๋ฒ์ ๋ช
์ด ์ผ์นํด์ผ ํจ -->
<rawPathResId>wear</rawPathResId> <!-- wearable ์ฑ apk ํ์ผ๋ช
(์ด๋ฆ ๋ถ๋ถ๋ง) -->
</wearableApp>
</source>
4. mobile ์ฑ์ AndroidManifest.xml ํ์ผ์ application ํ๊ทธ์ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ์ถ๊ฐ.
<source lang="xml">
<meta-data
android:name="com.google.android.wearable.beta.app"
android:resource="@xml/wear_app" /> <!-- ์์ ์์ฑํ xml -->
์๋ ๋ก๊ทธ ์๊ณ ์ํ.
/**
* Analog watch face with a ticking second hand. In ambient mode, the second hand isn't shown. On
* devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient mode.
*/
public class MyWatchFace extends CanvasWatchFaceService {
/**
* Update rate in milliseconds for interactive mode. We update once a second to advance the
* second hand.
*/
private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
private Bitmap mBackgroundImg;
private Bitmap mNeedleHour, mNeedleMin, mNeedleSec;
@Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
static final int MSG_UPDATE_TIME = 0;
Paint mBackgroundPaint;
boolean mAmbient;
Time mTime;
/**
* Handler to update the time once a second in interactive mode.
*/
final Handler mUpdateTimeHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_TIME:
invalidate();
if (shouldTimerBeRunning()) {
long timeMs = System.currentTimeMillis();
long delayMs = INTERACTIVE_UPDATE_RATE_MS
- (timeMs % INTERACTIVE_UPDATE_RATE_MS);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
}
break;
}
}
};
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mTime.clear(intent.getStringExtra("time-zone"));
mTime.setToNow();
}
};
boolean mRegisteredTimeZoneReceiver = false;
/**
* Whether the display supports fewer bits for each color in ambient mode. When true, we
* disable anti-aliasing in ambient mode.
*/
boolean mLowBitAmbient;
@Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
setWatchFaceStyle(new WatchFaceStyle.Builder(GirlsWatchFace.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = GirlsWatchFace.this.getResources();
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(resources.getColor(R.color.analog_background));
mTime = new Time();
mBackgroundImg = BitmapFactory.decodeResource(resources, R.drawable.clock_bg);
mNeedleHour = BitmapFactory.decodeResource(resources, R.drawable.hour);
mNeedleMin = BitmapFactory.decodeResource(resources, R.drawable.min);
mNeedleSec = BitmapFactory.decodeResource(resources, R.drawable.second);
mTextBoxImg = BitmapFactory.decodeResource(resources, R.drawable.fusen);
}
@Override
public void onDestroy() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
super.onDestroy();
}
@Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
}
@Override
public void onTimeTick() {
super.onTimeTick();
invalidate();
}
@Override
public void onAmbientModeChanged(boolean inAmbientMode) {
super.onAmbientModeChanged(inAmbientMode);
if (mAmbient != inAmbientMode) {
mAmbient = inAmbientMode;
if (mLowBitAmbient) {
mHandPaint.setAntiAlias(!inAmbientMode);
}
invalidate();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
void drawClockBackground(Canvas canvas, Rect bounds) {
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mBackgroundPaint);
canvas.drawBitmap(mBackgroundImg, null, bounds, null);
}
float mScale = 1f;
@Override
public void onDraw(Canvas canvas, Rect bounds) {
mTime.setToNow();
int width = bounds.width();
int height = bounds.height();
mScale = (float)width / (float)mBackgroundImg.getWidth();
drawClockBackground(canvas, bounds);
// Find the center. Ignore the window insets so that, on round watches with a
// "chin", the watch face is centered on the entire screen, not just the usable
// portion.
float centerX = width / 2f;
float centerY = height / 2f;
float secRot = mTime.second / 30f * (float) Math.PI;
int minutes = mTime.minute;
float minRot = minutes / 30f * (float) Math.PI;
float hrRot = ((mTime.hour + (minutes / 60f)) / 6f) * (float) Math.PI;
float secLength = centerX - 20;
float minLength = centerX - 40;
float hrLength = centerX - 80;
if (!mAmbient) {
drawNeedle_Sec(canvas, centerX, centerY, mTime.second);
}
drawNeedle_Min(canvas, centerX, centerY, minutes);
drawNeedle_Hour(canvas, centerX, centerY, mTime.hour % 12, minutes);
}
void drawNeedle_Sec(Canvas canvas, float centerX, float centerY, float seconds) {
canvas.save();
canvas.rotate(360f / 60f * seconds + 180f, centerX, centerY);
float x = centerX - mNeedleSec.getWidth()*mScale/2;
float y = centerY - 82f*mScale;
RectF destRect = new RectF(
x, y,
x + mNeedleSec.getWidth()*mScale,
y + mNeedleSec.getHeight()*mScale);
canvas.drawBitmap(mNeedleSec, null, destRect, null);
//canvas.drawBitmap(mNeedleSec, centerX - mNeedleSec.getWidth()/2f, centerY-82f, mHandPaint);
canvas.restore();
}
void drawNeedle_Min(Canvas canvas, float centerX, float centerY, float minutes) {
canvas.save();
canvas.rotate(360f / 60f * minutes + 180f, centerX, centerY);
float x = centerX - mNeedleMin.getWidth()*mScale/2;
float y = centerY - 64f*mScale;
RectF destRect = new RectF(
x, y,
x + mNeedleMin.getWidth()*mScale,
y + mNeedleMin.getHeight()*mScale);
canvas.drawBitmap(mNeedleMin, null, destRect, null);
//canvas.drawBitmap(mNeedleMin, centerX - mNeedleSec.getWidth()/2f, centerY-64f, mHandPaint);
canvas.restore();
}
void drawNeedle_Hour(Canvas canvas, float centerX, float centerY, float hours, float minutes) {
canvas.save();
canvas.rotate(360f / 720f * (hours * 60f + minutes) + 180f, centerX, centerY);
float x = centerX - mNeedleHour.getWidth()*mScale/2;
float y = centerY - 94f*mScale;
RectF destRect = new RectF(
x, y,
x + mNeedleHour.getWidth()*mScale,
y + mNeedleHour.getHeight()*mScale);
canvas.drawBitmap(mNeedleHour, null, destRect, null);
//canvas.drawBitmap(mNeedleHour, centerX - mNeedleSec.getWidth()/2f, centerY-94f, mHandPaint);
canvas.restore();
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
registerReceiver();
// Update time zone in case it changed while we weren't visible.
mTime.clear(TimeZone.getDefault().getID());
mTime.setToNow();
} else {
unregisterReceiver();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
private void registerReceiver() {
if (mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
GirlsWatchFace.this.registerReceiver(mTimeZoneReceiver, filter);
}
private void unregisterReceiver() {
if (!mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = false;
GirlsWatchFace.this.unregisterReceiver(mTimeZoneReceiver);
}
/**
* Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
* or stops it if it shouldn't be running but currently is.
*/
private void updateTimer() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
/**
* Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
* only run when we're visible and in interactive mode.
*/
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
}
}