From a79fbf7fdf4bd8455cb912ee3875fcfdc3e43f3c Mon Sep 17 00:00:00 2001 From: Alexey Kulish Date: Tue, 29 Dec 2015 11:57:18 +0300 Subject: [PATCH] Finished Recording Split. Fixed https://github.com/osmandapp/Osmand/issues/2084 --- OsmAnd/res/values/strings.xml | 1 + .../AudioVideoNoteMenuController.java | 2 +- .../AudioVideoNoteRecordingMenu.java | 52 +++- .../audionotes/AudioVideoNotesPlugin.java | 290 +++++++++++++----- .../osmand/plus/audionotes/NotesFragment.java | 4 +- .../other/RoutePreferencesMenu.java | 12 +- 6 files changed, 263 insertions(+), 98 deletions(-) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index b93ad0eaf4..768f63f610 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,7 @@ 3. All your modified/created strings are in the top of the file (to make easier find what\'s translated). PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy --> + Not selected Recorder Split Use Recorder Split Rewrite clips when used space exceeds the storage size diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java index fb4022e688..758934f710 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java @@ -50,7 +50,7 @@ public class AudioVideoNoteMenuController extends MenuController { @Override public void onClick(DialogInterface dialog, int which) { if (plugin != null) { - plugin.deleteRecording(getRecording()); + plugin.deleteRecording(getRecording(), true); getMapActivity().getContextMenu().close(); } } diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java index 054a028d01..5d9a331c50 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java @@ -2,6 +2,7 @@ package net.osmand.plus.audionotes; import android.os.Handler; import android.util.DisplayMetrics; +import android.util.Log; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; @@ -118,6 +119,7 @@ public class AudioVideoNoteRecordingMenu { } public void hide() { + stopCounter(); view.setVisibility(View.GONE); plugin.stopCamera(); viewfinder.removeAllViews(); @@ -169,16 +171,34 @@ public class AudioVideoNoteRecordingMenu { centerButtonView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - rec(plugin.getMapActivity()); + rec(plugin.getMapActivity(), false); } }); applyViewfinderVisibility(); } + public boolean restartRecordingIfNeeded() { + boolean restart = false; + CurrentRecording recording = plugin.getCurrentRecording(); + if (recording != null + && recording.getType() == AVActionType.REC_VIDEO + && plugin.AV_RECORDER_SPLIT.get()) { + int clipLength = plugin.AV_RS_CLIP_LENGTH.get() * 60; + int duration = (int) ((System.currentTimeMillis() - startTime) / 1000); + restart = duration >= clipLength; + if (restart) { + rec(getMapActivity(), true); + } + } + return restart; + } + public void updateDuration() { - TextView timeText = (TextView) view.findViewById(R.id.timeText); - int duration = (int) ((System.currentTimeMillis() - startTime) / 1000); - timeText.setText(Algorithms.formatDuration(duration)); + if (plugin.getCurrentRecording() != null) { + TextView timeText = (TextView) view.findViewById(R.id.timeText); + int duration = (int) ((System.currentTimeMillis() - startTime) / 1000); + timeText.setText(Algorithms.formatDuration(duration)); + } } protected void applyViewfinderVisibility() { @@ -236,21 +256,30 @@ public class AudioVideoNoteRecordingMenu { return res; } - public void rec(final MapActivity mapActivity) { + public void rec(final MapActivity mapActivity, final boolean restart) { stopCounter(); + final CurrentRecording recording = plugin.getCurrentRecording(); + int delay; + if (recording != null && recording.getType() == AVActionType.REC_PHOTO) { + delay = 200; + } else { + delay = 1; + } handler.postDelayed(new Runnable() { @Override public void run() { - CurrentRecording recording = plugin.getCurrentRecording(); if (recording != null) { if (recording.getType() == AVActionType.REC_PHOTO) { plugin.shoot(); } else { - plugin.stopRecording(mapActivity); + plugin.stopRecording(mapActivity, restart); + if (restart) { + startCounter(); + } } } } - }, 200); + }, delay); } public void recExternal(final MapActivity mapActivity) { @@ -270,7 +299,7 @@ public class AudioVideoNoteRecordingMenu { private void startCounter() { startTime = System.currentTimeMillis(); - + Log.e("111", "START"); if (recTimer != null) { recTimer.cancel(); } @@ -282,7 +311,9 @@ public class AudioVideoNoteRecordingMenu { handler.post(new Runnable() { @Override public void run() { - updateDuration(); + if (!restartRecordingIfNeeded()) { + updateDuration(); + } } }); } @@ -291,6 +322,7 @@ public class AudioVideoNoteRecordingMenu { } private void stopCounter() { + Log.e("111", "STOP"); if (recTimer != null) { recTimer.cancel(); recTimer = null; diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 038b9c0b87..e13bb70df9 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -21,6 +21,7 @@ import android.media.MediaRecorder; import android.media.SoundPool; import android.net.Uri; import android.os.Build; +import android.os.StatFs; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.view.Display; @@ -72,10 +73,12 @@ import org.apache.commons.logging.Log; import java.io.File; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.text.DateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; @@ -782,7 +785,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { @Override public void mapActivityScreenOff(MapActivity activity) { - stopCameraRecording(activity); + stopRecording(activity, false); } @Override @@ -803,7 +806,13 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { finishPhotoRecording(false); } else { activity.getContextMenu().close(); - stopRecording(activity); + if (currentRecording.getType() == AVActionType.REC_VIDEO && AV_RECORDER_SPLIT.get()) { + runAction = AV_DEFAULT_ACTION_VIDEO; + LatLon latLon = getNextRecordingLocation(); + actionLat = latLon.getLatitude(); + actionLon = latLon.getLongitude(); + } + stopRecording(activity, false); } finishRecording(); } @@ -854,21 +863,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { public void recordVideoCamera(final double lat, final double lon, final MapActivity mapActivity) { final CamcorderProfile p = CamcorderProfile.get(AV_VIDEO_QUALITY.get()); - final Camera.Size mPreviewSize; - if (mSupportedPreviewSizes != null) { - int width; - int height; - if (recordingMenu.isLandscapeLayout()) { - width = p.videoFrameWidth; - height = p.videoFrameHeight; - } else { - height = p.videoFrameWidth; - width = p.videoFrameHeight; - } - mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); - } else { - mPreviewSize = null; - } + final Camera.Size mPreviewSize = getPreviewSize(); final SurfaceView view; if (mPreviewSize != null) { @@ -888,23 +883,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { MediaRecorder mr = new MediaRecorder(); try { - Parameters parameters = cam.getParameters(); - - // camera focus type - List sfm = parameters.getSupportedFocusModes(); - if (sfm.contains("continuous-video")) { - parameters.setFocusMode("continuous-video"); - } - - int cameraOrientation = getCamOrientation(mapActivity, Camera.CameraInfo.CAMERA_FACING_BACK); - cam.setDisplayOrientation(cameraOrientation); - parameters.set("rotation", cameraOrientation); - if (mPreviewSize != null) { - parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); - } - cam.setParameters(parameters); - cam.setPreviewDisplay(holder); - cam.startPreview(); + startCamera(mPreviewSize, holder); cam.unlock(); mr.setCamera(cam); @@ -917,27 +896,12 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { return; } - - String ext = MPEG4_EXTENSION; -// if (AV_VIDEO_FORMAT.get() == VIDEO_OUTPUT_3GP) { -// ext = THREEGP_EXTENSION; -// } - final File f = getBaseFileName(lat, lon, app, ext); - - mr.setAudioSource(MediaRecorder.AudioSource.DEFAULT); - mr.setVideoSource(MediaRecorder.VideoSource.CAMERA); -// if (AV_VIDEO_FORMAT.get() == VIDEO_OUTPUT_3GP) { -// mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); -// } else { -// mr.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); -// } - - giveMediaRecorderHintRotatedScreen(mapActivity, mr); - //mr.setPreviewDisplay(holder.getSurface()); - - mr.setProfile(p); - mr.setOutputFile(f.getAbsolutePath()); + final File f = getBaseFileName(lat, lon, app, MPEG4_EXTENSION); + initMediaRecorder(mr, p, f); try { + if (AV_RECORDER_SPLIT.get()) { + cleanupSpace(); + } runMediaRecorder(mapActivity, mr, f); } catch (Exception e) { logErr(e); @@ -951,6 +915,17 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { recordingMenu.show(); } + private void initMediaRecorder(MediaRecorder mr, CamcorderProfile p, File f) { + mr.setAudioSource(MediaRecorder.AudioSource.DEFAULT); + mr.setVideoSource(MediaRecorder.VideoSource.CAMERA); + + giveMediaRecorderHintRotatedScreen(mapActivity, mr); + //mr.setPreviewDisplay(holder.getSurface()); + + mr.setProfile(p); + mr.setOutputFile(f.getAbsolutePath()); + } + private void giveMediaRecorderHintRotatedScreen(final MapActivity mapActivity, final MediaRecorder mr) { if (Build.VERSION.SDK_INT >= 9) { try { @@ -1013,6 +988,48 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { mapActivity.setRequestedOrientation(requestedOrientation); } + private Camera.Size getPreviewSize() { + final CamcorderProfile p = CamcorderProfile.get(AV_VIDEO_QUALITY.get()); + final Camera.Size mPreviewSize; + if (mSupportedPreviewSizes != null) { + int width; + int height; + if (recordingMenu.isLandscapeLayout()) { + width = p.videoFrameWidth; + height = p.videoFrameHeight; + } else { + height = p.videoFrameWidth; + width = p.videoFrameHeight; + } + mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); + } else { + mPreviewSize = null; + } + return mPreviewSize; + } + + protected void startCamera(Camera.Size mPreviewSize, SurfaceHolder holder) throws IOException { + Parameters parameters = cam.getParameters(); + + // camera focus type + List sfm = parameters.getSupportedFocusModes(); + if (sfm.contains("continuous-video")) { + parameters.setFocusMode("continuous-video"); + } + + int cameraOrientation = getCamOrientation(mapActivity, Camera.CameraInfo.CAMERA_FACING_BACK); + cam.setDisplayOrientation(cameraOrientation); + parameters.set("rotation", cameraOrientation); + if (mPreviewSize != null) { + parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); + } + cam.setParameters(parameters); + if (holder != null) { + cam.setPreviewDisplay(holder); + } + cam.startPreview(); + } + protected void stopCamera() { try { if (cam != null) { @@ -1027,18 +1044,44 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } } - private void stopCameraRecording(final MapActivity mapActivity) { + private boolean stopCameraRecording(boolean restart) { + boolean res = true; if (mediaRec != null) { mediaRec.stop(); + AVActionType type = currentRecording.type; + indexFile(true, mediaRecFile); mediaRec.release(); mediaRec = null; - indexFile(true, mediaRecFile); mediaRecFile = null; - stopCamera(); - } - if (recordControl != null) { - setRecordListener(recordControl, mapActivity); + + if (restart) { + try { + cam.lock(); + if (AV_RECORDER_SPLIT.get()) { + cleanupSpace(); + } + + currentRecording = new CurrentRecording(type); + MediaRecorder mr = new MediaRecorder(); + LatLon latLon = getNextRecordingLocation(); + final File f = getBaseFileName(latLon.getLatitude(), latLon.getLongitude(), app, MPEG4_EXTENSION); + + cam.unlock(); + mr.setCamera(cam); + initMediaRecorder(mr, CamcorderProfile.get(AV_VIDEO_QUALITY.get()), f); + mr.prepare(); + mr.start(); + mediaRec = mr; + mediaRecFile = f; + + } catch (Exception e) { + AccessibleToast.makeText(app, e.getMessage(), Toast.LENGTH_LONG).show(); + e.printStackTrace(); + res = false; + } + } } + return res; } public void recordAudio(double lat, double lon, final MapActivity mapActivity) { @@ -1162,6 +1205,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { parameters.setGpsLatitude(lat); parameters.setGpsLongitude(lon); parameters.setGpsProcessingMethod(locProvider.toUpperCase()); + parameters.setGpsTimestamp(System.currentTimeMillis() / 1000); } switch (AV_CAMERA_FOCUS_TYPE.get()) { case AV_CAMERA_FOCUS_HIPERFOCAL: @@ -1353,6 +1397,69 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } } + private void cleanupSpace() { + File[] files = app.getAppPath(IndexConstants.AV_INDEX_DIR).listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.endsWith("." + MPEG4_EXTENSION); + } + }); + + if (files != null) { + double usedSpace = 0; + for (File f : files) { + usedSpace += f.length(); + } + usedSpace /= (1 << 30); // gigabytes + + final CamcorderProfile p = CamcorderProfile.get(AV_VIDEO_QUALITY.get()); + double bitrate = (((p.videoBitRate + p.audioBitRate) / 8f) * 60f) / (1 << 30); // gigabytes per minute + double clipSpace = bitrate * AV_RS_CLIP_LENGTH.get(); + double storageSize = AV_RS_STORAGE_SIZE.get(); + + double availableSpace = storageSize; + File dir = app.getAppPath("").getParentFile(); + if (dir.canRead()) { + StatFs fs = new StatFs(dir.getAbsolutePath()); + availableSpace = (double) (fs.getAvailableBlocks()) * fs.getBlockSize() / (1 << 30) - clipSpace; + } + + if (usedSpace + clipSpace > storageSize || clipSpace > availableSpace) { + Arrays.sort(files, new Comparator() { + @Override + public int compare(File lhs, File rhs) { + return lhs.lastModified() < rhs.lastModified() ? -1 : (lhs.lastModified() == rhs.lastModified() ? 0 : 1); + } + }); + boolean wasAnyDeleted = false; + ArrayList arr = new ArrayList<>(Arrays.asList(files)); + while (arr.size() > 0 + && (usedSpace + clipSpace > storageSize || clipSpace > availableSpace)) { + File f = arr.remove(0); + double length = ((double) f.length()) / (1 << 30); + Recording r = recordingByFileName.get(f.getName()); + if (r != null) { + deleteRecording(r, false); + wasAnyDeleted = true; + usedSpace -= length; + availableSpace += length; + } else if (f.delete()) { + usedSpace -= length; + availableSpace += length; + } + } + if (wasAnyDeleted) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + mapActivity.refreshMap(); + } + }, 20); + } + } + } + } + private void runMediaRecorder(final MapActivity mapActivity, MediaRecorder mr, final File f) throws IOException { mr.prepare(); mr.start(); @@ -1375,26 +1482,42 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { recordControl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - stopRecording(mapActivity); + stopRecording(mapActivity, false); } }); } - public void stopRecording(final MapActivity mapActivity) { + public void stopRecording(final MapActivity mapActivity, boolean restart) { if (!recordingDone) { - recordingDone = true; - if (!recordControl.isVisible()) { - recordControl.setExplicitlyVisible(false); - mapActivity.getMapLayers().getMapInfoLayer().recreateControls(); + if (!restart || !stopCameraRecording(true)) { + recordingDone = true; + if (!recordControl.isVisible()) { + recordControl.setExplicitlyVisible(false); + mapActivity.getMapLayers().getMapInfoLayer().recreateControls(); + } + stopCameraRecording(false); + if (recordControl != null) { + setRecordListener(recordControl, mapActivity); + } + SHOW_RECORDINGS.set(true); + mapActivity.getMapView().refreshMap(); + updateWidgetIcon(recordControl); + closeRecordingMenu(); } - stopCameraRecording(mapActivity); - SHOW_RECORDINGS.set(true); - mapActivity.getMapView().refreshMap(); - updateWidgetIcon(recordControl); - closeRecordingMenu(); } } + private LatLon getNextRecordingLocation() { + double lat = mapActivity.getMapLocation().getLatitude(); + double lon = mapActivity.getMapLocation().getLongitude(); + Location loc = app.getLocationProvider().getLastKnownLocation(); + if (loc != null) { + lat = loc.getLatitude(); + lon = loc.getLongitude(); + } + return new LatLon(lat, lon); + } + private void updateContextMenu(Recording rec) { if (mapActivity != null && rec != null) { MapContextMenu menu = mapActivity.getContextMenu(); @@ -1450,14 +1573,17 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { recordingByFileName = newMap; if (isRecording()) { + AVActionType type = currentRecording.type; finishRecording(); - final Recording recordingForMenu = r; - app.runInUIThread(new Runnable() { - @Override - public void run() { - updateContextMenu(recordingForMenu); - } - }, 200); + if (!AV_RECORDER_SPLIT.get() || type != AVActionType.REC_VIDEO) { + final Recording recordingForMenu = r; + app.runInUIThread(new Runnable() { + @Override + public void run() { + updateContextMenu(recordingForMenu); + } + }, 200); + } } return true; @@ -1527,14 +1653,16 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } } - public void deleteRecording(Recording r) { + public void deleteRecording(Recording r, boolean updateUI) { recordings.unregisterObject(r.lat, r.lon, r); Map newMap = new LinkedHashMap<>(recordingByFileName); newMap.remove(r.file.getName()); recordingByFileName = newMap; Algorithms.removeAllFiles(r.file); - if (mapActivity != null) { - mapActivity.getContextMenu().close(); + if (mapActivity != null && updateUI) { + if (mapActivity.getContextMenu().getObject() == r) { + mapActivity.getContextMenu().close(); + } mapActivity.getMapView().refreshMap(); } } diff --git a/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java index b9cb0c2411..1b2ad689fa 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java @@ -213,7 +213,7 @@ public class NotesFragment extends OsmAndListFragment { Iterator it = selected.iterator(); while (it.hasNext()) { Recording pnt = it.next(); - plugin.deleteRecording(pnt); + plugin.deleteRecording(pnt, true); it.remove(); listAdapter.delete(pnt); } @@ -505,7 +505,7 @@ public class NotesFragment extends OsmAndListFragment { builder.setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - plugin.deleteRecording(recording); + plugin.deleteRecording(recording, true); listAdapter.remove(recording); } }); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java index 2cbef200cf..d2d7bbca58 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java @@ -327,12 +327,16 @@ public class RoutePreferencesMenu { btn.setTextColor(btn.getLinkTextColors()); String voiceProvider = settings.VOICE_PROVIDER.get(); String voiceProviderStr; - if (OsmandSettings.VOICE_PROVIDER_NOT_USE.equals(voiceProvider)) { - voiceProviderStr = getString(R.string.shared_string_do_not_use); + if (voiceProvider != null) { + if (OsmandSettings.VOICE_PROVIDER_NOT_USE.equals(voiceProvider)) { + voiceProviderStr = getString(R.string.shared_string_do_not_use); + } else { + voiceProviderStr = FileNameTranslationHelper.getVoiceName(mapActivity, voiceProvider); + } + voiceProviderStr += voiceProvider.contains("tts") ? " TTS" : ""; } else { - voiceProviderStr = FileNameTranslationHelper.getVoiceName(mapActivity, voiceProvider); + voiceProviderStr = getString(R.string.shared_string_not_selected); } - voiceProviderStr += voiceProvider.contains("tts") ? " TTS" : ""; btn.setText(voiceProviderStr); btn.setOnClickListener(new View.OnClickListener() { @Override