Skip to content

Most visited

Recently visited

navigation
WatchFace / Wearable / src / com.example.android.wearable.watchface /

OpenGLWatchFaceService.java

1
/*
2
 * Copyright (C) 2014 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.wearable.watchface;
18
 
19
import android.content.BroadcastReceiver;
20
import android.content.Context;
21
import android.content.Intent;
22
import android.content.IntentFilter;
23
import android.opengl.GLES20;
24
import android.opengl.Matrix;
25
import android.support.wearable.watchface.Gles2WatchFaceService;
26
import android.support.wearable.watchface.WatchFaceStyle;
27
import android.util.Log;
28
import android.view.Gravity;
29
import android.view.SurfaceHolder;
30
 
31
import java.util.Calendar;
32
import java.util.TimeZone;
33
import java.util.concurrent.TimeUnit;
34
 
35
/**
36
 * Sample watch face using OpenGL. The watch face is rendered using
37
 * {@link Gles2ColoredTriangleList}s. The camera moves around in interactive mode and stops moving
38
 * when the watch enters ambient mode.
39
 */
40
public class OpenGLWatchFaceService extends Gles2WatchFaceService {
41
 
42
    private static final String TAG = "OpenGLWatchFaceService";
43
 
44
    /** Expected frame rate in interactive mode. */
45
    private static final long FPS = 60;
46
 
47
    /** Z distance from the camera to the watchface. */
48
    private static final float EYE_Z = -2.3f;
49
 
50
    /** How long each frame is displayed at expected frame rate. */
51
    private static final long FRAME_PERIOD_MS = TimeUnit.SECONDS.toMillis(1) / FPS;
52
 
53
    @Override
54
    public Engine onCreateEngine() {
55
        return new Engine();
56
    }
57
 
58
    private class Engine extends Gles2WatchFaceService.Engine {
59
        /** Cycle time before the camera motion repeats. */
60
        private static final long CYCLE_PERIOD_SECONDS = 5;
61
 
62
        /** Number of camera angles to precompute. */
63
        private final int mNumCameraAngles = (int) (CYCLE_PERIOD_SECONDS * FPS);
64
 
65
        /** Projection transformation matrix. Converts from 3D to 2D. */
66
        private final float[] mProjectionMatrix = new float[16];
67
 
68
        /**
69
         * View transformation matrices to use in interactive mode. Converts from world to camera-
70
         * relative coordinates. One matrix per camera position.
71
         */
72
        private final float[][] mViewMatrices = new float[mNumCameraAngles][16];
73
 
74
        /** The view transformation matrix to use in ambient mode */
75
        private final float[] mAmbientViewMatrix = new float[16];
76
 
77
        /**
78
         * Model transformation matrices. Converts from model-relative coordinates to world
79
         * coordinates. One matrix per degree of rotation.
80
         */
81
        private final float[][] mModelMatrices = new float[360][16];
82
 
83
        /**
84
         * Products of {@link #mViewMatrices} and {@link #mProjectionMatrix}. One matrix per camera
85
         * position.
86
         */
87
        private final float[][] mVpMatrices = new float[mNumCameraAngles][16];
88
 
89
        /** The product of {@link #mAmbientViewMatrix} and {@link #mProjectionMatrix} */
90
        private final float[] mAmbientVpMatrix = new float[16];
91
 
92
        /**
93
         * Product of {@link #mModelMatrices}, {@link #mViewMatrices}, and
94
         * {@link #mProjectionMatrix}.
95
         */
96
        private final float[] mMvpMatrix = new float[16];
97
 
98
        /** Triangles for the 4 major ticks. These are grouped together to speed up rendering. */
99
        private Gles2ColoredTriangleList mMajorTickTriangles;
100
 
101
        /** Triangles for the 8 minor ticks. These are grouped together to speed up rendering. */
102
        private Gles2ColoredTriangleList mMinorTickTriangles;
103
 
104
        /** Triangle for the second hand. */
105
        private Gles2ColoredTriangleList mSecondHandTriangle;
106
 
107
        /** Triangle for the minute hand. */
108
        private Gles2ColoredTriangleList mMinuteHandTriangle;
109
 
110
        /** Triangle for the hour hand. */
111
        private Gles2ColoredTriangleList mHourHandTriangle;
112
 
113
        private Calendar mCalendar = Calendar.getInstance();
114
 
115
        /** Whether we've registered {@link #mTimeZoneReceiver}. */
116
        private boolean mRegisteredTimeZoneReceiver;
117
 
118
        private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
119
            @Override
120
            public void onReceive(Context context, Intent intent) {
121
                mCalendar.setTimeZone(TimeZone.getDefault());
122
                invalidate();
123
            }
124
        };
125
 
126
        @Override
127
        public void onCreate(SurfaceHolder surfaceHolder) {
128
            if (Log.isLoggable(TAG, Log.DEBUG)) {
129
                Log.d(TAG, "onCreate");
130
            }
131
            super.onCreate(surfaceHolder);
132
            setWatchFaceStyle(new WatchFaceStyle.Builder(OpenGLWatchFaceService.this)
133
                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
134
                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
135
                    .setStatusBarGravity(Gravity.RIGHT | Gravity.TOP)
136
                    .setHotwordIndicatorGravity(Gravity.LEFT | Gravity.TOP)
137
                    .setShowSystemUiTime(false)
138
                    .build());
139
        }
140
 
141
        @Override
142
        public void onGlContextCreated() {
143
            if (Log.isLoggable(TAG, Log.DEBUG)) {
144
                Log.d(TAG, "onGlContextCreated");
145
            }
146
            super.onGlContextCreated();
147
 
148
            // Create program for drawing triangles.
149
            Gles2ColoredTriangleList.Program triangleProgram =
150
                    new Gles2ColoredTriangleList.Program();
151
 
152
            // We only draw triangles which all use the same program so we don't need to switch
153
            // programs mid-frame. This means we can tell OpenGL to use this program only once
154
            // rather than having to do so for each frame. This makes OpenGL draw faster.
155
            triangleProgram.use();
156
 
157
            // Create triangles for the ticks.
158
            mMajorTickTriangles = createMajorTicks(triangleProgram);
159
            mMinorTickTriangles = createMinorTicks(triangleProgram);
160
 
161
            // Create triangles for the hands.
162
            mSecondHandTriangle = createHand(
163
                    triangleProgram,
164
                    0.02f /* width */,
165
                    1.0f /* height */,
166
                    new float[]{
167
                            1.0f /* red */,
168
                            0.0f /* green */,
169
                            0.0f /* blue */,
170
                            1.0f /* alpha */
171
                    }
172
            );
173
            mMinuteHandTriangle = createHand(
174
                    triangleProgram,
175
                    0.06f /* width */,
176
                    1f /* height */,
177
                    new float[]{
178
                            0.7f /* red */,
179
                            0.7f /* green */,
180
                            0.7f /* blue */,
181
                            1.0f /* alpha */
182
                    }
183
            );
184
            mHourHandTriangle = createHand(
185
                    triangleProgram,
186
                    0.1f /* width */,
187
                    0.6f /* height */,
188
                    new float[]{
189
                            0.9f /* red */,
190
                            0.9f /* green */,
191
                            0.9f /* blue */,
192
                            1.0f /* alpha */
193
                    }
194
            );
195
 
196
            // Precompute the clock angles.
197
            for (int i = 0; i < mModelMatrices.length; ++i) {
198
                Matrix.setRotateM(mModelMatrices[i], 0, i, 0, 0, 1);
199
            }
200
 
201
            // Precompute the camera angles.
202
            for (int i = 0; i < mNumCameraAngles; ++i) {
203
                // Set the camera position (View matrix). When active, move the eye around to show
204
                // off that this is 3D.
205
                final float cameraAngle = (float) (((float) i) / mNumCameraAngles * 2 * Math.PI);
206
                final float eyeX = (float) Math.cos(cameraAngle);
207
                final float eyeY = (float) Math.sin(cameraAngle);
208
                Matrix.setLookAtM(mViewMatrices[i],
209
                        0, // dest index
210
                        eyeX, eyeY, EYE_Z, // eye
211
                        0, 0, 0, // center
212
                        0, 1, 0); // up vector
213
            }
214
 
215
            Matrix.setLookAtM(mAmbientViewMatrix,
216
                    0, // dest index
217
                    0, 0, EYE_Z, // eye
218
                    0, 0, 0, // center
219
                    0, 1, 0); // up vector
220
        }
221
 
222
        @Override
223
        public void onGlSurfaceCreated(int width, int height) {
224
            if (Log.isLoggable(TAG, Log.DEBUG)) {
225
                Log.d(TAG, "onGlSurfaceCreated: " + width + " x " + height);
226
            }
227
            super.onGlSurfaceCreated(width, height);
228
 
229
            // Update the projection matrix based on the new aspect ratio.
230
            final float aspectRatio = (float) width / height;
231
            Matrix.frustumM(mProjectionMatrix,
232
                    0 /* offset */,
233
                    -aspectRatio /* left */,
234
                    aspectRatio /* right */,
235
                    -1 /* bottom */,
236
                    1 /* top */,
237
                    2 /* near */,
238
                    7 /* far */);
239
 
240
            // Precompute the products of Projection and View matrices for each camera angle.
241
            for (int i = 0; i < mNumCameraAngles; ++i) {
242
                Matrix.multiplyMM(mVpMatrices[i], 0, mProjectionMatrix, 0, mViewMatrices[i], 0);
243
            }
244
 
245
            Matrix.multiplyMM(mAmbientVpMatrix, 0, mProjectionMatrix, 0, mAmbientViewMatrix, 0);
246
        }
247
 
248
        /**
249
         * Creates a triangle for a hand on the watch face.
250
         *
251
         * @param program program for drawing triangles
252
         * @param width width of base of triangle
253
         * @param length length of triangle
254
         * @param color color in RGBA order, each in the range [0, 1]
255
         */
256
        private Gles2ColoredTriangleList createHand(Gles2ColoredTriangleList.Program program,
257
                float width, float length, float[] color) {
258
            // Create the data for the VBO.
259
            float[] triangleCoords = new float[]{
260
                    // in counterclockwise order:
261
                    0, length, 0,   // top
262
                    -width / 2, 0, 0,   // bottom left
263
                    width / 2, 0, 0    // bottom right
264
            };
265
            return new Gles2ColoredTriangleList(program, triangleCoords, color);
266
        }
267
 
268
        /**
269
         * Creates a triangle list for the major ticks on the watch face.
270
         *
271
         * @param program program for drawing triangles
272
         */
273
        private Gles2ColoredTriangleList createMajorTicks(
274
                Gles2ColoredTriangleList.Program program) {
275
            // Create the data for the VBO.
276
            float[] trianglesCoords = new float[9 * 4];
277
            for (int i = 0; i < 4; i++) {
278
                float[] triangleCoords = getMajorTickTriangleCoords(i);
279
                System.arraycopy(triangleCoords, 0, trianglesCoords, i * 9, triangleCoords.length);
280
            }
281
 
282
            return new Gles2ColoredTriangleList(program, trianglesCoords,
283
                    new float[]{
284
                            1.0f /* red */,
285
                            1.0f /* green */,
286
                            1.0f /* blue */,
287
                            1.0f /* alpha */
288
                    }
289
            );
290
        }
291
 
292
        /**
293
         * Creates a triangle list for the minor ticks on the watch face.
294
         *
295
         * @param program program for drawing triangles
296
         */
297
        private Gles2ColoredTriangleList createMinorTicks(
298
                Gles2ColoredTriangleList.Program program) {
299
            // Create the data for the VBO.
300
            float[] trianglesCoords = new float[9 * (12 - 4)];
301
            int index = 0;
302
            for (int i = 0; i < 12; i++) {
303
                if (i % 3 == 0) {