From efc2fe1ee8ff391fc5dbf8bb24d5bf6c3c8aa783 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Tue, 26 Mar 2019 02:53:12 +0200 Subject: [PATCH 01/23] - Reworked import audio files. - Fixed time format for long values. --- app/build.gradle | 2 +- .../audiorecorder/app/AppRecorderImpl.java | 1 - .../audiorecorder/app/main/MainActivity.java | 5 +- .../audiorecorder/app/main/MainPresenter.java | 91 ++++++++++++++++--- .../app/records/RecordsPresenter.java | 2 +- .../audiorecorder/audio/SoundFile.java | 52 ++++++----- .../audiorecorder/util/TimeUtils.java | 2 +- 7 files changed, 116 insertions(+), 39 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2b28fa66..5fd2280c 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 18 +def versionPatch = 19 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java b/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java index b6857744..6e82a26c 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java @@ -12,7 +12,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import timber.log.Timber; diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 6b5088e7..98b5c0cf 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -76,6 +76,7 @@ public class MainActivity extends Activity implements MainContract.View, View.On // TODO: Welcome screen // TODO: Guidelines // TODO: Check how work max recording duration +// TODO: Add scroll animation to start when stop playback public static final int REQ_CODE_REC_AUDIO_AND_WRITE_EXTERNAL = 101; public static final int REQ_CODE_RECORD_AUDIO = 303; @@ -258,7 +259,9 @@ public void onClick(View view) { private void startFileSelector() { Intent intent_upload = new Intent(); intent_upload.setType("audio/*"); - intent_upload.setAction(Intent.ACTION_GET_CONTENT); + intent_upload.addCategory(Intent.CATEGORY_OPENABLE); +// intent_upload.setAction(Intent.ACTION_GET_CONTENT); + intent_upload.setAction(Intent.ACTION_OPEN_DOCUMENT); startActivityForResult(intent_upload, REQ_CODE_IMPORT_AUDIO); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java index 5c297f7d..f4198355 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java @@ -44,6 +44,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; +import java.util.Date; import timber.log.Timber; @@ -454,8 +455,9 @@ public void run() { } }); } - } catch (IOException e) { + } catch (IOException | OutOfMemoryError e) { Timber.e(e); +// TODO: handle errors here } isProcessing = false; } @@ -533,18 +535,85 @@ public void run() { File newFile = fileRepository.provideRecordFile(name); if (FileUtil.copyFile(fileDescriptor, newFile)) { - id = localRepository.insertFile(newFile.getAbsolutePath()); - prefs.setActiveRecord(id); - } - AndroidUtils.runOnUIThread(new Runnable() { - @Override public void run() { - if (view != null) { - view.hideImportProgress(); - audioPlayer.stop(); - loadActiveRecord(); + long duration = AndroidUtils.readRecordDuration(newFile); + if (duration/1000000 < AppConstants.LONG_RECORD_THRESHOLD_SECONDS) { + //Do simple import for short records. + id = localRepository.insertFile(newFile.getAbsolutePath()); + prefs.setActiveRecord(id); + AndroidUtils.runOnUIThread(new Runnable() { + @Override public void run() { + if (view != null) { + view.hideImportProgress(); + audioPlayer.stop(); + loadActiveRecord(); + } + } + }); + } else { + //Do 2 step import: 1) Import record with empty waveform. 2) Process and update waveform in background. + record = localRepository.insertRecord( + new Record( + Record.NO_ID, + newFile.getName(), + duration, //mills + newFile.lastModified(), + new Date().getTime(), + newFile.getAbsolutePath(), + false, + true, + new int[ARApplication.getLongWaveformSampleCount()])); + + id = record.getId(); + prefs.setActiveRecord(id); + songDuration = duration; + dpPerSecond = ARApplication.getDpPerSecond((float) songDuration / 1000000f); + AndroidUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + audioPlayer.stop(); + view.showWaveForm(record.getAmps(), songDuration); + view.showName(FileUtil.removeFileExtension(record.getName())); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(songDuration / 1000)); + view.hideProgress(); + } + } + }); + + try { + if (view != null) { + AndroidUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + view.hideImportProgress(); + view.showRecordProcessing(); + } + } + }); + isProcessing = true; + localRepository.updateWaveform((int)id); + record = localRepository.getRecord((int)id); + AndroidUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + view.showWaveForm(record.getAmps(), songDuration); + view.hideRecordProcessing(); + } + } + }); + } + } catch (IOException e) { + Timber.e(e); + if (view != null) { + //TODO: show error on display + view.hideRecordProcessing(); + } } + isProcessing = false; } - }); + } } catch (SecurityException e) { Timber.e(e); AndroidUtils.runOnUIThread(new Runnable() { diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java index f1290c13..ffdfb250 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java @@ -478,7 +478,7 @@ public void run() { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null) { + if (view != null && record != null) { view.removedFromBookmarks(r.getId(), r.getId() == record.getId()); } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java index d2c43a8c..359b1d42 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java @@ -39,6 +39,8 @@ public class SoundFile { private File mInputFile = null; + private boolean isFailed = false; + private float dpPerSec = AppConstants.SHORT_RECORD_DP_PER_SECOND; private int mFileSize; private int mSampleRate; @@ -62,7 +64,7 @@ private SoundFile() { } // Create and return a SoundFile object using the file fileName. - public static SoundFile create(String fileName) throws IOException, FileNotFoundException { + public static SoundFile create(String fileName) throws IOException, OutOfMemoryError, FileNotFoundException { // First check that the file exists and that its extension is supported. File f = new File(fileName); if (!f.exists()) { @@ -215,6 +217,8 @@ private void readFile(File inputFile) throws IOException { if (retry == 0) { // Failed to allocate memory... Stop reading more data and finalize the // instance with the data decoded so far. + mFrameGains = new int[ARApplication.getLongWaveformSampleCount()]; + isFailed = true; break; } //ByteBuffer newDecodedBytes = ByteBuffer.allocate(newSize); @@ -257,31 +261,33 @@ private void readFile(File inputFile) throws IOException { codec.release(); codec = null; - // Temporary hack to make it work with the old version. - mNumFrames = mNumSamples / getSamplesPerFrame(); - if (mNumSamples % getSamplesPerFrame() != 0) { - mNumFrames++; - } - mFrameGains = new int[mNumFrames]; - int j; - int gain, value; - for (i = 0; i < mNumFrames; i++) { - gain = -1; - for (j = 0; j < getSamplesPerFrame(); j++) { - value = 0; - for (int k = 0; k < mChannels; k++) { - if (mDecodedSamples.remaining() > 0) { - value += Math.abs(mDecodedSamples.get()); + if (!isFailed) { + // Temporary hack to make it work with the old version. + mNumFrames = mNumSamples / getSamplesPerFrame(); + if (mNumSamples % getSamplesPerFrame() != 0) { + mNumFrames++; + } + mFrameGains = new int[mNumFrames]; + int j; + int gain, value; + for (i = 0; i < mNumFrames; i++) { + gain = -1; + for (j = 0; j < getSamplesPerFrame(); j++) { + value = 0; + for (int k = 0; k < mChannels; k++) { + if (mDecodedSamples.remaining() > 0) { + value += Math.abs(mDecodedSamples.get()); + } + } + value /= mChannels; + if (gain < value) { + gain = value; } } - value /= mChannels; - if (gain < value) { - gain = value; - } + mFrameGains[i] = (int) Math.sqrt(gain); // here gain = sqrt(max value of 1st channel)... } - mFrameGains[i] = (int) Math.sqrt(gain); // here gain = sqrt(max value of 1st channel)... + mDecodedSamples.rewind(); + // DumpSamples(); // Uncomment this line to dump the samples in a TSV file. } - mDecodedSamples.rewind(); - // DumpSamples(); // Uncomment this line to dump the samples in a TSV file. } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/util/TimeUtils.java b/app/src/main/java/com/dimowner/audiorecorder/util/TimeUtils.java index 8a580c3a..017a017b 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/util/TimeUtils.java +++ b/app/src/main/java/com/dimowner/audiorecorder/util/TimeUtils.java @@ -71,7 +71,7 @@ public static String formatTimeIntervalHourMinSec2(long length) { if (numHour == 0) { return String.format(Locale.getDefault(), "%02d:%02d", numMinutes, numSeconds % 60); } else { - return String.format(Locale.getDefault(), "%02d:%02d:%02d", numHour, numMinutes, numSeconds % 60); + return String.format(Locale.getDefault(), "%02d:%02d:%02d", numHour, numMinutes % 60, numSeconds % 60); } } From 433305763b6950691fe354099d3ec671a04a930a Mon Sep 17 00:00:00 2001 From: Dimowner Date: Tue, 26 Mar 2019 20:28:39 +0200 Subject: [PATCH 02/23] Added paging in records list. --- app/build.gradle | 2 +- .../dimowner/audiorecorder/AppConstants.java | 2 + .../audiorecorder/app/main/MainActivity.java | 2 + .../EndlessRecyclerViewScrollListener.java | 99 +++++++++++++++++++ .../app/records/RecordsActivity.java | 25 ++++- .../app/records/RecordsAdapter.java | 31 +++++- .../app/records/RecordsContract.java | 3 + .../app/records/RecordsPresenter.java | 41 +++++++- .../data/database/DataSource.java | 13 +++ .../data/database/LocalRepository.java | 2 + .../data/database/LocalRepositoryImpl.java | 15 +++ 11 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/dimowner/audiorecorder/app/records/EndlessRecyclerViewScrollListener.java diff --git a/app/build.gradle b/app/build.gradle index 5fd2280c..9d363335 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 19 +def versionPatch = 20 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java b/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java index 0fa3b78c..804577d8 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java +++ b/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java @@ -36,6 +36,8 @@ private AppConstants() {} public static final int RECORDING_FORMAT_M4A = 0; public static final int RECORDING_FORMAT_WAV = 1; + public static final int DEFAULT_PER_PAGE = 50; + //BEGINNING-------------- Waveform visualisation constants ---------------------------------- /** Density pixel count per one second of time. diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 98b5c0cf..5b4d65a7 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -311,6 +311,7 @@ public void showRecordingStart() { btnImport.setEnabled(false); btnShare.setEnabled(false); playProgress.setProgress(0); + playProgress.setEnabled(false); txtDuration.setText(R.string.zero_time); waveformView.showRecording(); } @@ -321,6 +322,7 @@ public void showRecordingStop() { btnPlay.setEnabled(true); btnImport.setEnabled(true); btnShare.setEnabled(true); + playProgress.setEnabled(true); waveformView.hideRecording(); waveformView.clearRecordingData(); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/EndlessRecyclerViewScrollListener.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/EndlessRecyclerViewScrollListener.java new file mode 100644 index 00000000..824410df --- /dev/null +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/EndlessRecyclerViewScrollListener.java @@ -0,0 +1,99 @@ +package com.dimowner.audiorecorder.app.records; + +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; + +public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener { + // Sets the starting page index + private static final int STARTING_PAGE_INDEX = 1; + // The minimum amount of items to have below your current scroll position + // before loading more. + private int visibleThreshold = 5; + // The current offset index of data you have loaded + private int currentPage = 1; + // The total number of items in the dataset after the last load + private int previousTotalItemCount = 0; + // True if we are still waiting for the last set of data to load. + private boolean loading = true; + + private RecyclerView.LayoutManager mLayoutManager; + + public EndlessRecyclerViewScrollListener(L layoutManager) { + this.mLayoutManager = layoutManager; + if (layoutManager instanceof StaggeredGridLayoutManager) { + visibleThreshold = visibleThreshold * ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); + } else if (layoutManager instanceof GridLayoutManager) { + visibleThreshold = visibleThreshold * ((GridLayoutManager) layoutManager).getSpanCount(); + } + } + + private int getLastVisibleItem(int[] lastVisibleItemPositions) { + int maxSize = 0; + for (int i = 0; i < lastVisibleItemPositions.length; i++) { + if (i == 0) { + maxSize = lastVisibleItemPositions[i]; + } else if (lastVisibleItemPositions[i] > maxSize) { + maxSize = lastVisibleItemPositions[i]; + } + } + return maxSize; + } + + // This happens many TIMES a second during a scroll, so be wary of the code you place here. + // We are given a few useful parameters to help us work out if we need to load some more data, + // but first we check if we are waiting for the previous load to finish. + @Override + public void onScrolled(RecyclerView view, int dx, int dy) { + int lastVisibleItemPosition = 0; + int totalItemCount = mLayoutManager.getItemCount(); + + if (mLayoutManager instanceof StaggeredGridLayoutManager) { + int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null); + // get maximum element within the list + lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions); + } else if (mLayoutManager instanceof LinearLayoutManager) { + lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); + } else if (mLayoutManager instanceof GridLayoutManager) { + lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition(); + } + + // If the total item count is zero and the previous isn't, assume the + // list is invalidated and should be reset back to initial state + if (totalItemCount < previousTotalItemCount) { + this.currentPage = STARTING_PAGE_INDEX; + this.previousTotalItemCount = totalItemCount; + if (totalItemCount == 0) { + this.loading = true; + } + } + // If it’s still loading, we check to see if the dataset count has + // changed, if so we conclude it has finished loading and update the current page + // number and total item count. + if (loading && (totalItemCount > previousTotalItemCount+1)) { + loading = false; + previousTotalItemCount = totalItemCount; + } + + // If it isn’t currently loading, we check to see if we have breached + // the visibleThreshold and need to reload more data. + // If we do need to reload some more data, we execute onLoadMore to fetch the data. + // threshold should reflect how many total columns there are too + if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount && totalItemCount > visibleThreshold) { + currentPage++; + onLoadMore(currentPage, totalItemCount); + loading = true; + } + } + + // Defines the process for actually loading more data based on page + public abstract void onLoadMore(int page, int totalItemsCount); + + //Used to reset inner state, if adapter data was fully changed + public void reset() { + currentPage = 1; + previousTotalItemCount = 0; + loading = true; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java index e7c8616b..b63e288b 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java @@ -178,6 +178,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { recyclerView.setHasFixedSize(true); layoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(layoutManager); + recyclerView.addOnScrollListener(new MyScrollListener(layoutManager)); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -535,7 +536,7 @@ public void run() { @Override public void showRecords(List records) { if (records.size() == 0) { - txtEmpty.setVisibility(View.VISIBLE); +// txtEmpty.setVisibility(View.VISIBLE); adapter.setData(new ArrayList()); } else { adapter.setData(records); @@ -546,6 +547,15 @@ public void showRecords(List records) { } } + @Override + public void addRecords(List records) { + adapter.addData(records); + txtEmpty.setVisibility(View.GONE); + if (touchLayout.getVisibility() == View.VISIBLE) { + adapter.showFooter(); + } + } + @Override public void showEmptyList() { txtEmpty.setText(R.string.no_records); @@ -702,4 +712,17 @@ public void hideKeyboard(){ InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); } + + public class MyScrollListener extends EndlessRecyclerViewScrollListener { + + public MyScrollListener(L layoutManager) { + super(layoutManager); + } + + @Override + public void onLoadMore(int page, int totalItemsCount) { +// Timber.v("onLoadMore page = " + page + " count = " + totalItemsCount); + presenter.loadRecordsPage(page); + } + } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index d48cae82..f461bcc2 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -183,26 +183,34 @@ public int getItemViewType(int position) { public void setData(List data) { this.data = data; if (showDateHeaders) { - addDateHeaders(); + this.data = addDateHeaders(data); } this.data.add(0, ListItem.createHeaderItem()); notifyDataSetChanged(); } - private void addDateHeaders() { + public void addData(List d) { + this.data.addAll(addDateHeaders(d)); + notifyItemRangeInserted(data.size() - d.size() - 1, d.size()); + } + + private List addDateHeaders(List data) { if (data.size() > 0) { - data.add(0, ListItem.createDateItem(data.get(0).getAdded())); + if (!hasDateHeader(data.get(0).getAdded())) { + data.add(0, ListItem.createDateItem(data.get(0).getAdded())); + } Calendar d1 = Calendar.getInstance(); d1.setTimeInMillis(data.get(0).getAdded()); Calendar d2 = Calendar.getInstance(); for (int i = 1; i < data.size(); i++) { d1.setTimeInMillis(data.get(i - 1).getAdded()); d2.setTimeInMillis(data.get(i).getAdded()); - if (!TimeUtils.isSameDay(d1, d2)) { + if (!TimeUtils.isSameDay(d1, d2) && !hasDateHeader(data.get(i).getAdded())) { data.add(i, ListItem.createDateItem(data.get(i).getAdded())); } } } + return data; } public void deleteItem(long id) { @@ -301,6 +309,21 @@ public void markRemovedFromBookmarks(int id) { } } + private boolean hasDateHeader(long time) { + for (int i = data.size()-1; i>= 0; i--) { + if (data.get(i).getType() == ListItem.ITEM_TYPE_DATE) { + Calendar d1 = Calendar.getInstance(); + d1.setTimeInMillis(data.get(i).getAdded()); + Calendar d2 = Calendar.getInstance(); + d2.setTimeInMillis(time); + if (TimeUtils.isSameDay(d1, d2)) { + return true; + } + } + } + return false; + } + public void setItemClickListener(ItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java index 1d0a714a..0b0df2bf 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java @@ -41,6 +41,7 @@ interface View extends Contract.View { void showDuration(String duration); void showRecords(List records); + void addRecords(List records); void showEmptyList(); void showEmptyBookmarksList(); @@ -78,6 +79,8 @@ interface UserActionsListener extends Contract.UserActionsListener recordList = localRepository.getAllRecords(); + final List recordList = localRepository.getRecords(0); record = localRepository.getRecord((int) prefs.getActiveRecord()); if (record != null) { dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration() / 1000000f); @@ -372,6 +372,45 @@ public void run() { } } + @Override + public void loadRecordsPage(final int page) { + if (view != null) { + view.showProgress(); + view.showPanelProgress(); + loadingTasks.postRunnable(new Runnable() { + @Override + public void run() { + final List recordList = localRepository.getRecords(page); + record = localRepository.getRecord((int) prefs.getActiveRecord()); + if (record != null) { + dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration() / 1000000f); + } + AndroidUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + view.addRecords(Mapper.recordsToListItems(recordList)); + if (record != null) { + view.showWaveForm(record.getAmps(), record.getDuration()); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(record.getDuration() / 1000)); + view.showRecordName(FileUtil.removeFileExtension(record.getName())); + if (record.isBookmarked()) { + view.bookmarksSelected(); + } else { + view.bookmarksUnselected(); + } + } + view.hideProgress(); + view.hidePanelProgress(); + view.bookmarksUnselected(); + } + } + }); + } + }); + } + } + public void loadBookmarks() { if (!showBookmarks) { loadRecords(); diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java index d973afe0..4562a9d8 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java @@ -26,6 +26,7 @@ import android.database.sqlite.SQLiteDatabase; import android.util.Log; +import com.dimowner.audiorecorder.AppConstants; import com.dimowner.audiorecorder.BuildConfig; /** @@ -135,6 +136,18 @@ public ArrayList getAll() { return convertCursor(cursor); } + /** + * Get records from database for table T. + * @return List that contains all records of table T. + */ + public ArrayList getRecords(int page) { + Cursor cursor = queryLocal("SELECT * FROM " + tableName + + " ORDER BY " + SQLiteHelper.COLUMN_DATE_ADDED + " DESC" + + " LIMIT " + AppConstants.DEFAULT_PER_PAGE + + " OFFSET " + (page-1) * AppConstants.DEFAULT_PER_PAGE); + return convertCursor(cursor); + } + /** * Delete all records from the table * @throws SQLException on error diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java index c0e54721..674693e4 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java @@ -29,6 +29,8 @@ public interface LocalRepository { List getAllRecords(); + List getRecords(int page); + boolean deleteAllRecords(); Record getLastRecord(); diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java index 7ae3a3f6..35aba3ba 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java @@ -215,6 +215,21 @@ public List getAllRecords() { return list; } + @Override + public List getRecords(int page) { + if (!dataSource.isOpen()) { + dataSource.open(); + } + List list = dataSource.getRecords(page); + //Remove not records with not existing audio files (which was lost or deleted) + for (int i = 0; i < list.size(); i++) { + if (!isFileExists(list.get(i).getPath())) { + dataSource.deleteItem(list.get(i).getId()); + } + } + return list; + } + @Override public Record getLastRecord() { if (!dataSource.isOpen()) { From 7b845b71817934a3afbdfb1a23cc3dd72a50b9fc Mon Sep 17 00:00:00 2001 From: Dimowner Date: Thu, 28 Mar 2019 12:47:59 +0200 Subject: [PATCH 03/23] Fix Wav recorder initialization. --- app/build.gradle | 2 +- .../audio/recorder/WavRecorder.java | 35 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9d363335..cd1117df 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 20 +def versionPatch = 21 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/WavRecorder.java b/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/WavRecorder.java index f3f03310..d9537fa5 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/WavRecorder.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/WavRecorder.java @@ -74,19 +74,30 @@ public void prepare(String outputFile, int channelCount, int sampleRate, int bit recordFile = new File(outputFile); if (recordFile.exists() && recordFile.isFile()) { int channel = channelCount == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO; - bufferSize = AudioRecord.getMinBufferSize(sampleRate, - channel, - AudioFormat.ENCODING_PCM_16BIT); - - recorder = new AudioRecord( - MediaRecorder.AudioSource.MIC, - sampleRate, - channel, - AudioFormat.ENCODING_PCM_16BIT, - bufferSize + try { + bufferSize = AudioRecord.getMinBufferSize(sampleRate, + channel, + AudioFormat.ENCODING_PCM_16BIT); + Timber.v("buffer size = %s", bufferSize); + if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) { + bufferSize = AudioRecord.getMinBufferSize(sampleRate, + channel, + AudioFormat.ENCODING_PCM_16BIT); + } + recorder = new AudioRecord( + MediaRecorder.AudioSource.MIC, + sampleRate, + channel, + AudioFormat.ENCODING_PCM_16BIT, + bufferSize ); - - if (recorder.getState() == AudioRecord.STATE_INITIALIZED) { + } catch (IllegalArgumentException e) { + Timber.e(e, "sampleRate = " + sampleRate + " channel = " + channel + " bufferSize = " + bufferSize); + if (recorder != null) { + recorder.release(); + } + } + if (recorder != null && recorder.getState() == AudioRecord.STATE_INITIALIZED) { if (recorderCallback != null) { recorderCallback.onPrepareRecord(); } From f7e6c487468fed14110238acbe480ff1f37ec06e Mon Sep 17 00:00:00 2001 From: Dimowner Date: Thu, 28 Mar 2019 13:04:08 +0200 Subject: [PATCH 04/23] Catch RuntimeException when recording start. --- app/build.gradle | 2 +- .../dimowner/audiorecorder/audio/recorder/AudioRecorder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cd1117df..b9d51405 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 21 +def versionPatch = 22 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/AudioRecorder.java b/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/AudioRecorder.java index 26239579..8161f0f0 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/AudioRecorder.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/recorder/AudioRecorder.java @@ -106,7 +106,7 @@ public void startRecording() { if (recorderCallback != null) { recorderCallback.onStartRecord(); } - } catch (IllegalStateException e) { + } catch (RuntimeException e) { Timber.e(e, "startRecording() failed"); if (recorderCallback != null) { recorderCallback.onError(new RecorderInitException()); From e5c9b2f712f6c01c40bbf703580c0202ac18baa3 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Thu, 28 Mar 2019 13:50:11 +0200 Subject: [PATCH 05/23] - Fix divide by zero error; - NullPointerException on record. --- app/build.gradle | 2 +- .../app/records/RecordsPresenter.java | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b9d51405..83bcc4d4 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 22 +def versionPatch = 23 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java index f1db3465..c4c431dd 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java @@ -119,7 +119,7 @@ public void onPlayProgress(final long mills) { if (view != null) { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null && record != null) { + if (view != null && record != null && record.getDuration() > 0) { view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, AndroidUtils.dpToPx(dpPerSecond)), (int)(1000 * mills/(record.getDuration()/1000))); } @@ -462,25 +462,27 @@ public void checkBookmarkActiveRecord() { recordingsTasks.postRunnable(new Runnable() { @Override public void run() { - if (record.isBookmarked()) { - localRepository.removeFromBookmarks(record.getId()); - } else { - localRepository.addToBookmarks(record.getId()); - } - record.setBookmark(!record.isBookmarked()); + if (record != null) { + if (record.isBookmarked()) { + localRepository.removeFromBookmarks(record.getId()); + } else { + localRepository.addToBookmarks(record.getId()); + } + record.setBookmark(!record.isBookmarked()); - AndroidUtils.runOnUIThread(new Runnable() { - @Override - public void run() { - if (view != null) { - if (record.isBookmarked()) { - view.addedToBookmarks(record.getId(), true); - } else { - view.removedFromBookmarks(record.getId(), true); + AndroidUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null && record != null) { + if (record.isBookmarked()) { + view.addedToBookmarks(record.getId(), true); + } else { + view.removedFromBookmarks(record.getId(), true); + } } } - } - }); + }); + } } }); } From 61d5976a4b528fc41b08460f240457251747d762 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Thu, 28 Mar 2019 14:15:54 +0200 Subject: [PATCH 06/23] Proguard --- app/build.gradle | 8 ++++---- app/proguard-rules.pro | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 83bcc4d4..6a478dab 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 23 +def versionPatch = 24 android { compileSdkVersion versions.targetSdkVersion @@ -59,9 +59,9 @@ android { buildTypes { release { - minifyEnabled false - shrinkResources false - useProguard false + minifyEnabled true + shrinkResources true + useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f1b42451..80ef6edc 100755 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -19,3 +19,5 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +-keep class com.dimowner.audiorecorder.** { *; } From 3bce415cc4e0216ab910fd229abab490b684903f Mon Sep 17 00:00:00 2001 From: Dimowner Date: Fri, 10 May 2019 12:32:33 +0300 Subject: [PATCH 07/23] Bulgarian localization --- app/src/main/res/values-bg/strings.xml | Bin 0 -> 8436 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/values-bg/strings.xml diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..6316651a1af630095212a3d1aeb343546e982694 GIT binary patch literal 8436 zcmcJU-BS~16vp3+z0tc;J35`w25ak0l@LGz34wrZ)oII2+p!l;Cles30|HH=*8cHr zpWiuo-3@F=vcbvZ``z>ToX`F5-+JhVPUwZx@K&Gp?QtW6q37do`S=$Oowy(sakt|EH zTJlKTutQDU8-~hKK)S31k@XiGZWu0_qFoAAWfs?rZ?$t8f%(mu`Ga6E zoRz~fXuc%7;u>WcZR;X(E6rxxX@ z98`o+cAHbouNqF)^-7d437sp+6JfHPe5WW+^G?j#8|Lq;k^M}PbtE5Ek;rtZ_ocHUyJ z<#5k)OHsHoBgHhX)BdA)7S77*l&Oi_bZy{g+v-FSKKUH(4AGt5K4~lt$12j#XQF9$M$h8SGg+u> zS>epQ{L*SbZ(yrA+2@(86OkG_ma--QI!fX@W`}RX*P$9JVM(Z{74TUW4|Kj@&gY_D zruGs&Gj~Pbh%GYr^xo_k{^*oi(uZE2{wi{9Ae_UXT~OQ3>E# zwpqd}6mc`&>ORYkp4Z|GJK5Y8k(Z~xca+oXoJCyB)ssp?hRW7(I``=%!27Rc@viKi z$N7dVzhm=DKJ{@nre`^@N`WtLSiZ=g)-Rv?K1fD9iKHpLIh&=xMxN&5WZM%x=D42Z zdnd|B=(Vvd%oXX^BE@}CJh9y27$zSRyG&qtl3uQRr0OSn?{dpzxw-Gii9YMc-OHh6Bu9zo zNBUE`OENQE4jm?QeXK{b$w59~!X;mm70J(iRV7}@*W|}k8tNn6Z7Ksh7P<^|7#DVL z(^x-pA}{|fu0)~tG518>yYi;A-qtsEa!3*@fm`RP?84MdkIuhErq!&6@}?p(Y1~gn zZ10Hcrg%TeRIU{w(EQ}GP)|OEJb3T5c`BRZ(E{l{w|eT6Sh@$Is$rik^@m>Py!wx- zIJ5fIYixG5Or?&|T~y+l?7F7N7?qnRkH@SeOyR_1TM>}mTw~L(g#XCX_i%2*`k+&Z zGof4AH;`$p7RjesOp>G0J(N%Cc)o41>#}-x6XR)e9ME0F?jz=wS>-*GY+dzP#53J3 zv!U-ez%7{YD2kH`eq0@(mMoX;`q*c(SH#|VnDlJp_Y_LuH}xBy$xGsNqCJFnAf<3$ zcMzCo$Z+@d`+NKUz)qL+dYC!uz1u_m!kvmq?juCXE#q;Z*%b}B*YP6kgva9jyPhv} z*Pt|bfYCjjx$M4%Hr<*w7Q>qLUYzEd?m(hz;kns4TG%(M*yB$+(vLp$4q$x&=);3#LO;zYrzt=+o_Q$^1&k1ednBMN3`G{txs&^_Gc#_&$&O zhMAjscq%T`WY7Q5!A9gcdUo$9d~RU=_Zh}>82F$XAxnDxB8B@pw`KWy+{t-ia3lFi zvGG8gEOt9Sp-g_=xRZC4zv?Cnm9-}6*(Z;?s-!ckdQNh7LAp{i$wq8Rn5ZVX&aK3L zg_&%eL|=;bW7U*=%3PhDk+N7IuBJf?QzTgh6pq5kJ=4}Z6LvyBhh;_BI33Pk^@px` zo^#bzYQtQ>eu6%WJ&1RLqx8Zjes6(JDCTQ+6ixY<`I#LsQ`--E-?e>M)%I8A96g5P zstE1H@aPj#U$VT%4sM+1ipah!Oe~u8pN9HqyI&=K_AJwlN6u%$1NT?xUNk2vCMweZ vNOj~)Q8dm17q4E{7OeQyYr;+Os~08vEZnK2l}q@K_YWtinA!fYXZ84h+C~kN literal 0 HcmV?d00001 From 2006190ab9d2b478e5f15fedcf08ba306a174bad Mon Sep 17 00:00:00 2001 From: Dimowner Date: Fri, 10 May 2019 13:50:23 +0300 Subject: [PATCH 08/23] Conflicts --- app/build.gradle | 34 +++++++++---------- .../dimowner/audiorecorder/ARApplication.java | 6 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6a478dab..c481ee2f 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ -//buildscript { -// repositories { -// maven { url 'https://maven.fabric.io/public' } -// } -// -// dependencies { -// classpath 'io.fabric.tools:gradle:1.+' -// } -//} +buildscript { + repositories { + maven { url 'https://maven.fabric.io/public' } + } + + dependencies { + classpath 'io.fabric.tools:gradle:1.+' + } +} apply plugin: 'com.android.application' -//apply plugin: 'io.fabric' +apply plugin: 'io.fabric' ext.versions = [ // Android @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 24 +def versionPatch = 21 android { compileSdkVersion versions.targetSdkVersion @@ -98,9 +98,9 @@ android { } } -//repositories { -// maven { url 'https://maven.fabric.io/public' } -//} +repositories { + maven { url 'https://maven.fabric.io/public' } +} // Remove not needed buildVariants. android.variantFilter { variant -> @@ -119,7 +119,7 @@ dependencies { implementation "com.jakewharton.timber:timber:$versions.timber" implementation "com.android.support:recyclerview-v7:$versions.support" -// implementation('com.crashlytics.sdk.android:crashlytics:2.9.8@aar') { -// transitive = true -// } + implementation('com.crashlytics.sdk.android:crashlytics:2.9.8@aar') { + transitive = true + } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/ARApplication.java b/app/src/main/java/com/dimowner/audiorecorder/ARApplication.java index f331fb85..9b46539d 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/ARApplication.java +++ b/app/src/main/java/com/dimowner/audiorecorder/ARApplication.java @@ -19,9 +19,9 @@ import android.app.Application; import android.os.Handler; -//import com.crashlytics.android.Crashlytics; +import com.crashlytics.android.Crashlytics; import com.dimowner.audiorecorder.util.AndroidUtils; -//import io.fabric.sdk.android.Fabric; +import io.fabric.sdk.android.Fabric; import timber.log.Timber; @@ -75,7 +75,7 @@ protected String createStackElementTag(StackTraceElement element) { } super.onCreate(); -// Fabric.with(this, new Crashlytics()); + Fabric.with(this, new Crashlytics()); PACKAGE_NAME = getApplicationContext().getPackageName(); applicationHandler = new Handler(getApplicationContext().getMainLooper()); From 0ce736b6f3ff8e2d12a0cb6db217928d3112ae5f Mon Sep 17 00:00:00 2001 From: Dimowner Date: Fri, 10 May 2019 18:22:42 +0300 Subject: [PATCH 09/23] - Fix crashes and errors; - Store new records in public dir by default. --- app/build.gradle | 2 +- .../audiorecorder/app/main/MainActivity.java | 1 + .../audiorecorder/app/main/MainContract.java | 2 + .../audiorecorder/app/main/MainPresenter.java | 5 + .../app/records/RecordsPresenter.java | 122 +++++++++--------- .../audiorecorder/audio/SoundFile.java | 14 +- .../audio/player/AudioPlayer.java | 106 +++++++++------ .../audiorecorder/data/PrefsImpl.java | 4 +- 8 files changed, 149 insertions(+), 107 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c481ee2f..9e136ae2 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 21 +def versionPatch = 27 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 5b4d65a7..777ce4d8 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -156,6 +156,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }); presenter = ARApplication.getInjector().provideMainPresenter(); + presenter.executeFirstRun(); waveformView.setOnSeekListener(new WaveformView.OnSeekListener() { @Override diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java index cc36dc66..d4742669 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java @@ -62,6 +62,8 @@ interface View extends Contract.View { interface UserActionsListener extends Contract.UserActionsListener { + void executeFirstRun(); + void setAudioRecorder(RecorderContract.Recorder recorder); void startRecording(); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java index f4198355..db4e3015 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java @@ -267,6 +267,11 @@ public void clear() { recordingsTasks.close(); } + @Override + public void executeFirstRun() { + prefs.firstRunExecuted(); + } + @Override public void setAudioRecorder(RecorderContract.Recorder recorder) { appRecorder.setRecorder(recorder); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java index c4c431dd..c44609e6 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java @@ -51,7 +51,7 @@ public class RecordsPresenter implements RecordsContract.UserActionsListener { private final LocalRepository localRepository; private final Prefs prefs; - private Record record; + private Record activeRecord; private float dpPerSecond = AppConstants.SHORT_RECORD_DP_PER_SECOND; private boolean showBookmarks = false; @@ -119,9 +119,9 @@ public void onPlayProgress(final long mills) { if (view != null) { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null && record != null && record.getDuration() > 0) { + if (view != null && activeRecord != null && (activeRecord.getDuration()/1000) > 0) { view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, - AndroidUtils.dpToPx(dpPerSecond)), (int)(1000 * mills/(record.getDuration()/1000))); + AndroidUtils.dpToPx(dpPerSecond)), (int)(1000 * mills/(activeRecord.getDuration()/1000))); } }}); } @@ -182,9 +182,9 @@ public void clear() { @Override public void startPlayback() { if (!appRecorder.isRecording()) { - if (record != null) { + if (activeRecord != null) { if (!audioPlayer.isPlaying()) { - audioPlayer.setData(record.getPath()); + audioPlayer.setData(activeRecord.getPath()); } audioPlayer.playOrPause(); } @@ -221,12 +221,12 @@ public void deleteActiveRecord() { audioPlayer.stop(); recordingsTasks.postRunnable(new Runnable() { @Override public void run() { - if (record != null) { - localRepository.deleteRecord(record.getId()); - fileRepository.deleteRecordFile(record.getPath()); + if (activeRecord != null) { + localRepository.deleteRecord(activeRecord.getId()); + fileRepository.deleteRecordFile(activeRecord.getPath()); prefs.setActiveRecord(-1); - final long id = record.getId(); - record = null; + final long id = activeRecord.getId(); + activeRecord = null; dpPerSecond = AppConstants.SHORT_RECORD_DP_PER_SECOND; AndroidUtils.runOnUIThread(new Runnable() { @Override @@ -283,10 +283,10 @@ public void renameRecord(final long id, String n) { ext = AppConstants.M4A_EXTENSION; } if (fileRepository.renameFile(r.getPath(), name, ext)) { - record = new Record(r.getId(), nameWithExt, r.getDuration(), r.getCreated(), + activeRecord = new Record(r.getId(), nameWithExt, r.getDuration(), r.getCreated(), r.getAdded(), renamed.getAbsolutePath(), r.isBookmarked(), r.isWaveformProcessed(), r.getAmps()); - if (localRepository.updateRecord(record)) { + if (localRepository.updateRecord(activeRecord)) { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { if (view != null) { @@ -339,20 +339,20 @@ public void loadRecords() { @Override public void run() { final List recordList = localRepository.getRecords(0); - record = localRepository.getRecord((int) prefs.getActiveRecord()); - if (record != null) { - dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration() / 1000000f); + activeRecord = localRepository.getRecord((int) prefs.getActiveRecord()); + if (activeRecord != null) { + dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration() / 1000000f); } AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { if (view != null) { view.showRecords(Mapper.recordsToListItems(recordList)); - if (record != null) { - view.showWaveForm(record.getAmps(), record.getDuration()); - view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(record.getDuration() / 1000)); - view.showRecordName(FileUtil.removeFileExtension(record.getName())); - if (record.isBookmarked()) { + if (activeRecord != null) { + view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); + view.showRecordName(FileUtil.removeFileExtension(activeRecord.getName())); + if (activeRecord.isBookmarked()) { view.bookmarksSelected(); } else { view.bookmarksUnselected(); @@ -381,20 +381,20 @@ public void loadRecordsPage(final int page) { @Override public void run() { final List recordList = localRepository.getRecords(page); - record = localRepository.getRecord((int) prefs.getActiveRecord()); - if (record != null) { - dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration() / 1000000f); + activeRecord = localRepository.getRecord((int) prefs.getActiveRecord()); + if (activeRecord != null) { + dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration() / 1000000f); } AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { if (view != null) { view.addRecords(Mapper.recordsToListItems(recordList)); - if (record != null) { - view.showWaveForm(record.getAmps(), record.getDuration()); - view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(record.getDuration() / 1000)); - view.showRecordName(FileUtil.removeFileExtension(record.getName())); - if (record.isBookmarked()) { + if (activeRecord != null) { + view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); + view.showRecordName(FileUtil.removeFileExtension(activeRecord.getName())); + if (activeRecord.isBookmarked()) { view.bookmarksSelected(); } else { view.bookmarksUnselected(); @@ -422,19 +422,19 @@ public void loadBookmarks() { @Override public void run() { final List recordList = localRepository.getBookmarks(); - record = localRepository.getRecord((int) prefs.getActiveRecord()); - if (record != null) { - dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration() / 1000000f); + activeRecord = localRepository.getRecord((int) prefs.getActiveRecord()); + if (activeRecord != null) { + dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration() / 1000000f); } AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { if (view != null) { view.showRecords(Mapper.recordsToListItems(recordList)); - if (record != null) { - view.showWaveForm(record.getAmps(), record.getDuration()); - view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(record.getDuration() / 1000)); - view.showRecordName(FileUtil.removeFileExtension(record.getName())); + if (activeRecord != null) { + view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); + view.showRecordName(FileUtil.removeFileExtension(activeRecord.getName())); } view.hideProgress(); view.hidePanelProgress(); @@ -462,22 +462,22 @@ public void checkBookmarkActiveRecord() { recordingsTasks.postRunnable(new Runnable() { @Override public void run() { - if (record != null) { - if (record.isBookmarked()) { - localRepository.removeFromBookmarks(record.getId()); + if (activeRecord != null) { + if (activeRecord.isBookmarked()) { + localRepository.removeFromBookmarks(activeRecord.getId()); } else { - localRepository.addToBookmarks(record.getId()); + localRepository.addToBookmarks(activeRecord.getId()); } - record.setBookmark(!record.isBookmarked()); + activeRecord.setBookmark(!activeRecord.isBookmarked()); AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null && record != null) { - if (record.isBookmarked()) { - view.addedToBookmarks(record.getId(), true); + if (view != null && activeRecord != null) { + if (activeRecord.isBookmarked()) { + view.addedToBookmarks(activeRecord.getId(), true); } else { - view.removedFromBookmarks(record.getId(), true); + view.removedFromBookmarks(activeRecord.getId(), true); } } } @@ -499,7 +499,7 @@ public void run() { @Override public void run() { if (view != null) { - view.addedToBookmarks(r.getId(), r.getId() == record.getId()); + view.addedToBookmarks(r.getId(), activeRecord != null && r.getId() == activeRecord.getId()); } } }); @@ -519,8 +519,8 @@ public void run() { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null && record != null) { - view.removedFromBookmarks(r.getId(), r.getId() == record.getId()); + if (view != null) { + view.removedFromBookmarks(r.getId(), activeRecord != null && r.getId() == activeRecord.getId()); } } }); @@ -539,21 +539,21 @@ public void setActiveRecord(final long id, final RecordsContract.Callback callba loadingTasks.postRunnable(new Runnable() { @Override public void run() { - record = localRepository.getRecord((int) id); - if (record != null) { - dpPerSecond = ARApplication.getDpPerSecond((float) record.getDuration()/1000000f); + activeRecord = localRepository.getRecord((int) id); + if (activeRecord != null) { + dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration()/1000000f); AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null) { - view.showWaveForm(record.getAmps(), record.getDuration()); - view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(record.getDuration() / 1000)); - view.showRecordName(FileUtil.removeFileExtension(record.getName())); + if (view != null && activeRecord != null) { + view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); + view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); + view.showRecordName(FileUtil.removeFileExtension(activeRecord.getName())); callback.onSuccess(); - if (record.isBookmarked()) { - view.addedToBookmarks(record.getId(), true); + if (activeRecord.isBookmarked()) { + view.addedToBookmarks(activeRecord.getId(), true); } else { - view.removedFromBookmarks(record.getId(), true); + view.removedFromBookmarks(activeRecord.getId(), true); } view.hidePanelProgress(); view.showPlayerPanel(); @@ -583,8 +583,8 @@ public long getActiveRecordId() { @Override public String getActiveRecordPath() { - if (record != null) { - return record.getPath(); + if (activeRecord != null) { + return activeRecord.getPath(); } else { return null; } @@ -592,8 +592,8 @@ public String getActiveRecordPath() { @Override public String getRecordName() { - if (record != null) { - return record.getName(); + if (activeRecord != null) { + return activeRecord.getName(); } else { return "Record"; } diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java index 359b1d42..bb3b88b0 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java @@ -31,6 +31,8 @@ import java.nio.ShortBuffer; import java.util.Arrays; +import timber.log.Timber; + /** * This class taken from Ringdroid app. * https://github.com/google/ringdroid @@ -126,11 +128,15 @@ private void readFile(File inputFile) throws IOException { mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); // Expected total number of samples per channel. - int expectedNumSamples = - (int) ((format.getLong(MediaFormat.KEY_DURATION) / 1000000.f) * mSampleRate + 0.5f); + int expectedNumSamples = 0; + try { + expectedNumSamples = (int) ((format.getLong(MediaFormat.KEY_DURATION) / 1000000.f) * mSampleRate + 0.5f); - //SoundFile duration. - duration = format.getLong(MediaFormat.KEY_DURATION); + //SoundFile duration. + duration = format.getLong(MediaFormat.KEY_DURATION); + } catch (Exception e) { + Timber.e(e); + } dpPerSec = ARApplication.getDpPerSecond((float) duration/1000000f); MediaCodec codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME)); diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/player/AudioPlayer.java b/app/src/main/java/com/dimowner/audiorecorder/audio/player/AudioPlayer.java index f95fed5c..e7fe7b72 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/player/AudioPlayer.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/player/AudioPlayer.java @@ -99,44 +99,57 @@ private void restartPlayer() { @Override public void playOrPause() { - if (mediaPlayer != null) { - if (mediaPlayer.isPlaying()) { - pause(); - } else { - if (!isPrepared) { - try { - mediaPlayer.setOnPreparedListener(this); - mediaPlayer.prepareAsync(); - } catch (IllegalStateException ex) { - Timber.e(ex); - restartPlayer(); - mediaPlayer.setOnPreparedListener(this); - mediaPlayer.prepareAsync(); - } + try { + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + pause(); } else { - mediaPlayer.start(); - mediaPlayer.seekTo((int) seekPos); - onStartPlay(); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - stop(); - onStopPlay(); - } - }); - - timerProgress = new Timer(); - timerProgress.schedule(new TimerTask() { - @Override - public void run() { - if (mediaPlayer != null && mediaPlayer.isPlaying()) { - int curPos = mediaPlayer.getCurrentPosition(); - onPlayProgress(curPos); + if (!isPrepared) { + try { + mediaPlayer.setOnPreparedListener(this); + mediaPlayer.prepareAsync(); + } catch (IllegalStateException ex) { + Timber.e(ex); + restartPlayer(); + mediaPlayer.setOnPreparedListener(this); + try { + mediaPlayer.prepareAsync(); + } catch (IllegalStateException e) { + Timber.e(e); + restartPlayer(); } } - }, 0, AppConstants.VISUALIZATION_INTERVAL); + } else { + mediaPlayer.start(); + mediaPlayer.seekTo((int) seekPos); + onStartPlay(); + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + stop(); + onStopPlay(); + } + }); + + timerProgress = new Timer(); + timerProgress.schedule(new TimerTask() { + @Override + public void run() { + try { + if (mediaPlayer != null && mediaPlayer.isPlaying()) { + int curPos = mediaPlayer.getCurrentPosition(); + onPlayProgress(curPos); + } + } catch(IllegalStateException e){ + Timber.e(e, "Player is not initialized!"); + } + } + }, 0, AppConstants.VISUALIZATION_INTERVAL); + } } } + } catch(IllegalStateException e){ + Timber.e(e, "Player is not initialized!"); } } @@ -164,9 +177,13 @@ public void onCompletion(MediaPlayer mp) { timerProgress.schedule(new TimerTask() { @Override public void run() { - if (mediaPlayer != null && mediaPlayer.isPlaying()) { - int curPos = mediaPlayer.getCurrentPosition(); - onPlayProgress(curPos); + try { + if (mediaPlayer != null && mediaPlayer.isPlaying()) { + int curPos = mediaPlayer.getCurrentPosition(); + onPlayProgress(curPos); + } + } catch(IllegalStateException e){ + Timber.e(e, "Player is not initialized!"); } } }, 0, AppConstants.VISUALIZATION_INTERVAL); @@ -175,9 +192,13 @@ public void run() { @Override public void seek(long mills) { seekPos = mills; - if (mediaPlayer != null && mediaPlayer.isPlaying()) { - mediaPlayer.seekTo((int) seekPos); - onSeek((int) seekPos); + try { + if (mediaPlayer != null && mediaPlayer.isPlaying()) { + mediaPlayer.seekTo((int) seekPos); + onSeek((int) seekPos); + } + } catch(IllegalStateException e){ + Timber.e(e, "Player is not initialized!"); } } @@ -214,7 +235,12 @@ public void stop() { @Override public boolean isPlaying() { - return mediaPlayer != null && mediaPlayer.isPlaying(); + try { + return mediaPlayer != null && mediaPlayer.isPlaying(); + } catch(IllegalStateException e){ + Timber.e(e, "Player is not initialized!"); + } + return false; } @Override diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java b/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java index f87e6cdb..9b4935d0 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java @@ -69,12 +69,14 @@ public boolean isFirstRun() { public void firstRunExecuted() { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(PREF_KEY_IS_FIRST_RUN, false); + editor.putBoolean(PREF_KEY_IS_STORE_DIR_PUBLIC, true); editor.apply(); +// setStoreDirPublic(true); } @Override public boolean isStoreDirPublic() { - return sharedPreferences.contains(PREF_KEY_IS_STORE_DIR_PUBLIC) && sharedPreferences.getBoolean(PREF_KEY_IS_STORE_DIR_PUBLIC, false); + return sharedPreferences.contains(PREF_KEY_IS_STORE_DIR_PUBLIC) && sharedPreferences.getBoolean(PREF_KEY_IS_STORE_DIR_PUBLIC, true); } @Override From 976604dc2d11369b1429ef6d6299e41487799489 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Thu, 1 Aug 2019 22:18:37 +0300 Subject: [PATCH 10/23] Fixed few crashes --- app/src/main/AndroidManifest.xml | 6 ++--- .../audiorecorder/app/AppRecorderImpl.java | 8 +++++- .../audiorecorder/app/main/MainActivity.java | 5 ++-- .../audiorecorder/app/main/MainPresenter.java | 13 ++++++---- .../app/records/RecordsPresenter.java | 9 ++++--- .../audiorecorder/audio/SoundFile.java | 10 ++++++-- .../data/database/LocalRepository.java | 2 +- .../data/database/LocalRepositoryImpl.java | 2 +- .../audiorecorder/exception/AppException.java | 3 ++- .../exception/CantProcessRecord.java | 24 ++++++++++++++++++ .../audiorecorder/exception/ErrorParser.java | 2 ++ app/src/main/res/values-bg/strings.xml | Bin 8436 -> 8580 bytes app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +-- 17 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/dimowner/audiorecorder/exception/CantProcessRecord.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 958bf45e..7eae71b9 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,9 +43,9 @@ - - - + diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java b/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java index 6e82a26c..38c87db4 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/AppRecorderImpl.java @@ -7,6 +7,7 @@ import com.dimowner.audiorecorder.data.Prefs; import com.dimowner.audiorecorder.data.database.LocalRepository; import com.dimowner.audiorecorder.exception.AppException; +import com.dimowner.audiorecorder.exception.CantProcessRecord; import com.dimowner.audiorecorder.util.AndroidUtils; import java.io.File; @@ -112,7 +113,12 @@ public void run() { onRecordFinishProcessing(); } }); - } catch (IOException e) { + } catch (IOException | OutOfMemoryError e) { + AndroidUtils.runOnUIThread(new Runnable() { + @Override public void run() { + onError(new CantProcessRecord()); + } + }); Timber.e(e); } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 777ce4d8..2fb6aa63 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -165,8 +165,9 @@ public void onSeek(int px) { } @Override public void onSeeking(int px, long mills) { - if (waveformView.getWaveformLength() > 0) { - playProgress.setProgress(1000 * (int) AndroidUtils.pxToDp(px) / waveformView.getWaveformLength()); + int length = waveformView.getWaveformLength(); + if (length > 0) { + playProgress.setProgress(1000 * (int) AndroidUtils.pxToDp(px) / length); } txtProgress.setText(TimeUtils.formatTimeIntervalHourMinSec2(mills)); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java index db4e3015..75110674 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java @@ -200,8 +200,11 @@ public void onPlayProgress(final long mills) { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { if (view != null) { - view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, - AndroidUtils.dpToPx(dpPerSecond)), (int)(1000 * mills/(songDuration/1000))); + long duration = songDuration/1000; + if (duration > 0) { + view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, + AndroidUtils.dpToPx(dpPerSecond)), (int) (1000 * mills / duration)); + } } }}); } @@ -462,7 +465,7 @@ public void run() { } } catch (IOException | OutOfMemoryError e) { Timber.e(e); -// TODO: handle errors here + view.showError(R.string.error_process_waveform); } isProcessing = false; } @@ -609,11 +612,11 @@ public void run() { } }); } - } catch (IOException e) { + } catch (IOException | OutOfMemoryError e) { Timber.e(e); if (view != null) { - //TODO: show error on display view.hideRecordProcessing(); + view.showError(R.string.error_process_waveform); } } isProcessing = false; diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java index c44609e6..c36f31d4 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsPresenter.java @@ -119,9 +119,12 @@ public void onPlayProgress(final long mills) { if (view != null) { AndroidUtils.runOnUIThread(new Runnable() { @Override public void run() { - if (view != null && activeRecord != null && (activeRecord.getDuration()/1000) > 0) { - view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, - AndroidUtils.dpToPx(dpPerSecond)), (int)(1000 * mills/(activeRecord.getDuration()/1000))); + if (view != null && activeRecord != null) { + long duration = activeRecord.getDuration()/1000; + if (duration > 0) { + view.onPlayProgress(mills, AndroidUtils.convertMillsToPx(mills, + AndroidUtils.dpToPx(dpPerSecond)), (int) (1000 * mills / duration)); + } } }}); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java index bb3b88b0..32c24833 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java +++ b/app/src/main/java/com/dimowner/audiorecorder/audio/SoundFile.java @@ -104,7 +104,7 @@ public int[] getFrameGains() { return mFrameGains; } - private void readFile(File inputFile) throws IOException { + private void readFile(File inputFile) throws IOException, OutOfMemoryError { MediaExtractor extractor = new MediaExtractor(); MediaFormat format = null; int i; @@ -157,7 +157,13 @@ private void readFile(File inputFile) throws IOException { // For longer streams, the buffer size will be increased later on, calculating a rough // estimate of the total size needed to store all the samples in order to resize the buffer // only once. - mDecodedBytes = ByteBuffer.allocate(1 << 20); + try { + mDecodedBytes = ByteBuffer.allocate(1 << 20); + } catch (IllegalArgumentException e) { + Timber.e(e); + mDecodedBytes = ByteBuffer.allocate(1 << 10); + } + Boolean firstSampleData = true; while (true) { // read data from file and feed it to the decoder input buffers. diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java index 674693e4..41372e3e 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java @@ -43,7 +43,7 @@ public interface LocalRepository { long insertFile(String filePath, long duration, int[] waveform) throws IOException; - boolean updateWaveform(int id) throws IOException; + boolean updateWaveform(int id) throws IOException, OutOfMemoryError; void deleteRecord(int id); diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java index 35aba3ba..ea4130f8 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java @@ -169,7 +169,7 @@ public long insertFile(String path, long duration, int[] waveform) throws IOExce } @Override - public boolean updateWaveform(int id) throws IOException { + public boolean updateWaveform(int id) throws IOException, OutOfMemoryError { Record record = getRecord(id); String path = record.getPath(); if (path != null && !path.isEmpty()) { diff --git a/app/src/main/java/com/dimowner/audiorecorder/exception/AppException.java b/app/src/main/java/com/dimowner/audiorecorder/exception/AppException.java index f6f42105..a4d479a2 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/exception/AppException.java +++ b/app/src/main/java/com/dimowner/audiorecorder/exception/AppException.java @@ -22,7 +22,8 @@ public abstract class AppException extends Exception { public static final int INVALID_OUTPUT_FILE = 2; public static final int RECORDER_INIT_EXCEPTION = 3; public static final int PLAYER_INIT_EXCEPTION = 4; - public static final int PLAYER_DATA_SOURCE_EXCEPTION= 5; + public static final int PLAYER_DATA_SOURCE_EXCEPTION = 5; + public static final int CANT_PROCESS_RECORD = 6; public abstract int getType(); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/exception/CantProcessRecord.java b/app/src/main/java/com/dimowner/audiorecorder/exception/CantProcessRecord.java new file mode 100644 index 00000000..977c8451 --- /dev/null +++ b/app/src/main/java/com/dimowner/audiorecorder/exception/CantProcessRecord.java @@ -0,0 +1,24 @@ +/* + * Copyright 2018 Dmitriy Ponomarenko + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dimowner.audiorecorder.exception; + +public class CantProcessRecord extends AppException { + @Override + public int getType() { + return AppException.CANT_PROCESS_RECORD; + } +} diff --git a/app/src/main/java/com/dimowner/audiorecorder/exception/ErrorParser.java b/app/src/main/java/com/dimowner/audiorecorder/exception/ErrorParser.java index 3efdc091..03439f46 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/exception/ErrorParser.java +++ b/app/src/main/java/com/dimowner/audiorecorder/exception/ErrorParser.java @@ -33,6 +33,8 @@ public static int parseException(AppException e) { return R.string.error_player_data_source; } else if (e.getType() == AppException.PLAYER_INIT_EXCEPTION) { return R.string.error_failed_to_init_player; + } else if (e.getType() == AppException.CANT_PROCESS_RECORD) { + return R.string.error_process_waveform; } return R.string.error_unknown; } diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 6316651a1af630095212a3d1aeb343546e982694..3d44503f01ba27cd6ab4f0e72c60756d5ceccc48 100644 GIT binary patch delta 104 zcmez3*y6k)M`Ch=n2vuDLq0Проигрыватель не может считать файл Не удалось считать аудио файл Доступ запрещен + Не удалось обработать аудиофайл Програвач не може зчитати запис Не вдалося зчитати аудіо файл Доступ заборонено + Не вдалося опрацювати аудіофайл Player can\'t read file Unable to read sound file Permission denied + Can\'t process waveform Date: Fri, 2 Aug 2019 09:08:13 +0300 Subject: [PATCH 11/23] Added options button to list item in recordings list --- app/build.gradle | 2 +- .../app/records/RecordsAdapter.java | 71 +++++++++++++++--- .../audiorecorder/util/AndroidUtils.java | 56 ++++++++++++++ app/src/main/res/drawable/ic_info.xml | 9 +++ app/src/main/res/drawable/ic_more_vert.xml | 9 +++ app/src/main/res/layout/list_item.xml | 22 ++++-- app/src/main/res/menu/menu_more.xml | 14 ++++ app/src/main/res/values-bg/strings.xml | Bin 8580 -> 8664 bytes app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 1 + 12 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/drawable/ic_info.xml create mode 100644 app/src/main/res/drawable/ic_more_vert.xml create mode 100644 app/src/main/res/menu/menu_more.xml diff --git a/app/build.gradle b/app/build.gradle index 9e136ae2..6b6b66f6 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 27 +def versionPatch = 28 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index f461bcc2..905eba52 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -16,18 +16,33 @@ package com.dimowner.audiorecorder.app.records; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.NonNull; +import android.support.v4.widget.PopupMenuCompat; import android.support.v7.widget.RecyclerView; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; import android.util.TypedValue; +import android.view.ContextMenu; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AnimationUtils; import android.widget.ImageButton; import android.widget.LinearLayout; +import android.widget.PopupMenu; +import android.widget.PopupWindow; import android.widget.TextView; import com.dimowner.audiorecorder.R; @@ -39,6 +54,8 @@ import java.util.Calendar; import java.util.List; +import timber.log.Timber; + public class RecordsAdapter extends RecyclerView.Adapter { private List data; @@ -108,9 +125,9 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, holder.description.setText(data.get(p).getDurationStr()); holder.created.setText(data.get(p).getAddedTimeStr()); if (data.get(p).isBookmarked()) { - holder.bookmark.setImageResource(R.drawable.ic_bookmark_small); + holder.btnBookmark.setImageResource(R.drawable.ic_bookmark_small); } else { - holder.bookmark.setImageResource(R.drawable.ic_bookmark_bordered_small); + holder.btnBookmark.setImageResource(R.drawable.ic_bookmark_bordered_small); } if (viewHolder.getLayoutPosition() == activeItem) { holder.view.setBackgroundResource(R.color.selected_item_color); @@ -118,7 +135,7 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, holder.view.setBackgroundResource(android.R.color.transparent); } - holder.bookmark.setOnClickListener(new View.OnClickListener() { + holder.btnBookmark.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (onAddToBookmarkListener != null && data.size() > p) { @@ -130,6 +147,13 @@ public void onClick(View v) { } } }); + holder.btnMore.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Timber.v("ONCLICK btnMore"); + showMenu(v); + } + }); holder.waveformView.setWaveform(data.get(p).getAmps()); holder.view.setOnClickListener(new View.OnClickListener() { @@ -145,6 +169,22 @@ public void onClick(View v) { } } + public void showMenu(View v) { + PopupMenu popup = new PopupMenu(v.getContext(), v); + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + Timber.v("onMenuItemClick"); + return false; + } + });// to implement on click event on items of menu + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.menu_more, popup.getMenu()); + AndroidUtils.insertMenuItemIcons(v.getContext(), popup); + popup.show(); + } + + public void setActiveItem(int activeItem) { int prev = this.activeItem; this.activeItem = activeItem; @@ -343,24 +383,35 @@ public interface OnAddToBookmarkListener { } public class ItemViewHolder extends RecyclerView.ViewHolder { +// implements View.OnCreateContextMenuListener { TextView name; TextView description; TextView created; // ImageView image; - ImageButton bookmark; + ImageButton btnBookmark; + ImageButton btnMore; SimpleWaveformView waveformView; View view; public ItemViewHolder(View itemView) { super(itemView); - this.view = itemView; - this.name = itemView.findViewById(R.id.list_item_name); - this.description = itemView.findViewById(R.id.list_item_description); - this.created = itemView.findViewById(R.id.list_item_date); + view = itemView; + name = itemView.findViewById(R.id.list_item_name); + description = itemView.findViewById(R.id.list_item_description); + created = itemView.findViewById(R.id.list_item_date); // this.image = itemView.findViewById(R.id.list_item_image); - this.bookmark = itemView.findViewById(R.id.bookmark); - this.waveformView = itemView.findViewById(R.id.list_item_waveform); + btnBookmark = itemView.findViewById(R.id.list_item_bookmark); + waveformView = itemView.findViewById(R.id.list_item_waveform); + btnMore = itemView.findViewById(R.id.list_item_more); +// btnMore.setOnCreateContextMenuListener(this); } + +// @Override +// public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { +// Timber.v("onCreateContextMenu"); +// menu.add(Menu.NONE, 22, Menu.NONE, R.string.delete_record); +// menu.add(Menu.NONE, 33, Menu.NONE, R.string.app_name); +// } } public class UniversalViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java index 847dd773..0c70358d 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java +++ b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java @@ -19,15 +19,24 @@ import android.app.Activity; import android.content.Context; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.Point; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.media.MediaExtractor; import android.media.MediaFormat; import android.os.Build; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; import android.view.Display; +import android.view.Menu; +import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; +import android.widget.PopupMenu; import com.dimowner.audiorecorder.ARApplication; +import com.dimowner.audiorecorder.R; import java.io.File; import java.io.IOException; @@ -212,4 +221,51 @@ public static long readRecordDuration(File file) { } return -1; } + + /** + * Moves icons from the PopupMenu MenuItems' icon fields into the menu title as a Spannable with the icon and title text. + */ + public static void insertMenuItemIcons(Context context, PopupMenu popupMenu) { + Menu menu = popupMenu.getMenu(); + if (hasIcon(menu)) { + for (int i = 0; i < menu.size(); i++) { + insertMenuItemIcon(context, menu.getItem(i)); + } + } + } + + /** + * @return true if the menu has at least one MenuItem with an icon. + */ + private static boolean hasIcon(Menu menu) { + for (int i = 0; i < menu.size(); i++) { + if (menu.getItem(i).getIcon() != null) return true; + } + return false; + } + + /** + * Converts the given MenuItem title into a Spannable containing both its icon and title. + */ + private static void insertMenuItemIcon(Context context, MenuItem menuItem) { + Drawable icon = menuItem.getIcon(); + + // If there no icon, we insert a transparent one to keep the title aligned with the items + // which do have icons. + if (icon == null) icon = new ColorDrawable(Color.TRANSPARENT); + + int iconSize = context.getResources().getDimensionPixelSize(R.dimen.menu_item_icon_size); + icon.setBounds(0, 0, iconSize, iconSize); + ImageSpan imageSpan = new ImageSpan(icon); + + // Add a space placeholder for the icon, before the title. + SpannableStringBuilder ssb = new SpannableStringBuilder(" " + menuItem.getTitle()); + + // Replace the space placeholder with the icon. + ssb.setSpan(imageSpan, 1, 2, 0); + menuItem.setTitle(ssb); + // Set the icon to null just in case, on some weird devices, they've customized Android to display + // the icon in the menu... we don't want two icons to appear. + menuItem.setIcon(null); + } } diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 00000000..650f3807 --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_vert.xml b/app/src/main/res/drawable/ic_more_vert.xml new file mode 100644 index 00000000..5de1a38b --- /dev/null +++ b/app/src/main/res/drawable/ic_more_vert.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index dd3cc7d6..e2cc06d5 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -49,9 +49,9 @@ android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginStart="80dp" + android:layout_marginEnd="36dp" android:layout_gravity="center_vertical"> - @@ -71,15 +71,14 @@ /> @@ -109,9 +108,20 @@ tools:text="@string/app_name" android:layout_gravity="end" android:paddingStart="@dimen/spacing_tiny" - android:paddingEnd="@dimen/spacing_normal" + android:paddingEnd="@dimen/spacing_xsmall" android:maxLines="1"/> + + diff --git a/app/src/main/res/menu/menu_more.xml b/app/src/main/res/menu/menu_more.xml new file mode 100644 index 00000000..fc377999 --- /dev/null +++ b/app/src/main/res/menu/menu_more.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 3d44503f01ba27cd6ab4f0e72c60756d5ceccc48..974f2e13e63660f3af5c0eb7e342f6f5fb4a97ef 100644 GIT binary patch delta 61 zcmZp1zTv##3EyM^PC3O)hCGHehI|Gk20I2%D7%Oumm!g%gdr0so;O*MMRan5kitX< Kj?Fy$6+!^_Y7a#K delta 16 XcmccN+~U093E$)ZNw&>){Ii4rJsbuz diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index bc84ec19..536feaf1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -32,6 +32,7 @@ Обратная связь Не найдено ни одного почтового клиента Послать по электронной почте… + Информация Настройки приложения: Настройки записи: diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e4abb92d..a6c7619f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -32,6 +32,7 @@ Зворотній зв\'язок Не знайдено жодного поштового клієнта Надіслати по електронній пошті… + Інформація Налаштування додатка: Налаштування запису: diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 5cf46b09..590a71b8 100755 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -32,4 +32,5 @@ 316dp + 24dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3bfc150..61bfe6d5 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Request or feedback Not found any Email client Send by email… + Information App settings: Recording settings: From edeaced2226552aa8124f52bb45040cdd655a383 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Sun, 4 Aug 2019 14:52:12 +0300 Subject: [PATCH 12/23] - Added actions to options button in the recordings list; - Added setting 'Show renaming dialog after stop recording'. --- .../com/dimowner/audiorecorder/Injector.java | 12 +++- .../audiorecorder/app/main/MainActivity.java | 27 ++------ .../audiorecorder/app/main/MainPresenter.java | 4 +- .../app/records/RecordsActivity.java | 53 ++++++++++++++- .../app/records/RecordsAdapter.java | 55 ++++++--------- .../app/records/RecordsContract.java | 6 ++ .../app/records/RecordsPresenter.java | 63 ++++++++++++++---- .../app/settings/SettingsActivity.java | 59 +++++++++------- .../app/settings/SettingsContract.java | 4 ++ .../app/settings/SettingsPresenter.java | 6 ++ .../dimowner/audiorecorder/data/Prefs.java | 3 + .../audiorecorder/data/PrefsImpl.java | 14 ++++ .../audiorecorder/util/AndroidUtils.java | 39 +++++++++++ .../dimowner/audiorecorder/util/FileUtil.java | 32 +++++++++ app/src/main/res/drawable/ic_open_with.xml | 9 +++ app/src/main/res/drawable/ic_pencil.xml | 6 +- app/src/main/res/drawable/ic_pencil_small.xml | 9 +++ app/src/main/res/drawable/ic_save_alt.xml | 9 +++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/activity_records.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 49 +++++++++++--- app/src/main/res/menu/menu_more.xml | 27 ++++++-- app/src/main/res/values-bg/strings.xml | Bin 8664 -> 9342 bytes app/src/main/res/values-ru/strings.xml | 7 ++ app/src/main/res/values-uk/strings.xml | 7 ++ app/src/main/res/values/strings.xml | 7 ++ 26 files changed, 395 insertions(+), 116 deletions(-) create mode 100644 app/src/main/res/drawable/ic_open_with.xml create mode 100644 app/src/main/res/drawable/ic_pencil_small.xml create mode 100644 app/src/main/res/drawable/ic_save_alt.xml diff --git a/app/src/main/java/com/dimowner/audiorecorder/Injector.java b/app/src/main/java/com/dimowner/audiorecorder/Injector.java index c2559ab8..d1eeb060 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/Injector.java +++ b/app/src/main/java/com/dimowner/audiorecorder/Injector.java @@ -47,6 +47,7 @@ public class Injector { private BackgroundQueue recordingTasks; private BackgroundQueue importTasks; private BackgroundQueue processingTasks; + private BackgroundQueue copyTasks; private MainContract.UserActionsListener mainPresenter; private RecordsContract.UserActionsListener recordsPresenter; @@ -105,6 +106,13 @@ public BackgroundQueue provideProcessingTasksQueue() { return processingTasks; } + public BackgroundQueue provideCopyTasksQueue() { + if (copyTasks == null) { + copyTasks = new BackgroundQueue("CopyTasks"); + } + return copyTasks; + } + public ColorMap provideColorMap() { return ColorMap.getInstance(providePrefs()); } @@ -133,8 +141,8 @@ public MainContract.UserActionsListener provideMainPresenter() { public RecordsContract.UserActionsListener provideRecordsPresenter() { if (recordsPresenter == null) { recordsPresenter = new RecordsPresenter(provideLocalRepository(), provideFileRepository(), - provideLoadingTasksQueue(), provideRecordingTasksQueue(), provideAudioPlayer(), - provideAppRecorder(), providePrefs()); + provideLoadingTasksQueue(), provideRecordingTasksQueue(), provideCopyTasksQueue(), + provideAudioPlayer(), provideAppRecorder(), providePrefs()); } return recordsPresenter; } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 2fb6aa63..c9fb60ab 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -26,11 +26,10 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.support.v4.content.FileProvider; +import android.support.annotation.NonNull; import android.text.Editable; import android.text.TextWatcher; import android.util.TypedValue; @@ -72,7 +71,6 @@ public class MainActivity extends Activity implements MainContract.View, View.On // TODO: Ability to delete record by swipe left // TODO: Ability to scroll up from the bottom of the list // TODO: Ability to search by record name in list -// TODO: Add pagination for records list // TODO: Welcome screen // TODO: Guidelines // TODO: Check how work max recording duration @@ -228,22 +226,7 @@ public void onClick(View view) { startActivity(SettingsActivity.getStartIntent(getApplicationContext())); break; case R.id.btn_share: - String sharePath = presenter.getActiveRecordPath(); - if (sharePath != null) { - Uri photoURI = FileProvider.getUriForFile( - getApplicationContext(), - getApplicationContext().getApplicationContext().getPackageName() + ".app_file_provider", - new File(sharePath) - ); - Intent share = new Intent(Intent.ACTION_SEND); - share.setType("audio/*"); - share.putExtra(Intent.EXTRA_STREAM, photoURI); - share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - startActivity(Intent.createChooser(share, getResources().getString(R.string.share_record, presenter.getActiveRecordName()))); - } else { - Timber.e("There no active record selected!"); - Toast.makeText(getApplicationContext(), R.string.please_select_record_to_share, Toast.LENGTH_LONG).show(); - } + AndroidUtils.shareAudioFile(getApplicationContext(), presenter.getActiveRecordPath(), presenter.getActiveRecordName()); break; case R.id.btn_import: if (checkStoragePermission()) { @@ -604,7 +587,7 @@ && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageMan } @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQ_CODE_REC_AUDIO_AND_WRITE_EXTERNAL && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED @@ -621,6 +604,10 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { startFileSelector(); + } else if (requestCode == REQ_CODE_WRITE_EXTERNAL_STORAGE + && grantResults[0] == PackageManager.PERMISSION_DENIED) { + Timber.v("WRITE_EXTERNAL_STORAGE DENIED"); + //TODO: show message that app cant write into private directory and record will be written in private } } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java index 75110674..e6e3c251 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainPresenter.java @@ -151,7 +151,9 @@ public void onRecordingStopped(long id, File file) { view.hideProgress(); view.showRecordingStop(); loadActiveRecord(); - view.askRecordingNewName(id, file); + if (prefs.isAskToRenameAfterStopRecording()) { + view.askRecordingNewName(id, file); + } } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java index b63e288b..3b0e056c 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java @@ -27,6 +27,7 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.support.annotation.NonNull; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Editable; @@ -182,7 +183,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override - public void onScrolled(RecyclerView rv, int dx, int dy) { + public void onScrolled(@NonNull RecyclerView rv, int dx, int dy) { super.onScrolled(rv, dx, dy); handleToolbarScroll(dy); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -230,6 +231,48 @@ public void onItemClick(View view, long id, String path, final int position) { presenter.removeFromBookmarks(id); } }); + adapter.setOnItemOptionListener(new RecordsAdapter.OnItemOptionListener() { + @Override + public void onItemOptionSelected(int menuId, final ListItem item) { + switch (menuId) { + case R.id.menu_share: + AndroidUtils.shareAudioFile(getApplicationContext(), item.getPath(), item.getName()); + break; +// case R.id.menu_info: +// break; + case R.id.menu_rename: + setRecordName(item.getId(), new File(item.getPath())); + break; + case R.id.menu_open_with: + AndroidUtils.openAudioFile(getApplicationContext(), item.getPath(), item.getName()); + break; +// case R.id.menu_download: +// presenter.copyToDownloads(item.getPath(), item.getName()); +// break; + case R.id.menu_delete: + AlertDialog.Builder builder = new AlertDialog.Builder(RecordsActivity.this); + builder.setTitle(R.string.warning) + .setIcon(R.drawable.ic_delete_forever) + .setMessage(R.string.delete_record) + .setCancelable(false) + .setPositiveButton(R.string.btn_yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + presenter.deleteRecord(item.getId(), item.getPath()); + dialog.dismiss(); + } + }) + .setNegativeButton(R.string.btn_no, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + break; + } + } + }); recyclerView.setAdapter(adapter); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -585,10 +628,15 @@ public void showRecordName(String name) { @Override public void onDeleteRecord(long id) { - adapter.deleteItem(id); +// adapter.deleteItem(id); + presenter.loadRecords(); if (adapter.getAudioRecordsCount() == 0) { showEmptyList(); } + } + + @Override + public void hidePlayPanel() { hidePanel(); } @@ -683,6 +731,7 @@ public void onClick(DialogInterface dialog, int id) { String newName = editText.getText().toString(); if (!fileName.equalsIgnoreCase(newName)) { presenter.renameRecord(recordId, newName); + presenter.loadRecords(); } hideKeyboard(); dialog.dismiss(); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index 905eba52..cda35195 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -16,33 +16,21 @@ package com.dimowner.audiorecorder.app.records; -import android.app.Activity; -import android.content.Context; -import android.graphics.Color; import android.graphics.Typeface; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.NonNull; -import android.support.v4.widget.PopupMenuCompat; import android.support.v7.widget.RecyclerView; -import android.text.SpannableStringBuilder; -import android.text.style.ImageSpan; import android.util.TypedValue; -import android.view.ContextMenu; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.PopupMenu; -import android.widget.PopupWindow; import android.widget.TextView; import com.dimowner.audiorecorder.R; @@ -54,8 +42,6 @@ import java.util.Calendar; import java.util.List; -import timber.log.Timber; - public class RecordsAdapter extends RecyclerView.Adapter { private List data; @@ -65,6 +51,7 @@ public class RecordsAdapter extends RecyclerView.Adapter(); @@ -118,9 +105,10 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, } @Override - public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int p) { + public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int pos) { if (viewHolder.getItemViewType() == ListItem.ITEM_TYPE_NORMAL) { final ItemViewHolder holder = (ItemViewHolder) viewHolder; + final int p = holder.getAdapterPosition(); holder.name.setText(data.get(p).getName()); holder.description.setText(data.get(p).getDurationStr()); holder.created.setText(data.get(p).getAddedTimeStr()); @@ -150,8 +138,7 @@ public void onClick(View v) { holder.btnMore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Timber.v("ONCLICK btnMore"); - showMenu(v); + showMenu(v, p); } }); holder.waveformView.setWaveform(data.get(p).getAmps()); @@ -165,19 +152,21 @@ public void onClick(View v) { }}); } else if (viewHolder.getItemViewType() == ListItem.ITEM_TYPE_DATE) { UniversalViewHolder holder = (UniversalViewHolder) viewHolder; - ((TextView)holder.view).setText(TimeUtils.formatDateSmart(data.get(p).getAdded(), holder.view.getContext())); + ((TextView)holder.view).setText(TimeUtils.formatDateSmart(data.get(viewHolder.getAdapterPosition()).getAdded(), holder.view.getContext())); } } - public void showMenu(View v) { + public void showMenu(View v, final int pos) { PopupMenu popup = new PopupMenu(v.getContext(), v); popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - Timber.v("onMenuItemClick"); - return false; + if (onItemOptionListener != null) { + onItemOptionListener.onItemOptionSelected(item.getItemId(), data.get(pos)); + } + return false; } - });// to implement on click event on items of menu + }); MenuInflater inflater = popup.getMenuInflater(); inflater.inflate(R.menu.menu_more, popup.getMenu()); AndroidUtils.insertMenuItemIcons(v.getContext(), popup); @@ -234,6 +223,10 @@ public void addData(List d) { notifyItemRangeInserted(data.size() - d.size() - 1, d.size()); } + public ListItem getItem(int pos) { + return data.get(pos); + } + private List addDateHeaders(List data) { if (data.size() > 0) { if (!hasDateHeader(data.get(0).getAdded())) { @@ -376,18 +369,23 @@ public interface ItemClickListener{ void onItemClick(View view, long id, String path, int position); } + public void setOnItemOptionListener(OnItemOptionListener onItemOptionListener) { + this.onItemOptionListener = onItemOptionListener; + } public interface OnAddToBookmarkListener { void onAddToBookmarks(int id); void onRemoveFromBookmarks(int id); } + public interface OnItemOptionListener { + void onItemOptionSelected(int menuId, ListItem item); + } + public class ItemViewHolder extends RecyclerView.ViewHolder { -// implements View.OnCreateContextMenuListener { TextView name; TextView description; TextView created; - // ImageView image; ImageButton btnBookmark; ImageButton btnMore; SimpleWaveformView waveformView; @@ -399,19 +397,10 @@ public ItemViewHolder(View itemView) { name = itemView.findViewById(R.id.list_item_name); description = itemView.findViewById(R.id.list_item_description); created = itemView.findViewById(R.id.list_item_date); -// this.image = itemView.findViewById(R.id.list_item_image); btnBookmark = itemView.findViewById(R.id.list_item_bookmark); waveformView = itemView.findViewById(R.id.list_item_waveform); btnMore = itemView.findViewById(R.id.list_item_more); -// btnMore.setOnCreateContextMenuListener(this); } - -// @Override -// public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { -// Timber.v("onCreateContextMenu"); -// menu.add(Menu.NONE, 22, Menu.NONE, R.string.delete_record); -// menu.add(Menu.NONE, 33, Menu.NONE, R.string.app_name); -// } } public class UniversalViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java index 0b0df2bf..7457be09 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java @@ -52,6 +52,8 @@ interface View extends Contract.View { void onDeleteRecord(long id); + void hidePlayPanel(); + void addedToBookmarks(int id, boolean isActive); void removedFromBookmarks(int id, boolean isActive); @@ -75,8 +77,12 @@ interface UserActionsListener extends Contract.UserActionsListener + + diff --git a/app/src/main/res/drawable/ic_pencil.xml b/app/src/main/res/drawable/ic_pencil.xml index 73ed8d58..881a2bde 100644 --- a/app/src/main/res/drawable/ic_pencil.xml +++ b/app/src/main/res/drawable/ic_pencil.xml @@ -1,9 +1,9 @@ diff --git a/app/src/main/res/drawable/ic_pencil_small.xml b/app/src/main/res/drawable/ic_pencil_small.xml new file mode 100644 index 00000000..73ed8d58 --- /dev/null +++ b/app/src/main/res/drawable/ic_pencil_small.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_save_alt.xml b/app/src/main/res/drawable/ic_save_alt.xml new file mode 100644 index 00000000..5ae0dcb4 --- /dev/null +++ b/app/src/main/res/drawable/ic_save_alt.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 0d4ba76d..6a72f756 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -210,7 +210,7 @@ android:textColor="@color/text_primary_light" android:maxLines="1" android:textSize="@dimen/text_large" - android:drawableEnd="@drawable/ic_pencil" + android:drawableEnd="@drawable/ic_pencil_small" android:visibility="invisible" tools:text="@string/app_name" /> diff --git a/app/src/main/res/layout/activity_records.xml b/app/src/main/res/layout/activity_records.xml index 8343df22..e9bfb477 100644 --- a/app/src/main/res/layout/activity_records.xml +++ b/app/src/main/res/layout/activity_records.xml @@ -192,7 +192,7 @@ android:textColor="@color/text_primary_light" android:textSize="@dimen/text_xmedium" android:maxLines="1" - android:drawableEnd="@drawable/ic_pencil" + android:drawableEnd="@drawable/ic_pencil_small" tools:text="Record 2321"/> + + + + + + - + + + + + + + + + + + + android:id="@+id/menu_share" + android:icon="@drawable/ic_share" + android:title="@string/share" /> + + + + + + + + + + - \ No newline at end of file + android:title="@string/delete" /> + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 974f2e13e63660f3af5c0eb7e342f6f5fb4a97ef..3a7d48a7119f8220cda8e4ce98c2fe40696566a3 100644 GIT binary patch delta 342 zcmccN{LfОбратная связь Не найдено ни одного почтового клиента Послать по электронной почте… + Открыть %s с помощью + Удалить Информация + Переименовать + Открыть с помощью̷ + Скачать + Поделиться + Показывать диалог переименования после завершения записи Настройки приложения: Настройки записи: diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a6c7619f..c15fd6db 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -32,7 +32,14 @@ Зворотній зв\'язок Не знайдено жодного поштового клієнта Надіслати по електронній пошті… + Відкрити %s з допомогою + Видалити Інформація + Перейменувати + Відкрити з допомогою̷ + Скачати + Поділитися + Показувати діалог перейменування після завершення звукозапису Налаштування додатка: Налаштування запису: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 61bfe6d5..6896ecc9 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,14 @@ Request or feedback Not found any Email client Send by email… + Open %s with + Delete Information + Rename + Open with̷ + Download + Share + Show renaming dialog after stop recording App settings: Recording settings: From 31dddaf92b85d252d173294762396b761affc2e5 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Sun, 4 Aug 2019 20:10:05 +0300 Subject: [PATCH 13/23] UI improvements; Show layout under navigation bar and status bar --- .../audiorecorder/app/main/MainActivity.java | 11 ++ .../audiorecorder/app/records/ListItem.java | 5 + .../app/records/RecordsActivity.java | 22 +-- .../app/records/RecordsAdapter.java | 131 +++++++++++------- .../app/settings/SettingsActivity.java | 15 ++ .../audiorecorder/util/AndroidUtils.java | 34 +++++ app/src/main/res/layout/activity_main.xml | 9 +- app/src/main/res/layout/activity_records.xml | 11 +- app/src/main/res/layout/activity_settings.xml | 10 +- 9 files changed, 178 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index c9fb60ab..b94ec665 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -113,6 +113,17 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + LinearLayout toolbar = findViewById(R.id.toolbar); + toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); + + View space = findViewById(R.id.space); + ViewGroup.LayoutParams params = space.getLayoutParams(); + params.height = AndroidUtils.getNavigationBarHeight(getApplicationContext()); + space.setLayoutParams(params); + waveformView = findViewById(R.id.record); txtProgress = findViewById(R.id.txt_progress); txtDuration = findViewById(R.id.txt_duration); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java index 15c4ce3f..4570d794 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java @@ -28,6 +28,7 @@ public class ListItem implements Parcelable { public final static int ITEM_TYPE_HEADER = 2; public final static int ITEM_TYPE_DATE = 3; public final static int ITEM_TYPE_FOOTER = 4; + public final static int ITEM_TYPE_FOOTER_SMALL = 5; private final long id; private final int type; @@ -71,6 +72,10 @@ public static ListItem createFooterItem() { return new ListItem(-1, ListItem.ITEM_TYPE_FOOTER, "FOOTER", "", 0, 0, 0, "", false, null); } + public static ListItem createFooterSmallItem() { + return new ListItem(-1, ListItem.ITEM_TYPE_FOOTER_SMALL, "FOOTER_SMALL", "", 0, 0, 0, "", false, null); + } + public static ListItem createDateItem(long date) { return new ListItem(-1, ListItem.ITEM_TYPE_DATE, "DATE", "", 0, 0, date, "", false, null); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java index 3b0e056c..d70a0739 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java @@ -36,6 +36,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageButton; @@ -109,7 +110,12 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_records); - AndroidUtils.setTranslucent(this, true); + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + toolbar = findViewById(R.id.toolbar); + toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); +// AndroidUtils.setTranslucent(this, true); ImageButton btnBack = findViewById(R.id.btn_back); btnBack.setOnClickListener(new View.OnClickListener() { @@ -117,7 +123,6 @@ protected void onCreate(Bundle savedInstanceState) { finish(); ARApplication.getInjector().releaseRecordsPresenter(); }}); - toolbar = findViewById(R.id.toolbar); bottomDivider = findViewById(R.id.bottomDivider); progressBar = findViewById(R.id.progress); @@ -275,10 +280,10 @@ public void onClick(DialogInterface dialog, int id) { }); recyclerView.setAdapter(adapter); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - // Set the padding to match the Status Bar height - toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); - } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { +// // Set the padding to match the Status Bar height +// toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); +// } presenter = ARApplication.getInjector().provideRecordsPresenter(); waveformView.setOnSeekListener(new WaveformView.OnSeekListener() { @@ -307,7 +312,7 @@ public void showPlayerPanel() { } adapter.showFooter(); final ViewPropertyAnimator animator = touchLayout.animate(); - animator.translationY(0) + animator.translationY(-AndroidUtils.getNavigationBarHeight(getApplicationContext())) .setDuration(200) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @@ -594,9 +599,6 @@ public void showRecords(List records) { public void addRecords(List records) { adapter.addData(records); txtEmpty.setVisibility(View.GONE); - if (touchLayout.getVisibility() == View.VISIBLE) { - adapter.showFooter(); - } } @Override diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index cda35195..9aed79c0 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -53,7 +53,7 @@ public class RecordsAdapter extends RecyclerView.Adapter(); } @@ -78,6 +78,14 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, View view = new View(viewGroup.getContext()); int height = (int) viewGroup.getContext().getResources().getDimension(R.dimen.panel_height); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, height); + view.setLayoutParams(lp); + return new UniversalViewHolder(view); + } else if (type == ListItem.ITEM_TYPE_FOOTER_SMALL) { + View view = new View(viewGroup.getContext()); + int height = AndroidUtils.getNavigationBarHeight(viewGroup.getContext()); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, height); view.setLayoutParams(lp); @@ -156,7 +164,7 @@ public void onClick(View v) { } } - public void showMenu(View v, final int pos) { + private void showMenu(View v, final int pos) { PopupMenu popup = new PopupMenu(v.getContext(), v); popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override @@ -173,22 +181,21 @@ public boolean onMenuItemClick(MenuItem item) { popup.show(); } - - public void setActiveItem(int activeItem) { + void setActiveItem(int activeItem) { int prev = this.activeItem; this.activeItem = activeItem; notifyItemChanged(prev); notifyItemChanged(activeItem); } - public void setActiveItemById(long id) { - int pos = findPositionById(id); - if (pos >= 0) { - setActiveItem(pos); - } - } +// public void setActiveItemById(long id) { +// int pos = findPositionById(id); +// if (pos >= 0) { +// setActiveItem(pos); +// } +// } - public int findPositionById(long id) { + int findPositionById(long id) { if (id >= 0) { for (int i = 0; i < data.size() - 1; i++) { if (data.get(i).getId() == id) { @@ -209,18 +216,26 @@ public int getItemViewType(int position) { return data.get(position).getType(); } - public void setData(List data) { - this.data = data; + void setData(List d) { + data = d; if (showDateHeaders) { - this.data = addDateHeaders(data); + data = addDateHeaders(d); } - this.data.add(0, ListItem.createHeaderItem()); + data.add(0, ListItem.createHeaderItem()); + data.add(ListItem.createFooterSmallItem()); notifyDataSetChanged(); } - public void addData(List d) { - this.data.addAll(addDateHeaders(d)); - notifyItemRangeInserted(data.size() - d.size() - 1, d.size()); +// public void addData(List d) { +// this.data.addAll(addDateHeaders(d)); +// notifyItemRangeInserted(data.size() - d.size(), d.size()); +// } + + void addData(List d) { + if (data.size() > 0 && (findFooter() >= 0 || findFooterSmall() >= 0)) { + data.addAll(data.size() - 1, addDateHeaders(d)); + notifyItemRangeInserted(data.size() - d.size(), d.size()); + } } public ListItem getItem(int pos) { @@ -246,21 +261,21 @@ private List addDateHeaders(List data) { return data; } - public void deleteItem(long id) { - for (int i = 0; i < data.size(); i++) { - if (data.get(i).getId() == id) { - data.remove(i); - if (getAudioRecordsCount() == 0) { - data.clear(); - notifyDataSetChanged(); - } else { - notifyItemRemoved(i); - } - } - } - } - - public int getAudioRecordsCount() { +// public void deleteItem(long id) { +// for (int i = 0; i < data.size(); i++) { +// if (data.get(i).getId() == id) { +// data.remove(i); +// if (getAudioRecordsCount() == 0) { +// data.clear(); +// notifyDataSetChanged(); +// } else { +// notifyItemRemoved(i); +// } +// } +// } +// } + + int getAudioRecordsCount() { int count = 0; for (int i = 0; i < data.size(); i++) { if (data.get(i).getType() == ListItem.ITEM_TYPE_NORMAL) { @@ -270,22 +285,29 @@ public int getAudioRecordsCount() { return count; } - public void showFooter() { - if (findFooter() == -1) { - this.data.add(ListItem.createFooterItem()); + void showFooter() { + if (data.size() > 0 && findFooterSmall() >= 0) { + data.remove(data.size() - 1); + data.add(ListItem.createFooterItem()); + notifyItemChanged(data.size()-1); + } else { + data.add(ListItem.createFooterItem()); notifyItemInserted(data.size()-1); } } - public void hideFooter() { - int pos = findFooter(); - if (pos != -1) { - this.data.remove(pos); - notifyItemRemoved(pos); + void hideFooter() { + if (data.size() > 0 && findFooter() >= 0) { + data.remove(data.size() - 1); + data.add(ListItem.createFooterSmallItem()); + notifyItemChanged(data.size() - 1); + } else { + data.add(ListItem.createFooterSmallItem()); + notifyItemInserted(data.size() - 1); } } - public long getNextTo(long id) { + long getNextTo(long id) { if (id >= 0) { for (int i = 0; i < data.size() - 1; i++) { if (data.get(i).getId() == id) { @@ -300,7 +322,7 @@ public long getNextTo(long id) { return -1; } - public long getPrevTo(long id) { + long getPrevTo(long id) { if (id >= 0) { for (int i = 1; i < data.size(); i++) { if (data.get(i).getId() == id) { @@ -324,7 +346,16 @@ private int findFooter() { return -1; } - public void markAddedToBookmarks(int id) { + private int findFooterSmall() { + for (int i = data.size()-1; i>= 0; i--) { + if (data.get(i).getType() == ListItem.ITEM_TYPE_FOOTER_SMALL) { + return i; + } + } + return -1; + } + + void markAddedToBookmarks(int id) { for (int i = 0; i < data.size(); i++) { if (data.get(i).getId() == id) { data.get(i).setBookmarked(true); @@ -333,7 +364,7 @@ public void markAddedToBookmarks(int id) { } } - public void markRemovedFromBookmarks(int id) { + void markRemovedFromBookmarks(int id) { for (int i = 0; i < data.size(); i++) { if (data.get(i).getId() == id) { data.get(i).setBookmarked(false); @@ -357,11 +388,11 @@ private boolean hasDateHeader(long time) { return false; } - public void setItemClickListener(ItemClickListener itemClickListener) { + void setItemClickListener(ItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } - public void setOnAddToBookmarkListener(OnAddToBookmarkListener onAddToBookmarkListener) { + void setOnAddToBookmarkListener(OnAddToBookmarkListener onAddToBookmarkListener) { this.onAddToBookmarkListener = onAddToBookmarkListener; } @@ -369,7 +400,7 @@ public interface ItemClickListener{ void onItemClick(View view, long id, String path, int position); } - public void setOnItemOptionListener(OnItemOptionListener onItemOptionListener) { + void setOnItemOptionListener(OnItemOptionListener onItemOptionListener) { this.onItemOptionListener = onItemOptionListener; } @@ -391,7 +422,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder { SimpleWaveformView waveformView; View view; - public ItemViewHolder(View itemView) { + ItemViewHolder(View itemView) { super(itemView); view = itemView; name = itemView.findViewById(R.id.list_item_name); @@ -406,7 +437,7 @@ public ItemViewHolder(View itemView) { public class UniversalViewHolder extends RecyclerView.ViewHolder { View view; - public UniversalViewHolder(View view) { + UniversalViewHolder(View view) { super(view); this.view = view; } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java index 0b37ead5..8a61062f 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java @@ -30,9 +30,12 @@ import android.text.Html; import android.text.SpannableStringBuilder; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.AdapterView; import android.widget.CompoundButton; import android.widget.ImageButton; +import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.Switch; import android.widget.TextView; @@ -42,6 +45,7 @@ import com.dimowner.audiorecorder.AppConstants; import com.dimowner.audiorecorder.ColorMap; import com.dimowner.audiorecorder.R; +import com.dimowner.audiorecorder.util.AndroidUtils; import java.util.ArrayList; import java.util.List; @@ -81,6 +85,17 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + LinearLayout toolbar = findViewById(R.id.toolbar); + toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); + + View space = findViewById(R.id.space); + ViewGroup.LayoutParams params = space.getLayoutParams(); + params.height = AndroidUtils.getNavigationBarHeight(getApplicationContext()); + space.setLayoutParams(params); + ImageButton btnBack = findViewById(R.id.btn_back); // TextView btnDeleteAll = findViewById(R.id.btnDeleteAll); TextView btnRate = findViewById(R.id.btnRate); diff --git a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java index 1c79019f..c60bcbb4 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java +++ b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java @@ -32,6 +32,8 @@ import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import android.view.Display; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.Window; @@ -120,6 +122,38 @@ public static int getStatusBarHeight(Context context) { return result; } + // A method to find height of the navigation bar + public static int getNavigationBarHeight(Context context) { + int result = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + if (hasNavBar(context)) { + int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = context.getResources().getDimensionPixelSize(resourceId); + } + } + } catch (Resources.NotFoundException e) { + Timber.e(e); + return 0; + } + } + return result; + } + + //This method works not correctly + public static boolean hasNavBar (Context context) { +// int id = context.getResources().getIdentifier("config_showNavigationBar", "bool", "android"); +// return id > 0 && context.getResources().getBoolean(id); +// boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey(); +// boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); +// return !hasMenuKey && !hasBackKey; + + boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); + boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME); + return !hasHomeKey && !hasBackKey; + } + public static void setTranslucent(Activity activity, boolean translucent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window w = activity.getWindow(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6a72f756..195d94ec 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,7 +35,7 @@ android:layout_height="wrap_content" android:contentDescription="@null" android:layout_gravity="start" - android:background="?android:selectableItemBackground" + android:background="?android:selectableItemBackgroundBorderless" android:padding="@dimen/spacing_normal" android:src="@drawable/ic_import"/> @@ -313,4 +313,11 @@ + + diff --git a/app/src/main/res/layout/activity_records.xml b/app/src/main/res/layout/activity_records.xml index e9bfb477..02ee958c 100644 --- a/app/src/main/res/layout/activity_records.xml +++ b/app/src/main/res/layout/activity_records.xml @@ -38,7 +38,7 @@ android:layout_height="wrap_content" android:contentDescription="@null" android:layout_gravity="start" - android:background="?android:selectableItemBackground" + android:background="?android:selectableItemBackgroundBorderless" android:padding="@dimen/spacing_normal" android:src="@drawable/ic_arrow_back"/> @@ -59,13 +59,12 @@ diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 2198e2d4..fc7541c9 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -20,8 +20,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> @@ -38,7 +36,7 @@ android:layout_height="wrap_content" android:contentDescription="@null" android:layout_gravity="start" - android:background="?android:selectableItemBackground" + android:background="?android:selectableItemBackgroundBorderless" android:padding="@dimen/spacing_normal" android:src="@drawable/ic_arrow_back"/> @@ -309,5 +307,11 @@ android:drawablePadding="@dimen/spacing_double" /> + \ No newline at end of file From bff6d8601ea36d8cf3799245fdee0467b479b6be Mon Sep 17 00:00:00 2001 From: Dimowner Date: Sun, 4 Aug 2019 21:26:47 +0300 Subject: [PATCH 14/23] Remove NO_LIMITS from RecordsActivity; Fix crash or create chooser --- app/build.gradle | 2 +- .../audiorecorder/app/main/MainActivity.java | 11 ---- .../audiorecorder/app/records/ListItem.java | 5 -- .../app/records/RecordsActivity.java | 15 ++--- .../app/records/RecordsAdapter.java | 57 ++++++------------- .../app/settings/SettingsActivity.java | 4 +- .../audiorecorder/util/AndroidUtils.java | 10 +++- app/src/main/res/layout/activity_main.xml | 7 --- 8 files changed, 35 insertions(+), 76 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6b6b66f6..063dc268 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 28 +def versionPatch = 29 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index b94ec665..c9fb60ab 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -113,17 +113,6 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - getWindow().setFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - LinearLayout toolbar = findViewById(R.id.toolbar); - toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); - - View space = findViewById(R.id.space); - ViewGroup.LayoutParams params = space.getLayoutParams(); - params.height = AndroidUtils.getNavigationBarHeight(getApplicationContext()); - space.setLayoutParams(params); - waveformView = findViewById(R.id.record); txtProgress = findViewById(R.id.txt_progress); txtDuration = findViewById(R.id.txt_duration); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java index 4570d794..15c4ce3f 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/ListItem.java @@ -28,7 +28,6 @@ public class ListItem implements Parcelable { public final static int ITEM_TYPE_HEADER = 2; public final static int ITEM_TYPE_DATE = 3; public final static int ITEM_TYPE_FOOTER = 4; - public final static int ITEM_TYPE_FOOTER_SMALL = 5; private final long id; private final int type; @@ -72,10 +71,6 @@ public static ListItem createFooterItem() { return new ListItem(-1, ListItem.ITEM_TYPE_FOOTER, "FOOTER", "", 0, 0, 0, "", false, null); } - public static ListItem createFooterSmallItem() { - return new ListItem(-1, ListItem.ITEM_TYPE_FOOTER_SMALL, "FOOTER_SMALL", "", 0, 0, 0, "", false, null); - } - public static ListItem createDateItem(long date) { return new ListItem(-1, ListItem.ITEM_TYPE_DATE, "DATE", "", 0, 0, date, "", false, null); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java index d70a0739..3da7aac8 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java @@ -36,7 +36,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageButton; @@ -110,11 +109,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_records); - getWindow().setFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); toolbar = findViewById(R.id.toolbar); - toolbar.setPadding(0, AndroidUtils.getStatusBarHeight(getApplicationContext()), 0, 0); // AndroidUtils.setTranslucent(this, true); ImageButton btnBack = findViewById(R.id.btn_back); @@ -312,7 +307,7 @@ public void showPlayerPanel() { } adapter.showFooter(); final ViewPropertyAnimator animator = touchLayout.animate(); - animator.translationY(-AndroidUtils.getNavigationBarHeight(getApplicationContext())) + animator.translationY(0) .setDuration(200) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @@ -500,11 +495,11 @@ public void onBackPressed() { private void handleToolbarScroll(int dy) { float inset = toolbar.getTranslationY() - dy; int height; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - height = toolbar.getHeight() + AndroidUtils.getStatusBarHeight(getApplicationContext()); - } else { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { +// height = toolbar.getHeight() + AndroidUtils.getStatusBarHeight(getApplicationContext()); +// } else { height = toolbar.getHeight(); - } +// } if (inset < -height) { inset = -height; diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index 9aed79c0..1b627df5 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -64,11 +64,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, View view = new View(viewGroup.getContext()); int height; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - height = AndroidUtils.getStatusBarHeight(viewGroup.getContext()) + (int) viewGroup.getContext().getResources().getDimension(R.dimen.toolbar_height); - } else { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { +// height = AndroidUtils.getStatusBarHeight(viewGroup.getContext()) + (int) viewGroup.getContext().getResources().getDimension(R.dimen.toolbar_height); +// } else { height = (int) viewGroup.getContext().getResources().getDimension(R.dimen.toolbar_height); - } +// } LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, height); @@ -78,14 +78,6 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, View view = new View(viewGroup.getContext()); int height = (int) viewGroup.getContext().getResources().getDimension(R.dimen.panel_height); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, height); - view.setLayoutParams(lp); - return new UniversalViewHolder(view); - } else if (type == ListItem.ITEM_TYPE_FOOTER_SMALL) { - View view = new View(viewGroup.getContext()); - int height = AndroidUtils.getNavigationBarHeight(viewGroup.getContext()); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, height); view.setLayoutParams(lp); @@ -222,7 +214,6 @@ void setData(List d) { data = addDateHeaders(d); } data.add(0, ListItem.createHeaderItem()); - data.add(ListItem.createFooterSmallItem()); notifyDataSetChanged(); } @@ -232,8 +223,12 @@ void setData(List d) { // } void addData(List d) { - if (data.size() > 0 && (findFooter() >= 0 || findFooterSmall() >= 0)) { - data.addAll(data.size() - 1, addDateHeaders(d)); + if (data.size() > 0) { + if (findFooter() >= 0) { + data.addAll(data.size() - 1, addDateHeaders(d)); + } else { + data.addAll(addDateHeaders(d)); + } notifyItemRangeInserted(data.size() - d.size(), d.size()); } } @@ -285,25 +280,18 @@ int getAudioRecordsCount() { return count; } - void showFooter() { - if (data.size() > 0 && findFooterSmall() >= 0) { - data.remove(data.size() - 1); - data.add(ListItem.createFooterItem()); - notifyItemChanged(data.size()-1); - } else { - data.add(ListItem.createFooterItem()); + public void showFooter() { + if (findFooter() == -1) { + this.data.add(ListItem.createFooterItem()); notifyItemInserted(data.size()-1); } } - void hideFooter() { - if (data.size() > 0 && findFooter() >= 0) { - data.remove(data.size() - 1); - data.add(ListItem.createFooterSmallItem()); - notifyItemChanged(data.size() - 1); - } else { - data.add(ListItem.createFooterSmallItem()); - notifyItemInserted(data.size() - 1); + public void hideFooter() { + int pos = findFooter(); + if (pos != -1) { + this.data.remove(pos); + notifyItemRemoved(pos); } } @@ -346,15 +334,6 @@ private int findFooter() { return -1; } - private int findFooterSmall() { - for (int i = data.size()-1; i>= 0; i--) { - if (data.get(i).getType() == ListItem.ITEM_TYPE_FOOTER_SMALL) { - return i; - } - } - return -1; - } - void markAddedToBookmarks(int id) { for (int i = 0; i < data.size(); i++) { if (data.get(i).getId() == id) { diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java index 8a61062f..504ec6eb 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/settings/SettingsActivity.java @@ -336,7 +336,9 @@ private void requestFeature() { "[" + getResources().getString(R.string.app_name) + "] - " + getResources().getString(R.string.request) ); try { - startActivity(Intent.createChooser(i, getResources().getString(R.string.send_email))); + Intent chooser = Intent.createChooser(i, getResources().getString(R.string.send_email)); + chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(chooser); } catch (android.content.ActivityNotFoundException ex) { showError(R.string.email_clients_not_found); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java index c60bcbb4..277690b3 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java +++ b/app/src/main/java/com/dimowner/audiorecorder/util/AndroidUtils.java @@ -318,7 +318,10 @@ public static void shareAudioFile(Context context, String sharePath, String name share.setType("audio/*"); share.putExtra(Intent.EXTRA_STREAM, fileUri); share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - context.startActivity(Intent.createChooser(share, context.getResources().getString(R.string.share_record, name))); + + Intent chooser = Intent.createChooser(share, context.getResources().getString(R.string.share_record, name)); + chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(chooser); } else { Timber.e("There no record selected!"); Toast.makeText(context, R.string.please_select_record_to_share, Toast.LENGTH_LONG).show(); @@ -335,7 +338,10 @@ public static void openAudioFile(Context context, String sharePath, String name) Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(fileUri, "audio/*"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.open_record_with, name))); + + Intent chooser = Intent.createChooser(intent, context.getResources().getString(R.string.open_record_with, name)); + chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(chooser); } else { Timber.e("There no record selected!"); Toast.makeText(context, R.string.error_unknown, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 195d94ec..27c62d2b 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -313,11 +313,4 @@ - - From 42aacc7ebfa29ebf7273eec34f33c5a2ef0323f7 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Sun, 4 Aug 2019 23:55:02 +0300 Subject: [PATCH 15/23] Replace drawPath by drawLines in SimpleWaveformView. --- app/build.gradle | 2 +- .../app/widget/SimpleWaveformView.java | 44 +++++++++++++------ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 063dc268..5e57bd30 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ ext.versions = [ def versionMajor = 0 def versionMinor = 5 -def versionPatch = 29 +def versionPatch = 30 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/widget/SimpleWaveformView.java b/app/src/main/java/com/dimowner/audiorecorder/app/widget/SimpleWaveformView.java index b0f1a009..b17631fe 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/widget/SimpleWaveformView.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/widget/SimpleWaveformView.java @@ -19,7 +19,6 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Path; import android.util.AttributeSet; import android.view.View; @@ -67,7 +66,9 @@ private void init(Context context) { setFocusable(false); waveformPaint = new Paint(); - waveformPaint.setStyle(Paint.Style.FILL); + waveformPaint.setStyle(Paint.Style.STROKE); + waveformPaint.setStrokeWidth(AndroidUtils.dpToPx(1)); + waveformPaint.setStrokeJoin(Paint.Join.ROUND); waveformPaint.setAntiAlias(true); waveformPaint.setColor(context.getResources().getColor(waveformColorRes)); @@ -147,19 +148,36 @@ private void drawWaveForm(Canvas canvas) { width = getMeasuredWidth(); } - Path path = new Path(); - path.moveTo(0, half); - path.lineTo(0, half); +// Path path = new Path(); +// path.moveTo(0, half); +// path.lineTo(0, half); +// float dpi = AndroidUtils.dpToPx(1); +// for (int i = 1; i < width; i++) { +// path.lineTo(i * dpi, half - waveformData[i]); +// } +// for (int i = width - 1; i >= 0; i--) { +// path.lineTo(i * dpi, half + 1 + waveformData[i]); +// } +// path.lineTo(0, half); +// path.close(); +// canvas.drawPath(path, waveformPaint); + float dpi = AndroidUtils.dpToPx(1); - for (int i = 1; i < width; i++) { - path.lineTo(i * dpi, half - waveformData[i]); - } - for (int i = width - 1; i >= 0; i--) { - path.lineTo(i * dpi, half + 1 + waveformData[i]); + float[] lines = new float[width*4+4]; + int step = 0; + for (int i = 0; i < width; i++) { + lines[step] = i*dpi; + lines[step+1] = half + waveformData[i]; + lines[step+2] = i*dpi; + lines[step+3] = half - waveformData[i]; + step +=4; } - path.lineTo(0, half); - path.close(); - canvas.drawPath(path, waveformPaint); + //Horizontal zero line + lines[step] = 0; + lines[step+1] = half; + lines[step+2] = width*dpi; + lines[step+3] = half; + canvas.drawLines(lines, 0, lines.length, waveformPaint); } /** From cb799ff2aa653a0ba8686047ccc6369b7392eeaf Mon Sep 17 00:00:00 2001 From: Dimowner Date: Tue, 6 Aug 2019 23:17:01 +0300 Subject: [PATCH 16/23] Context menu for active recording on mains screen --- .../com/dimowner/audiorecorder/Contract.java | 2 + .../audiorecorder/app/main/MainActivity.java | 62 +++++++++++++++++- .../audiorecorder/app/main/MainContract.java | 2 + .../audiorecorder/app/main/MainPresenter.java | 30 +++++++++ .../app/records/RecordsActivity.java | 5 ++ .../app/records/RecordsPresenter.java | 1 + .../app/settings/SettingsActivity.java | 5 ++ app/src/main/res/drawable/ic_more_vert.xml | 14 ++-- .../res/drawable/ic_more_vert_transparent.xml | 9 +++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/list_item.xml | 2 +- app/src/main/res/values-bg/strings.xml | Bin 9342 -> 9504 bytes app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 15 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/drawable/ic_more_vert_transparent.xml diff --git a/app/src/main/java/com/dimowner/audiorecorder/Contract.java b/app/src/main/java/com/dimowner/audiorecorder/Contract.java index 3bf56ec5..60f962b0 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/Contract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/Contract.java @@ -26,6 +26,8 @@ interface View { void showError(String message); void showError(int resId); + + void showMessage(int resId); } interface UserActionsListener { diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index c9fb60ab..6fef1e84 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -33,6 +33,8 @@ import android.text.Editable; import android.text.TextWatcher; import android.util.TypedValue; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -40,6 +42,7 @@ import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; +import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; @@ -226,7 +229,8 @@ public void onClick(View view) { startActivity(SettingsActivity.getStartIntent(getApplicationContext())); break; case R.id.btn_share: - AndroidUtils.shareAudioFile(getApplicationContext(), presenter.getActiveRecordPath(), presenter.getActiveRecordName()); +// AndroidUtils.shareAudioFile(getApplicationContext(), presenter.getActiveRecordPath(), presenter.getActiveRecordName()); + showMenu(view); break; case R.id.btn_import: if (checkStoragePermission()) { @@ -289,6 +293,11 @@ public void showError(int resId) { Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show(); } + @Override + public void showMessage(int resId) { + Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show(); + } + @Override public void showRecordingStart() { btnRecord.setImageResource(R.drawable.ic_record_rec); @@ -471,6 +480,57 @@ public void hideRecordProcessing() { pnlRecordProcessing.setVisibility(View.INVISIBLE); } + private void showMenu(View v) { + PopupMenu popup = new PopupMenu(v.getContext(), v); + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_share: + AndroidUtils.shareAudioFile(getApplicationContext(), presenter.getActiveRecordPath(), presenter.getActiveRecordName()); + break; +// case R.id.menu_info: +// break; + case R.id.menu_rename: + setRecordName(presenter.getActiveRecordId(), new File(presenter.getActiveRecordPath())); + break; + case R.id.menu_open_with: + AndroidUtils.openAudioFile(getApplicationContext(), presenter.getActiveRecordPath(), presenter.getActiveRecordName()); + break; +// case R.id.menu_download: +// presenter.copyToDownloads(item.getPath(), item.getName()); +// break; + case R.id.menu_delete: + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setTitle(R.string.warning) + .setIcon(R.drawable.ic_delete_forever) + .setMessage(R.string.delete_record) + .setCancelable(false) + .setPositiveButton(R.string.btn_yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + presenter.deleteActiveRecord(); + dialog.dismiss(); + } + }) + .setNegativeButton(R.string.btn_no, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + break; + } + return false; + } + }); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.menu_more, popup.getMenu()); + AndroidUtils.insertMenuItemIcons(v.getContext(), popup); + popup.show(); + } + public void setRecordName(final long recordId, File file) { //Create dialog layout programmatically. LinearLayout container = new LinearLayout(getApplicationContext()); diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java index d4742669..e43351c9 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java @@ -90,5 +90,7 @@ interface UserActionsListener extends Contract.UserActionsListener - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_more_vert_transparent.xml b/app/src/main/res/drawable/ic_more_vert_transparent.xml new file mode 100644 index 00000000..5de1a38b --- /dev/null +++ b/app/src/main/res/drawable/ic_more_vert_transparent.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 27c62d2b..7d99746b 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -62,7 +62,7 @@ android:contentDescription="@null" android:layout_gravity="end" android:scaleType="center" - android:src="@drawable/ic_share"/> + android:src="@drawable/ic_more_vert"/> diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index e2cc06d5..864bf2e1 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -122,6 +122,6 @@ android:paddingEnd="@dimen/spacing_tiny" android:paddingStart="@dimen/spacing_zero" android:layout_gravity="center_vertical|end" - android:src="@drawable/ic_more_vert"/> + android:src="@drawable/ic_more_vert_transparent"/> diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 3a7d48a7119f8220cda8e4ce98c2fe40696566a3..e7b68676f4b377209fc1d5574cd8e11b165836dc 100644 GIT binary patch delta 125 zcmez8vA}D?J;BKWoH~gq45gPJSpYJ-IСкачать Поделиться Показывать диалог переименования после завершения записи + Запись удалена успешно Настройки приложения: Настройки записи: diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index c15fd6db..1d91f9c5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -40,6 +40,7 @@ Скачати Поділитися Показувати діалог перейменування після завершення звукозапису + Запис %s видалено успішно Налаштування додатка: Налаштування запису: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6896ecc9..441e8c37 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ Download Share Show renaming dialog after stop recording + Record deleted successfully App settings: Recording settings: From 6842845016aed644a7bffec6b7996ee7c779893d Mon Sep 17 00:00:00 2001 From: Dimowner Date: Wed, 7 Aug 2019 00:33:18 +0300 Subject: [PATCH 17/23] Ability to sort records by date, name and duration --- .../dimowner/audiorecorder/AppConstants.java | 4 ++ .../app/records/RecordsActivity.java | 46 ++++++++++++++++-- .../app/records/RecordsAdapter.java | 33 ++++++++++--- .../app/records/RecordsContract.java | 7 ++- .../app/records/RecordsPresenter.java | 18 +++++-- .../dimowner/audiorecorder/data/Prefs.java | 3 ++ .../audiorecorder/data/PrefsImpl.java | 13 +++++ .../data/database/DataSource.java | 12 +++++ .../data/database/LocalRepository.java | 2 + .../data/database/LocalRepositoryImpl.java | 28 +++++++++++ app/src/main/res/drawable/ic_access_time.xml | 9 ++++ .../main/res/drawable/ic_calendar_today.xml | 9 ++++ app/src/main/res/drawable/ic_filter_list.xml | 9 ++++ .../main/res/drawable/ic_sort_by_alpha.xml | 9 ++++ app/src/main/res/layout/activity_records.xml | 10 ++++ app/src/main/res/menu/menu_sort.xml | 19 ++++++++ app/src/main/res/values-bg/strings.xml | Bin 9504 -> 9766 bytes app/src/main/res/values-ru/strings.xml | 3 ++ app/src/main/res/values-uk/strings.xml | 3 ++ app/src/main/res/values/strings.xml | 3 ++ 20 files changed, 221 insertions(+), 19 deletions(-) create mode 100644 app/src/main/res/drawable/ic_access_time.xml create mode 100644 app/src/main/res/drawable/ic_calendar_today.xml create mode 100644 app/src/main/res/drawable/ic_filter_list.xml create mode 100644 app/src/main/res/drawable/ic_sort_by_alpha.xml create mode 100644 app/src/main/res/menu/menu_sort.xml diff --git a/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java b/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java index 804577d8..953f2afe 100755 --- a/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java +++ b/app/src/main/java/com/dimowner/audiorecorder/AppConstants.java @@ -75,6 +75,10 @@ private AppConstants() {} public final static int RECORD_ENCODING_BITRATE_128000 = 128000; public final static int RECORD_ENCODING_BITRATE_192000 = 192000; + public static final int ORDER_DATE = 1; + public static final int ORDER_NAME = 2; + public static final int ORDER_DURATION = 3; + // public final static int RECORD_AUDIO_CHANNELS_COUNT = 2; public final static int RECORD_AUDIO_MONO = 1; public final static int RECORD_AUDIO_STEREO = 2; diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java index d362d120..a8e90c67 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsActivity.java @@ -33,6 +33,8 @@ import android.text.Editable; import android.text.TextWatcher; import android.util.TypedValue; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; @@ -40,6 +42,7 @@ import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; +import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; @@ -79,6 +82,7 @@ public class RecordsActivity extends Activity implements RecordsContract.View, V private ImageButton btnPrev; private ImageButton btnDelete; private ImageButton btnBookmarks; + private ImageButton btnSort; private ImageButton btnCheckBookmark; private TextView txtProgress; private TextView txtDuration; @@ -128,6 +132,7 @@ protected void onCreate(Bundle savedInstanceState) { btnPrev = findViewById(R.id.btn_prev); btnDelete = findViewById(R.id.btn_delete); btnBookmarks = findViewById(R.id.btn_bookmarks); + btnSort = findViewById(R.id.btn_sort); btnCheckBookmark = findViewById(R.id.btn_check_bookmark); txtEmpty = findViewById(R.id.txtEmpty); txtTitle = findViewById(R.id.txt_title); @@ -138,6 +143,7 @@ protected void onCreate(Bundle savedInstanceState) { btnDelete.setOnClickListener(this); btnBookmarks.setOnClickListener(this); btnCheckBookmark.setOnClickListener(this); + btnSort.setOnClickListener(this); playProgress = findViewById(R.id.play_progress); txtProgress = findViewById(R.id.txt_progress); @@ -462,6 +468,9 @@ public void onClick(DialogInterface dialog, int id) { case R.id.btn_bookmarks: presenter.applyBookmarksFilter(); break; + case R.id.btn_sort: + showMenu(view); + break; case R.id.txt_name: if (presenter.getActiveRecordId() != -1) { setRecordName(presenter.getActiveRecordId(), new File(presenter.getActiveRecordPath())); @@ -470,6 +479,31 @@ public void onClick(DialogInterface dialog, int id) { } } + private void showMenu(View v) { + PopupMenu popup = new PopupMenu(v.getContext(), v); + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_date: + presenter.updateRecordsOrder(AppConstants.ORDER_DATE); + break; + case R.id.menu_name: + presenter.updateRecordsOrder(AppConstants.ORDER_NAME); + break; + case R.id.menu_duration: + presenter.updateRecordsOrder(AppConstants.ORDER_DURATION); + break; + } + return false; + } + }); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.menu_sort, popup.getMenu()); + AndroidUtils.insertMenuItemIcons(v.getContext(), popup); + popup.show(); + } + @Override protected void onStart() { super.onStart(); @@ -577,12 +611,12 @@ public void run() { } @Override - public void showRecords(List records) { + public void showRecords(List records, int order) { if (records.size() == 0) { // txtEmpty.setVisibility(View.VISIBLE); - adapter.setData(new ArrayList()); + adapter.setData(new ArrayList(), order); } else { - adapter.setData(records); + adapter.setData(records, order); txtEmpty.setVisibility(View.GONE); if (touchLayout.getVisibility() == View.VISIBLE) { adapter.showFooter(); @@ -591,8 +625,8 @@ public void showRecords(List records) { } @Override - public void addRecords(List records) { - adapter.addData(records); + public void addRecords(List records, int order) { + adapter.addData(records, order); txtEmpty.setVisibility(View.GONE); } @@ -657,12 +691,14 @@ public void removedFromBookmarks(int id, boolean isActive) { public void bookmarksSelected() { btnBookmarks.setImageResource(R.drawable.ic_bookmark); txtTitle.setText(R.string.bookmarks); + btnSort.setVisibility(View.GONE); } @Override public void bookmarksUnselected() { btnBookmarks.setImageResource(R.drawable.ic_bookmark_bordered); txtTitle.setText(R.string.records); + btnSort.setVisibility(View.VISIBLE); } @Override diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java index 1b627df5..23638b02 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsAdapter.java @@ -17,7 +17,6 @@ package com.dimowner.audiorecorder.app.records; import android.graphics.Typeface; -import android.os.Build; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; @@ -33,6 +32,7 @@ import android.widget.PopupMenu; import android.widget.TextView; +import com.dimowner.audiorecorder.AppConstants; import com.dimowner.audiorecorder.R; import com.dimowner.audiorecorder.app.widget.SimpleWaveformView; import com.dimowner.audiorecorder.util.AndroidUtils; @@ -208,10 +208,12 @@ public int getItemViewType(int position) { return data.get(position).getType(); } - void setData(List d) { - data = d; + void setData(List d, int order) { + updateShowHeader(order); if (showDateHeaders) { data = addDateHeaders(d); + } else { + data = d; } data.add(0, ListItem.createHeaderItem()); notifyDataSetChanged(); @@ -222,17 +224,34 @@ void setData(List d) { // notifyItemRangeInserted(data.size() - d.size(), d.size()); // } - void addData(List d) { + void addData(List d, int order) { if (data.size() > 0) { - if (findFooter() >= 0) { - data.addAll(data.size() - 1, addDateHeaders(d)); + updateShowHeader(order); + if (showDateHeaders) { + if (findFooter() >= 0) { + data.addAll(data.size() - 1, addDateHeaders(d)); + } else { + data.addAll(addDateHeaders(d)); + } } else { - data.addAll(addDateHeaders(d)); + if (findFooter() >= 0) { + data.addAll(data.size() - 1, d); + } else { + data.addAll(d); + } } notifyItemRangeInserted(data.size() - d.size(), d.size()); } } + private void updateShowHeader(int order) { + if (order == AppConstants.ORDER_DATE) { + showDateHeaders = true; + } else { + showDateHeaders = false; + } + } + public ListItem getItem(int pos) { return data.get(pos); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java index 7457be09..d6c846b8 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/records/RecordsContract.java @@ -40,8 +40,9 @@ interface View extends Contract.View { void showWaveForm(int[] waveForm, long duration); void showDuration(String duration); - void showRecords(List records); - void addRecords(List records); + void showRecords(List records, int order); + void addRecords(List records, int order); + void showEmptyList(); void showEmptyBookmarksList(); @@ -85,6 +86,8 @@ interface UserActionsListener extends Contract.UserActionsListener recordList = localRepository.getRecords(0); + final int order = prefs.getRecordsOrder(); + final List recordList = localRepository.getRecords(0, order); activeRecord = localRepository.getRecord((int) prefs.getActiveRecord()); if (activeRecord != null) { dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration() / 1000000f); @@ -386,7 +387,7 @@ public void run() { @Override public void run() { if (view != null) { - view.showRecords(Mapper.recordsToListItems(recordList)); + view.showRecords(Mapper.recordsToListItems(recordList), order); if (activeRecord != null) { view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); @@ -411,6 +412,12 @@ public void run() { } } + @Override + public void updateRecordsOrder(int order) { + prefs.setRecordOrder(order); + loadRecords(); + } + @Override public void loadRecordsPage(final int page) { if (view != null) { @@ -419,7 +426,8 @@ public void loadRecordsPage(final int page) { loadingTasks.postRunnable(new Runnable() { @Override public void run() { - final List recordList = localRepository.getRecords(page); + final int order = prefs.getRecordsOrder(); + final List recordList = localRepository.getRecords(page, order); activeRecord = localRepository.getRecord((int) prefs.getActiveRecord()); if (activeRecord != null) { dpPerSecond = ARApplication.getDpPerSecond((float) activeRecord.getDuration() / 1000000f); @@ -428,7 +436,7 @@ public void run() { @Override public void run() { if (view != null) { - view.addRecords(Mapper.recordsToListItems(recordList)); + view.addRecords(Mapper.recordsToListItems(recordList), order); if (activeRecord != null) { view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); @@ -469,7 +477,7 @@ public void run() { @Override public void run() { if (view != null) { - view.showRecords(Mapper.recordsToListItems(recordList)); + view.showRecords(Mapper.recordsToListItems(recordList), AppConstants.ORDER_DATE); if (activeRecord != null) { view.showWaveForm(activeRecord.getAmps(), activeRecord.getDuration()); view.showDuration(TimeUtils.formatTimeIntervalHourMinSec2(activeRecord.getDuration() / 1000)); diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/Prefs.java b/app/src/main/java/com/dimowner/audiorecorder/data/Prefs.java index d220e424..b34f163d 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/Prefs.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/Prefs.java @@ -50,4 +50,7 @@ public interface Prefs { void setSampleRate(int rate); int getSampleRate(); + + void setRecordOrder(int order); + int getRecordsOrder(); } diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java b/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java index e14572f8..02dc0553 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/PrefsImpl.java @@ -38,6 +38,7 @@ public class PrefsImpl implements Prefs { private static final String PREF_KEY_FORMAT = "pref_format"; private static final String PREF_KEY_BITRATE = "pref_bitrate"; private static final String PREF_KEY_SAMPLE_RATE = "pref_sample_rate"; + private static final String PREF_KEY_RECORDS_ORDER = "pref_records_order"; //Recording prefs. private static final String PREF_KEY_RECORD_CHANNEL_COUNT = "record_channel_count"; @@ -195,4 +196,16 @@ public void setSampleRate(int rate) { public int getSampleRate() { return sharedPreferences.getInt(PREF_KEY_SAMPLE_RATE, AppConstants.RECORD_SAMPLE_RATE_44100); } + + @Override + public void setRecordOrder(int order) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(PREF_KEY_RECORDS_ORDER, order); + editor.apply(); + } + + @Override + public int getRecordsOrder() { + return sharedPreferences.getInt(PREF_KEY_RECORDS_ORDER, AppConstants.ORDER_DATE); + } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java index 4562a9d8..b9fbd188 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/DataSource.java @@ -148,6 +148,18 @@ public ArrayList getRecords(int page) { return convertCursor(cursor); } + /** + * Get records from database for table T. + * @return List that contains all records of table T. + */ + public ArrayList getRecords(int page, String order) { + Cursor cursor = queryLocal("SELECT * FROM " + tableName + + " ORDER BY " + order + + " LIMIT " + AppConstants.DEFAULT_PER_PAGE + + " OFFSET " + (page-1) * AppConstants.DEFAULT_PER_PAGE); + return convertCursor(cursor); + } + /** * Delete all records from the table * @throws SQLException on error diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java index 41372e3e..dc433bdc 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepository.java @@ -31,6 +31,8 @@ public interface LocalRepository { List getRecords(int page); + List getRecords(int page, int order); + boolean deleteAllRecords(); Record getLastRecord(); diff --git a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java index ea4130f8..5a63461a 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java +++ b/app/src/main/java/com/dimowner/audiorecorder/data/database/LocalRepositoryImpl.java @@ -19,6 +19,7 @@ import android.database.Cursor; import android.database.SQLException; +import com.dimowner.audiorecorder.AppConstants; import com.dimowner.audiorecorder.audio.SoundFile; import java.io.File; @@ -230,6 +231,33 @@ public List getRecords(int page) { return list; } + @Override + public List getRecords(int page, int order) { + if (!dataSource.isOpen()) { + dataSource.open(); + } + String orderStr; + switch (order) { + case AppConstants.ORDER_NAME: + orderStr = SQLiteHelper.COLUMN_NAME + " ASC"; + break; + case AppConstants.ORDER_DURATION: + orderStr = SQLiteHelper.COLUMN_DURATION + " DESC"; + break; + case AppConstants.ORDER_DATE: + default: + orderStr = SQLiteHelper.COLUMN_DATE_ADDED + " DESC"; + } + List list = dataSource.getRecords(page, orderStr); + //Remove not records with not existing audio files (which was lost or deleted) + for (int i = 0; i < list.size(); i++) { + if (!isFileExists(list.get(i).getPath())) { + dataSource.deleteItem(list.get(i).getId()); + } + } + return list; + } + @Override public Record getLastRecord() { if (!dataSource.isOpen()) { diff --git a/app/src/main/res/drawable/ic_access_time.xml b/app/src/main/res/drawable/ic_access_time.xml new file mode 100644 index 00000000..ae994688 --- /dev/null +++ b/app/src/main/res/drawable/ic_access_time.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_calendar_today.xml b/app/src/main/res/drawable/ic_calendar_today.xml new file mode 100644 index 00000000..f267d069 --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar_today.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_filter_list.xml b/app/src/main/res/drawable/ic_filter_list.xml new file mode 100644 index 00000000..3b1b5a4d --- /dev/null +++ b/app/src/main/res/drawable/ic_filter_list.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_sort_by_alpha.xml b/app/src/main/res/drawable/ic_sort_by_alpha.xml new file mode 100644 index 00000000..755bdb9e --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_by_alpha.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_records.xml b/app/src/main/res/layout/activity_records.xml index 02ee958c..7c2dca68 100644 --- a/app/src/main/res/layout/activity_records.xml +++ b/app/src/main/res/layout/activity_records.xml @@ -57,6 +57,16 @@ android:text="@string/records" /> + + + + + + + + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index e7b68676f4b377209fc1d5574cd8e11b165836dc..d0c1aed0eb7ac3ef2495a355ef8638339233d421 100644 GIT binary patch delta 122 zcmZ4BwajNjgV1CFPC5A`hDwHbh7^WGh7yKU1|Поделиться Показывать диалог переименования после завершения записи Запись удалена успешно + По дате + По имени + По длительности Настройки приложения: Настройки записи: diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 1d91f9c5..017ef629 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -41,6 +41,9 @@ Поділитися Показувати діалог перейменування після завершення звукозапису Запис %s видалено успішно + По даті + По імені + По тривалості Налаштування додатка: Налаштування запису: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 441e8c37..d06c428c 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,6 +41,9 @@ Share Show renaming dialog after stop recording Record deleted successfully + By date + By name + By duration App settings: Recording settings: From 8a7db2d0b77401c361c424fb31970faae801c626 Mon Sep 17 00:00:00 2001 From: Dimowner Date: Tue, 13 Aug 2019 23:52:32 +0300 Subject: [PATCH 18/23] Fix check permissions; If not allow write external storage then save records in private directory. --- app/build.gradle | 4 +- .../audiorecorder/app/main/MainActivity.java | 59 ++++++++++-- .../audiorecorder/app/main/MainContract.java | 2 + .../audiorecorder/app/main/MainPresenter.java | 8 +- .../audiorecorder/util/AndroidUtils.java | 51 +++++++++++ .../res/drawable/raised_button_background.xml | 27 ++++++ app/src/main/res/layout/dialog_layout.xml | 86 ++++++++++++++++++ app/src/main/res/values-bg/strings.xml | Bin 9766 -> 10224 bytes app/src/main/res/values-ru/strings.xml | 3 + app/src/main/res/values-uk/strings.xml | 3 + app/src/main/res/values-v21/styles.xml | 75 --------------- app/src/main/res/values/colors.xml | 3 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/styles.xml | 81 ++++++++++++++++- 14 files changed, 317 insertions(+), 88 deletions(-) create mode 100644 app/src/main/res/drawable/raised_button_background.xml create mode 100644 app/src/main/res/layout/dialog_layout.xml delete mode 100755 app/src/main/res/values-v21/styles.xml diff --git a/app/build.gradle b/app/build.gradle index 5e57bd30..86464db4 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ ext.versions = [ ] def versionMajor = 0 -def versionMinor = 5 -def versionPatch = 30 +def versionMinor = 6 +def versionPatch = 0 android { compileSdkVersion versions.targetSdkVersion diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java index 6fef1e84..9a50dc00 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainActivity.java @@ -214,9 +214,11 @@ public void onClick(View view) { presenter.startPlayback(); break; case R.id.btn_record: - if (checkRecordPermission()) { - //Start or stop recording - presenter.startRecording(); + if (checkRecordPermission2()) { + if (checkStoragePermission2()) { + //Start or stop recording + presenter.startRecording(); + } } break; case R.id.btn_stop: @@ -617,6 +619,42 @@ && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageMana return true; } + private boolean checkRecordPermission2() { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQ_CODE_RECORD_AUDIO); + return false; + } + } + return true; + } + + private boolean checkStoragePermission2() { + if (presenter.isStorePublic()) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + AndroidUtils.showDialog(this, R.string.warning, R.string.need_write_permission, + new View.OnClickListener() { + @Override + public void onClick(View v) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQ_CODE_WRITE_EXTERNAL_STORAGE); + } + }, + new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.setStoragePrivate(getApplicationContext()); + presenter.startRecording(); + } + } + ); + return false; + } + } + } + return true; + } + private boolean checkRecordPermission() { if (presenter.isStorePublic()) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -655,19 +693,24 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permi presenter.startRecording(); } else if (requestCode == REQ_CODE_RECORD_AUDIO && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - presenter.startRecording(); + if (checkStoragePermission2()) { + presenter.startRecording(); + } } else if (requestCode == REQ_CODE_WRITE_EXTERNAL_STORAGE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { - presenter.startRecording(); + if (checkRecordPermission2()) { + presenter.startRecording(); + } } else if (requestCode == REQ_CODE_READ_EXTERNAL_STORAGE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { startFileSelector(); } else if (requestCode == REQ_CODE_WRITE_EXTERNAL_STORAGE - && grantResults[0] == PackageManager.PERMISSION_DENIED) { - Timber.v("WRITE_EXTERNAL_STORAGE DENIED"); - //TODO: show message that app cant write into private directory and record will be written in private + && (grantResults[0] == PackageManager.PERMISSION_DENIED + || grantResults[1] == PackageManager.PERMISSION_DENIED)) { + presenter.setStoragePrivate(getApplicationContext()); + presenter.startRecording(); } } } diff --git a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java index e43351c9..840dc9c3 100644 --- a/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java +++ b/app/src/main/java/com/dimowner/audiorecorder/app/main/MainContract.java @@ -82,6 +82,8 @@ interface UserActionsListener extends Contract.UserActionsListener=0) { + negativeBtn.setText(negativeBtnTextRes); + } + negativeBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + negativeBtnListener.onClick(v); + dialog.dismiss(); + } + }); + } else { + view.findViewById(R.id.dialog_negative_btn).setVisibility(View.GONE); + } + if (positiveBtnListener != null) { + Button positiveBtn = view.findViewById(R.id.dialog_positive_btn); + if (positiveBtnTextRes >=0) { + positiveBtn.setText(positiveBtnTextRes); + } + positiveBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + positiveBtnListener.onClick(v); + dialog.dismiss(); + } + }); + } else { + view.findViewById(R.id.dialog_positive_btn).setVisibility(View.GONE); + } + dialog.setContentView(view); + dialog.show(); + } } diff --git a/app/src/main/res/drawable/raised_button_background.xml b/app/src/main/res/drawable/raised_button_background.xml new file mode 100644 index 00000000..14ec274d --- /dev/null +++ b/app/src/main/res/drawable/raised_button_background.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_layout.xml b/app/src/main/res/layout/dialog_layout.xml new file mode 100644 index 00000000..2cc61e72 --- /dev/null +++ b/app/src/main/res/layout/dialog_layout.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + +