From 75086e6a186c28b6ef0dc22b93eb3b1b53e55846 Mon Sep 17 00:00:00 2001 From: "NTGEG\\anabil" Date: Sat, 28 Apr 2018 11:24:31 +0200 Subject: [PATCH 001/111] Implement pause feature by adding 2 main component: 1-make recording service a bound service to deliver pause and resume. 2-integrate with open source library mp4 parser to append multiple files to one. //issues : 1-file naming increment issue. --- .idea/misc.xml | 5 +- .idea/vcs.xml | 5 +- app/app.iml | 41 +++-- app/build.gradle | 1 + .../soundrecorder/RecordingService.java | 151 ++++++++++++++++-- .../fragments/RecordFragment.java | 33 +++- app/src/main/res/values/strings.xml | 2 + 7 files changed, 191 insertions(+), 47 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index b0a270f5..39638799 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,8 +1,5 @@ - - - - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index c63efbb2..94a25f7f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,9 +1,6 @@ - - - - + \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index 1cdca55a..0166f041 100644 --- a/app/app.iml +++ b/app/app.iml @@ -22,7 +22,7 @@ - + @@ -62,13 +62,6 @@ - - - - - - - @@ -76,13 +69,17 @@ + + + + + + + - - - @@ -92,8 +89,6 @@ - - @@ -101,20 +96,20 @@ - - + - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ae3d3494..7d0aec16 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,4 +28,5 @@ dependencies { compile 'com.android.support:recyclerview-v7:21.0.+' compile 'com.melnykov:floatingactionbutton:1.1.0' compile 'com.jpardogo.materialtabstrip:library:1.0.6' + compile 'com.googlecode.mp4parser:isoparser:1.1.21' } diff --git a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java index a8b36a18..b6f0aef5 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java +++ b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java @@ -6,20 +6,31 @@ import android.app.Service; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.media.MediaRecorder; +import android.os.Binder; import android.os.Environment; import android.os.IBinder; -import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.widget.Toast; +import com.coremedia.iso.boxes.Container; import com.danielkim.soundrecorder.activities.MainActivity; +import com.googlecode.mp4parser.FileDataSourceImpl; +import com.googlecode.mp4parser.authoring.Movie; +import com.googlecode.mp4parser.authoring.Track; +import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder; +import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator; +import com.googlecode.mp4parser.authoring.tracks.AppendTrack; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -47,9 +58,19 @@ public class RecordingService extends Service { private Timer mTimer = null; private TimerTask mIncrementTimerTask = null; + private boolean isPausePressed; + private int tempFileCount = 0; + + private ArrayList filesPaused = new ArrayList<>(); + private ArrayList pauseDurations= new ArrayList<>(); + + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + + @Override public IBinder onBind(Intent intent) { - return null; + return mBinder; } public interface OnTimerChangedListener { @@ -104,23 +125,36 @@ public void startRecording() { } } - public void setFileNameAndPath(){ - int count = 0; - File f; + public void setFileNameAndPath() { + if (isPausePressed) { + mFileName = getString(R.string.default_file_name) + (++tempFileCount )+ "_" + ".tmp"; + mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFilePath += "/SoundRecorder/" + mFileName; + } else { + int count = 0; + File f; + + do { + count++; - do{ - count++; + mFileName = + getString(R.string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; - mFileName = getString(R.string.default_file_name) - + "_" + (mDatabase.getCount() + count) + ".mp4"; - mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + mFileName; + mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFilePath += "/SoundRecorder/" + mFileName; - f = new File(mFilePath); - }while (f.exists() && !f.isDirectory()); + f = new File(mFilePath); + } while (f.exists() && !f.isDirectory()); } + } public void stopRecording() { + if (isPausePressed) + filesPaused.add(mFilePath); + + isPausePressed=false; + setFileNameAndPath(); + mRecorder.stop(); mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); mRecorder.release(); @@ -133,6 +167,12 @@ public void stopRecording() { } mRecorder = null; + if (filesPaused != null && !filesPaused.isEmpty()) { + if (makeSingleFile(filesPaused)) { + for (long duration : pauseDurations) + mElapsedMillis += duration; + } + } try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); @@ -142,6 +182,76 @@ public void stopRecording() { } } + /** + * collect temp generated files because of pause to one target file + * @param filesPaused contains all temp files due to pause + */ + private boolean makeSingleFile(ArrayList filesPaused) { + ArrayList tracks =new ArrayList<>(); + Movie finalMovie =new Movie(); + for (String filePath : filesPaused) { + try { + Movie movie = MovieCreator.build(new FileDataSourceImpl(filePath)); + List movieTracks = movie.getTracks(); + tracks.addAll(movieTracks); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + if (tracks.size() > 0) { + try { + finalMovie.addTrack(new AppendTrack(tracks.toArray(new Track[tracks.size()]))); + } catch (IOException e) { + e.printStackTrace(); + } + } +// try { +// finalMovie.addTrack(new AppendTrack((Track) tracks)); +// } catch (IOException e) { +// e.printStackTrace(); +// } + Container mp4file = new DefaultMp4Builder().build(finalMovie); + FileChannel fc = null; + try { + setFileNameAndPath(); + fc = new FileOutputStream(new File(mFilePath)).getChannel(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return false; + } + try { + mp4file.writeContainer(fc); + fc.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + } + + public void pauseRecording(){ + isPausePressed=true; + mRecorder.stop(); + mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); + pauseDurations.add(mElapsedMillis); + Toast.makeText(this, getString(R.string.toast_recording_paused), Toast.LENGTH_LONG).show(); + + //remove notification + if (mIncrementTimerTask != null) { + mIncrementTimerTask.cancel(); + mIncrementTimerTask = null; + } + filesPaused.add(mFilePath); + + } + + public void resumeRecording(){ + startRecording(); + } + private void startTimer() { mTimer = new Timer(); mIncrementTimerTask = new TimerTask() { @@ -171,4 +281,17 @@ private Notification createNotification() { return mBuilder.build(); } + + + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + public RecordingService getService() { + // Return this instance of LocalService so clients can call public methods + return RecordingService.this; + } + } } diff --git a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java index 151822c0..8328eea6 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java @@ -1,8 +1,12 @@ package com.danielkim.soundrecorder.fragments; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; import android.os.Environment; +import android.os.IBinder; import android.os.SystemClock; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -47,6 +51,8 @@ public class RecordFragment extends Fragment { private Chronometer mChronometer = null; long timeWhenPaused = 0; //stores time when user clicks pause button + RecordingService mRecordingService; + /** * Use this factory method to create a new instance of * this fragment using the provided parameters. @@ -104,6 +110,24 @@ public void onClick(View v) { return recordView; } + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + RecordingService.LocalBinder binder = (RecordingService.LocalBinder) service; + mRecordingService = binder.getService(); + + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + + } + }; + // Recording Start/Stop //TODO: recording pause private void onRecord(boolean start){ @@ -113,7 +137,7 @@ private void onRecord(boolean start){ if (start) { // start recording mRecordButton.setImageResource(R.drawable.ic_media_stop); - //mPauseButton.setVisibility(View.VISIBLE); + mPauseButton.setVisibility(View.VISIBLE); Toast.makeText(getActivity(),R.string.toast_recording_start,Toast.LENGTH_SHORT).show(); File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder"); if (!folder.exists()) { @@ -142,6 +166,7 @@ public void onChronometerTick(Chronometer chronometer) { //start RecordingService getActivity().startService(intent); + getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); //keep screen on while recording getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -151,13 +176,15 @@ public void onChronometerTick(Chronometer chronometer) { } else { //stop recording mRecordButton.setImageResource(R.drawable.ic_mic_white_36dp); - //mPauseButton.setVisibility(View.GONE); + mPauseButton.setVisibility(View.GONE); mChronometer.stop(); mChronometer.setBase(SystemClock.elapsedRealtime()); timeWhenPaused = 0; mRecordingPrompt.setText(getString(R.string.record_prompt)); getActivity().stopService(intent); + getActivity().unbindService(mConnection); + //allow the screen to turn off again once recording is finished getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } @@ -167,6 +194,7 @@ public void onChronometerTick(Chronometer chronometer) { private void onPauseRecord(boolean pause) { if (pause) { //pause recording + mRecordingService.pauseRecording(); mPauseButton.setCompoundDrawablesWithIntrinsicBounds (R.drawable.ic_media_play ,0 ,0 ,0); mRecordingPrompt.setText((String)getString(R.string.resume_recording_button).toUpperCase()); @@ -174,6 +202,7 @@ private void onPauseRecord(boolean pause) { mChronometer.stop(); } else { //resume recording + mRecordingService.resumeRecording(); mPauseButton.setCompoundDrawablesWithIntrinsicBounds (R.drawable.ic_media_pause ,0 ,0 ,0); mRecordingPrompt.setText((String)getString(R.string.pause_recording_button).toUpperCase()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5061b9dc..489017ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,8 @@ Recording started Recording saved to + Recording paused + Recording resumed %1$s successfully deleted The file %1$s already exists. Please choose a different file name. From bc37781d6af65f6c95793dcb0808e7e478ad4751 Mon Sep 17 00:00:00 2001 From: "NTGEG\\anabil" Date: Sat, 28 Apr 2018 12:18:34 +0200 Subject: [PATCH 002/111] 1-file naming increment issue : fixed. 2-handle case : user press stop after pause --- .../soundrecorder/RecordingService.java | 90 ++++++++++--------- .../fragments/RecordFragment.java | 2 + 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java index b6f0aef5..ad09a6d1 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java +++ b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java @@ -58,7 +58,8 @@ public class RecordingService extends Service { private Timer mTimer = null; private TimerTask mIncrementTimerTask = null; - private boolean isPausePressed; + private boolean isFilePathTemp = true; + private boolean isPaused; private int tempFileCount = 0; private ArrayList filesPaused = new ArrayList<>(); @@ -98,7 +99,32 @@ public void onDestroy() { super.onDestroy(); } + public void setFileNameAndPath() { + if (isFilePathTemp) { + mFileName = getString(R.string.default_file_name) + (++tempFileCount )+ "_" + ".tmp"; + mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFilePath += "/SoundRecorder/" + mFileName; + } else { + int count = 0; + File f; + + do { + count++; + + mFileName = + getString(R.string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; + + mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFilePath += "/SoundRecorder/" + mFileName; + + f = new File(mFilePath); + } while (f.exists() && !f.isDirectory()); + } + } + public void startRecording() { + isPaused = false; + isFilePathTemp=true; setFileNameAndPath(); mRecorder = new MediaRecorder(); @@ -125,41 +151,37 @@ public void startRecording() { } } - public void setFileNameAndPath() { - if (isPausePressed) { - mFileName = getString(R.string.default_file_name) + (++tempFileCount )+ "_" + ".tmp"; - mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + mFileName; - } else { - int count = 0; - File f; - - do { - count++; - - mFileName = - getString(R.string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; + public void pauseRecording(){ + isPaused = true; + mRecorder.stop(); + mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); + pauseDurations.add(mElapsedMillis); + Toast.makeText(this, getString(R.string.toast_recording_paused), Toast.LENGTH_LONG).show(); - mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + mFileName; + //remove notification + if (mIncrementTimerTask != null) { + mIncrementTimerTask.cancel(); + mIncrementTimerTask = null; + } + filesPaused.add(mFilePath); - f = new File(mFilePath); - } while (f.exists() && !f.isDirectory()); } - } public void stopRecording() { - if (isPausePressed) - filesPaused.add(mFilePath); + if(!isPaused) + filesPaused.add(mFilePath); - isPausePressed=false; + isFilePathTemp =false; setFileNameAndPath(); - mRecorder.stop(); - mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); + if (!isPaused) { + mRecorder.stop(); + mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); + } mRecorder.release(); Toast.makeText(this, getString(R.string.toast_recording_finish) + " " + mFilePath, Toast.LENGTH_LONG).show(); + isPaused = false; //remove notification if (mIncrementTimerTask != null) { mIncrementTimerTask.cancel(); @@ -215,7 +237,6 @@ private boolean makeSingleFile(ArrayList filesPaused) { Container mp4file = new DefaultMp4Builder().build(finalMovie); FileChannel fc = null; try { - setFileNameAndPath(); fc = new FileOutputStream(new File(mFilePath)).getChannel(); } catch (FileNotFoundException e) { e.printStackTrace(); @@ -232,23 +253,8 @@ private boolean makeSingleFile(ArrayList filesPaused) { } - public void pauseRecording(){ - isPausePressed=true; - mRecorder.stop(); - mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); - pauseDurations.add(mElapsedMillis); - Toast.makeText(this, getString(R.string.toast_recording_paused), Toast.LENGTH_LONG).show(); - - //remove notification - if (mIncrementTimerTask != null) { - mIncrementTimerTask.cancel(); - mIncrementTimerTask = null; - } - filesPaused.add(mFilePath); - - } - public void resumeRecording(){ + isPaused=false; startRecording(); } diff --git a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java index 8328eea6..508b45eb 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java @@ -182,6 +182,8 @@ public void onChronometerTick(Chronometer chronometer) { timeWhenPaused = 0; mRecordingPrompt.setText(getString(R.string.record_prompt)); + //handle case : user press stop after pause + if(!mPauseRecording) mPauseRecording = true; getActivity().stopService(intent); getActivity().unbindService(mConnection); From 0bb7944de24e28ab446a6c2a491165cfecf8cb97 Mon Sep 17 00:00:00 2001 From: "NTGEG\\anabil" Date: Sat, 28 Apr 2018 12:40:25 +0200 Subject: [PATCH 003/111] delete todos regarding pause/resume feature --- .../com/danielkim/soundrecorder/fragments/RecordFragment.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java index 508b45eb..06a868c1 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java @@ -129,7 +129,6 @@ public void onServiceDisconnected(ComponentName arg0) { }; // Recording Start/Stop - //TODO: recording pause private void onRecord(boolean start){ Intent intent = new Intent(getActivity(), RecordingService.class); @@ -192,7 +191,6 @@ public void onChronometerTick(Chronometer chronometer) { } } - //TODO: implement pause recording private void onPauseRecord(boolean pause) { if (pause) { //pause recording From 00251360acdb9be587cb9240f97968f1f6ab889d Mon Sep 17 00:00:00 2001 From: naXa! Date: Sun, 19 Aug 2018 01:06:04 +0300 Subject: [PATCH 004/111] + Change package name (com.danielkim -> by.naxa) + update build tools + update dependencies --- .idea/compiler.xml | 23 ----- .idea/copyright/profiles_settings.xml | 3 - .idea/encodings.xml | 5 - .idea/gradle.xml | 3 +- .idea/inspectionProfiles/Project_Default.xml | 11 --- .../inspectionProfiles/profiles_settings.xml | 7 -- .idea/misc.xml | 29 +----- .idea/scopes/scope_settings.xml | 5 - .idea/vcs.xml | 2 +- SoundRecorder.iml | 2 +- app/app.iml | 97 ++++++++++++++----- app/build.gradle | 24 +++-- .../naxa/soundrecorder/ApplicationTest.java | 25 +++++ .../soundrecorder/ApplicationTest.java | 13 --- app/src/main/AndroidManifest.xml | 6 +- .../naxa}/soundrecorder/DBHelper.java | 4 +- .../soundrecorder/MySharedPreferences.java | 2 +- .../naxa}/soundrecorder/RecordingItem.java | 2 +- .../naxa}/soundrecorder/RecordingService.java | 16 +-- .../activities/MainActivity.java | 21 ++-- .../activities/SettingsActivity.java | 16 ++- .../adapters/FileViewerAdapter.java | 23 +++-- .../fragments/FileViewerFragment.java | 12 +-- .../fragments/LicensesFragment.java | 4 +- .../fragments/PlaybackFragment.java | 36 +++---- .../fragments/RecordFragment.java | 18 ++-- .../fragments/SettingsFragment.java | 10 +- .../listeners/OnDatabaseChangedListener.java | 2 +- .../main/res/layout/fragment_file_viewer.xml | 2 +- app/src/main/res/values-v21/styles.xml | 2 - app/src/main/res/values/styles.xml | 3 +- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 33 files changed, 210 insertions(+), 224 deletions(-) delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copyright/profiles_settings.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/scopes/scope_settings.xml create mode 100644 app/src/androidTest/java/by/naxa/soundrecorder/ApplicationTest.java delete mode 100644 app/src/androidTest/java/com/danielkim/soundrecorder/ApplicationTest.java rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/DBHelper.java (98%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/MySharedPreferences.java (95%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/RecordingItem.java (98%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/RecordingService.java (91%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/activities/MainActivity.java (80%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/activities/SettingsActivity.java (66%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/adapters/FileViewerAdapter.java (94%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/fragments/FileViewerFragment.java (89%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/fragments/LicensesFragment.java (91%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/fragments/PlaybackFragment.java (91%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/fragments/RecordFragment.java (91%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/fragments/SettingsFragment.java (87%) rename app/src/main/java/{com/danielkim => by/naxa}/soundrecorder/listeners/OnDatabaseChangedListener.java (81%) diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 217af471..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf33..00000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index e206d70d..00000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 2a7f5bfc..7ac24c77 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,9 +3,8 @@ - + diff --git a/SoundRecorder.iml b/SoundRecorder.iml index d89502ee..5783e78f 100644 --- a/SoundRecorder.iml +++ b/SoundRecorder.iml @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index fbdd1d96..2da151bc 100644 --- a/app/app.iml +++ b/app/app.iml @@ -96,6 +96,7 @@ + diff --git a/app/src/main/res/layout/card_view.xml b/app/src/main/res/layout/card_view.xml index 0ec48c46..47269330 100644 --- a/app/src/main/res/layout/card_view.xml +++ b/app/src/main/res/layout/card_view.xml @@ -1,9 +1,9 @@ - - @@ -64,5 +65,3 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_file_viewer.xml b/app/src/main/res/layout/fragment_file_viewer.xml index 62922284..dc85fb82 100644 --- a/app/src/main/res/layout/fragment_file_viewer.xml +++ b/app/src/main/res/layout/fragment_file_viewer.xml @@ -6,6 +6,9 @@ tools:context="by.naxa.soundrecorder.fragments.FileViewerFragment"> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5fec2bc8..99e0858d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -40,4 +40,6 @@ Настройки Запись + Иконка микрофона + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c16037dd..5ed4ba47 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,5 +54,6 @@ pref_about About v%s + Microphone icon From 19a04da4365254da038649889df3f9c1c68a4208 Mon Sep 17 00:00:00 2001 From: naXa! Date: Mon, 20 Aug 2018 00:38:10 +0300 Subject: [PATCH 009/111] Change directory for storing records + refactoring --- .../java/by/naxa/soundrecorder/Paths.java | 21 +++++ .../naxa/soundrecorder/RecordingService.java | 76 +++++++------------ .../activities/MainActivity.java | 7 +- .../adapters/FileViewerAdapter.java | 18 +++-- .../fragments/FileViewerFragment.java | 24 +++--- .../fragments/RecordFragment.java | 15 +--- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/xml/filepaths.xml | 1 + 8 files changed, 78 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/by/naxa/soundrecorder/Paths.java diff --git a/app/src/main/java/by/naxa/soundrecorder/Paths.java b/app/src/main/java/by/naxa/soundrecorder/Paths.java new file mode 100644 index 00000000..ac69134d --- /dev/null +++ b/app/src/main/java/by/naxa/soundrecorder/Paths.java @@ -0,0 +1,21 @@ +package by.naxa.soundrecorder; + +import java.io.File; + +public class Paths { + + public static final String SOUND_RECORDER_FOLDER = "/SoundRecorder.by"; + + public static String combine(String parent, String... children) { + return combine(new File(parent), children); + } + + public static String combine(File parent, String... children) { + File path = parent; + for (String child : children) { + path = new File(path, child); + } + return path.toString(); + } + +} diff --git a/app/src/main/java/by/naxa/soundrecorder/RecordingService.java b/app/src/main/java/by/naxa/soundrecorder/RecordingService.java index 2c5c171c..9175619b 100644 --- a/app/src/main/java/by/naxa/soundrecorder/RecordingService.java +++ b/app/src/main/java/by/naxa/soundrecorder/RecordingService.java @@ -10,6 +10,7 @@ import android.widget.Toast; import com.coremedia.iso.boxes.Container; +import com.crashlytics.android.Crashlytics; import com.googlecode.mp4parser.FileDataSourceImpl; import com.googlecode.mp4parser.authoring.Movie; import com.googlecode.mp4parser.authoring.Track; @@ -22,12 +23,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import java.util.Timer; -import java.util.TimerTask; + +import static by.naxa.soundrecorder.R.string; /** * Created by Daniel on 12/28/2014. @@ -45,14 +44,7 @@ public class RecordingService extends Service { private long mStartingTimeMillis = 0; private long mElapsedMillis = 0; - private int mElapsedSeconds = 0; - private OnTimerChangedListener onTimerChangedListener = null; - private static final SimpleDateFormat mTimerFormat = new SimpleDateFormat("mm:ss", Locale.getDefault()); - - private Timer mTimer = null; - private TimerTask mIncrementTimerTask = null; - private boolean isFilePathTemp = true; private boolean isPaused; private int tempFileCount = 0; @@ -68,10 +60,6 @@ public IBinder onBind(Intent intent) { return mBinder; } - public interface OnTimerChangedListener { - void onTimerChanged(int seconds); - } - @Override public void onCreate() { super.onCreate(); @@ -93,23 +81,24 @@ public void onDestroy() { super.onDestroy(); } - public void setFileNameAndPath() { + public void setFileNameAndPath(boolean isFilePathTemp) { if (isFilePathTemp) { - mFileName = getString(by.naxa.soundrecorder.R.string.default_file_name) + (++tempFileCount) + "_" + ".tmp"; - mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + mFileName; + mFileName = getString(string.default_file_name) + (++tempFileCount) + "_" + ".tmp"; + mFilePath = Paths.combine(Environment.getExternalStorageDirectory().getAbsolutePath(), + Paths.SOUND_RECORDER_FOLDER, mFileName); } else { int count = 0; File f; do { - count++; + ++count; mFileName = - getString(by.naxa.soundrecorder.R.string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; + getString(string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; - mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + mFileName; + mFilePath = Paths.combine( + Environment.getExternalStorageDirectory().getAbsolutePath(), + Paths.SOUND_RECORDER_FOLDER, mFileName); f = new File(mFilePath); } while (f.exists() && !f.isDirectory()); @@ -117,9 +106,8 @@ public void setFileNameAndPath() { } public void startRecording() { - isPaused = false; - isFilePathTemp = true; - setFileNameAndPath(); + boolean isTemporary = true; + setFileNameAndPath(isTemporary); mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); @@ -135,10 +123,11 @@ public void startRecording() { try { mRecorder.prepare(); mRecorder.start(); + isPaused = false; mStartingTimeMillis = System.currentTimeMillis(); - } catch (IOException e) { // TODO propagate this exception to MainActivity + Crashlytics.logException(e); Log.e(LOG_TAG, "prepare() failed"); } } @@ -147,41 +136,30 @@ public void pauseRecording() { if (isPaused) return; - isPaused = true; mRecorder.stop(); + isPaused = true; mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); pauseDurations.add(mElapsedMillis); - Toast.makeText(this, getString(by.naxa.soundrecorder.R.string.toast_recording_paused), Toast.LENGTH_LONG).show(); + Toast.makeText(this, getString(string.toast_recording_paused), Toast.LENGTH_LONG).show(); - //remove notification - if (mIncrementTimerTask != null) { - mIncrementTimerTask.cancel(); - mIncrementTimerTask = null; - } filesPaused.add(mFilePath); - } public void stopRecording() { if (!isPaused) filesPaused.add(mFilePath); - isFilePathTemp = false; - setFileNameAndPath(); + boolean isTemporary = false; + setFileNameAndPath(isTemporary); if (!isPaused) { mRecorder.stop(); mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); } mRecorder.release(); - Toast.makeText(this, getString(by.naxa.soundrecorder.R.string.toast_recording_finish) + " " + mFilePath, Toast.LENGTH_LONG).show(); + Toast.makeText(this, getString(string.toast_recording_finish) + " " + mFilePath, Toast.LENGTH_LONG).show(); isPaused = false; - //remove notification - if (mIncrementTimerTask != null) { - mIncrementTimerTask.cancel(); - mIncrementTimerTask = null; - } mRecorder = null; if (filesPaused != null && !filesPaused.isEmpty()) { @@ -193,8 +171,8 @@ public void stopRecording() { try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); - } catch (Exception e) { + Crashlytics.logException(e); Log.e(LOG_TAG, "exception", e); } } @@ -213,6 +191,7 @@ private boolean makeSingleFile(ArrayList filesPaused) { List movieTracks = movie.getTracks(); tracks.addAll(movieTracks); } catch (IOException e) { + Crashlytics.logException(e); e.printStackTrace(); return false; } @@ -222,19 +201,17 @@ private boolean makeSingleFile(ArrayList filesPaused) { try { finalMovie.addTrack(new AppendTrack(tracks.toArray(new Track[tracks.size()]))); } catch (IOException e) { + Crashlytics.logException(e); e.printStackTrace(); } } -// try { -// finalMovie.addTrack(new AppendTrack((Track) tracks)); -// } catch (IOException e) { -// e.printStackTrace(); -// } + Container mp4file = new DefaultMp4Builder().build(finalMovie); FileChannel fc = null; try { fc = new FileOutputStream(new File(mFilePath)).getChannel(); } catch (FileNotFoundException e) { + Crashlytics.logException(e); e.printStackTrace(); return false; } @@ -243,6 +220,7 @@ private boolean makeSingleFile(ArrayList filesPaused) { fc.close(); return true; } catch (IOException e) { + Crashlytics.logException(e); e.printStackTrace(); return false; } diff --git a/app/src/main/java/by/naxa/soundrecorder/activities/MainActivity.java b/app/src/main/java/by/naxa/soundrecorder/activities/MainActivity.java index 8878a4a7..dbe75bdc 100644 --- a/app/src/main/java/by/naxa/soundrecorder/activities/MainActivity.java +++ b/app/src/main/java/by/naxa/soundrecorder/activities/MainActivity.java @@ -12,10 +12,11 @@ import android.view.MenuItem; import com.astuetz.PagerSlidingTabStrip; +import com.crashlytics.android.Crashlytics; + import by.naxa.soundrecorder.R; import by.naxa.soundrecorder.fragments.FileViewerFragment; import by.naxa.soundrecorder.fragments.RecordFragment; -import com.crashlytics.android.Crashlytics; import io.fabric.sdk.android.Fabric; public class MainActivity extends AppCompatActivity { @@ -82,10 +83,10 @@ public MyAdapter(FragmentManager fm) { public Fragment getItem(int position) { switch(position){ case 0:{ - return RecordFragment.newInstance(position); + return RecordFragment.newInstance(); } case 1:{ - return FileViewerFragment.newInstance(position); + return FileViewerFragment.newInstance(); } } return null; diff --git a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java index a42f6379..a89d6301 100644 --- a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java +++ b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java @@ -10,6 +10,7 @@ import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.text.format.DateUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -17,18 +18,18 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; -import android.text.format.DateUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; import by.naxa.soundrecorder.DBHelper; +import by.naxa.soundrecorder.Paths; import by.naxa.soundrecorder.R; import by.naxa.soundrecorder.RecordingItem; import by.naxa.soundrecorder.fragments.PlaybackFragment; import by.naxa.soundrecorder.listeners.OnDatabaseChangedListener; -import java.io.File; -import java.util.concurrent.TimeUnit; -import java.util.ArrayList; - /** * Created by Daniel on 12/29/2014. */ @@ -47,7 +48,7 @@ public FileViewerAdapter(Context context, LinearLayoutManager linearLayoutManage super(); mContext = context; mDatabase = new DBHelper(mContext); - mDatabase.setOnDatabaseChangedListener(this); + DBHelper.setOnDatabaseChangedListener(this); llm = linearLayoutManager; } @@ -210,8 +211,9 @@ public void removeOutOfApp(String filePath) { public void rename(int position, String name) { //rename a file - String mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); - mFilePath += "/SoundRecorder/" + name; + String mFilePath = Paths.combine( + Environment.getExternalStorageDirectory().getAbsolutePath(), + Paths.SOUND_RECORDER_FOLDER, name); File f = new File(mFilePath); if (f.exists() && !f.isDirectory()) { diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java index 7e5600fa..16cd9e78 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java @@ -11,23 +11,23 @@ import android.view.View; import android.view.ViewGroup; +import by.naxa.soundrecorder.Paths; import by.naxa.soundrecorder.R; import by.naxa.soundrecorder.adapters.FileViewerAdapter; +import static android.os.Environment.getExternalStorageDirectory; + /** * Created by Daniel on 12/23/2014. */ public class FileViewerFragment extends Fragment { - private static final String ARG_POSITION = "position"; private static final String LOG_TAG = "FileViewerFragment"; - private int position; private FileViewerAdapter mFileViewerAdapter; - public static FileViewerFragment newInstance(int position) { + public static FileViewerFragment newInstance() { FileViewerFragment f = new FileViewerFragment(); Bundle b = new Bundle(); - b.putInt(ARG_POSITION, position); f.setArguments(b); return f; @@ -36,7 +36,6 @@ public static FileViewerFragment newInstance(int position) { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - position = getArguments().getInt(ARG_POSITION); observer.startWatching(); } @@ -62,21 +61,18 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return v; } - FileObserver observer = - new FileObserver(android.os.Environment.getExternalStorageDirectory().toString() - + "/SoundRecorder") { + private final FileObserver observer = + new FileObserver(Paths.combine( + getExternalStorageDirectory(), Paths.SOUND_RECORDER_FOLDER)) { // set up a file observer to watch this directory on sd card @Override public void onEvent(int event, String file) { if (event == FileObserver.DELETE) { // user deletes a recording file out of the app - String filePath = android.os.Environment.getExternalStorageDirectory().toString() - + "/SoundRecorder" + file + "]"; - - Log.d(LOG_TAG, "File deleted [" - + android.os.Environment.getExternalStorageDirectory().toString() - + "/SoundRecorder" + file + "]"); + String filePath = Paths.combine(getExternalStorageDirectory(), + Paths.SOUND_RECORDER_FOLDER, file); + Log.d(LOG_TAG, "File deleted [" + filePath + "]"); // remove file from database and recyclerview mFileViewerAdapter.removeOutOfApp(filePath); diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java index 58818642..84408277 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java @@ -23,6 +23,7 @@ import java.io.File; +import by.naxa.soundrecorder.Paths; import by.naxa.soundrecorder.PermissionsHelper; import by.naxa.soundrecorder.R; import by.naxa.soundrecorder.RecordingService; @@ -35,14 +36,10 @@ * create an instance of this fragment. */ public class RecordFragment extends Fragment { - // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_POSITION = "position"; private static final String LOG_TAG = RecordFragment.class.getSimpleName(); private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 1; private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO_RESUME = 2; - private int position; - //Recording controls private FloatingActionButton mRecordButton = null; private Button mPauseButton = null; @@ -64,22 +61,17 @@ public class RecordFragment extends Fragment { * * @return A new instance of fragment Record_Fragment. */ - public static RecordFragment newInstance(int position) { + public static RecordFragment newInstance() { RecordFragment f = new RecordFragment(); Bundle b = new Bundle(); - b.putInt(ARG_POSITION, position); f.setArguments(b); return f; } - public RecordFragment() { - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - position = getArguments().getInt(ARG_POSITION); } @Override @@ -169,7 +161,8 @@ private void startRecording(Intent intent) { mRecordButton.setImageResource(R.drawable.ic_media_stop); mPauseButton.setVisibility(View.VISIBLE); Toast.makeText(getActivity(), R.string.toast_recording_start, Toast.LENGTH_SHORT).show(); - File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder"); + File folder = new File(Environment.getExternalStorageDirectory(), + Paths.SOUND_RECORDER_FOLDER); if (!folder.exists()) { //folder /SoundRecorder doesn't exist, create the folder folder.mkdir(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ed4ba47..5bce991f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ - Website: github.com/naXa777/SoundRecorder + Website: https://github.com/naXa777/SoundRecorder Sound Recorder diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml index 51d645d9..a54f7a93 100644 --- a/app/src/main/res/xml/filepaths.xml +++ b/app/src/main/res/xml/filepaths.xml @@ -1,3 +1,4 @@ + From f5c3ad51fe75407a852cf19e1fcb7a792aa2adfe Mon Sep 17 00:00:00 2001 From: naXa! Date: Mon, 20 Aug 2018 04:07:10 +0300 Subject: [PATCH 010/111] Show error (in Snackbar) if permission is not granted --- app/app.iml | 2 ++ app/build.gradle | 1 + .../naxa/soundrecorder/fragments/PlaybackFragment.java | 5 +++++ .../by/naxa/soundrecorder/fragments/RecordFragment.java | 9 +++++++++ app/src/main/res/values-ru/strings.xml | 8 +++++--- app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/app.iml b/app/app.iml index 2da151bc..5b257424 100644 --- a/app/app.iml +++ b/app/app.iml @@ -152,9 +152,11 @@ + + diff --git a/app/build.gradle b/app/build.gradle index ef19a9e6..7308240e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'com.android.support:appcompat-v7:28.0.+' implementation 'com.android.support:cardview-v7:28.0.+' implementation 'com.android.support:recyclerview-v7:28.0.+' + implementation 'com.android.support:design:28.0.+' implementation 'com.melnykov:floatingactionbutton:1.3.0' implementation 'com.jpardogo.materialtabstrip:library:1.1.1' implementation 'com.googlecode.mp4parser:isoparser:1.1.22' diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java index 2f0c48e5..8a3d1558 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java @@ -10,6 +10,7 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; import android.util.Log; import android.view.View; @@ -363,6 +364,10 @@ public void onRequestPermissionsResult(int requestCode, && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! startOrResumePlaying(); + } else { + Snackbar.make(getActivity().findViewById(android.R.id.content), + R.string.error_no_permission_granted_for_playback, Snackbar.LENGTH_LONG) + .show(); } break; } diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java index 84408277..f51e7b9a 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java @@ -9,6 +9,7 @@ import android.os.Environment; import android.os.IBinder; import android.os.SystemClock; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; @@ -241,6 +242,10 @@ public void onRequestPermissionsResult(int requestCode, final Intent intent = new Intent(getActivity(), RecordingService.class); startRecording(intent); mStartRecording = false; + } else { + Snackbar.make(getActivity().findViewById(android.R.id.content), + R.string.error_no_permission_granted_record, Snackbar.LENGTH_SHORT) + .show(); } break; } @@ -251,6 +256,10 @@ public void onRequestPermissionsResult(int requestCode, // permission was granted, yay! resumeRecording(); mPauseRecording = true; + } else { + Snackbar.make(getActivity().findViewById(android.R.id.content), + R.string.error_no_permission_granted_record, Snackbar.LENGTH_LONG) + .show(); } break; } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 99e0858d..0b2819bb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -4,12 +4,12 @@ Sound Recorder Лицензии - Сохраненные + Сохранённые Запись началась Запись сохранена в - Файл %1$s успешно удален + Файл %1$s успешно удалён Файл %1$s уже существует. Пожалуйста, выберите другое имя файла. @@ -29,7 +29,7 @@ Да Нет - Нет сохраненных записей + Нет сохранённых записей Пауза Продолжить Запись @@ -41,5 +41,7 @@ Запись Иконка микрофона + Не могу начать запись без вашего разрешения. + Не могу воспроизвести сохранённую запись без вашего разрешения. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5bce991f..f4d0c7ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,5 +55,7 @@ About v%s Microphone icon + Can not start sound recording without your permission. + Can not play the recording without your permission to access the external storage. From c5f5583bfbf9da890feb70a21f9398e4aee395d9 Mon Sep 17 00:00:00 2001 From: naXa! Date: Mon, 20 Aug 2018 05:53:08 +0300 Subject: [PATCH 011/111] Modernization: replace com.melnykov.fab.FloatingActionButton (deprecated) with android.support.design.widget.FloatingActionButton --- app/app.iml | 2 - app/build.gradle | 1 - .../fragments/PlaybackFragment.java | 8 +- .../fragments/RecordFragment.java | 7 +- app/src/main/res/layout/card_view.xml | 103 +++++++++--------- .../res/layout/fragment_media_playback.xml | 18 ++- app/src/main/res/layout/fragment_record.xml | 16 +-- 7 files changed, 76 insertions(+), 79 deletions(-) diff --git a/app/app.iml b/app/app.iml index 5b257424..769e0443 100644 --- a/app/app.iml +++ b/app/app.iml @@ -149,7 +149,6 @@ - @@ -161,7 +160,6 @@ - diff --git a/app/build.gradle b/app/build.gradle index 7308240e..c9a3ea9a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,7 +28,6 @@ dependencies { implementation 'com.android.support:cardview-v7:28.0.+' implementation 'com.android.support:recyclerview-v7:28.0.+' implementation 'com.android.support:design:28.0.+' - implementation 'com.melnykov:floatingactionbutton:1.3.0' implementation 'com.jpardogo.materialtabstrip:library:1.1.1' implementation 'com.googlecode.mp4parser:isoparser:1.1.22' androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java index 8a3d1558..2a8ad7b1 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/PlaybackFragment.java @@ -7,9 +7,11 @@ import android.graphics.ColorFilter; import android.graphics.LightingColorFilter; import android.media.MediaPlayer; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; import android.util.Log; @@ -19,8 +21,6 @@ import android.widget.SeekBar; import android.widget.TextView; -import com.melnykov.fab.FloatingActionButton; - import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -112,7 +112,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { ColorFilter filter = new LightingColorFilter (getResources().getColor(R.color.primary), getResources().getColor(R.color.primary)); mSeekBar.getProgressDrawable().setColorFilter(filter); - mSeekBar.getThumb().setColorFilter(filter); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mSeekBar.getThumb().setColorFilter(filter); + } mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java index f51e7b9a..40f864f1 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java @@ -9,6 +9,7 @@ import android.os.Environment; import android.os.IBinder; import android.os.SystemClock; +import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -20,8 +21,6 @@ import android.widget.TextView; import android.widget.Toast; -import com.melnykov.fab.FloatingActionButton; - import java.io.File; import by.naxa.soundrecorder.Paths; @@ -85,8 +84,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mRecordingPrompt = recordView.findViewById(R.id.recording_status_text); mRecordButton = recordView.findViewById(R.id.btnRecord); - mRecordButton.setColorNormal(getResources().getColor(R.color.primary)); - mRecordButton.setColorPressed(getResources().getColor(R.color.primary_dark)); + //mRecordButton.setColorNormal(getResources().getColor(R.color.primary)); + //mRecordButton.setColorPressed(getResources().getColor(R.color.primary_dark)); mRecordButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/app/src/main/res/layout/card_view.xml b/app/src/main/res/layout/card_view.xml index 47269330..12f4d15d 100644 --- a/app/src/main/res/layout/card_view.xml +++ b/app/src/main/res/layout/card_view.xml @@ -1,67 +1,66 @@ - + + + android:layout_height="match_parent" + android:orientation="horizontal"> + + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:orientation="vertical"> - + android:fontFamily="sans-serif-condensed" + android:text="file_name" + android:textSize="15sp" + android:textStyle="bold" /> - - - - - - - - + android:layout_marginTop="7dp" + android:fontFamily="sans-serif-condensed" + android:text="00:00" + android:textSize="12sp" /> + - + + + diff --git a/app/src/main/res/layout/fragment_media_playback.xml b/app/src/main/res/layout/fragment_media_playback.xml index f1a82d7e..a41ac23c 100644 --- a/app/src/main/res/layout/fragment_media_playback.xml +++ b/app/src/main/res/layout/fragment_media_playback.xml @@ -7,12 +7,11 @@ android:layout_height="wrap_content"> @@ -28,6 +27,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" android:layout_marginTop="7dp" android:layout_marginBottom="7dp" android:text="file_name.mp4" @@ -53,18 +53,16 @@ android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> - - + android:layout_marginTop="16dp" + android:layout_marginBottom="16dp" + android:src="@drawable/ic_media_play" + app:backgroundTint="@color/primary" + app:fabSize="normal" /> - + app:fabSize="normal" + app:srcCompat="@drawable/ic_mic_white_36dp" /> + android:layout_marginBottom="16dp" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp"/> Date: Mon, 20 Aug 2018 05:57:44 +0300 Subject: [PATCH 012/111] Add missing russian translations --- app/src/main/res/values-ru/strings.xml | 7 ++++++- app/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0b2819bb..7eed7577 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -2,7 +2,7 @@ - Sound Recorder + Диктофон Лицензии Сохранённые @@ -40,6 +40,11 @@ Настройки Запись + + Включить запись высокого качества + Записывать в CD качестве + О приложении + Иконка микрофона Не могу начать запись без вашего разрешения. Не могу воспроизвести сохранённую запись без вашего разрешения. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4d0c7ff..acb57576 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ pref_about About v%s + Microphone icon Can not start sound recording without your permission. Can not play the recording without your permission to access the external storage. From 0de0d04c0f68bfda06f6eab1a31a23fe0571a644 Mon Sep 17 00:00:00 2001 From: naXa! Date: Mon, 20 Aug 2018 21:00:59 +0300 Subject: [PATCH 013/111] + Verify that external storage is available and show error (if it's not) + Do not start chronometer if the recording is not going to be saved (because of I/O error) --- .../java/by/naxa/soundrecorder/Paths.java | 18 ++++++++++ .../naxa/soundrecorder/RecordingService.java | 5 +-- .../adapters/FileViewerAdapter.java | 12 +++---- .../fragments/FileViewerFragment.java | 9 ++--- .../fragments/RecordFragment.java | 34 +++++++++++++------ app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/by/naxa/soundrecorder/Paths.java b/app/src/main/java/by/naxa/soundrecorder/Paths.java index ac69134d..15558f4c 100644 --- a/app/src/main/java/by/naxa/soundrecorder/Paths.java +++ b/app/src/main/java/by/naxa/soundrecorder/Paths.java @@ -1,5 +1,7 @@ package by.naxa.soundrecorder; +import android.os.Environment; + import java.io.File; public class Paths { @@ -18,4 +20,20 @@ public static String combine(File parent, String... children) { return path.toString(); } + /** + * Checks if external storage is available for read and write. + */ + public static boolean isExternalStorageWritable() { + return (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())); + } + + /** + * Checks if external storage is available to at least read. + */ + public static boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + return Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); + } + } diff --git a/app/src/main/java/by/naxa/soundrecorder/RecordingService.java b/app/src/main/java/by/naxa/soundrecorder/RecordingService.java index 9175619b..dc54970d 100644 --- a/app/src/main/java/by/naxa/soundrecorder/RecordingService.java +++ b/app/src/main/java/by/naxa/soundrecorder/RecordingService.java @@ -84,7 +84,8 @@ public void onDestroy() { public void setFileNameAndPath(boolean isFilePathTemp) { if (isFilePathTemp) { mFileName = getString(string.default_file_name) + (++tempFileCount) + "_" + ".tmp"; - mFilePath = Paths.combine(Environment.getExternalStorageDirectory().getAbsolutePath(), + mFilePath = Paths.combine( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), Paths.SOUND_RECORDER_FOLDER, mFileName); } else { int count = 0; @@ -97,7 +98,7 @@ public void setFileNameAndPath(boolean isFilePathTemp) { getString(string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; mFilePath = Paths.combine( - Environment.getExternalStorageDirectory().getAbsolutePath(), + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), Paths.SOUND_RECORDER_FOLDER, mFileName); f = new File(mFilePath); diff --git a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java index a89d6301..b6e61ee2 100644 --- a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java +++ b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java @@ -208,20 +208,20 @@ public void removeOutOfApp(String filePath) { //user deletes a saved recording out of the application through another application } + /** + * rename a file + */ public void rename(int position, String name) { - //rename a file - - String mFilePath = Paths.combine( - Environment.getExternalStorageDirectory().getAbsolutePath(), + final String mFilePath = Paths.combine( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), Paths.SOUND_RECORDER_FOLDER, name); - File f = new File(mFilePath); + final File f = new File(mFilePath); if (f.exists() && !f.isDirectory()) { //file name is not unique, cannot rename file. Toast.makeText(mContext, String.format(mContext.getString(R.string.toast_file_exists), name), Toast.LENGTH_SHORT).show(); - } else { //file name is unique, rename file File oldFilePath = new File(getItem(position).getFilePath()); diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java index 16cd9e78..2b3ae11e 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/FileViewerFragment.java @@ -1,6 +1,7 @@ package by.naxa.soundrecorder.fragments; import android.os.Bundle; +import android.os.Environment; import android.os.FileObserver; import android.support.v4.app.Fragment; import android.support.v7.widget.DefaultItemAnimator; @@ -15,8 +16,6 @@ import by.naxa.soundrecorder.R; import by.naxa.soundrecorder.adapters.FileViewerAdapter; -import static android.os.Environment.getExternalStorageDirectory; - /** * Created by Daniel on 12/23/2014. */ @@ -63,14 +62,16 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa private final FileObserver observer = new FileObserver(Paths.combine( - getExternalStorageDirectory(), Paths.SOUND_RECORDER_FOLDER)) { + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), + Paths.SOUND_RECORDER_FOLDER)) { // set up a file observer to watch this directory on sd card @Override public void onEvent(int event, String file) { if (event == FileObserver.DELETE) { // user deletes a recording file out of the app - String filePath = Paths.combine(getExternalStorageDirectory(), + final String filePath = Paths.combine( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), Paths.SOUND_RECORDER_FOLDER, file); Log.d(LOG_TAG, "File deleted [" + filePath + "]"); diff --git a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java index 40f864f1..8b7e9a64 100644 --- a/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/by/naxa/soundrecorder/fragments/RecordFragment.java @@ -130,11 +130,11 @@ private boolean onRecord(boolean start) { final Intent intent = new Intent(getActivity(), RecordingService.class); if (start) { - if (!PermissionsHelper.checkAndRequestPermissions(this, - MY_PERMISSIONS_REQUEST_RECORD_AUDIO)) { + if (!PermissionsHelper.checkAndRequestPermissions( + this, MY_PERMISSIONS_REQUEST_RECORD_AUDIO) || + !startRecording(intent)) { return start; } - startRecording(intent); } else { //stop recording mRecordButton.setImageResource(R.drawable.ic_mic_white_36dp); @@ -156,18 +156,29 @@ private boolean onRecord(boolean start) { return !start; } - private void startRecording(Intent intent) { - // start recording - mRecordButton.setImageResource(R.drawable.ic_media_stop); - mPauseButton.setVisibility(View.VISIBLE); - Toast.makeText(getActivity(), R.string.toast_recording_start, Toast.LENGTH_SHORT).show(); - File folder = new File(Environment.getExternalStorageDirectory(), + /** + * Start recording + */ + private boolean startRecording(Intent intent) { + final File folder = new File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), Paths.SOUND_RECORDER_FOLDER); if (!folder.exists()) { - //folder /SoundRecorder doesn't exist, create the folder - folder.mkdir(); + // a folder for sound recordings doesn't exist -> create the folder + boolean ok = Paths.isExternalStorageWritable() && folder.mkdir(); + if (!ok) { + Snackbar.make(getActivity().findViewById(android.R.id.content), + R.string.error_mkdir, Snackbar.LENGTH_LONG) + .show(); + return false; + } } + // change UI + mRecordButton.setImageResource(R.drawable.ic_media_stop); + mPauseButton.setVisibility(View.VISIBLE); + Toast.makeText(getActivity(), R.string.toast_recording_start, Toast.LENGTH_SHORT).show(); + //start Chronometer mChronometer.setBase(SystemClock.elapsedRealtime()); mChronometer.start(); @@ -197,6 +208,7 @@ public void onChronometerTick(Chronometer chronometer) { mRecordPromptCount++; mStartRecording = false; + return true; } private boolean onPauseRecord(boolean pause) { diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7eed7577..36c8c98b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -48,5 +48,6 @@ Иконка микрофона Не могу начать запись без вашего разрешения. Не могу воспроизвести сохранённую запись без вашего разрешения. + Ошибка! Внешний накопитель (SD карта) не доступен для сохранения записи. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index acb57576..3702246d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,5 +58,6 @@ Microphone icon Can not start sound recording without your permission. Can not play the recording without your permission to access the external storage. + Error! External storage (SD card) is not available for writing. From 4d4ed281ad7b8aefedd5480f5cddb2317d2cf861 Mon Sep 17 00:00:00 2001 From: naXa! Date: Tue, 21 Aug 2018 01:13:13 +0300 Subject: [PATCH 014/111] Update strings --- app/src/main/res/values-de/strings.xml | 11 +++++++++-- app/src/main/res/values-es/strings.xml | 13 ++++++++++--- app/src/main/res/values-fr/strings.xml | 9 +++++++++ app/src/main/res/values-lt/strings.xml | 13 +++++++++++-- app/src/main/res/values/strings.xml | 6 +++--- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5e0d1f0f..d3d05160 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -15,10 +15,10 @@ Die Datei %1$s existiert bereits. Bitte wähle einen anderen Dateinamen. - Nehme auf... + Nehme auf… - Löschen bestätigen... + Löschen bestätigen… Möchtest du diese Datei wirklich löschen? Open-Source-Lizenzen Datei teilen @@ -40,4 +40,11 @@ Berühre den Button, um die Aufzeichnung zu starten Nehme auf + Sende nach + + + Aktivieren Sie die Aufnahme in Hoher Qualität + Aufnehmen in CD Qualität + Über + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 465df114..7d9a6cfa 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -6,7 +6,7 @@ Licencias Ajustes Grabar - Guardar grabaciones + Grabaciones guardadas Grabación iniciada @@ -15,10 +15,10 @@ El archivo %1$s ya existe. Elija un nombre de archivo diferente. - Grabando... + Grabando… - Confirmar eliminación... + Confirmar eliminación… ¿Está seguro que desea eliminar el archivo? Licencias de Código Abierto Renombrar archivo @@ -38,5 +38,12 @@ Pulse el botón para iniciar la grabación Grabando + Enviar a + + + Habilitar Grabación en Alta Calidad + Grabar en calidad CD + Acerca de + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 882fb9f3..7a144e33 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -21,9 +21,11 @@ Confirmation de la suppression… Êtes-vous sûr de vouloir supprimer ce fichier ? Licences Open Source + Partager le fichier Renommer le fichier Options + Partager le fichier Renommer le fichier Supprimer le fichier Annuler @@ -38,4 +40,11 @@ Appuyez sur le bouton pour démarrer l\'enregistrement Enregistrement en cours + Envoyer à + + + Activer l\'enregistrement Haute Qualité + Enregistrer en qualité CD + À propos + diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 49be7462..f020f79a 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -15,15 +15,17 @@ Byla %1$s jau egzisuoja. Prašome pasirinkti kitokį bylos pavadinimą. - Įrašoma... + Įrašoma… - Patvirtinkite ištrynimą... + Patvirtinkite ištrynimą… Ar tikrai norite ištrinti šią bylą? Atviro kodo licenzijos + Pasidalinti byla Pervardinti bylą Nustatymai + Pasidalinti byla Pervardinti bylą Ištrinti bylą Atšaukti @@ -38,4 +40,11 @@ Paspauskite mygtuką, kad pradėti įrašymą Įrašoma + Siųsti + + + Įgalinti aukštos kokybės įrašymą + CD kokybės įrašymas + Apie + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3702246d..9ffe20f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ - Website: https://github.com/naXa777/SoundRecorder + Website: https://github.com/naXa777/SoundRecorder Sound Recorder @@ -48,10 +48,10 @@ Send to - pref_high_quality + pref_high_quality Enable High Quality Recording Record in CD Quality - pref_about + pref_about About v%s From 797c62c4e5f795daef3b2249ba9a4dc7a3b708b6 Mon Sep 17 00:00:00 2001 From: naXa! Date: Tue, 21 Aug 2018 01:13:51 +0300 Subject: [PATCH 015/111] Update Gradle build plugin to the latest version --- app/app.iml | 25 +++++++++++++++++-------- build.gradle | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/app.iml b/app/app.iml index 769e0443..c3d5d5c5 100644 --- a/app/app.iml +++ b/app/app.iml @@ -23,23 +23,23 @@ - - + + - - + + - + - - + + - + @@ -85,12 +85,15 @@ + + + @@ -98,16 +101,22 @@ + + + + + + diff --git a/build.gradle b/build.gradle index 3d178f7b..efae757c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' + classpath 'com.android.tools.build:gradle:3.3.0-alpha06' classpath 'io.fabric.tools:gradle:1.25.4' // NOTE: Do not place your application dependencies here; they belong From a34b3c59b09fce4a2c9e9353a2b013ffe836ad24 Mon Sep 17 00:00:00 2001 From: naXa! Date: Tue, 21 Aug 2018 01:17:52 +0300 Subject: [PATCH 016/111] Warn when file operations fail --- .../adapters/FileViewerAdapter.java | 17 ++++++++++++++--- app/src/main/res/values-ru/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java index b6e61ee2..fcc170f4 100644 --- a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java +++ b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java @@ -188,7 +188,13 @@ public void remove(int position) { //delete file from storage File file = new File(getItem(position).getFilePath()); - file.delete(); + if (!file.delete()) { + Toast.makeText(mContext, + String.format(mContext.getString(R.string.toast_file_delete_failed), + getItem(position).getName()), + Toast.LENGTH_LONG).show(); + return; + } Toast.makeText( mContext, @@ -221,11 +227,16 @@ public void rename(int position, String name) { //file name is not unique, cannot rename file. Toast.makeText(mContext, String.format(mContext.getString(R.string.toast_file_exists), name), - Toast.LENGTH_SHORT).show(); + Toast.LENGTH_LONG).show(); } else { //file name is unique, rename file File oldFilePath = new File(getItem(position).getFilePath()); - oldFilePath.renameTo(f); + if (!oldFilePath.renameTo(f)) { + Toast.makeText(mContext, + String.format(mContext.getString(R.string.toast_file_rename_failed), name), + Toast.LENGTH_LONG).show(); + return; + } mDatabase.renameItem(getItem(position), name, mFilePath); notifyItemChanged(position); } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 36c8c98b..037cf340 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -10,6 +10,8 @@ Запись началась Запись сохранена в Файл %1$s успешно удалён + Не удалось удалить %1$s + Не удалось переименовать %1$s Файл %1$s уже существует. Пожалуйста, выберите другое имя файла. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9ffe20f7..e893a5d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,8 @@ Recording paused Recording resumed %1$s successfully deleted + Failed to delete %1$s + Failed to rename %1$s The file %1$s already exists. Please choose a different file name. From a8a531a50d4cf60ab24154324b23ee2d5dae3007 Mon Sep 17 00:00:00 2001 From: naXa! Date: Tue, 21 Aug 2018 02:19:20 +0300 Subject: [PATCH 017/111] cleanup --- app/app.iml | 4 ++ .../java/by/naxa/soundrecorder/DBHelper.java | 17 ------ .../adapters/FileViewerAdapter.java | 52 +++++++++---------- app/src/main/res/values-cs/strings.xml | 4 +- app/src/main/res/values-eo/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 4 +- app/src/main/res/values-he/strings.xml | 4 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- 10 files changed, 41 insertions(+), 54 deletions(-) diff --git a/app/app.iml b/app/app.iml index c3d5d5c5..16e7c73c 100644 --- a/app/app.iml +++ b/app/app.iml @@ -85,6 +85,7 @@ + @@ -101,8 +102,11 @@ + + + diff --git a/app/src/main/java/by/naxa/soundrecorder/DBHelper.java b/app/src/main/java/by/naxa/soundrecorder/DBHelper.java index 3bcf01e1..10fd30dc 100644 --- a/app/src/main/java/by/naxa/soundrecorder/DBHelper.java +++ b/app/src/main/java/by/naxa/soundrecorder/DBHelper.java @@ -9,14 +9,10 @@ import by.naxa.soundrecorder.listeners.OnDatabaseChangedListener; -import java.util.Comparator; - /** * Created by Daniel on 12/29/2014. */ public class DBHelper extends SQLiteOpenHelper { - private Context mContext; - private static final String LOG_TAG = "DBHelper"; private static OnDatabaseChangedListener mOnDatabaseChangedListener; @@ -58,7 +54,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); - mContext = context; } public static void setOnDatabaseChangedListener(OnDatabaseChangedListener listener) { @@ -103,18 +98,6 @@ public int getCount() { return count; } - public Context getContext() { - return mContext; - } - - public class RecordingComparator implements Comparator { - public int compare(RecordingItem item1, RecordingItem item2) { - Long o1 = item1.getTime(); - Long o2 = item2.getTime(); - return o2.compareTo(o1); - } - } - public long addRecording(String recordingName, String filePath, long length) { SQLiteDatabase db = getWritableDatabase(); diff --git a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java index fcc170f4..8ef3808a 100644 --- a/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java +++ b/app/src/main/java/by/naxa/soundrecorder/adapters/FileViewerAdapter.java @@ -34,7 +34,7 @@ * Created by Daniel on 12/29/2014. */ public class FileViewerAdapter extends RecyclerView.Adapter - implements OnDatabaseChangedListener { + implements OnDatabaseChangedListener { private static final String LOG_TAG = "FileViewerAdapter"; @@ -65,11 +65,11 @@ public void onBindViewHolder(final RecordingsViewHolder holder, int position) { holder.vName.setText(item.getName()); holder.vLength.setText(String.format("%02d:%02d", minutes, seconds)); holder.vDateAdded.setText( - DateUtils.formatDateTime( - mContext, - item.getTime(), - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR - ) + DateUtils.formatDateTime( + mContext, + item.getTime(), + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR + ) ); // define an on click listener to open PlaybackFragment @@ -111,7 +111,7 @@ public boolean onLongClick(View v) { public void onClick(DialogInterface dialog, int item) { if (item == 0) { shareFileDialog(holder.getPosition()); - } if (item == 1) { + } else if (item == 1) { renameFileDialog(holder.getPosition()); } else if (item == 2) { deleteFileDialog(holder.getPosition()); @@ -197,12 +197,12 @@ public void remove(int position) { } Toast.makeText( - mContext, - String.format( - mContext.getString(R.string.toast_file_delete), - getItem(position).getName() - ), - Toast.LENGTH_SHORT + mContext, + String.format( + mContext.getString(R.string.toast_file_delete), + getItem(position).getName() + ), + Toast.LENGTH_SHORT ).show(); mDatabase.removeItemWithId(getItem(position).getId()); @@ -250,7 +250,7 @@ public void shareFileDialog(int position) { mContext.startActivity(Intent.createChooser(shareIntent, mContext.getText(R.string.send_to))); } - public void renameFileDialog (final int position) { + public void renameFileDialog(final int position) { // File rename dialog AlertDialog.Builder renameFileBuilder = new AlertDialog.Builder(mContext); @@ -276,30 +276,25 @@ public void onClick(DialogInterface dialog, int id) { } }); renameFileBuilder.setNegativeButton(mContext.getString(R.string.dialog_action_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); + new CancelDialogListener()); renameFileBuilder.setView(view); AlertDialog alert = renameFileBuilder.create(); alert.show(); } - public void deleteFileDialog (final int position) { + private void deleteFileDialog(final int position) { // File delete confirm AlertDialog.Builder confirmDelete = new AlertDialog.Builder(mContext); confirmDelete.setTitle(mContext.getString(R.string.dialog_title_delete)); confirmDelete.setMessage(mContext.getString(R.string.dialog_text_delete)); confirmDelete.setCancelable(true); - confirmDelete.setPositiveButton(mContext.getString(R.string.dialog_action_yes), + confirmDelete.setPositiveButton(mContext.getString(R.string.dialog_action_yes_delete), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { try { //remove item from database, recyclerview, and storage remove(position); - } catch (Exception e) { Log.e(LOG_TAG, "exception", e); } @@ -308,13 +303,16 @@ public void onClick(DialogInterface dialog, int id) { } }); confirmDelete.setNegativeButton(mContext.getString(R.string.dialog_action_no), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); + new CancelDialogListener()); AlertDialog alert = confirmDelete.create(); alert.show(); } + + static final class CancelDialogListener implements DialogInterface.OnClickListener { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + } } diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 525548c0..514453cc 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -15,10 +15,10 @@ Soubor %1$s již existuje. Prosím, zvolte jiný název. - Nahrávání... + Nahrávání… - Potvrdit smazání... + Potvrdit smazání… Jsi si jistí, že chcete smazat tento soubor? Open Source Licence Přejmenovat soubor diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 5c2f55c1..9d085242 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -15,7 +15,7 @@ La dosiero %1$s jam ekzistas. Bonvole elektu malsaman dosiernomon. - Registrado... + Registrado… Konfirmo por forigo diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 06c54247..4dd790c9 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -15,10 +15,10 @@ Tiedosto %1$s on jo olemassa. Valitse jokin toinen tiedostonimi. - Äänitetään... + Äänitetään… - Vahvista poisto... + Vahvista poisto… Haluatko varmasti poistaa tämän tiedoston? Avoimen lähdekoodin lisenssit Nimeä tiedosto uudelleen diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index e9f7930e..8ffa1322 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -15,10 +15,10 @@ הקובץ %1$s כבר קיים. אנא בחר שם קובץ שונה. - כעת מקליט... + כעת מקליט… - אמת מחיקה... + אמת מחיקה… האם אתה בטוח כי ברצונך למחוק את קובץ זה? רשיונות קוד פתוח שנה שם קובץ diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 15fafc5b..975e55aa 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -15,7 +15,7 @@ Il file %1$s già esiste. Scegliere un nome del file differente. - Registrazione... + Registrazione… Conferma cancellazione diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 037cf340..218aeef9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -19,7 +19,7 @@ Подтвердите удаление… - Вы уверены, что хотите удалить этот файл? + Вы уверены, что хотите удалить эту запись? \n\n Задумайтесь о последствиях. Open Source Licenses Переименовать файл Опции @@ -29,6 +29,7 @@ Отмена ОК Да + Да, удалить Нет Нет сохранённых записей diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e893a5d0..a1a0ce8e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,7 +25,7 @@ Confirm Delete… - Are you sure you would like to delete this file? + Are you sure you would like to delete this recording? \n\nThis is permanent and cannot be undone. Open Source Licenses Share File Rename File @@ -37,6 +37,7 @@ Cancel OK Yes + Yes, delete No No saved recordings From 58d617821988c405ce83f1b22f8a49f3cbc662da Mon Sep 17 00:00:00 2001 From: naXa! Date: Tue, 21 Aug 2018 14:00:37 +0300 Subject: [PATCH 018/111] Use Android Design Support CoordinatorLayout + AppBarLayout + TabLayout to implement Material Design tabs --- .idea/gradle.xml | 3 + SoundRecorder.iml | 11 +- app/app.iml | 5 +- app/build.gradle | 3 +- .../activities/MainActivity.java | 43 ++++--- app/src/main/res/layout/activity_main.xml | 50 ++++---- .../main/res/layout/activity_preferences.xml | 9 +- .../main/res/layout/dialog_rename_file.xml | 2 +- .../main/res/layout/fragment_file_viewer.xml | 2 +- app/src/main/res/layout/fragment_licenses.xml | 111 +++++++++--------- .../res/layout/fragment_media_playback.xml | 35 +++--- app/src/main/res/layout/fragment_record.xml | 6 +- app/src/main/res/layout/toolbar.xml | 8 +- app/src/main/res/values-v21/styles.xml | 7 -- app/src/main/res/values/styles.xml | 10 +- 15 files changed, 159 insertions(+), 146 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c77..f43d4284 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,6 +3,9 @@ - + diff --git a/app/app.iml b/app/app.iml index 0166f041..89619316 100644 --- a/app/app.iml +++ b/app/app.iml @@ -78,29 +78,21 @@ - - - - - - - + - - - + diff --git a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java index ad09a6d1..2827a025 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java +++ b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java @@ -229,11 +229,6 @@ private boolean makeSingleFile(ArrayList filesPaused) { e.printStackTrace(); } } -// try { -// finalMovie.addTrack(new AppendTrack((Track) tracks)); -// } catch (IOException e) { -// e.printStackTrace(); -// } Container mp4file = new DefaultMp4Builder().build(finalMovie); FileChannel fc = null; try { From 1322063f0d041756019a155d7728196126a4b0a3 Mon Sep 17 00:00:00 2001 From: Ahmed Nabil Date: Tue, 21 Aug 2018 23:14:23 +0200 Subject: [PATCH 022/111] add Toast for resumed status. change mRecordingPrompt properly. --- .../java/com/danielkim/soundrecorder/RecordingService.java | 1 + .../com/danielkim/soundrecorder/fragments/RecordFragment.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java index 2827a025..215719dc 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java +++ b/app/src/main/java/com/danielkim/soundrecorder/RecordingService.java @@ -250,6 +250,7 @@ private boolean makeSingleFile(ArrayList filesPaused) { public void resumeRecording(){ isPaused=false; + Toast.makeText(this, getString(R.string.toast_recording_resumed), Toast.LENGTH_LONG).show(); startRecording(); } diff --git a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java index 06a868c1..a2e749c7 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java @@ -197,7 +197,7 @@ private void onPauseRecord(boolean pause) { mRecordingService.pauseRecording(); mPauseButton.setCompoundDrawablesWithIntrinsicBounds (R.drawable.ic_media_play ,0 ,0 ,0); - mRecordingPrompt.setText((String)getString(R.string.resume_recording_button).toUpperCase()); + mRecordingPrompt.setText((String)getString(R.string.pause_recording_button).toUpperCase()); timeWhenPaused = mChronometer.getBase() - SystemClock.elapsedRealtime(); mChronometer.stop(); } else { @@ -205,7 +205,7 @@ private void onPauseRecord(boolean pause) { mRecordingService.resumeRecording(); mPauseButton.setCompoundDrawablesWithIntrinsicBounds (R.drawable.ic_media_pause ,0 ,0 ,0); - mRecordingPrompt.setText((String)getString(R.string.pause_recording_button).toUpperCase()); + mRecordingPrompt.setText((String)getString(R.string.record_in_progress).toUpperCase()); mChronometer.setBase(SystemClock.elapsedRealtime() + timeWhenPaused); mChronometer.start(); } From 0150800133b7c10d5d6a0ac6e8039603b80c5c7e Mon Sep 17 00:00:00 2001 From: Ahmed Nabil Date: Tue, 21 Aug 2018 23:41:11 +0200 Subject: [PATCH 023/111] change mPlayButton to FAB. texts minor fixes --- .../soundrecorder/fragments/RecordFragment.java | 14 +++++++------- app/src/main/res/layout/fragment_record.xml | 5 ++--- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java index a2e749c7..1b3f86df 100644 --- a/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java +++ b/app/src/main/java/com/danielkim/soundrecorder/fragments/RecordFragment.java @@ -40,7 +40,7 @@ public class RecordFragment extends Fragment { //Recording controls private FloatingActionButton mRecordButton = null; - private Button mPauseButton = null; + private FloatingActionButton mPauseButton = null; private TextView mRecordingPrompt; private int mRecordPromptCount = 0; @@ -97,7 +97,9 @@ public void onClick(View v) { } }); - mPauseButton = (Button) recordView.findViewById(R.id.btnPause); + mPauseButton = (FloatingActionButton) recordView.findViewById(R.id.btnPause); + mPauseButton.setColorNormal(getResources().getColor(R.color.primary)); + mPauseButton.setColorPressed(getResources().getColor(R.color.primary_dark)); mPauseButton.setVisibility(View.GONE); //hide pause button before recording starts mPauseButton.setOnClickListener(new View.OnClickListener() { @Override @@ -195,16 +197,14 @@ private void onPauseRecord(boolean pause) { if (pause) { //pause recording mRecordingService.pauseRecording(); - mPauseButton.setCompoundDrawablesWithIntrinsicBounds - (R.drawable.ic_media_play ,0 ,0 ,0); - mRecordingPrompt.setText((String)getString(R.string.pause_recording_button).toUpperCase()); + mPauseButton.setImageResource(R.drawable.ic_media_play); + mRecordingPrompt.setText((String)getString(R.string.pause_recording_button)); timeWhenPaused = mChronometer.getBase() - SystemClock.elapsedRealtime(); mChronometer.stop(); } else { //resume recording mRecordingService.resumeRecording(); - mPauseButton.setCompoundDrawablesWithIntrinsicBounds - (R.drawable.ic_media_pause ,0 ,0 ,0); + mPauseButton.setImageResource(R.drawable.ic_media_pause); mRecordingPrompt.setText((String)getString(R.string.record_in_progress).toUpperCase()); mChronometer.setBase(SystemClock.elapsedRealtime() + timeWhenPaused); mChronometer.start(); diff --git a/app/src/main/res/layout/fragment_record.xml b/app/src/main/res/layout/fragment_record.xml index f5f774bb..5368349a 100644 --- a/app/src/main/res/layout/fragment_record.xml +++ b/app/src/main/res/layout/fragment_record.xml @@ -40,12 +40,11 @@ android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> -