0
0
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:
Mike Hardy 2021-11-04 16:24:43 -05:00
parent d3b54dd923
commit 0e43472891
12 changed files with 70 additions and 16 deletions

View File

@ -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"

View File

@ -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();

View File

@ -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());

View File

@ -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;

View File

@ -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();

View File

@ -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))) {

View File

@ -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) {

View File

@ -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)

View 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)
}
}

View File

@ -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());
}

View File

@ -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);

View File

@ -12,7 +12,7 @@ repositories {
mavenCentral()
}
android {
compileSdkVersion 30
compileSdkVersion 31
defaultConfig {
minSdkVersion 14