mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-20 03:52:15 +02:00
fix(android, api31): implement compileSdkVersion 31 compatibility
- added new CompatV31 object to compat infrastructure - handled MediaRecorder() ctor deprecation by threading Context through, adding new Compat method - handled Context.VIBRATOR_SERVICE deprecation with recommended new APIs in CompatV31 override
This commit is contained in:
parent
d3b54dd923
commit
0e43472891
@ -26,7 +26,7 @@ idea {
|
||||
|
||||
def homePath = System.properties['user.home']
|
||||
android {
|
||||
compileSdkVersion 30 // change api compileSdkVersion at the same time
|
||||
compileSdkVersion 31 // change api compileSdkVersion at the same time
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.ichi2.anki"
|
||||
|
@ -20,8 +20,11 @@
|
||||
|
||||
package com.ichi2.anki.multimediacard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaRecorder;
|
||||
|
||||
import com.ichi2.compat.CompatHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@ -36,8 +39,8 @@ public class AudioRecorder {
|
||||
private Runnable mOnRecordingInitialized;
|
||||
|
||||
|
||||
private MediaRecorder initMediaRecorder(String audioPath) {
|
||||
MediaRecorder mr = new MediaRecorder();
|
||||
private MediaRecorder initMediaRecorder(Context context, String audioPath) {
|
||||
MediaRecorder mr = CompatHelper.getCompat().getMediaRecorder(context);
|
||||
mr.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
|
||||
onRecordingInitialized();
|
||||
@ -52,12 +55,12 @@ public class AudioRecorder {
|
||||
}
|
||||
}
|
||||
|
||||
public void startRecording(String audioPath) throws IOException {
|
||||
public void startRecording(Context context, String audioPath) throws IOException {
|
||||
boolean highSampling = false;
|
||||
try {
|
||||
// try high quality AAC @ 44.1kHz / 192kbps first
|
||||
// can throw IllegalArgumentException if codec isn't supported
|
||||
mRecorder = initMediaRecorder(audioPath);
|
||||
mRecorder = initMediaRecorder(context, audioPath);
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
||||
mRecorder.setAudioChannels(2);
|
||||
mRecorder.setAudioSamplingRate(44100);
|
||||
@ -74,7 +77,7 @@ public class AudioRecorder {
|
||||
if (!highSampling) {
|
||||
// if we are here, either the codec didn't work or output file was invalid
|
||||
// fall back on default
|
||||
mRecorder = initMediaRecorder(audioPath);
|
||||
mRecorder = initMediaRecorder(context, audioPath);
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
|
||||
|
||||
mRecorder.prepare();
|
||||
|
@ -393,7 +393,7 @@ public class AudioView extends LinearLayout {
|
||||
case STOPPED: // if already recorded or played
|
||||
|
||||
try {
|
||||
mAudioRecorder.startRecording(mAudioPath);
|
||||
mAudioRecorder.startRecording(mContext, mAudioPath);
|
||||
} catch (Exception e) {
|
||||
// either output file failed or codec didn't work, in any case fail out
|
||||
Timber.e("RecordButton.onClick() :: error recording to %s\n%s", mAudioPath, e.getMessage());
|
||||
|
@ -22,6 +22,7 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaRecorder;
|
||||
import android.net.Uri;
|
||||
import android.widget.TimePicker;
|
||||
|
||||
@ -79,6 +80,7 @@ public interface Compat {
|
||||
int getHour(TimePicker picker);
|
||||
int getMinute(TimePicker picker);
|
||||
void vibrate(Context context, long durationMillis);
|
||||
MediaRecorder getMediaRecorder(Context context);
|
||||
void copyFile(String source, String target) throws IOException;
|
||||
long copyFile(String source, OutputStream target) throws IOException;
|
||||
long copyFile(InputStream source, String target) throws IOException;
|
||||
|
@ -26,7 +26,9 @@ public class CompatHelper {
|
||||
|
||||
|
||||
private CompatHelper() {
|
||||
if (getSdkVersion() >= Build.VERSION_CODES.Q) {
|
||||
if (getSdkVersion() >= Build.VERSION_CODES.S) {
|
||||
mCompat = new CompatV31();
|
||||
} else if (getSdkVersion() >= Build.VERSION_CODES.Q) {
|
||||
mCompat = new CompatV29();
|
||||
} else if (getSdkVersion() >= Build.VERSION_CODES.O) {
|
||||
mCompat = new CompatV26();
|
||||
|
@ -22,6 +22,7 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.ThumbnailUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
@ -76,6 +77,13 @@ public class CompatV21 implements Compat {
|
||||
}
|
||||
}
|
||||
|
||||
// Until API31 the MediaRecorder constructor was default, ignoring the Context
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public MediaRecorder getMediaRecorder(Context context) {
|
||||
return new MediaRecorder();
|
||||
}
|
||||
|
||||
// Until API 26 do the copy using streams
|
||||
public void copyFile(@NonNull String source, @NonNull String target) throws IOException {
|
||||
try (InputStream fileInputStream = new FileInputStream(new File(source))) {
|
||||
|
@ -73,6 +73,7 @@ public class CompatV26 extends CompatV23 implements Compat {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void vibrate(Context context, long durationMillis) {
|
||||
Vibrator vibratorManager = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vibratorManager != null) {
|
||||
|
@ -29,7 +29,7 @@ import java.io.File
|
||||
|
||||
/** Implementation of [Compat] for SDK level 29 */
|
||||
@TargetApi(29)
|
||||
class CompatV29 : CompatV26(), Compat {
|
||||
open class CompatV29 : CompatV26(), Compat {
|
||||
override fun hasVideoThumbnail(path: String): Boolean {
|
||||
return try {
|
||||
ThumbnailUtils.createVideoThumbnail(File(path), THUMBNAIL_MINI_KIND, null)
|
||||
|
37
AnkiDroid/src/main/java/com/ichi2/compat/CompatV31.kt
Normal file
37
AnkiDroid/src/main/java/com/ichi2/compat/CompatV31.kt
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Mike Hardy <mike@mikehardy.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.ichi2.compat
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.media.MediaRecorder
|
||||
import android.os.VibrationEffect
|
||||
import android.os.VibratorManager
|
||||
|
||||
/** Implementation of [Compat] for SDK level 31 */
|
||||
@TargetApi(31)
|
||||
class CompatV31 : CompatV29(), Compat {
|
||||
override fun vibrate(context: Context, durationMillis: Long) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
val effect = VibrationEffect.createOneShot(durationMillis, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
val vibrator = vibratorManager.getDefaultVibrator()
|
||||
vibrator.vibrate(effect)
|
||||
}
|
||||
|
||||
override fun getMediaRecorder(context: Context): MediaRecorder {
|
||||
return MediaRecorder(context)
|
||||
}
|
||||
}
|
@ -71,7 +71,7 @@ public class AudioRecorderTest extends RobolectricTest {
|
||||
Runnable recordingHandler = mock(Runnable.class);
|
||||
|
||||
mAudioRecorder.setOnRecordingInitializedHandler(recordingHandler);
|
||||
mAudioRecorder.startRecording("testpath");
|
||||
mAudioRecorder.startRecording(getTargetContext(), "testpath");
|
||||
|
||||
verify(recordingHandler, times(1)).run();
|
||||
}
|
||||
@ -98,7 +98,7 @@ public class AudioRecorderTest extends RobolectricTest {
|
||||
}
|
||||
initHandlerWithError recordingHandler = new initHandlerWithError();
|
||||
mAudioRecorder.setOnRecordingInitializedHandler(recordingHandler);
|
||||
mAudioRecorder.startRecording("testpath");
|
||||
mAudioRecorder.startRecording(getTargetContext(), "testpath");
|
||||
|
||||
assertEquals("Initialization handler should run twice", 2, recordingHandler.getTimesRun());
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@ -78,7 +79,7 @@ public class KeyboardShortcutIntegrationTest extends RobolectricTest {
|
||||
|
||||
pressShiftAndVThenRelease();
|
||||
|
||||
verify(recorder, times(1)).startRecording(anyString());
|
||||
verify(recorder, times(1)).startRecording(any(), anyString());
|
||||
verifyNoMoreInteractions(recorder);
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ public class KeyboardShortcutIntegrationTest extends RobolectricTest {
|
||||
|
||||
pressAndHoldShiftV();
|
||||
|
||||
verify(recorder, times(1)).startRecording(anyString());
|
||||
verify(recorder, times(1)).startRecording(any(), anyString());
|
||||
verifyNoMoreInteractions(recorder);
|
||||
}
|
||||
|
||||
@ -113,7 +114,7 @@ public class KeyboardShortcutIntegrationTest extends RobolectricTest {
|
||||
|
||||
pressShiftAndVThenRelease();
|
||||
|
||||
verify(recorder, times(1)).startRecording(anyString());
|
||||
verify(recorder, times(1)).startRecording(any(), anyString());
|
||||
verifyNoMoreInteractions(recorder, player);
|
||||
assertStatus(AudioView.Status.RECORDING);
|
||||
|
||||
@ -153,7 +154,7 @@ public class KeyboardShortcutIntegrationTest extends RobolectricTest {
|
||||
pressShiftAndVThenRelease();
|
||||
|
||||
verify(player, times(1)).stop();
|
||||
verify(recorder, times(1)).startRecording(anyString());
|
||||
verify(recorder, times(1)).startRecording(any(), anyString());
|
||||
verifyNoMoreInteractions(recorder, player);
|
||||
assertStatus(AudioView.Status.RECORDING);
|
||||
|
||||
|
@ -12,7 +12,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 31
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
|
Loading…
Reference in New Issue
Block a user