package pl.llp.aircasting.storage.repository; import pl.llp.aircasting.android.Logger; import pl.llp.aircasting.helper.NoOp; import pl.llp.aircasting.model.Measurement; import pl.llp.aircasting.model.MeasurementStream; import pl.llp.aircasting.storage.db.AirCastingDB; import pl.llp.aircasting.storage.db.ReadOnlyDatabaseTask; import pl.llp.aircasting.storage.db.WritableDatabaseTask; import pl.llp.aircasting.util.Constants; import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.google.common.base.Stopwatch; import com.google.inject.Inject; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; import static com.google.common.collect.Lists.newArrayList; import static pl.llp.aircasting.storage.DBHelper.*; import static pl.llp.aircasting.storage.db.DBConstants.*; public class StreamRepository { @Language("SQL") private static final String STREAM_IS_SUBMITTED_FOR_DELETE = STREAM_SUBMITTED_FOR_REMOVAL + " = 1 "; @Inject AirCastingDB airCastingDB; MeasurementRepository measurements; public StreamRepository() { measurements = new MeasurementRepository(NoOp.progressListener()); } @Internal List<MeasurementStream> findAllForSession(@NotNull final Long sessionId) { return airCastingDB.executeReadOnlyTask(new ReadOnlyDatabaseTask<List<MeasurementStream>>() { @Override public List<MeasurementStream> execute(SQLiteDatabase readOnlyDatabase) { List<MeasurementStream> result = newArrayList(); Cursor c = readOnlyDatabase.rawQuery("SELECT * FROM " + STREAM_TABLE_NAME + " WHERE " + STREAM_SESSION_ID + " = " + sessionId + "", null); c.moveToFirst(); while (!c.isAfterLast()) { String sensor = getString(c, STREAM_SENSOR_NAME); String packageName = getString(c, STREAM_SENSOR_PACKAGE_NAME); String symbol = getString(c, STREAM_MEASUREMENT_SYMBOL); String unit = getString(c, STREAM_MEASUREMENT_UNIT); String type = getString(c, STREAM_MEASUREMENT_TYPE); String shortType = getString(c, STREAM_SHORT_TYPE); int thresholdVeryLow = getInt(c, STREAM_THRESHOLD_VERY_LOW); int thresholdLow = getInt(c, STREAM_THRESHOLD_LOW); int thresholdMedium = getInt(c, STREAM_THRESHOLD_MEDIUM); int thresholdHigh = getInt(c, STREAM_THRESHOLD_HIGH); int thresholdVeryHigh = getInt(c, STREAM_THRESHOLD_VERY_HIGH); MeasurementStream stream; stream = new MeasurementStream(packageName, sensor, type, shortType, unit, symbol, thresholdVeryLow, thresholdLow, thresholdMedium, thresholdHigh, thresholdVeryHigh); double avg = getDouble(c, STREAM_AVG); double peak = getDouble(c, STREAM_PEAK); long id = getLong(c, STREAM_ID); boolean markedForRemoval = getBool(c, STREAM_MARKED_FOR_REMOVAL); stream.setAvg(avg); stream.setPeak(peak); stream.setId(id); stream.setSessionId(sessionId); stream.setMarkedForRemoval(markedForRemoval); result.add(stream); c.moveToNext(); } c.close(); return result; } }); } @Internal private long saveOne(MeasurementStream stream, long sessionId, SQLiteDatabase writableDatabase) { ContentValues values = values(stream); values.put(STREAM_SESSION_ID, sessionId); long streamId = writableDatabase.insertOrThrow(STREAM_TABLE_NAME, null, values); stream.setId(streamId); return streamId; } private ContentValues values(MeasurementStream stream) { ContentValues values = new ContentValues(); values.put(STREAM_SENSOR_PACKAGE_NAME, stream.getPackageName()); values.put(STREAM_SENSOR_NAME, stream.getSensorName()); values.put(STREAM_MEASUREMENT_SYMBOL, stream.getSymbol()); values.put(STREAM_MEASUREMENT_UNIT, stream.getUnit()); values.put(STREAM_MEASUREMENT_TYPE, stream.getMeasurementType()); values.put(STREAM_SHORT_TYPE, stream.getShortType()); values.put(STREAM_AVG, stream.getAvg()); values.put(STREAM_PEAK, stream.getPeak()); values.put(STREAM_THRESHOLD_VERY_LOW, stream.getThresholdVeryLow()); values.put(STREAM_THRESHOLD_LOW, stream.getThresholdLow()); values.put(STREAM_THRESHOLD_MEDIUM, stream.getThresholdMedium()); values.put(STREAM_THRESHOLD_HIGH, stream.getThresholdHigh()); values.put(STREAM_THRESHOLD_VERY_HIGH, stream.getThresholdVeryHigh()); values.put(STREAM_MARKED_FOR_REMOVAL, stream.isMarkedForRemoval()); values.put(STREAM_SUBMITTED_FOR_REMOVAL, stream.isSubmittedForRemoval()); return values; } @Internal public void saveAll(Collection<MeasurementStream> streamsToSave, long sessionId, SQLiteDatabase writableDatabase) { for (MeasurementStream oneToSave : streamsToSave) { Stopwatch s = new Stopwatch().start(); oneToSave.setSessionId(sessionId); long streamId = saveOne(oneToSave, sessionId, writableDatabase); Log.d(Constants.PERFORMANCE_TAG, "Saving stream took: " + s.elapsed(TimeUnit.MILLISECONDS)); s.reset().start(); List<Measurement> measurementsToSave = oneToSave.getMeasurements(); measurements.save(measurementsToSave, sessionId, streamId, writableDatabase); Log.d(Constants.PERFORMANCE_TAG, "Saving " + measurementsToSave.size() + " measurements took: " + s.elapsed(TimeUnit.MILLISECONDS)); } } @Internal void markForRemoval(MeasurementStream stream, long sessionId, SQLiteDatabase writableDatabase) { try { ContentValues values = new ContentValues(); values.put(STREAM_MARKED_FOR_REMOVAL, true); writableDatabase.update(STREAM_TABLE_NAME, values, STREAM_ID + " = " + stream.getId(), null); } catch (SQLException e) { Logger.e("Unable to mark stream [" + stream.getId() + "] from session [" + sessionId + "] to be deleted", e); } } @API public void update(final MeasurementStream stream) { final ContentValues values = values(stream); values.put(STREAM_SESSION_ID, stream.getSessionId()); airCastingDB.executeWritableTask(new WritableDatabaseTask<Void>() { @Override public Void execute(SQLiteDatabase writableDatabase) { try { writableDatabase.update(STREAM_TABLE_NAME, values, STREAM_ID + " = " + stream.getId(), null); } catch(SQLException e) { Logger.e("Error updating stream [" + stream.getId() + "]", e); } return null; } }); } @Internal void deleteMeasurements(long streamId, SQLiteDatabase writableDatabase) { try { measurements.deleteAllFrom(streamId, writableDatabase); } catch (SQLException e) { Logger.e("Error deleting measurements from stream [" + streamId + "]", e); } } @Internal void deleteSubmitted(SQLiteDatabase writableDatabase) { try { Cursor cursor = writableDatabase.query(STREAM_TABLE_NAME, null, STREAM_IS_SUBMITTED_FOR_DELETE, null, null, null, null); cursor.moveToFirst(); while(!cursor.isAfterLast()) { Long streamId = getLong(cursor, STREAM_ID); deleteMeasurements(streamId, writableDatabase); cursor.moveToNext(); } cursor.close(); writableDatabase.execSQL("DELETE FROM " + STREAM_TABLE_NAME + " WHERE " + STREAM_IS_SUBMITTED_FOR_DELETE); } catch (SQLException e) { Logger.e("Error deleting streams submitted to be deleted", e); } } @API public void markRemovedForRemovalAsSubmitted() { airCastingDB.executeWritableTask(new WritableDatabaseTask<Void>() { @Override public Void execute(SQLiteDatabase writableDatabase) { @Language("SQL") String sql = "UPDATE " + STREAM_TABLE_NAME + " SET " + STREAM_SUBMITTED_FOR_REMOVAL + "= 1 WHERE " + STREAM_MARKED_FOR_REMOVAL + "=1"; writableDatabase.execSQL(sql); return null; } }); } }