diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index 9bbc957164..b58a294857 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -7,12 +7,16 @@ - + + + + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 9a3d88ab4e..6c6ad72072 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,14 @@ 1. 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 --> + Select video output format + Video output format + Use system recorder for video + Use external recorder + Configure audio and video settings + Audio/video settings + Error ocurred while recording + Camera is not available Audio/video is being recorded. To stop it press stop button on the widget. An audio from the specified recording is being played.\n%1$s Open external player diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index 32ee423ee8..3765ace344 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -705,7 +705,6 @@ public class OsmandSettings { POSITION_ON_MAP.setModeDefaultValue(ApplicationMode.CAR, BOTTOM_CONSTANT); POSITION_ON_MAP.setModeDefaultValue(ApplicationMode.BICYCLE, BOTTOM_CONSTANT); POSITION_ON_MAP.setModeDefaultValue(ApplicationMode.PEDESTRIAN, CENTER_CONSTANT); - } // this value string is synchronized with settings_pref.xml preference name @@ -1421,6 +1420,12 @@ public class OsmandSettings { public final CommonPreference SHOW_RULER = new BooleanPreference("show_ruler", true).makeProfile().cache(); + public final CommonPreference AV_EXTERNAL_RECORDER = new BooleanPreference("av_external_recorder", false).makeGlobal(); + + public static final int VIDEO_OUTPUT_MP4 = 0; + public static final int VIDEO_OUTPUT_3GP = 1; + public final CommonPreference AV_VIDEO_FORMAT = new IntPreference("av_video_format", 0).makeGlobal(); + public final OsmandPreference NUMBER_OF_FREE_DOWNLOADS = new IntPreference("free_downloads_v1", 0).makeGlobal(); diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 80872bd30d..7ad2394f2b 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -156,7 +156,6 @@ public class MapActivity extends AccessibleActivity implements IMapLocationListe @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - System.out.println("DELETE On create"); settings = getMyApplication().getSettings(); mapActions = new MapActivityActions(this); mapLayers = new MapActivityLayers(this); @@ -239,7 +238,6 @@ public class MapActivity extends AccessibleActivity implements IMapLocationListe addDialogProvider(mapActions); OsmandPlugin.onMapActivityCreate(this); - System.out.println("On create finish"); } private void createProgressBarForRouting() { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 9403a511b1..39abef55bb 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -1,6 +1,7 @@ package net.osmand.plus.audionotes; import java.io.File; +import java.lang.reflect.Method; import java.util.EnumSet; import java.util.List; @@ -15,29 +16,50 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuAdapter.OnContextMenuClick; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.ResourceManager; import net.osmand.plus.activities.ApplicationMode; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.views.MapInfoLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.TextInfoControl; import org.apache.commons.logging.Log; +import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; +import android.hardware.Camera; import android.location.Location; import android.media.MediaPlayer; import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Build; +import android.preference.ListPreference; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.provider.MediaStore; import android.text.format.DateFormat; +import android.view.Display; +import android.view.Gravity; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; import android.view.View; +import android.view.WindowManager; +import android.widget.FrameLayout; import android.widget.Toast; public class AudioVideoNotesPlugin extends OsmandPlugin { public static final String ID = "osmand.audionotes"; - private static final String AV_EXTENSION = "3gp"; + private static final String THREEGP_EXTENSION = "3gp"; + private static final String MPEG4_EXTENSION = "mp4"; + private static final String IMG_EXTENSION = "jpg"; private static final Log log = LogUtil.getLog(AudioVideoNotesPlugin.class); private OsmandApplication app; private TextInfoControl recordControl; @@ -46,6 +68,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { private DataTileManager recordings = new DataTileManager(14); private AudioNotesLayer audioNotesLayer; private MapActivity activity; + private MediaRecorder mediaRec; public static class Recording { public Recording(File f) { @@ -159,16 +182,16 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { @Override public void onContextMenuClick(int itemId, int pos, boolean isChecked, DialogInterface dialog) { - recordAudioVideo(latitude, longitude, mapActivity, false); + recordAudio(latitude, longitude, mapActivity); } }, 6); -// adapter.registerItem(R.string.recording_context_menu_vrecord, 0, new OnContextMenuClick() { -// -// @Override -// public void onContextMenuClick(int itemId, int pos, boolean isChecked, DialogInterface dialog) { -// recordAudioVideo(latitude, longitude, mapActivity, true); -// } -// }, 7); + adapter.registerItem(R.string.recording_context_menu_vrecord, 0, new OnContextMenuClick() { + + @Override + public void onContextMenuClick(int itemId, int pos, boolean isChecked, DialogInterface dialog) { + recordVideo(latitude, longitude, mapActivity); + } + }, 7); } @Override @@ -210,55 +233,193 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { AccessibleToast.makeText(app, R.string.audionotes_location_not_defined, Toast.LENGTH_LONG).show(); return; } - recordAudioVideo(loc.getLatitude(), loc.getLongitude(), mapActivity, false); + recordAudio(loc.getLatitude(), loc.getLongitude(), mapActivity); } }); } - private File getBaseFileNameForAV(double lat, double lon, OsmandApplication app) { + + private File getBaseFileName(double lat, double lon, OsmandApplication app, String ext) { String basename = MapUtils.createShortLocString(lat, lon, 15); int k = 1; File f = app.getSettings().extendOsmandPath(ResourceManager.AV_PATH); f.mkdirs(); File fl; do { - fl = new File(f, basename + "_" + (k++) + "." + AV_EXTENSION); + fl = new File(f, basename + "-" + (k++) + "." + ext); } while(fl.exists()); return fl; } - private void recordAudioVideo(double lat, double lon, final MapActivity mapActivity, boolean recordVideo) { - final MediaRecorder mr = new MediaRecorder(); - final File f = getBaseFileNameForAV(lat, lon, app); - mr.setOutputFile(f.getAbsolutePath()); - if(recordVideo) { - mr.setVideoSource(MediaRecorder.VideoSource.CAMERA); + public void captureImage(double lat, double lon, final MapActivity mapActivity) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + Uri fileUri = Uri.fromFile(getBaseFileName(lat, lon, app, IMG_EXTENSION)); + intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name + // start the image capture Intent + mapActivity.startActivityForResult(intent, 105); + } + + public void captureVideoExternal(double lat, double lon, final MapActivity mapActivity) { + Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + + String ext = MPEG4_EXTENSION; + if(app.getSettings().AV_VIDEO_FORMAT.get() == OsmandSettings.VIDEO_OUTPUT_3GP ){ + ext = THREEGP_EXTENSION; } + Uri fileUri = Uri.fromFile(getBaseFileName(lat, lon, app, ext)); + intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name + + intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high + // start the video capture Intent + mapActivity.startActivityForResult(intent, 205); + } + + @Override + public void mapActivityPause(MapActivity activity) { + stopRecording(activity); + } + + public void recordVideo(final double lat, final double lon, final MapActivity mapActivity) { + if(app.getSettings().AV_EXTERNAL_RECORDER.get()) { + captureVideoExternal(lat, lon, mapActivity); + } else { + recordVideoCamera(lat, lon, mapActivity); + } + } + + public void recordVideoCamera(final double lat, final double lon, final MapActivity mapActivity) { + Dialog dlg = new Dialog(mapActivity); + SurfaceView view = new SurfaceView(dlg.getContext()); + view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + view.getHolder().addCallback(new Callback() { + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + MediaRecorder mr = new MediaRecorder(); + String ext = MPEG4_EXTENSION; + if(app.getSettings().AV_VIDEO_FORMAT.get() == OsmandSettings.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(app.getSettings().AV_VIDEO_FORMAT.get() == OsmandSettings.VIDEO_OUTPUT_3GP ){ + mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + } else { + mr.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + } + + giveMediaRecorderHintRotatedScreen(mapActivity, mr); +// mr.setPreviewDisplay(holder.getSurface()); + mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); + mr.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT); + mr.setOutputFile(f.getAbsolutePath()); + try { + mr.prepare(); + mr.start(); + mediaRec = mr; + AccessibleToast.makeText(mapActivity, R.string.recording_is_recorded, Toast.LENGTH_LONG).show(); + recordControl.setText(app.getString(R.string.av_control_stop), ""); + recordControl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stopRecording(mapActivity); + indexFile(f); + mapActivity.getMapView().refreshMap(); + } + }); + } catch (Exception e) { + logErr(e); + return; + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } + }); +// FrameLayout p = (FrameLayout) mapActivity.getMapView().getParent(); +// android.widget.FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(50, 50); +// lp.gravity = Gravity.CENTER; +// p.addView(view, lp); + dlg.setContentView(view); + dlg.show(); +// ((MapStackControl) view.getParent()).addView(view); + } + + private void giveMediaRecorderHintRotatedScreen(final MapActivity mapActivity, final MediaRecorder mr) { + if(Build.VERSION.SDK_INT >= 9) { + try { + Method m = mr.getClass().getDeclaredMethod("setOrientationHint", Integer.class); + Display display = ((WindowManager)mapActivity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + if(display.getRotation() == Surface.ROTATION_0) { + m.invoke(mr, 90); + } else if(display.getRotation() == Surface.ROTATION_270) { + m.invoke(mr, 180); + } else if(display.getRotation() == Surface.ROTATION_180) { + m.invoke(mr, 270); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + } + + private void logErr(Exception e) { + log.error("Error starting recorder ", e); + AccessibleToast.makeText(app, app.getString(R.string.recording_error) + " : " + e.getMessage(), Toast.LENGTH_LONG).show(); + return; + } + + protected Camera openCamera() { + try { + return Camera.open(); + } catch (Exception e ){ + logErr(e); + return null; + } + } + + private void stopRecording(final MapActivity mapActivity) { + if (mediaRec != null) { + mediaRec.stop(); + mediaRec.release(); + mediaRec = null; + } + setRecordListener(recordControl, mapActivity); + } + + + public void recordAudio(double lat, double lon, final MapActivity mapActivity) { + MediaRecorder mr = new MediaRecorder(); + final File f = getBaseFileName(lat, lon, app, THREEGP_EXTENSION); mr.setAudioSource(MediaRecorder.AudioSource.MIC); mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); - if(recordVideo){ - mr.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT); - } mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); - - recordControl.setText(app.getString(R.string.av_control_stop), ""); - AccessibleToast.makeText(mapActivity, R.string.recording_is_recorded, Toast.LENGTH_LONG).show(); + mr.setOutputFile(f.getAbsolutePath()); try { mr.prepare(); mr.start(); + mediaRec = mr; + recordControl.setText(app.getString(R.string.av_control_stop), ""); + AccessibleToast.makeText(mapActivity, R.string.recording_is_recorded, Toast.LENGTH_LONG).show(); recordControl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + stopRecording(mapActivity); indexFile(f); - mr.stop(); - mr.release(); mapActivity.getMapView().refreshMap(); - setRecordListener(recordControl, mapActivity); } + }); } catch (Exception e) { log.error("Error starting audio recorder ", e); - AccessibleToast.makeText(app, app.getString(R.string.error_io_error) + " : " + e.getMessage(), Toast.LENGTH_LONG).show(); + AccessibleToast.makeText(app, app.getString(R.string.recording_error) + " : " + e.getMessage(), Toast.LENGTH_LONG).show(); } } @@ -266,7 +427,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { public void indexFile(File f){ Recording r = new Recording(f); String encodeName = f.getName(); - int i = encodeName.indexOf('_'); + int i = encodeName.indexOf('-'); if(i > 0) { encodeName = encodeName.substring(0, i); } @@ -292,7 +453,8 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { File[] files = avPath.listFiles(); if (files != null) { for (File f : files) { - if(f.getName().endsWith(AV_EXTENSION)) { + if(f.getName().endsWith(THREEGP_EXTENSION) + || f.getName().endsWith(MPEG4_EXTENSION)) { indexFile(f); } } @@ -312,5 +474,26 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { activity.getMapLayers().getContextMenuLayer().setLocation(null, ""); activity.getMapView().refreshMap(); } + + + @Override + public void settingsActivityCreate(final SettingsActivity activity, PreferenceScreen screen) { + PreferenceGroup grp = screen.getPreferenceManager().createPreferenceScreen(activity); + grp.setSummary(R.string.av_settings_descr); + grp.setTitle(R.string.av_settings); + grp.setKey("av_settings"); + screen.addPreference(grp); + OsmandSettings settings = app.getSettings(); + + grp.addPreference(activity.createCheckBoxPreference(settings.AV_EXTERNAL_RECORDER, + R.string.av_use_external_recorder, R.string.av_use_external_recorder_descr)); + + String[] entries = new String[] {"3GP", "MP4"}; + Integer[] intValues = new Integer[] {OsmandSettings.VIDEO_OUTPUT_3GP, OsmandSettings.VIDEO_OUTPUT_MP4}; + ListPreference lp = activity.createListPreference(settings.AV_VIDEO_FORMAT, + entries, intValues, R.string.av_video_format, R.string.av_video_format_descr); + grp.addPreference(lp); + + } }