diff --git a/app/res/menu/detail_menu.xml b/app/res/menu/detail_menu.xml index f2c73e473..6295a80f8 100644 --- a/app/res/menu/detail_menu.xml +++ b/app/res/menu/detail_menu.xml @@ -32,8 +32,8 @@ android:orderInCategory="100" android:title="@string/Delete_activity"/> diff --git a/app/src/org/runnerup/db/ActivityMerger.java b/app/src/org/runnerup/db/ActivityMerger.java new file mode 100644 index 000000000..f206a7956 --- /dev/null +++ b/app/src/org/runnerup/db/ActivityMerger.java @@ -0,0 +1,125 @@ +package org.runnerup.db; + +/* + * Copyright (C) 2013 jonas.oreland@gmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import android.content.ContentValues; +import android.database.sqlite.SQLiteDatabase; + +import org.runnerup.common.util.Constants; + + +public class ActivityMerger implements Constants { + public static boolean canMerge(SQLiteDatabase db, long activityId) { + // previous activity must exist and be the same activity type + // there is a deleted flag in the DB, but it doesn't seem to be used? + // I deleted a previous activity (id = 50) and I successfully get the + // (undeleted) previous activity (id = 49)... + try { + long previousActivityId = previousActivityId(db, activityId); + long type = activityType(db, activityId); + long previousType = activityType(db, previousActivityId); + return type == previousType; + } catch (Exception e) { + return false; + } + } + + public static void merge(SQLiteDatabase db, long activityId) { + long previousActivityId = previousActivityId(db, activityId); + + // update the 3 activity tables - LOCATION, LAP and ACTIVITY + // through this operation we re-order the new laps based on the older laps + // and move everything to the current activity ID + updateLocation(db, activityId, previousActivityId); + updateLap(db, activityId, previousActivityId); + updateActivity(db, activityId, previousActivityId); + + // cleanup + new ActivityCleaner().recompute(db, activityId); + DBHelper.deleteActivity(db, previousActivityId); + } + + private static void updateActivity(SQLiteDatabase db, long activityId, long previousActivityId) { + // update current activity start time with older activity start time + db.execSQL("UPDATE " + DB.ACTIVITY.TABLE + " SET " + DB.ACTIVITY.START_TIME + " = " + startTime(db, previousActivityId) + + " WHERE _id = " + activityId); + } + + private static void updateLap(SQLiteDatabase db, long activityId, long previousActivityId) { + // # VERIFY THAT time IS UPDATED BY recompute IF NECESSARY + + // newer activity lap numbers should continue where previousActivity laps stopped + // lap: old min = 0, new min = 0, old max = x, new max = y + long nextLap = lapMinMax(db, DB.LAP.TABLE, "MAX", previousActivityId) + 1; + // has this code already run? if so, don't re-run it. + if (lapMinMax(db, DB.LAP.TABLE, "MIN", activityId) == 0 && nextLap !=0) + db.execSQL("UPDATE " + DB.LAP.TABLE + " SET " + DB.LAP.LAP + "=" + DB.LAP.LAP + "+" + nextLap + + " WHERE " + DB.LAP.ACTIVITY + "=" + activityId); + + // assign all older laps to the current activity + db.execSQL("UPDATE " + DB.LAP.TABLE + " SET " + DB.LAP.ACTIVITY + " = " + activityId + + " WHERE " + DB.LAP.ACTIVITY + " = " + previousActivityId); + } + + private static void updateLocation(SQLiteDatabase db, long activityId, long previousActivityId) { + // location: set middle start/end type to pause/resume + ContentValues values = new ContentValues(); + values.put(DB.LOCATION.TYPE, DB.LOCATION.TYPE_PAUSE); + db.update(DB.LOCATION.TABLE, values, DB.LOCATION.TYPE + "=" + DB.LOCATION.TYPE_END + " AND " + DB.LOCATION.ACTIVITY + "=" + previousActivityId, null); + values = new ContentValues(); + values.put(DB.LOCATION.TYPE, DB.LOCATION.TYPE_RESUME); + db.update(DB.LOCATION.TABLE, values, DB.LOCATION.TYPE + "=" + DB.LOCATION.TYPE_START + " AND " + DB.LOCATION.ACTIVITY + "=" + activityId, null); + + // location: newer lap numbers should continue where previousActivity laps stopped + long nextLap = lapMinMax(db, DB.LOCATION.TABLE, "MAX", previousActivityId) + 1; + // has this code already run? if so, don't re-run it. + if (lapMinMax(db, DB.LOCATION.TABLE, "MIN", activityId) == 0 && nextLap !=0) + db.execSQL("UPDATE " + DB.LOCATION.TABLE + " SET " + DB.LOCATION.LAP + "=" + DB.LOCATION.LAP + "+" + nextLap + + " WHERE " + DB.LOCATION.ACTIVITY + "=" + activityId); + + // location: assign newer activityId to older locations + values = new ContentValues(); + values.put(DB.LOCATION.ACTIVITY, activityId); + db.update(DB.LOCATION.TABLE, values, DB.LOCATION.ACTIVITY + "=" + previousActivityId, null); + } + + private static long activityType(SQLiteDatabase db, long activityId) { + return db.compileStatement("SELECT type FROM " + DB.ACTIVITY.TABLE + + " WHERE _id = " + activityId).simpleQueryForLong(); + } + + private static long previousActivityId(SQLiteDatabase db, long activityId) { + return db.compileStatement("SELECT MAX(_id) FROM " + DB.ACTIVITY.TABLE + + " WHERE _id < " + activityId).simpleQueryForLong(); + } + + private static long startTime(SQLiteDatabase db, long activityId) { + return db.compileStatement("SELECT " + DB.ACTIVITY.START_TIME + " FROM " + DB.ACTIVITY.TABLE + + " WHERE _id = " + activityId).simpleQueryForLong(); + } + + private static long lapMinMax(SQLiteDatabase db, String table, String minMax, long activityId) { + try { + return db.compileStatement("SELECT " + minMax + "(" + DB.LAP.LAP + ") FROM " + table + + " WHERE " + DB.LAP.ACTIVITY + "=" + activityId).simpleQueryForLong(); + } catch (Exception e) { + // no laps, return -1 + return -1; + } + } +} diff --git a/app/src/org/runnerup/view/DetailActivity.java b/app/src/org/runnerup/view/DetailActivity.java index 73f2e9c9b..87145f1e8 100644 --- a/app/src/org/runnerup/view/DetailActivity.java +++ b/app/src/org/runnerup/view/DetailActivity.java @@ -72,8 +72,8 @@ import org.runnerup.content.ActivityProvider; import org.runnerup.content.WorkoutFileProvider; import org.runnerup.db.ActivityCleaner; +import org.runnerup.db.ActivityMerger; import org.runnerup.db.DBHelper; -import org.runnerup.db.entities.ActivityEntity; import org.runnerup.db.entities.LocationEntity; import org.runnerup.export.SyncManager; import org.runnerup.export.Synchronizer; @@ -122,7 +122,7 @@ public class DetailActivity extends FragmentActivity implements Constants { TitleSpinner sport = null; EditText notes = null; - MenuItem recomputeMenuItem = null; + MenuItem mergeMenuItem = null; MapView map = null; AsyncTask loadRouteTask = null; @@ -269,15 +269,16 @@ private void setEdit(boolean value) { uploadButton.setEnabled(value); WidgetUtil.setEditable(notes, value); sport.setEnabled(value); - if (recomputeMenuItem != null) - recomputeMenuItem.setEnabled(value); + if (mergeMenuItem != null) { + mergeMenuItem.setEnabled(value); + } } @Override public boolean onCreateOptionsMenu(Menu menu) { if (mode == MODE_DETAILS) { getMenuInflater().inflate(R.menu.detail_menu, menu); - recomputeMenuItem = menu.findItem(R.id.menu_recompute_activity); + mergeMenuItem = menu.findItem(R.id.menu_merge_activity); } return true; } @@ -295,9 +296,26 @@ public boolean onOptionsItemSelected(MenuItem item) { requery(); } break; - case R.id.menu_recompute_activity: - new ActivityCleaner().recompute(mDB, mID); - requery(); + case R.id.menu_merge_activity: + if (ActivityMerger.canMerge(mDB, mID)) { + ActivityMerger.merge(mDB, mID); + requery(); + fillHeaderData(); + loadRoute(); + } else { + AlertDialog.Builder builder1 = new AlertDialog.Builder(this); + builder1.setMessage("Previous activity must be of the same type to merge."); + builder1.setCancelable(true); + builder1.setNeutralButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert11 = builder1.create(); + alert11.show(); + } break; case R.id.menu_share_activity: shareActivity(); diff --git a/common/src/main/res/values-de/strings.xml b/common/src/main/res/values-de/strings.xml index e636ded44..631ee748d 100644 --- a/common/src/main/res/values-de/strings.xml +++ b/common/src/main/res/values-de/strings.xml @@ -127,7 +127,7 @@ Aktivität bearbeiten Aktivität teilen Aktivität löschen - Aktivität neu berechnen + Aktivität neu berechnen Herzfrequenzzonen konfigurieren Herzfrequenzzoneneinstellungen zurücksetzen Benutzerkonten verwalten diff --git a/common/src/main/res/values-en-rNL/strings.xml b/common/src/main/res/values-en-rNL/strings.xml index 2499bdbae..ada2af896 100644 --- a/common/src/main/res/values-en-rNL/strings.xml +++ b/common/src/main/res/values-en-rNL/strings.xml @@ -125,7 +125,7 @@ Activiteit bewerken Activiteit delen Activiteit verwijderen - Activiteit herberekenen + Activiteit herberekenen Hartslagzones instellen Hartslagzone instellingen verwijderen Accounts beheren diff --git a/common/src/main/res/values-fr/strings.xml b/common/src/main/res/values-fr/strings.xml index e211d27dc..165e40ac1 100644 --- a/common/src/main/res/values-fr/strings.xml +++ b/common/src/main/res/values-fr/strings.xml @@ -127,7 +127,7 @@ Éditer la séance Partager la séance Supprimer la séance - Recalculer la séance + Recalculer la séance Configurer les zones de fréq. cardiaque Supprimer les paramètres de zone de fréq. cardiaque Gérer les comptes diff --git a/common/src/main/res/values-it/strings.xml b/common/src/main/res/values-it/strings.xml index c9397eae8..acaf4c08e 100644 --- a/common/src/main/res/values-it/strings.xml +++ b/common/src/main/res/values-it/strings.xml @@ -125,7 +125,7 @@ Modifica attività Condividi attività Elimina attività - Ricalcola attività + Ricalcola attività Configura zone BPM Elimina impostazioni zona BPM Gestisci account diff --git a/common/src/main/res/values-ja/strings.xml b/common/src/main/res/values-ja/strings.xml index 3a9a8c0b0..4a3bfd354 100644 --- a/common/src/main/res/values-ja/strings.xml +++ b/common/src/main/res/values-ja/strings.xml @@ -127,7 +127,7 @@ アクティビティを編集 アクティビティを共有 アクティビティを削除 - アクティビティを再計算 + アクティビティを再計算 心拍数ゾーンを設定 心拍数ゾーンの設定をクリア アカウントの管理 diff --git a/common/src/main/res/values-nl-rNL/strings.xml b/common/src/main/res/values-nl-rNL/strings.xml index fdc95dba5..abe876ecf 100644 --- a/common/src/main/res/values-nl-rNL/strings.xml +++ b/common/src/main/res/values-nl-rNL/strings.xml @@ -126,7 +126,7 @@ Activiteit bewerken Activiteit delen Activiteit verwijderen - Activiteit herberekenen + Activiteit herberekenen Hartslagzones instellen Hartslagzone instellingen verwijderen Accounts beheren diff --git a/common/src/main/res/values-nl/strings.xml b/common/src/main/res/values-nl/strings.xml index ce310e1fd..bceefe7da 100644 --- a/common/src/main/res/values-nl/strings.xml +++ b/common/src/main/res/values-nl/strings.xml @@ -124,7 +124,7 @@ Activiteit bewerken Activiteit delen Activiteit verwijderen - Activiteit herberekenen + Activiteit herberekenen Hartslagzones instellen Hartslagzone instellingen verwijderen Accounts beheren diff --git a/common/src/main/res/values-pl/strings.xml b/common/src/main/res/values-pl/strings.xml index 407478b76..c61207f5c 100644 --- a/common/src/main/res/values-pl/strings.xml +++ b/common/src/main/res/values-pl/strings.xml @@ -125,7 +125,7 @@ Edytuj aktywność Udostępnij aktywność Usuń aktywność - Przelicz aktywność + Przelicz aktywność Ustaw strefy tętna Wyczyść ustawienia stref tętna Zarządzaj kontami diff --git a/common/src/main/res/values-pt-rBR/strings.xml b/common/src/main/res/values-pt-rBR/strings.xml index 780852c27..1d97b9e6e 100644 --- a/common/src/main/res/values-pt-rBR/strings.xml +++ b/common/src/main/res/values-pt-rBR/strings.xml @@ -127,7 +127,7 @@ Editar atividade Compartilhar atividade Excluir atividade - Recalcular atividade + Recalcular atividade Configurar zonas de frequência cardíaca Apagar configurações da zona de frequência cardíaca Gerenciar contas diff --git a/common/src/main/res/values-ru/strings.xml b/common/src/main/res/values-ru/strings.xml index 283d4a998..d9622d16b 100644 --- a/common/src/main/res/values-ru/strings.xml +++ b/common/src/main/res/values-ru/strings.xml @@ -126,7 +126,7 @@ Редактировать Опубликовать Удалить - Пересчитать + Пересчитать Настройка зон ЧСС Очистить зоны ЧСС Управление аккаунтами diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index be70929f7..75730f4b4 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -127,7 +127,7 @@ Edit activity Share activity Delete activity - Recompute activity + Merge activity Configure heart rate zones Clear heart rate zone settings Manage accounts