Back to project page gdk-line-sample.
The source code is released under:
Apache License
If you think the Android project gdk-line-sample listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright (C) 2013 The Android Open Source Project */* w w w. j a va 2 s.c o m*/ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.kitware.android.glass.sample.line; import com.kitware.android.glass.sample.line.model.Landmarks; import com.kitware.android.glass.sample.line.model.Place; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Typeface; import android.location.Location; import android.os.SystemClock; import android.util.Log; import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.View; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.TextView; import java.io.File; import java.util.List; import java.util.concurrent.TimeUnit; /** * The surface callback that provides the rendering logic for the line live card. This callback * also manages the lifetime of the sensor and location event listeners (through * {@link OrientationManager}) so that tracking only occurs when the card is visible. */ public class LineRenderer implements SurfaceHolder.Callback { private static final String TAG = LineRenderer.class.getSimpleName(); /** * The (absolute) pitch angle beyond which the line will display a message telling the user * that his or her head is at too steep an angle to be reliable. */ private static final float TOO_STEEP_PITCH_DEGREES = 70.0f; /** The refresh rate, in frames per second, of the line. */ private static final int REFRESH_RATE_FPS = 45; /** The duration, in milliseconds, of one frame. */ private static final long FRAME_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1) / REFRESH_RATE_FPS; private SurfaceHolder mHolder; private boolean mTooSteep; private boolean mInterference; private RenderThread mRenderThread; private int mSurfaceWidth; private int mSurfaceHeight; private final FrameLayout mLayout; private final LineView mLineView; private final RelativeLayout mTipsContainer; private final TextView mTipsView; private final OrientationManager mOrientationManager; private final Landmarks mLandmarks; private final OrientationManager.OnChangedListener mLineListener = new OrientationManager.OnChangedListener() { @Override public void onOrientationChanged(OrientationManager orientationManager) { mLineView.setHeading(orientationManager.getHeading()); boolean oldTooSteep = mTooSteep; mTooSteep = (Math.abs(orientationManager.getPitch()) > TOO_STEEP_PITCH_DEGREES); if (mTooSteep != oldTooSteep) { updateTipsView(); } } @Override public void onLocationChanged(OrientationManager orientationManager) { Location location = orientationManager.getLocation(); List<Place> places = mLandmarks.getNearbyLandmarks( location.getLatitude(), location.getLongitude()); mLineView.setNearbyPlaces(places); } @Override public void onAccuracyChanged(OrientationManager orientationManager) { mInterference = orientationManager.hasInterference(); updateTipsView(); } }; /** * Creates a new instance of the {@code LineRenderer} with the specified context, * orientation manager, and landmark collection. */ public LineRenderer(Context context, OrientationManager orientationManager, Landmarks landmarks) { LayoutInflater inflater = LayoutInflater.from(context); mLayout = (FrameLayout) inflater.inflate(R.layout.line, null); mLayout.setWillNotDraw(false); mLineView = (LineView) mLayout.findViewById(R.id.line); mTipsContainer = (RelativeLayout) mLayout.findViewById(R.id.tips_container); mTipsView = (TextView) mLayout.findViewById(R.id.tips_view); mOrientationManager = orientationManager; mLandmarks = landmarks; mLineView.setOrientationManager(mOrientationManager); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mSurfaceWidth = width; mSurfaceHeight = height; doLayout(); } @Override public void surfaceCreated(SurfaceHolder holder) { mHolder = holder; mOrientationManager.addOnChangedListener(mLineListener); mOrientationManager.start(); if (mOrientationManager.hasLocation()) { Location location = mOrientationManager.getLocation(); List<Place> nearbyPlaces = mLandmarks.getNearbyLandmarks( location.getLatitude(), location.getLongitude()); mLineView.setNearbyPlaces(nearbyPlaces); } mRenderThread = new RenderThread(); mRenderThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mRenderThread.quit(); mOrientationManager.removeOnChangedListener(mLineListener); mOrientationManager.stop(); } /** * Requests that the views redo their layout. This must be called manually every time the * tips view's text is updated because this layout doesn't exist in a GUI thread where those * requests will be enqueued automatically. */ private void doLayout() { // Measure and update the layout so that it will take up the entire surface space // when it is drawn. int measuredWidth = View.MeasureSpec.makeMeasureSpec(mSurfaceWidth, View.MeasureSpec.EXACTLY); int measuredHeight = View.MeasureSpec.makeMeasureSpec(mSurfaceHeight, View.MeasureSpec.EXACTLY); mLayout.measure(measuredWidth, measuredHeight); mLayout.layout(0, 0, mLayout.getMeasuredWidth(), mLayout.getMeasuredHeight()); } /** * Repaints the line. */ private synchronized void repaint() { Canvas canvas = null; try { canvas = mHolder.lockCanvas(); } catch (RuntimeException e) { Log.d(TAG, "lockCanvas failed", e); } if (canvas != null) { mLayout.draw(canvas); try { mHolder.unlockCanvasAndPost(canvas); } catch (RuntimeException e) { Log.d(TAG, "unlockCanvasAndPost failed", e); } } } /** * Shows or hides the tip view with an appropriate message based on the current accuracy of the * line. */ private void updateTipsView() { int stringId = 0; // Only one message (with magnetic interference being higher priority than pitch too steep) // will be displayed in the tip. if (mInterference) { stringId = R.string.magnetic_interference; } else if (mTooSteep) { stringId = R.string.pitch_too_steep; } boolean show = (stringId != 0); if (show) { mTipsView.setText(stringId); doLayout(); } if (mTipsContainer.getAnimation() == null) { float newAlpha = (show ? 1.0f : 0.0f); mTipsContainer.animate().alpha(newAlpha).start(); } } /** * Redraws the line in the background. */ private class RenderThread extends Thread { private boolean mShouldRun; /** * Initializes the background rendering thread. */ public RenderThread() { mShouldRun = true; } /** * Returns true if the rendering thread should continue to run. * * @return true if the rendering thread should continue to run */ private synchronized boolean shouldRun() { return mShouldRun; } /** * Requests that the rendering thread exit at the next opportunity. */ public synchronized void quit() { mShouldRun = false; } @Override public void run() { while (shouldRun()) { long frameStart = SystemClock.elapsedRealtime(); repaint(); long frameLength = SystemClock.elapsedRealtime() - frameStart; long sleepTime = FRAME_TIME_MILLIS - frameLength; if (sleepTime > 0) { SystemClock.sleep(sleepTime); } } } } }