Java tutorial
/* * Copyright 2014 Thomas Hoffmann * * 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 ngoc.com.pedometer.ui; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.support.annotation.ColorRes; import android.support.annotation.DimenRes; import android.support.annotation.IntegerRes; import android.support.design.widget.NavigationView; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.method.LinkMovementMethod; import android.util.Pair; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.dd.morphingbutton.MorphingButton; import org.eazegraph.lib.charts.BarChart; import org.eazegraph.lib.charts.PieChart; import org.eazegraph.lib.models.BarModel; import org.eazegraph.lib.models.PieModel; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import ngoc.com.pedometer.BuildConfig; import ngoc.com.pedometer.Database; import ngoc.com.pedometer.R; import ngoc.com.pedometer.util.Util; import ngoc.com.pedometer.SensorListener; import ngoc.com.pedometer.util.Logger; public class Fragment_Overview extends Fragment implements SensorEventListener { private TextView stepsView, totalView, averageView; private PieModel sliceGoal, sliceCurrent; private PieChart pg; private int todayOffset, total_start, goal, since_boot, total_days; public final static NumberFormat formatter = NumberFormat.getInstance(Locale.getDefault()); private boolean showSteps = true; private NavigationView navigationView; private DrawerLayout drawer; private Toolbar toolbar; private View v; private int mMorphCounter1 = 1; private View navHeader; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { v = inflater.inflate(R.layout.fragment_overview, null); stepsView = (TextView) v.findViewById(R.id.steps); totalView = (TextView) v.findViewById(R.id.total); averageView = (TextView) v.findViewById(R.id.average); drawer = (DrawerLayout) v.findViewById(R.id.drawer_layout); navigationView = (NavigationView) v.findViewById(R.id.nvView); pg = (PieChart) v.findViewById(R.id.graph); toolbar = (Toolbar) v.findViewById(R.id.toolbar); // Navigation view header navHeader = navigationView.getHeaderView(0); // slice for the steps taken today sliceCurrent = new PieModel("", 0, Color.parseColor("#99CC00")); pg.addPieSlice(sliceCurrent); // slice for the "missing" steps until reaching the goal sliceGoal = new PieModel("", Fragment_Settings.DEFAULT_GOAL, Color.parseColor("#CC0000")); pg.addPieSlice(sliceGoal); pg.setOnClickListener(new OnClickListener() { @Override public void onClick(final View view) { showSteps = !showSteps; stepsDistanceChanged(); } }); pg.setDrawValueInPie(false); pg.setUsePieRotation(true); pg.startAnimation(); setUpNavigationView(); setMorphingButton(); setToolbarTitle(); return v; } private void setToolbarTitle() { toolbar.setTitle(getString(R.string.app_name)); } private void setUpNavigationView() { //Setting Navigation View Item Selected Listener to handle the item click of the navigation menu navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { // This method will trigger on item Click of navigation menu @Override public boolean onNavigationItemSelected(MenuItem menuItem) { //Check to see which item was being clicked and perform appropriate action switch (menuItem.getItemId()) { //Replacing the main content with ContentFragment Which is our Inbox View; case R.id.action_settings: drawer.closeDrawers(); ActivityOptionsCompat options = ActivityOptionsCompat.makeCustomAnimation(getActivity(), R.anim.push_in_to_right, R.anim.push_in_to_left); Intent intent = new Intent(getActivity(), SettingActivity.class); ((Activity) getActivity()).startActivity(intent, options.toBundle()); break; case R.id.action_split_count: drawer.closeDrawers(); Dialog_Split.getDialog(getActivity(), total_start + Math.max(todayOffset + since_boot, 0)) .show(); break; case R.id.action_faq: drawer.closeDrawers(); FAQDialog.getInstance().show(getActivity()); break; case R.id.action_about: drawer.closeDrawers(); showAboutDialog(); break; default: } //Checking if the item is in checked state or not, if not make it in checked state if (menuItem.isChecked()) { menuItem.setChecked(false); } else { menuItem.setChecked(true); } menuItem.setChecked(true); return true; } }); ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(getActivity(), drawer, toolbar, R.string.openDrawer, R.string.closeDrawer) { @Override public void onDrawerClosed(View drawerView) { // Code here will be triggered once the drawer closes as we dont want anything to happen so we leave this blank super.onDrawerClosed(drawerView); } @Override public void onDrawerOpened(View drawerView) { // Code here will be triggered once the drawer open as we dont want anything to happen so we leave this blank super.onDrawerOpened(drawerView); } }; //Setting the actionbarToggle to drawer layout drawer.addDrawerListener(actionBarDrawerToggle); //calling sync state is necessary or else your hamburger icon wont show up actionBarDrawerToggle.syncState(); } private void showAboutDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.about); TextView tv = new TextView(getActivity()); tv.setPadding(10, 10, 10, 10); tv.setText(R.string.about_text_links); try { tv.append(getString(R.string.about_app_version, getActivity().getPackageManager() .getPackageInfo(getActivity().getPackageName(), 0).versionName)); } catch (PackageManager.NameNotFoundException e1) { // should not happen as the app is definitely installed when // seeing the dialog e1.printStackTrace(); } tv.setMovementMethod(LinkMovementMethod.getInstance()); builder.setView(tv); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.create().show(); } private void setMorphingButton() { final MorphingButton btnMorph1 = (MorphingButton) v.findViewById(R.id.btnMorph1); btnMorph1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onMorphButton1Clicked(btnMorph1); startCountOrPause(); } }); morphToSquare(btnMorph1, integer(R.integer.mb_animation)); } private void onMorphButton1Clicked(final MorphingButton btnMorph) { if (mMorphCounter1 == 0) { mMorphCounter1++; morphToSquare(btnMorph, integer(R.integer.mb_animation)); } else if (mMorphCounter1 == 1) { mMorphCounter1 = 0; morphToSuccess(btnMorph); } } private void startCountOrPause() { SensorManager sm = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE); if (getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE).contains("pauseCount")) { // currently paused -> now resumed sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI, 0); } else { sm.unregisterListener(this); } getActivity().startService( new Intent(getActivity(), SensorListener.class).putExtra("action", SensorListener.ACTION_PAUSE)); } private void morphToSquare(final MorphingButton btnMorph, int duration) { MorphingButton.Params square = MorphingButton.Params.create().duration(duration) .cornerRadius(dimen(R.dimen.mb_height_56)).width(dimen(R.dimen.mb_width_200)) .height(dimen(R.dimen.mb_height_56)).color(color(R.color.mb_green)) .colorPressed(color(R.color.mb_green_dark)).text(getString(R.string.start)); btnMorph.morph(square); } private void morphToSuccess(final MorphingButton btnMorph) { MorphingButton.Params circle = MorphingButton.Params.create().duration(integer(R.integer.mb_animation)) .cornerRadius(dimen(R.dimen.mb_height_56)).width(dimen(R.dimen.mb_width_120)) .height(dimen(R.dimen.mb_height_56)).color(color(R.color.mb_red)) .colorPressed(color(R.color.mb_red_dark)).icon(R.drawable.ic_action_cancel) .text(getString(R.string.pause)); btnMorph.morph(circle); } public int dimen(@DimenRes int resId) { return (int) getResources().getDimension(resId); } public int color(@ColorRes int resId) { return ContextCompat.getColor(getActivity(), resId); } public int integer(@IntegerRes int resId) { return getResources().getInteger(resId); } @Override public void onResume() { super.onResume(); // getActivity().getActionBar().setDisplayHomeAsUpEnabled(false); Database db = Database.getInstance(getActivity()); if (BuildConfig.DEBUG) db.logState(); // read todays offset todayOffset = db.getSteps(Util.getToday()); SharedPreferences prefs = getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE); goal = prefs.getInt("goal", Fragment_Settings.DEFAULT_GOAL); since_boot = db.getCurrentSteps(); // do not use the value from the sharedPreferences int pauseDifference = since_boot - prefs.getInt("pauseCount", since_boot); // register a sensorlistener to live update the UI if a step is taken if (!prefs.contains("pauseCount")) { SensorManager sm = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE); Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); if (sensor == null) { new AlertDialog.Builder(getActivity()).setTitle(R.string.no_sensor) .setMessage(R.string.no_sensor_explain) .setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(final DialogInterface dialogInterface) { getActivity().finish(); } }).setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }).create().show(); } else { sm.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI, 0); } } since_boot -= pauseDifference; total_start = db.getTotalWithoutToday(); total_days = db.getDays(); db.close(); stepsDistanceChanged(); } /** * Call this method if the Fragment should update the "steps"/"km" text in * the pie graph as well as the pie and the bars graphs. */ private void stepsDistanceChanged() { if (showSteps) { ((TextView) getView().findViewById(R.id.unit)).setText(getString(R.string.steps)); } else { String unit = getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE) .getString("stepsize_unit", Fragment_Settings.DEFAULT_STEP_UNIT); if (unit.equals("cm")) { unit = "km"; } else { unit = "mi"; } ((TextView) getView().findViewById(R.id.unit)).setText(unit); } updatePie(); updateBars(); } @Override public void onPause() { super.onPause(); try { SensorManager sm = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE); sm.unregisterListener(this); } catch (Exception e) { e.printStackTrace(); } Database db = Database.getInstance(getActivity()); db.saveCurrentSteps(since_boot); db.close(); } @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { // inflater.inflate(R.menu.main, menu); // MenuItem pause = menu.getItem(0); // Drawable d; // if (getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE) // .contains("pauseCount")) { // currently paused // pause.setTitle(R.string.resume); // d = getResources().getDrawable(R.drawable.ic_resume); // } else { // pause.setTitle(R.string.pause); // d = getResources().getDrawable(R.drawable.ic_pause); // } // d.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP); // pause.setIcon(d); } @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.action_split_count: Dialog_Split.getDialog(getActivity(), total_start + Math.max(todayOffset + since_boot, 0)).show(); return true; case R.id.action_pause: SensorManager sm = (SensorManager) getActivity().getSystemService(Context.SENSOR_SERVICE); Drawable d; if (getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE).contains("pauseCount")) { // currently paused -> now resumed sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI, 0); item.setTitle(R.string.pause); d = getResources().getDrawable(R.drawable.ic_pause); } else { sm.unregisterListener(this); item.setTitle(R.string.resume); d = getResources().getDrawable(R.drawable.ic_resume); } d.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP); item.setIcon(d); getActivity().startService(new Intent(getActivity(), SensorListener.class).putExtra("action", SensorListener.ACTION_PAUSE)); return true; default: return ((Activity_Main) getActivity()).optionsItemSelected(item); } } @Override public void onAccuracyChanged(final Sensor sensor, int accuracy) { // won't happen } @Override public void onSensorChanged(final SensorEvent event) { if (BuildConfig.DEBUG) Logger.log("UI - sensorChanged | todayOffset: " + todayOffset + " since boot: " + event.values[0]); if (event.values[0] > Integer.MAX_VALUE || event.values[0] == 0) { return; } if (todayOffset == Integer.MIN_VALUE) { // no values for today // we dont know when the reboot was, so set todays steps to 0 by // initializing them with -STEPS_SINCE_BOOT todayOffset = -(int) event.values[0]; Database db = Database.getInstance(getActivity()); db.insertNewDay(Util.getToday(), (int) event.values[0]); db.close(); } since_boot = (int) event.values[0]; updatePie(); } /** * Updates the pie graph to show todays steps/distance as well as the * yesterday and total values. Should be called when switching from step * count to distance. */ private void updatePie() { if (BuildConfig.DEBUG) Logger.log("UI - update steps: " + since_boot); // todayOffset might still be Integer.MIN_VALUE on first start int steps_today = Math.max(todayOffset + since_boot, 0); sliceCurrent.setValue(steps_today); if (goal - steps_today > 0) { // goal not reached yet if (pg.getData().size() == 1) { // can happen if the goal value was changed: old goal value was // reached but now there are some steps missing for the new goal pg.addPieSlice(sliceGoal); } sliceGoal.setValue(goal - steps_today); } else { // goal reached pg.clearChart(); pg.addPieSlice(sliceCurrent); } pg.update(); if (showSteps) { stepsView.setText(formatter.format(steps_today)); totalView.setText(formatter.format(total_start + steps_today)); averageView.setText(formatter.format((total_start + steps_today) / total_days)); } else { // update only every 10 steps when displaying distance SharedPreferences prefs = getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE); float stepsize = prefs.getFloat("stepsize_value", Fragment_Settings.DEFAULT_STEP_SIZE); float distance_today = steps_today * stepsize; float distance_total = (total_start + steps_today) * stepsize; if (prefs.getString("stepsize_unit", Fragment_Settings.DEFAULT_STEP_UNIT).equals("cm")) { distance_today /= 100000; distance_total /= 100000; } else { distance_today /= 5280; distance_total /= 5280; } stepsView.setText(formatter.format(distance_today)); totalView.setText(formatter.format(distance_total)); averageView.setText(formatter.format(distance_total / total_days)); } } /** * Updates the bar graph to show the steps/distance of the last week. Should * be called when switching from step count to distance. */ private void updateBars() { SimpleDateFormat df = new SimpleDateFormat("E", Locale.getDefault()); BarChart barChart = (BarChart) getView().findViewById(R.id.bargraph); if (barChart.getData().size() > 0) barChart.clearChart(); int steps; float distance, stepsize = Fragment_Settings.DEFAULT_STEP_SIZE; boolean stepsize_cm = true; if (!showSteps) { // load some more settings if distance is needed SharedPreferences prefs = getActivity().getSharedPreferences("pedometer", Context.MODE_PRIVATE); stepsize = prefs.getFloat("stepsize_value", Fragment_Settings.DEFAULT_STEP_SIZE); stepsize_cm = prefs.getString("stepsize_unit", Fragment_Settings.DEFAULT_STEP_UNIT).equals("cm"); } barChart.setShowDecimal(!showSteps); // show decimal in distance view only BarModel bm; Database db = Database.getInstance(getActivity()); List<Pair<Long, Integer>> last = db.getLastEntries(8); db.close(); for (int i = last.size() - 1; i > 0; i--) { Pair<Long, Integer> current = last.get(i); steps = current.second; if (steps > 0) { bm = new BarModel(df.format(new Date(current.first)), 0, steps > goal ? Color.parseColor("#99CC00") : Color.parseColor("#0099cc")); if (showSteps) { bm.setValue(steps); } else { distance = steps * stepsize; if (stepsize_cm) { distance /= 100000; } else { distance /= 5280; } distance = Math.round(distance * 1000) / 1000f; // 3 decimals bm.setValue(distance); } barChart.addBar(bm); } } if (barChart.getData().size() > 0) { barChart.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { Dialog_Statistics.getDialog(getActivity(), since_boot).show(); } }); barChart.startAnimation(); } else { barChart.setVisibility(View.GONE); } } }