1*90c8c64dSAndroid Build Coastguard Worker /*
2*90c8c64dSAndroid Build Coastguard Worker  * Copyright (C) 2014 Google Inc. All Rights Reserved.
3*90c8c64dSAndroid Build Coastguard Worker  *
4*90c8c64dSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker  *
8*90c8c64dSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker  *
10*90c8c64dSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker  * limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker  */
16*90c8c64dSAndroid Build Coastguard Worker 
17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.wearable.jumpingjack;
18*90c8c64dSAndroid Build Coastguard Worker 
19*90c8c64dSAndroid Build Coastguard Worker import com.example.android.wearable.jumpingjack.fragments.CounterFragment;
20*90c8c64dSAndroid Build Coastguard Worker import com.example.android.wearable.jumpingjack.fragments.SettingsFragment;
21*90c8c64dSAndroid Build Coastguard Worker 
22*90c8c64dSAndroid Build Coastguard Worker import android.app.Activity;
23*90c8c64dSAndroid Build Coastguard Worker import android.content.Context;
24*90c8c64dSAndroid Build Coastguard Worker import android.hardware.Sensor;
25*90c8c64dSAndroid Build Coastguard Worker import android.hardware.SensorEvent;
26*90c8c64dSAndroid Build Coastguard Worker import android.hardware.SensorEventListener;
27*90c8c64dSAndroid Build Coastguard Worker import android.hardware.SensorManager;
28*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle;
29*90c8c64dSAndroid Build Coastguard Worker import android.os.Handler;
30*90c8c64dSAndroid Build Coastguard Worker import android.support.v4.view.ViewPager;
31*90c8c64dSAndroid Build Coastguard Worker import android.util.Log;
32*90c8c64dSAndroid Build Coastguard Worker import android.view.WindowManager;
33*90c8c64dSAndroid Build Coastguard Worker import android.widget.ImageView;
34*90c8c64dSAndroid Build Coastguard Worker 
35*90c8c64dSAndroid Build Coastguard Worker import java.util.Timer;
36*90c8c64dSAndroid Build Coastguard Worker import java.util.TimerTask;
37*90c8c64dSAndroid Build Coastguard Worker 
38*90c8c64dSAndroid Build Coastguard Worker /**
39*90c8c64dSAndroid Build Coastguard Worker  * The main activity for the Jumping Jack application. This activity registers itself to receive
40*90c8c64dSAndroid Build Coastguard Worker  * sensor values. Since on wearable devices a full screen activity is very short-lived, we set the
41*90c8c64dSAndroid Build Coastguard Worker  * FLAG_KEEP_SCREEN_ON to give user adequate time for taking actions but since we don't want to
42*90c8c64dSAndroid Build Coastguard Worker  * keep screen on for an extended period of time, there is a SCREEN_ON_TIMEOUT_MS that is enforced
43*90c8c64dSAndroid Build Coastguard Worker  * if no interaction is discovered.
44*90c8c64dSAndroid Build Coastguard Worker  *
45*90c8c64dSAndroid Build Coastguard Worker  * This activity includes a {@link android.support.v4.view.ViewPager} with two pages, one that
46*90c8c64dSAndroid Build Coastguard Worker  * shows the current count and one that allows user to reset the counter. the current value of the
47*90c8c64dSAndroid Build Coastguard Worker  * counter is persisted so that upon re-launch, the counter picks up from the last value. At any
48*90c8c64dSAndroid Build Coastguard Worker  * stage, user can set this counter to 0.
49*90c8c64dSAndroid Build Coastguard Worker  */
50*90c8c64dSAndroid Build Coastguard Worker public class MainActivity extends Activity
51*90c8c64dSAndroid Build Coastguard Worker         implements SensorEventListener {
52*90c8c64dSAndroid Build Coastguard Worker 
53*90c8c64dSAndroid Build Coastguard Worker     private static final String TAG = "JJMainActivity";
54*90c8c64dSAndroid Build Coastguard Worker 
55*90c8c64dSAndroid Build Coastguard Worker     /** How long to keep the screen on when no activity is happening **/
56*90c8c64dSAndroid Build Coastguard Worker     private static final long SCREEN_ON_TIMEOUT_MS = 20000; // in milliseconds
57*90c8c64dSAndroid Build Coastguard Worker 
58*90c8c64dSAndroid Build Coastguard Worker     /** an up-down movement that takes more than this will not be registered as such **/
59*90c8c64dSAndroid Build Coastguard Worker     private static final long TIME_THRESHOLD_NS = 2000000000; // in nanoseconds (= 2sec)
60*90c8c64dSAndroid Build Coastguard Worker 
61*90c8c64dSAndroid Build Coastguard Worker     /**
62*90c8c64dSAndroid Build Coastguard Worker      * Earth gravity is around 9.8 m/s^2 but user may not completely direct their hand vertical
63*90c8c64dSAndroid Build Coastguard Worker      * during the exercise so we leave some room. Basically if the x-component of gravity, as
64*90c8c64dSAndroid Build Coastguard Worker      * measured by the Gravity sensor, changes with a variation (delta) > GRAVITY_THRESHOLD,
65*90c8c64dSAndroid Build Coastguard Worker      * we consider that a successful count.
66*90c8c64dSAndroid Build Coastguard Worker      */
67*90c8c64dSAndroid Build Coastguard Worker     private static final float GRAVITY_THRESHOLD = 7.0f;
68*90c8c64dSAndroid Build Coastguard Worker 
69*90c8c64dSAndroid Build Coastguard Worker     private SensorManager mSensorManager;
70*90c8c64dSAndroid Build Coastguard Worker     private Sensor mSensor;
71*90c8c64dSAndroid Build Coastguard Worker     private long mLastTime = 0;
72*90c8c64dSAndroid Build Coastguard Worker     private boolean mUp = false;
73*90c8c64dSAndroid Build Coastguard Worker     private int mJumpCounter = 0;
74*90c8c64dSAndroid Build Coastguard Worker     private ViewPager mPager;
75*90c8c64dSAndroid Build Coastguard Worker     private CounterFragment mCounterPage;
76*90c8c64dSAndroid Build Coastguard Worker     private SettingsFragment mSettingPage;
77*90c8c64dSAndroid Build Coastguard Worker     private ImageView mSecondIndicator;
78*90c8c64dSAndroid Build Coastguard Worker     private ImageView mFirstIndicator;
79*90c8c64dSAndroid Build Coastguard Worker     private Timer mTimer;
80*90c8c64dSAndroid Build Coastguard Worker     private TimerTask mTimerTask;
81*90c8c64dSAndroid Build Coastguard Worker     private Handler mHandler;
82*90c8c64dSAndroid Build Coastguard Worker 
83*90c8c64dSAndroid Build Coastguard Worker     @Override
onCreate(Bundle savedInstanceState)84*90c8c64dSAndroid Build Coastguard Worker     protected void onCreate(Bundle savedInstanceState) {
85*90c8c64dSAndroid Build Coastguard Worker         super.onCreate(savedInstanceState);
86*90c8c64dSAndroid Build Coastguard Worker         setContentView(R.layout.jj_layout);
87*90c8c64dSAndroid Build Coastguard Worker         setupViews();
88*90c8c64dSAndroid Build Coastguard Worker         mHandler = new Handler();
89*90c8c64dSAndroid Build Coastguard Worker         mJumpCounter = Utils.getCounterFromPreference(this);
90*90c8c64dSAndroid Build Coastguard Worker         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
91*90c8c64dSAndroid Build Coastguard Worker         renewTimer();
92*90c8c64dSAndroid Build Coastguard Worker         mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
93*90c8c64dSAndroid Build Coastguard Worker         mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
94*90c8c64dSAndroid Build Coastguard Worker     }
95*90c8c64dSAndroid Build Coastguard Worker 
setupViews()96*90c8c64dSAndroid Build Coastguard Worker     private void setupViews() {
97*90c8c64dSAndroid Build Coastguard Worker         mPager = (ViewPager) findViewById(R.id.pager);
98*90c8c64dSAndroid Build Coastguard Worker         mFirstIndicator = (ImageView) findViewById(R.id.indicator_0);
99*90c8c64dSAndroid Build Coastguard Worker         mSecondIndicator = (ImageView) findViewById(R.id.indicator_1);
100*90c8c64dSAndroid Build Coastguard Worker         final PagerAdapter adapter = new PagerAdapter(getFragmentManager());
101*90c8c64dSAndroid Build Coastguard Worker         mCounterPage = new CounterFragment();
102*90c8c64dSAndroid Build Coastguard Worker         mSettingPage = new SettingsFragment();
103*90c8c64dSAndroid Build Coastguard Worker         adapter.addFragment(mCounterPage);
104*90c8c64dSAndroid Build Coastguard Worker         adapter.addFragment(mSettingPage);
105*90c8c64dSAndroid Build Coastguard Worker         setIndicator(0);
106*90c8c64dSAndroid Build Coastguard Worker         mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
107*90c8c64dSAndroid Build Coastguard Worker             @Override
108*90c8c64dSAndroid Build Coastguard Worker             public void onPageScrolled(int i, float v, int i2) {
109*90c8c64dSAndroid Build Coastguard Worker             }
110*90c8c64dSAndroid Build Coastguard Worker 
111*90c8c64dSAndroid Build Coastguard Worker             @Override
112*90c8c64dSAndroid Build Coastguard Worker             public void onPageSelected(int i) {
113*90c8c64dSAndroid Build Coastguard Worker                 setIndicator(i);
114*90c8c64dSAndroid Build Coastguard Worker                 renewTimer();
115*90c8c64dSAndroid Build Coastguard Worker             }
116*90c8c64dSAndroid Build Coastguard Worker 
117*90c8c64dSAndroid Build Coastguard Worker             @Override
118*90c8c64dSAndroid Build Coastguard Worker             public void onPageScrollStateChanged(int i) {
119*90c8c64dSAndroid Build Coastguard Worker             }
120*90c8c64dSAndroid Build Coastguard Worker         });
121*90c8c64dSAndroid Build Coastguard Worker 
122*90c8c64dSAndroid Build Coastguard Worker         mPager.setAdapter(adapter);
123*90c8c64dSAndroid Build Coastguard Worker     }
124*90c8c64dSAndroid Build Coastguard Worker 
125*90c8c64dSAndroid Build Coastguard Worker     @Override
onResume()126*90c8c64dSAndroid Build Coastguard Worker     protected void onResume() {
127*90c8c64dSAndroid Build Coastguard Worker         super.onResume();
128*90c8c64dSAndroid Build Coastguard Worker         if (mSensorManager.registerListener(this, mSensor,
129*90c8c64dSAndroid Build Coastguard Worker                 SensorManager.SENSOR_DELAY_NORMAL)) {
130*90c8c64dSAndroid Build Coastguard Worker             if (Log.isLoggable(TAG, Log.DEBUG)) {
131*90c8c64dSAndroid Build Coastguard Worker                 Log.d(TAG, "Successfully registered for the sensor updates");
132*90c8c64dSAndroid Build Coastguard Worker             }
133*90c8c64dSAndroid Build Coastguard Worker         }
134*90c8c64dSAndroid Build Coastguard Worker     }
135*90c8c64dSAndroid Build Coastguard Worker 
136*90c8c64dSAndroid Build Coastguard Worker     @Override
onPause()137*90c8c64dSAndroid Build Coastguard Worker     protected void onPause() {
138*90c8c64dSAndroid Build Coastguard Worker         super.onPause();
139*90c8c64dSAndroid Build Coastguard Worker         mSensorManager.unregisterListener(this);
140*90c8c64dSAndroid Build Coastguard Worker         if (Log.isLoggable(TAG, Log.DEBUG)) {
141*90c8c64dSAndroid Build Coastguard Worker             Log.d(TAG, "Unregistered for sensor events");
142*90c8c64dSAndroid Build Coastguard Worker         }
143*90c8c64dSAndroid Build Coastguard Worker     }
144*90c8c64dSAndroid Build Coastguard Worker 
145*90c8c64dSAndroid Build Coastguard Worker     @Override
onSensorChanged(SensorEvent event)146*90c8c64dSAndroid Build Coastguard Worker     public void onSensorChanged(SensorEvent event) {
147*90c8c64dSAndroid Build Coastguard Worker         detectJump(event.values[0], event.timestamp);
148*90c8c64dSAndroid Build Coastguard Worker     }
149*90c8c64dSAndroid Build Coastguard Worker 
150*90c8c64dSAndroid Build Coastguard Worker     @Override
onAccuracyChanged(Sensor sensor, int accuracy)151*90c8c64dSAndroid Build Coastguard Worker     public void onAccuracyChanged(Sensor sensor, int accuracy) {
152*90c8c64dSAndroid Build Coastguard Worker     }
153*90c8c64dSAndroid Build Coastguard Worker 
154*90c8c64dSAndroid Build Coastguard Worker     /**
155*90c8c64dSAndroid Build Coastguard Worker      * A simple algorithm to detect a successful up-down movement of hand(s). The algorithm is
156*90c8c64dSAndroid Build Coastguard Worker      * based on the assumption that when a person is wearing the watch, the x-component of gravity
157*90c8c64dSAndroid Build Coastguard Worker      * as measured by the Gravity Sensor is +9.8 when the hand is downward and -9.8 when the hand
158*90c8c64dSAndroid Build Coastguard Worker      * is upward (signs are reversed if the watch is worn on the right hand). Since the upward or
159*90c8c64dSAndroid Build Coastguard Worker      * downward may not be completely accurate, we leave some room and instead of 9.8, we use
160*90c8c64dSAndroid Build Coastguard Worker      * GRAVITY_THRESHOLD. We also consider the up <-> down movement successful if it takes less than
161*90c8c64dSAndroid Build Coastguard Worker      * TIME_THRESHOLD_NS.
162*90c8c64dSAndroid Build Coastguard Worker      */
detectJump(float xValue, long timestamp)163*90c8c64dSAndroid Build Coastguard Worker     private void detectJump(float xValue, long timestamp) {
164*90c8c64dSAndroid Build Coastguard Worker         if ((Math.abs(xValue) > GRAVITY_THRESHOLD)) {
165*90c8c64dSAndroid Build Coastguard Worker             if(timestamp - mLastTime < TIME_THRESHOLD_NS && mUp != (xValue > 0)) {
166*90c8c64dSAndroid Build Coastguard Worker                 onJumpDetected(!mUp);
167*90c8c64dSAndroid Build Coastguard Worker             }
168*90c8c64dSAndroid Build Coastguard Worker             mUp = xValue > 0;
169*90c8c64dSAndroid Build Coastguard Worker             mLastTime = timestamp;
170*90c8c64dSAndroid Build Coastguard Worker         }
171*90c8c64dSAndroid Build Coastguard Worker     }
172*90c8c64dSAndroid Build Coastguard Worker 
173*90c8c64dSAndroid Build Coastguard Worker     /**
174*90c8c64dSAndroid Build Coastguard Worker      * Called on detection of a successful down -> up or up -> down movement of hand.
175*90c8c64dSAndroid Build Coastguard Worker      */
onJumpDetected(boolean up)176*90c8c64dSAndroid Build Coastguard Worker     private void onJumpDetected(boolean up) {
177*90c8c64dSAndroid Build Coastguard Worker         // we only count a pair of up and down as one successful movement
178*90c8c64dSAndroid Build Coastguard Worker         if (up) {
179*90c8c64dSAndroid Build Coastguard Worker             return;
180*90c8c64dSAndroid Build Coastguard Worker         }
181*90c8c64dSAndroid Build Coastguard Worker         mJumpCounter++;
182*90c8c64dSAndroid Build Coastguard Worker         setCounter(mJumpCounter);
183*90c8c64dSAndroid Build Coastguard Worker         renewTimer();
184*90c8c64dSAndroid Build Coastguard Worker     }
185*90c8c64dSAndroid Build Coastguard Worker 
186*90c8c64dSAndroid Build Coastguard Worker     /**
187*90c8c64dSAndroid Build Coastguard Worker      * Updates the counter on UI, saves it to preferences and vibrates the watch when counter
188*90c8c64dSAndroid Build Coastguard Worker      * reaches a multiple of 10.
189*90c8c64dSAndroid Build Coastguard Worker      */
setCounter(int i)190*90c8c64dSAndroid Build Coastguard Worker     private void setCounter(int i) {
191*90c8c64dSAndroid Build Coastguard Worker         mCounterPage.setCounter(i);
192*90c8c64dSAndroid Build Coastguard Worker         Utils.saveCounterToPreference(this, i);
193*90c8c64dSAndroid Build Coastguard Worker         if (i > 0 && i % 10 == 0) {
194*90c8c64dSAndroid Build Coastguard Worker             Utils.vibrate(this, 0);
195*90c8c64dSAndroid Build Coastguard Worker         }
196*90c8c64dSAndroid Build Coastguard Worker     }
197*90c8c64dSAndroid Build Coastguard Worker 
resetCounter()198*90c8c64dSAndroid Build Coastguard Worker     public void resetCounter() {
199*90c8c64dSAndroid Build Coastguard Worker         setCounter(0);
200*90c8c64dSAndroid Build Coastguard Worker         renewTimer();
201*90c8c64dSAndroid Build Coastguard Worker     }
202*90c8c64dSAndroid Build Coastguard Worker 
203*90c8c64dSAndroid Build Coastguard Worker     /**
204*90c8c64dSAndroid Build Coastguard Worker      * Starts a timer to clear the flag FLAG_KEEP_SCREEN_ON.
205*90c8c64dSAndroid Build Coastguard Worker      */
renewTimer()206*90c8c64dSAndroid Build Coastguard Worker     private void renewTimer() {
207*90c8c64dSAndroid Build Coastguard Worker         if (null != mTimer) {
208*90c8c64dSAndroid Build Coastguard Worker             mTimer.cancel();
209*90c8c64dSAndroid Build Coastguard Worker         }
210*90c8c64dSAndroid Build Coastguard Worker         mTimerTask = new TimerTask() {
211*90c8c64dSAndroid Build Coastguard Worker             @Override
212*90c8c64dSAndroid Build Coastguard Worker             public void run() {
213*90c8c64dSAndroid Build Coastguard Worker                 if (Log.isLoggable(TAG, Log.DEBUG)) {
214*90c8c64dSAndroid Build Coastguard Worker                     Log.d(TAG,
215*90c8c64dSAndroid Build Coastguard Worker                             "Removing the FLAG_KEEP_SCREEN_ON flag to allow going to background");
216*90c8c64dSAndroid Build Coastguard Worker                 }
217*90c8c64dSAndroid Build Coastguard Worker                 resetFlag();
218*90c8c64dSAndroid Build Coastguard Worker             }
219*90c8c64dSAndroid Build Coastguard Worker         };
220*90c8c64dSAndroid Build Coastguard Worker         mTimer = new Timer();
221*90c8c64dSAndroid Build Coastguard Worker         mTimer.schedule(mTimerTask, SCREEN_ON_TIMEOUT_MS);
222*90c8c64dSAndroid Build Coastguard Worker     }
223*90c8c64dSAndroid Build Coastguard Worker 
224*90c8c64dSAndroid Build Coastguard Worker     /**
225*90c8c64dSAndroid Build Coastguard Worker      * Resets the FLAG_KEEP_SCREEN_ON flag so activity can go into background.
226*90c8c64dSAndroid Build Coastguard Worker      */
resetFlag()227*90c8c64dSAndroid Build Coastguard Worker     private void resetFlag() {
228*90c8c64dSAndroid Build Coastguard Worker         mHandler.post(new Runnable() {
229*90c8c64dSAndroid Build Coastguard Worker             @Override
230*90c8c64dSAndroid Build Coastguard Worker             public void run() {
231*90c8c64dSAndroid Build Coastguard Worker                 if (Log.isLoggable(TAG, Log.DEBUG)) {
232*90c8c64dSAndroid Build Coastguard Worker                     Log.d(TAG, "Resetting FLAG_KEEP_SCREEN_ON flag to allow going to background");
233*90c8c64dSAndroid Build Coastguard Worker                 }
234*90c8c64dSAndroid Build Coastguard Worker                 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
235*90c8c64dSAndroid Build Coastguard Worker                 finish();
236*90c8c64dSAndroid Build Coastguard Worker             }
237*90c8c64dSAndroid Build Coastguard Worker         });
238*90c8c64dSAndroid Build Coastguard Worker     }
239*90c8c64dSAndroid Build Coastguard Worker 
240*90c8c64dSAndroid Build Coastguard Worker     /**
241*90c8c64dSAndroid Build Coastguard Worker      * Sets the page indicator for the ViewPager.
242*90c8c64dSAndroid Build Coastguard Worker      */
setIndicator(int i)243*90c8c64dSAndroid Build Coastguard Worker     private void setIndicator(int i) {
244*90c8c64dSAndroid Build Coastguard Worker         switch (i) {
245*90c8c64dSAndroid Build Coastguard Worker             case 0:
246*90c8c64dSAndroid Build Coastguard Worker                 mFirstIndicator.setImageResource(R.drawable.full_10);
247*90c8c64dSAndroid Build Coastguard Worker                 mSecondIndicator.setImageResource(R.drawable.empty_10);
248*90c8c64dSAndroid Build Coastguard Worker                 break;
249*90c8c64dSAndroid Build Coastguard Worker             case 1:
250*90c8c64dSAndroid Build Coastguard Worker                 mFirstIndicator.setImageResource(R.drawable.empty_10);
251*90c8c64dSAndroid Build Coastguard Worker                 mSecondIndicator.setImageResource(R.drawable.full_10);
252*90c8c64dSAndroid Build Coastguard Worker                 break;
253*90c8c64dSAndroid Build Coastguard Worker         }
254*90c8c64dSAndroid Build Coastguard Worker     }
255*90c8c64dSAndroid Build Coastguard Worker 
256*90c8c64dSAndroid Build Coastguard Worker 
257*90c8c64dSAndroid Build Coastguard Worker }
258