diff --git a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java index b3579b30..197c5c0e 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java @@ -59,6 +59,7 @@ import com.health.openscale.gui.fragments.OverviewFragment; import com.health.openscale.gui.fragments.StatisticsFragment; import com.health.openscale.gui.fragments.TableFragment; import com.health.openscale.gui.utils.PermissionHelper; +import com.health.openscale.gui.views.MeasurementView; import java.lang.reflect.Field; @@ -78,6 +79,8 @@ public class MainActivity extends AppCompatActivity private BottomNavigationView navBottomDrawer; private ActionBarDrawerToggle drawerToggle; + private boolean settingsActivityRunning = false; + @Override protected void onCreate(Bundle savedInstanceState) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -90,6 +93,9 @@ public class MainActivity extends AppCompatActivity super.onCreate(savedInstanceState); + PreferenceManager.getDefaultSharedPreferences(this) + .registerOnSharedPreferenceChangeListener(this); + CaocConfig.Builder.create() .trackActivities(true) .apply(); @@ -183,33 +189,24 @@ public class MainActivity extends AppCompatActivity } } - private void registerOnSharedPreferenceChangeListener() { - PreferenceManager.getDefaultSharedPreferences(this) - .registerOnSharedPreferenceChangeListener(this); - } - - private void unregisterOnSharedPreferenceChangeListener() { - PreferenceManager.getDefaultSharedPreferences(this) - .unregisterOnSharedPreferenceChangeListener(this); - } - @Override public void onResume() { super.onResume(); - // Stop listening when returning from settings - unregisterOnSharedPreferenceChangeListener(); + settingsActivityRunning = false; } @Override public void onDestroy() { - // Clean up when shutting down - unregisterOnSharedPreferenceChangeListener(); + PreferenceManager.getDefaultSharedPreferences(this) + .unregisterOnSharedPreferenceChangeListener(this); super.onDestroy(); } @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { - recreate(); + if (settingsActivityRunning || key == MeasurementView.PREF_MEASUREMENT_ORDER) { + recreate(); + } } private void positiveFeedbackDialog() { @@ -307,10 +304,10 @@ public class MainActivity extends AppCompatActivity prefs.edit().putInt("lastFragmentId", menuItemId).commit(); break; case R.id.nav_settings: - registerOnSharedPreferenceChangeListener(); Intent settingsIntent = new Intent(this, SettingsActivity.class); settingsIntent.putExtra(SettingsActivity.EXTRA_TINT_COLOR, navDrawer.getItemTextColor().getDefaultColor()); startActivity(settingsIntent); + settingsActivityRunning = true; drawerLayout.closeDrawers(); return; case R.id.nav_help: diff --git a/android_app/app/src/main/java/com/health/openscale/gui/activities/DataEntryActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/activities/DataEntryActivity.java index 521f37b2..8ceb9d62 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/activities/DataEntryActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/activities/DataEntryActivity.java @@ -20,15 +20,18 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Color; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.view.DragEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; @@ -127,6 +130,16 @@ public class DataEntryActivity extends AppCompatActivity { : MeasurementView.MeasurementViewMode.VIEW; for (MeasurementView measurement : dataEntryMeasurements) { measurement.setEditMode(mode); + + // Date and time can not be reordered (as they can be both first and last) + if (measurement instanceof DateMeasurementView || measurement instanceof TimeMeasurementView) { + continue; + } + + onLongClickListener longClickListener = new onLongClickListener(); + measurement.setOnTouchListener(longClickListener); + measurement.setOnLongClickListener(longClickListener); + measurement.setOnDragListener(new onDragListener()); } updateOnView(); @@ -456,4 +469,95 @@ public class DataEntryActivity extends AppCompatActivity { } } } + + private class onLongClickListener implements View.OnTouchListener, View.OnLongClickListener { + float x = 0; + float y = 0; + + @Override + public boolean onTouch(View view, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + // Save x and y so that the drag shadow can have the touch point set to where + // the user did the touch (and not in the center of the view). + x = event.getX(); + y = event.getY(); + } + return false; + } + + @Override + public boolean onLongClick(View view) { + return view.startDrag(null, new dragShadowBuilder(view), view, 0); + } + + private class dragShadowBuilder extends View.DragShadowBuilder { + public dragShadowBuilder(View view) { + super(view); + } + + @Override + public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { + super.onProvideShadowMetrics(outShadowSize, outShadowTouchPoint); + outShadowTouchPoint.set(Math.round(x), Math.round(y)); + } + } + } + + private class onDragListener implements View.OnDragListener { + Drawable background = null; + // background may be set to null, thus the extra boolean + boolean hasBackground = false; + + @Override + public boolean onDrag(View view, DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + if (view == event.getLocalState() && !hasBackground) { + background = view.getBackground(); + hasBackground = true; + view.setBackgroundColor(Color.GRAY); + } + break; + case DragEvent.ACTION_DRAG_LOCATION: + // Ignore + break; + case DragEvent.ACTION_DRAG_ENTERED: + if (view != event.getLocalState()) { + background = view.getBackground(); + hasBackground = true; + view.setBackgroundColor(Color.LTGRAY); + } + break; + case DragEvent.ACTION_DRAG_EXITED: + if (view != event.getLocalState() && hasBackground) { + view.setBackground(background); + background = null; + hasBackground = false; + } + break; + case DragEvent.ACTION_DROP: + View draggedView = (View) event.getLocalState(); + TableLayout table = (TableLayout) draggedView.getParent(); + final int draggedIndex = table.indexOfChild(draggedView); + final int targetIndex = table.indexOfChild(view); + if (draggedIndex != targetIndex) { + // A view that is moved down is placed after the target view, + // and a view that is moved up is placed before the target view. + table.removeView(draggedView); + table.addView(draggedView, targetIndex); + MeasurementView.saveMeasurementViewsOrder(table); + } + break; + case DragEvent.ACTION_DRAG_ENDED: + if (hasBackground) { + // Restore background + view.setBackground(background); + background = null; + hasBackground = false; + } + break; + } + return true; + } + } } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/views/MeasurementView.java b/android_app/app/src/main/java/com/health/openscale/gui/views/MeasurementView.java index 39b6b600..e274b234 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/views/MeasurementView.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/views/MeasurementView.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -58,6 +59,8 @@ import static com.health.openscale.gui.views.MeasurementView.MeasurementViewMode public abstract class MeasurementView extends TableLayout { public enum MeasurementViewMode {VIEW, EDIT, ADD, STATISTIC} + public static String PREF_MEASUREMENT_ORDER = "measurementOrder"; + private TableRow measurementRow; private ImageView iconView; private TextView nameView; @@ -84,38 +87,76 @@ public abstract class MeasurementView extends TableLayout { public enum DateTimeOrder { FIRST, LAST, NONE } - public static final List getMeasurementList(Context context, DateTimeOrder order) { - final List measurementViews = new ArrayList<>(); - - if (order == DateTimeOrder.FIRST) { - measurementViews.add(new DateMeasurementView(context)); - measurementViews.add(new TimeMeasurementView(context)); - } - measurementViews.add(new WeightMeasurementView(context)); - measurementViews.add(new BMIMeasurementView(context)); - measurementViews.add(new WaterMeasurementView(context)); - measurementViews.add(new MuscleMeasurementView(context)); - measurementViews.add(new LBWMeasurementView(context)); - measurementViews.add(new FatMeasurementView(context)); - measurementViews.add(new BoneMeasurementView(context)); - measurementViews.add(new WaistMeasurementView(context)); - measurementViews.add(new WHtRMeasurementView(context)); - measurementViews.add(new HipMeasurementView(context)); - measurementViews.add(new WHRMeasurementView(context)); - measurementViews.add(new BMRMeasurementView(context)); - measurementViews.add(new CommentMeasurementView(context)); - if (order == DateTimeOrder.LAST) { - measurementViews.add(new DateMeasurementView(context)); - measurementViews.add(new TimeMeasurementView(context)); + public static final List getMeasurementList(Context context, DateTimeOrder dateTimeOrder) { + final List sorted = new ArrayList<>(); + if (dateTimeOrder == DateTimeOrder.FIRST) { + sorted.add(new DateMeasurementView(context)); + sorted.add(new TimeMeasurementView(context)); } SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - for (MeasurementView measurement : measurementViews) { + { + final List unsorted = new ArrayList<>(); + + unsorted.add(new WeightMeasurementView(context)); + unsorted.add(new BMIMeasurementView(context)); + unsorted.add(new WaterMeasurementView(context)); + unsorted.add(new MuscleMeasurementView(context)); + unsorted.add(new LBWMeasurementView(context)); + unsorted.add(new FatMeasurementView(context)); + unsorted.add(new BoneMeasurementView(context)); + unsorted.add(new WaistMeasurementView(context)); + unsorted.add(new WHtRMeasurementView(context)); + unsorted.add(new HipMeasurementView(context)); + unsorted.add(new WHRMeasurementView(context)); + unsorted.add(new BMRMeasurementView(context)); + unsorted.add(new CommentMeasurementView(context)); + + // Get sort order + final String[] sortOrder = TextUtils.split( + prefs.getString(PREF_MEASUREMENT_ORDER, ""), ","); + + // Move views from unsorted to sorted in the correct order + for (String key : sortOrder) { + for (MeasurementView measurement : unsorted) { + if (key.equals(measurement.getKey())) { + sorted.add(measurement); + unsorted.remove(measurement); + break; + } + } + } + + // Any new views end up at the end + sorted.addAll(unsorted); + } + + if (dateTimeOrder == DateTimeOrder.LAST) { + sorted.add(new DateMeasurementView(context)); + sorted.add(new TimeMeasurementView(context)); + } + + for (MeasurementView measurement : sorted) { measurement.updatePreferences(prefs); } - return measurementViews; + return sorted; + } + + public static void saveMeasurementViewsOrder(TableLayout tableLayout) { + ArrayList order = new ArrayList<>(); + for (int i = 0; i < tableLayout.getChildCount(); ++i) { + MeasurementView view = (MeasurementView) tableLayout.getChildAt(i); + if (view instanceof DateMeasurementView || view instanceof TimeMeasurementView) { + continue; + } + order.add(view.getKey()); + } + PreferenceManager.getDefaultSharedPreferences(tableLayout.getContext()) + .edit() + .putString(PREF_MEASUREMENT_ORDER, TextUtils.join(",", order)) + .commit(); } private void initView(Context context) { @@ -180,9 +221,7 @@ public abstract class MeasurementView extends TableLayout { evaluatorView.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.99f)); spaceAfterEvaluatorView.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.01f)); - onClickListenerEvaluation onClickListener = new onClickListenerEvaluation(); - measurementRow.setOnClickListener(onClickListener); - evaluatorRow.setOnClickListener(onClickListener); + setOnClickListener(new onClickListenerEvaluation()); } protected LinearLayout getIncDecLayout() {