From 0d8eaf9ccfedd0a0e37949675ce909359ed78374 Mon Sep 17 00:00:00 2001 From: Francesco Gabbrielli Date: Sat, 29 Sep 2018 01:17:23 +0200 Subject: [PATCH] Version 0.3.1 ------------- - OpenCV Camera (removed old Camera API) - FTP Bugfixes - Configurable image type - Sounds --- app/build.gradle | 4 + app/src/main/assets/defaults.properties | 15 +- .../apps/sensorlogger/ILogTarget.java | 3 +- .../apps/sensorlogger/LogFTPUploader.java | 20 ++- .../apps/sensorlogger/LoggingService.java | 35 +++-- .../apps/sensorlogger/MainActivity.java | 146 +++++++++++------- .../apps/sensorlogger/Recorder.java | 84 +++++----- .../apps/sensorlogger/SettingsActivity.java | 1 + .../apps/sensorlogger/Util.java | 63 ++++---- app/src/main/res/layout/activity_main.xml | 9 ++ app/src/main/res/values/strings.xml | 17 +- app/src/main/res/xml/pref_capture.xml | 9 +- app/src/main/res/xml/pref_file.xml | 1 - app/src/main/res/xml/pref_ftp.xml | 3 - 14 files changed, 248 insertions(+), 162 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6adea6b..2d94fe1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,10 @@ android { versionCode 8 versionName "0.3.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + ndk { + moduleName "sensor-logger" + abiFilters 'armeabi-v7a'//, 'arm64-v8a' + } } buildTypes { release { diff --git a/app/src/main/assets/defaults.properties b/app/src/main/assets/defaults.properties index 1a1e86f..a2e732b 100644 --- a/app/src/main/assets/defaults.properties +++ b/app/src/main/assets/defaults.properties @@ -1,9 +1,12 @@ -# filenames (TODO: use just prefixes) -pref_filename_data=sensors.csv -pref_filename_frame=frame.jpg +pref_app_folder =SensorLogger +# filenames +pref_filename_data =sensors.csv +pref_filename_frame =frame -# milliseconds between one sample and the other (frequency in Hz = 1 / (default_logging_rate/1000)) -pref_logging_rate=100 +# nanoseconds between one sample and the other (frequency in Hz = 1000000000 / pref_logging_rate) +pref_logging_rate =100000000 -pref_app_folder=SensorLogger \ No newline at end of file +# capture +pref_capture_imgformat =.png +pref_capture_camera =true diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/ILogTarget.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/ILogTarget.java index c804cae..c177963 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/ILogTarget.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/ILogTarget.java @@ -37,7 +37,8 @@ public void write(byte[] data) throws IOException { * Implement final cleanup */ public void close() throws IOException { - out.close(); + if (out!=null) + out.close(); out = null; } diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LogFTPUploader.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LogFTPUploader.java index 39ee60e..880a573 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LogFTPUploader.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LogFTPUploader.java @@ -34,14 +34,22 @@ public LogFTPUploader(SharedPreferences prefs) { public void open(String folder, String filename) throws IOException { if (!client.isConnected()) try { + Util.Log.d(getTag(), "Connecting to "+address); client.connect(address); client.login(user, password); client.enterLocalPassiveMode(); client.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); - out = client.storeFileStream(folder+"/"+filename); - Log.d(getTag(), "Client connected"); + client.makeDirectory(folder); + client.changeWorkingDirectory(folder); + out = client.storeFileStream(filename); + if (out!=null) + Util.Log.d(getTag(), "File opened: "+filename); + else { + Util.Log.w(getTag(), "Cannot create or access file "+filename); + close(); + } } catch (Exception e) { - Log.e(getTag(), "Can't connect to " + address + ", user: " + user); + Util.Log.e(getTag(), "Can't connect to " + address + ", user: " + user, e); } } @@ -51,17 +59,17 @@ public void open(String folder, String filename) throws IOException { @Override public void close() throws IOException { super.close(); + Util.Log.d(getTag(), "Disconnecting from "+address); if (client != null && client.isConnected()) { try { client.completePendingCommand(); client.logout(); client.disconnect(); - Log.d(getTag(), "Client disconnected"); + Util.Log.d(getTag(), "Client disconnected"); } catch (Exception e) { - Log.e(getTag(), "Error finalizing FTP connection", e); + Util.Log.e(getTag(), "Error finalizing FTP connection", e); } } } - } diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LoggingService.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LoggingService.java index fbc88c3..7b66505 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LoggingService.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/LoggingService.java @@ -12,6 +12,10 @@ import android.support.annotation.Nullable; import android.util.Log; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.imgcodecs.Imgcodecs; + import java.util.LinkedList; import java.util.List; @@ -35,6 +39,7 @@ public class LoggingService extends Service { HandlerThread thread; Handler handler; + private String ext; public class Binder extends android.os.Binder { @@ -74,26 +79,27 @@ public int onStartCommand(@Nullable Intent intent, int flags, int startId) { @Nullable @Override public IBinder onBind(Intent intent) { - Log.v(TAG, "in onBind"); + Util.Log.v(TAG, "in onBind"); return binder; } @Override public void onRebind(Intent intent) { - Log.v(TAG, "in onRebind"); + Util.Log.v(TAG, "in onRebind"); + ext = prefs.getString(Util.PREF_CAPTURE_IMGFORMAT, ".png"); super.onRebind(intent); } @Override public boolean onUnbind(Intent intent) { - Log.v(TAG, "in onUnbind"); + Util.Log.v(TAG, "in onUnbind"); return true; } @Override public void onLowMemory() { super.onLowMemory(); - Log.d(TAG, "onLowMemory"); + Util.Log.d(TAG, "onLowMemory"); } @Override @@ -101,7 +107,7 @@ public void onDestroy() { atomicLoggers.clear(); openLoggers.clear(); thread.quit(); - Log.d(TAG, "onDestroy"); + Util.Log.d(TAG, "onDestroy"); super.onDestroy(); } @@ -125,8 +131,15 @@ private void handleStart(final Bundle extras) { log(folder, filename, type, data); } - public void log(final String folder, final String filename, final int type, final byte[] data) { -// Log.v(TAG, "Logging to "+filename+" type "+type); + /** + * Log on available {@link ILogTarget}s + * @param folder + * @param filename + * @param type + * @param data + */ + public void log(final String folder, final String filename, final int type, final Object data) { + Util.Log.v(TAG, "Logging to "+filename+" type "+type+"; data: "+(data!=null)); handler.post(new Runnable() { @Override public void run() { @@ -139,7 +152,7 @@ public void run() { t.open(folder, filename); case ILogTarget.WRITE: for (ILogTarget t : openLoggers) - t.write(data); + t.write((byte[]) data); break; case ILogTarget.CLOSE: for (ILogTarget t : openLoggers) @@ -149,14 +162,14 @@ public void run() { case ILogTarget.SEND: for (ILogTarget t : atomicLoggers) { t.open(folder, filename); - t.write(data); + t.write((byte[]) data); t.close(); } break; } -// Log.d(TAG, "Logged to "+filename+", type "+type); +// Util.Log.d(TAG, "Logged to "+filename+", type "+type); } catch(Exception exc) { - Log.e(TAG, "Logging error", exc); + Util.Log.e(TAG, "Logging error", exc); } } }); diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/MainActivity.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/MainActivity.java index ff82c25..af0184b 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/MainActivity.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/MainActivity.java @@ -6,9 +6,9 @@ import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; -import android.hardware.Camera; import android.hardware.SensorManager; import android.media.AudioManager; +import android.media.ToneGenerator; import android.os.Environment; import android.os.SystemClock; import android.preference.PreferenceManager; @@ -17,7 +17,6 @@ import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -32,11 +31,15 @@ import org.opencv.android.JavaCameraView; import org.opencv.android.OpenCVLoader; import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.imgcodecs.Imgcodecs; import java.io.File; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Locale; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -46,24 +49,22 @@ public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener { private final static String TAG = MainActivity.class.getSimpleName(); + private final static double ONE_BILLION = 1000000000d; private final static int REQUEST_PERMISSIONS = 3; static { if (!OpenCVLoader.initDebug()) - Log.e(TAG, "Cannot initialize OpenCV"); + Util.Log.w(TAG, "Cannot initialize OpenCV!"); } private JavaCameraView camera; -// private CameraHandlerThread cameraHandlerThread; -// private CameraPreview cameraPreview; -// private Camera.ShutterCallback shutterCallback; private SharedPreferences prefs; private Recorder recorder; - private long timestamp, framerate; -// private boolean safeToTakePicture; + private long timestamp, frameDuration; + private String imgFormat; @Override protected void onCreate(Bundle savedInstanceState) { @@ -85,25 +86,6 @@ protected void onCreate(Bundle savedInstanceState) { } -// /** -// * Take a picture -// * @param pictureCallback the callback -// */ -// @Override -// public void takePicture(Camera.PictureCallback pictureCallback) { -// if (safeToTakePicture) -// try { -// Camera camera = cameraHandlerThread.getCamera(); -// camera.startPreview(); -// safeToTakePicture = false; -// //TODO: settings -> image raw data -// camera.takePicture(shutterCallback, null, pictureCallback); -// } catch (Exception e) { -// Log.e(TAG, "Picture not taken", e); -// pictureCallback.onData(null, null); -// } -// } - /** * Setup the camera; can be called anytime */ @@ -112,28 +94,37 @@ private void setupCamera() { camera.setVisibility(SurfaceView.VISIBLE); camera.setCvCameraViewListener(this); camera.enableView(); - framerate = Util.getLongPref(prefs, Util.PREF_LOGGING_RATE); + frameDuration = Util.getLongPref(prefs, Util.PREF_LOGGING_RATE); + imgFormat = prefs.getString(Util.PREF_CAPTURE_IMGFORMAT, ".png"); } @Override protected void onResume() { super.onResume(); - Log.d(TAG, "onResume"); + Util.Log.d(TAG, "onResume"); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); verifyPermissions(); } @Override protected void onPause() { - Log.d(TAG, "onPause"); + Util.Log.d(TAG, "onPause"); stopRecording(R.string.toast_recording_interrupted); - if (animExec!=null) - animExec.shutdown(); if (camera != null) camera.disableView(); + if (toneGenerator!=null) + toneGenerator.release(); + toneGenerator = null; super.onPause(); } + @Override + protected void onDestroy() { + super.onDestroy(); + if (animExec!=null) + animExec.shutdown(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); @@ -177,8 +168,11 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { private ScheduledExecutorService animExec; private ScheduledFuture prepareFuture, recordingFuture; + private ToneGenerator toneGenerator; private void showPrepareAnimation() { + if (toneGenerator==null && prefs.getBoolean(Util.PREF_CAPTURE_SOUND, false)) + toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 100); if (animExec==null) animExec = Executors.newSingleThreadScheduledExecutor(); prepareFuture = animExec.schedule(new Runnable() { @@ -191,9 +185,13 @@ public void run() { int val = 4; try { val = Integer.parseInt(t.getText().toString()); } catch(Exception e) {} if (--val>0) { + if (toneGenerator!=null) + toneGenerator.startTone(ToneGenerator.TONE_CDMA_PIP, 150); t.setText(String.valueOf(val)); showPrepareAnimation(); } else { + if (toneGenerator!=null) + toneGenerator.startTone(ToneGenerator.TONE_CDMA_PIP, 300); recording = true; recorder.start(MainActivity.this); hidePrepareAnimation(); @@ -212,6 +210,7 @@ private void hidePrepareAnimation() { } private void showBlinkingAnimation() { + timestamp = SystemClock.elapsedRealtimeNanos()- frameDuration; if (animExec==null) animExec = Executors.newSingleThreadScheduledExecutor(); recordingFuture = animExec.scheduleAtFixedRate(new Runnable() { @@ -230,15 +229,24 @@ public void run() { } private void hideBlinkingAnimation() { + if (animExec==null) + animExec = Executors.newSingleThreadScheduledExecutor(); if (recordingFuture!=null) recordingFuture.cancel(true); - runOnUiThread(new Runnable() { + animExec.schedule(new Runnable() { @Override public void run() { - ImageView i = findViewById(R.id.img_record); - i.setVisibility(View.INVISIBLE); + runOnUiThread(new Runnable() { + @Override + public void run() { + ImageView i = findViewById(R.id.img_record); + i.setVisibility(View.INVISIBLE); + if (toneGenerator!=null) + toneGenerator.startTone(ToneGenerator.TONE_CDMA_CONFIRM, 300); + } + }); } - }); + }, 0, TimeUnit.SECONDS); } void stopRecording(int msg) { @@ -310,20 +318,20 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } protected void onPermissionOk(String permission) { - Log.d(TAG, "Permission ok: " + permission); + Util.Log.d(TAG, "Permission ok: " + permission); if (Manifest.permission.CAMERA.equals(permission)) { setupCamera(); } else if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) { File dir = new File(Environment.getExternalStorageDirectory(), getString(R.string.app_folder)); if (dir.mkdirs()) - Log.i(TAG, "Creating app folder " + dir.getAbsolutePath()); + Util.Log.i(TAG, "Creating app folder " + dir.getAbsolutePath()); } else if (Manifest.permission.INTERNET.equals(permission)) { } } protected void onPermissionGranted(String permission) { - Log.d(TAG, "Permission granted: " + permission); + Util.Log.d(TAG, "Permission granted: " + permission); if (Manifest.permission.CAMERA.equals(permission)) { prefs.edit().putBoolean(Util.PREF_CAPTURE_CAMERA, true).apply(); } else if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) { @@ -334,7 +342,7 @@ protected void onPermissionGranted(String permission) { } protected void onPermissionDenied(String permission) { - Log.d(TAG, "Permission denied: " + permission); + Util.Log.d(TAG, "Permission denied: " + permission); if (Manifest.permission.CAMERA.equals(permission)) prefs.edit().putBoolean(Util.PREF_CAPTURE_CAMERA, false).apply(); else if (Manifest.permission.INTERNET.equals(permission)) @@ -350,28 +358,62 @@ else if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) // ---------------------------------- OPENCV CAMERA CALLBACKS ---------------------------------- // @Override public void onCameraViewStarted(int width, int height) { - Log.d(TAG, String.format("openCV Camera started: %dx%d", width, height)); + Util.Log.d(TAG, String.format("openCV camera started: %dx%d", width, height)); + frameExec = Executors.newSingleThreadExecutor(); + frameDurationAvg = frameDuration; + frameNumber = 0; + lastTime = 0; } - - private long t, max=0, min=1000000000, n=0; - double avg; + double frameDurationAvg;//milliseconds + long frameNumber, lastTime; + int lastFps; + private Executor frameExec; + + private void fps(long t) { + if (lastTime>0) { + frameDurationAvg = (frameDurationAvg * frameNumber + t - lastTime) / ++frameNumber; + if (frameNumber>40) + frameNumber=1; + double duration = recording ? Math.max(frameDuration, frameDurationAvg) : frameDurationAvg; + final int fps = (int) (ONE_BILLION/duration + 0.5d); + if (fps!=lastFps) + runOnUiThread(new Runnable() { + @Override + public void run() { + TextView tv = findViewById(R.id.text_fps); + tv.setText(String.format(Locale.US, "%d FPS", fps)); + lastFps = fps; + } + }); + } + lastTime = t; + } @Override - public Mat onCameraFrame(Mat inputFrame) { - long t = SystemClock.elapsedRealtimeNanos(); - if (recording && t-timestamp >= framerate) { - byte[] buff = new byte[(int) (inputFrame.total() * inputFrame.channels())]; - inputFrame.get(0, 0, buff); - recorder.onData(buff, timestamp); + public Mat onCameraFrame(final Mat inputFrame) { + final long t = SystemClock.elapsedRealtimeNanos(); + fps(t); + if (recording && t-timestamp >= frameDuration) { + frameExec.execute(new Runnable() { + @Override + public void run() { + MatOfByte buf = new MatOfByte(); + Imgcodecs.imencode(imgFormat, inputFrame, buf); + recorder.record(buf.toArray(), t); + } + }); + timestamp = t; } - timestamp = t; return inputFrame; } @Override public void onCameraViewStopped() { - Log.d(TAG, "openCV Camera stopped ~ "+avg); + Util.Log.d(TAG, String.format("openCV camera stopped ~ %2.1f (%d) [target=%2.1f])", + ONE_BILLION/frameDurationAvg, + (int) (frameDurationAvg/1000000), + ONE_BILLION/frameDuration)); } // // ----------------------------------- ---------------------- ---------------------------------- diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Recorder.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Recorder.java index d306127..d92457f 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Recorder.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Recorder.java @@ -5,11 +5,9 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; -import android.hardware.Camera; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.os.IBinder; -import android.os.SystemClock; import android.preference.PreferenceManager; import android.util.Log; import android.util.SparseIntArray; @@ -30,58 +28,62 @@ public class Recorder implements ServiceConnection { private LoggingService service; private boolean bound; -// private ScheduledExecutorService executor; -// private ScheduledFuture schedule; private SensorReader reader; - private ICamera camera; - private long start, rate; - private boolean flagTime, flagTimestamp, flagNetwork, flagHeaders; - private String filenameData, filenameFrame, folder; + private long start, duration; + private boolean flagTime, flagTimestamp, flagNetwork, flagHeaders, stopped; + private String filenameData, filenameFrame, folder, ext; private int counter; private final SparseIntArray dataLengths; Recorder(SensorReader reader) { -// executor = Executors.newScheduledThreadPool(10); -// pictureExec = Executors.newScheduledThreadPool(4); this.reader = reader; dataLengths = new SparseIntArray(); } - private String getFilename(String s, int param) { + private String getImageFilename(String s, int param) { return flagTimestamp - ? s.replaceFirst("\\.([a-z]+)", String.format("%08d.$1", param<0 ? 0 : param)) - : s; + ? String.format(Locale.US, "%s%07d%s", s, param<0 ? 0 : param, ext) : s; } - public void onData(byte[] data, long timestamp) { + /** + * Record image data + * + * @param data the image data + * @param timestamp the timestamp of the frame capture + */ + public void record(Object data, long timestamp) { + + if (stopped) + return; if (counter==0) start = timestamp; - int t = (int) ((timestamp-start)/1000000l); + int t = (int) ((timestamp-start)/1000000L); if (t > MAX_RECORDING_TIME) { context.stopRecording(R.string.toast_recording_offlimits); return; } - byte[] sensorsData = readSensors(t).getBytes(); //log precise frames and fill in missing frames - for (long time=start+counter*rate; time < timestamp+rate/2; time+=rate) { + long max = timestamp-start+duration/2; + for (long time = counter*duration; time < max; time+=duration) { + byte[] sensorsData = readSensors((int)(time/1000000L)).getBytes(); logSensors(sensorsData); if (data != null) logImage(data, counter); - // else - // Log.d(TAG, "No image!"); + else + Util.Log.d(TAG, "No image!"); + counter++; } - counter++; } - private void logImage(byte[] data, int param) { + private void logImage(Object data, int param) { service.log(folder, - getFilename(filenameFrame, param), + getImageFilename(filenameFrame, param), ILogTarget.SEND, data); } @@ -109,32 +111,29 @@ private String readSensors(int timestamp) { for (SensorEvent e : reader) { int l = Math.min(e.values.length, getSensorDataLength(e.sensor)); for (int i = 0; i < l; i++) { - if (flagHeaders && counter == 0) - headers.append(Util.getSensorName(e.sensor)) - .append(" ") - .append(Util.DATA_HEADERS[i]) - .append(","); - buffer.append(String.format("%2.5f", e.values[i])); - buffer.append(','); + if (flagHeaders && counter==0) { + headers.append(Util.getSensorName(e.sensor)); + if (l>1) + headers.append(" ").append(Util.DATA_HEADERS[i]).append(","); + } + buffer.append(String.format(Locale.US, "%2.5f,", e.values[i])); } } if (buffer.length() > 0) buffer.deleteCharAt(buffer.length() - 1); - if (headers != null && headers.length() > 0) { + if (headers!=null && headers.length() > 0) { headers.replace(headers.length() - 1, headers.length(), "\n"); buffer.insert(0, headers.toString()); } -// Log.v(TAG, "Sensor reading: "+buffer); - - buffer.append('\n'); + Util.Log.v(TAG, "Sensor reading: "+buffer); - return buffer.toString(); + return buffer.append('\n').toString(); } private void logSensors(byte[] data) { service.log(folder, - filenameData,//getFilename(filenameData, timestamp), + filenameData,//getImageFilename(filenameData, timestamp), counter==0 ? ILogTarget.OPEN : ILogTarget.WRITE, data); } @@ -142,13 +141,14 @@ private void logSensors(byte[] data) { public void start(MainActivity context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); filenameData = prefs.getString(Util.PREF_FILENAME_DATA, "sensors.csv"); - filenameFrame = prefs.getString(Util.PREF_FILENAME_FRAME, "image.jpg"); + filenameFrame = prefs.getString(Util.PREF_FILENAME_FRAME, "frame"); flagTime = prefs.getBoolean(Util.PREF_LOGGING_TIME, false); flagTimestamp = prefs.getBoolean(Util.PREF_LOGGING_TIMESTAMP, false); flagHeaders = prefs.getBoolean(Util.PREF_LOGGING_HEADERS, false); flagNetwork = prefs.getBoolean(Util.PREF_FTP,false); folder = dateFormat.format(new Date()); - rate = Util.getLongPref(prefs, Util.PREF_LOGGING_RATE); + duration = Util.getLongPref(prefs, Util.PREF_LOGGING_RATE); + ext = prefs.getString(Util.PREF_CAPTURE_IMGFORMAT,".png"); this.context = context; counter = 0; @@ -156,19 +156,13 @@ public void start(MainActivity context) { context.bindService(new Intent(context, LoggingService.class), this, Context.BIND_AUTO_CREATE); reader.start(); -// schedule = executor.scheduleAtFixedRate(new Runnable() { -// @Override -// public void run() { -// camera.takePicture(Recorder.this); -// } -// }, 3000, Util.getLongPref(prefs, Util.PREF_LOGGING_RATE), TimeUnit.MILLISECONDS); + stopped = false; } public void stop() { + stopped = true; if (context==null) return; -// schedule.cancel(true); -// schedule = null; reader.stop(); if (counter>0) service.log(folder, null, ILogTarget.CLOSE, null); diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/SettingsActivity.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/SettingsActivity.java index 5bc5d45..a0a5752 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/SettingsActivity.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/SettingsActivity.java @@ -221,6 +221,7 @@ public static class CapturePreferenceFragment extends AppCompatPreferenceFragmen public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.pref_capture); + bindPreferenceSummaryToValue(findPreference(Util.PREF_CAPTURE_IMGFORMAT)); } } diff --git a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Util.java b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Util.java index 6a3a1b6..556907e 100644 --- a/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Util.java +++ b/app/src/main/java/it/francescogabbrielli/apps/sensorlogger/Util.java @@ -1,16 +1,11 @@ package it.francescogabbrielli.apps.sensorlogger; - -import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; -import android.hardware.Camera; import android.hardware.Sensor; import android.hardware.SensorManager; import android.os.Build; import android.preference.PreferenceManager; -import android.util.Log; -import android.view.Surface; import java.lang.reflect.Method; import java.util.ArrayList; @@ -39,6 +34,7 @@ public class Util { public final static String PREF_LOGGING_TIMESTAMP = "pref_logging_timestamp"; public final static String PREF_CAPTURE_CAMERA = "pref_capture_camera"; + public final static String PREF_CAPTURE_IMGFORMAT = "pref_capture_imgformat"; public final static String PREF_CAPTURE_SOUND = "pref_capture_sound"; public final static String EXTRA_TYPE = "extra_type"; @@ -50,6 +46,26 @@ public class Util { "X", "Y", "Z", "Param1", "Param2", "Param3" }; + public static class Log { + public static void w(String tag, String msg) { + Log.w(tag, msg); + } + public static void e(String tag, String msg, Throwable t) { + Log.e(tag, msg, t); + } + public static void i(String tag, String msg) { + Log.i(tag, msg); + } + public static void d(String tag, String msg) { + if (BuildConfig.DEBUG) + Log.d(tag, msg); + } + public static void v(String tag, String msg) { + if (BuildConfig.DEBUG) + Log.v(tag, msg); + } + } + /** * Load dedaults from config file (asynchronously) @@ -58,18 +74,24 @@ public class Util { * application context */ public static void loadDefaults(final Context context) { - final Properties defaults = new Properties(); new Thread() { @Override public void run() { + Properties defaults = new Properties(); try{ defaults.load(context.getAssets().open("defaults.properties")); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = prefs.edit(); for (Enumeration e=defaults.propertyNames(); e.hasMoreElements();) { String key = String.valueOf(e.nextElement()); - if (prefs.getString(key, null)==null) - editor.putString(key, defaults.getProperty(key)); + if (!prefs.contains(key)) { + String val = defaults.getProperty(key); + //TODO: manage different types? + if ("TRUE".equalsIgnoreCase(val) || "FALSE".equalsIgnoreCase(val)) + editor.putBoolean(key, Boolean.parseBoolean(val.toLowerCase())); + else + editor.putString(key, val); + } } editor.apply(); Log.d(TAG, "Defaults loaded: "+defaults.toString()); @@ -132,31 +154,6 @@ public static int getSensorMaxLength(Sensor sensor) { return ret; } - public static void setCameraDisplayOrientation(Activity activity, - int cameraId, android.hardware.Camera camera) { - android.hardware.Camera.CameraInfo info = - new android.hardware.Camera.CameraInfo(); - android.hardware.Camera.getCameraInfo(cameraId, info); - int rotation = activity.getWindowManager().getDefaultDisplay() - .getRotation(); - int degrees = 0; - switch (rotation) { - case Surface.ROTATION_0: degrees = 0; break; - case Surface.ROTATION_90: degrees = 90; break; - case Surface.ROTATION_180: degrees = 180; break; - case Surface.ROTATION_270: degrees = 270; break; - } - - int result; - if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - result = (info.orientation + degrees) % 360; - result = (360 - result) % 360; // compensate the mirror - } else { // back-facing - result = (info.orientation - degrees + 360) % 360; - } - camera.setDisplayOrientation(result); - } - public static long getLongPref(SharedPreferences prefs, String prefKey) { long ret = 0; try { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f3ab750..34d5427 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -37,6 +37,15 @@ android:visibility="invisible" /> + + Enable FTP Upload If this is enabled, the data will be logged through FTP to a remote server - IP address - User + Host address + Username Password @@ -70,11 +70,22 @@ Camera Capture frames from the main camera present on the device + Image Format + Save images as %s + + PNG + JPEG + + + .png + .jpg + + Screen Capture device\'s screen Play sound - Play a shutter-like sound whenever a frame is captured + Play sounds on record operations WARNING! Denying the permission will set this preference back to its default diff --git a/app/src/main/res/xml/pref_capture.xml b/app/src/main/res/xml/pref_capture.xml index 831576b..0b874ea 100644 --- a/app/src/main/res/xml/pref_capture.xml +++ b/app/src/main/res/xml/pref_capture.xml @@ -3,11 +3,18 @@ + + diff --git a/app/src/main/res/xml/pref_ftp.xml b/app/src/main/res/xml/pref_ftp.xml index 88e8156..303fa64 100644 --- a/app/src/main/res/xml/pref_ftp.xml +++ b/app/src/main/res/xml/pref_ftp.xml @@ -5,14 +5,11 @@ android:key="pref_ftp" android:title="@string/pref_ftp_title" android:summary="@string/pref_ftp_description" - android:defaultValue="false" />