Skip to content

Most visited

Recently visited

navigation
JumpingJack / src / com.example.android.wearable.jumpingjack /

MainActivity.java

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