mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Android min sdk 24 part 1 (#170882)
- **bump template version, update migration code to handle api 21-23 and expand it to cover multiple min api level definitions** - **bump add to app project to minimum api level 24** - **PathUtilsTest converted to using config, removed test code that branched on versions older than 24** - **Update additional build.gradle files to have minsdk 24 and documenation** Related to #170807 G3 cls - espresso https://critique.corp.google.com/cl/776592881 - integration tests cl/778034198 - removal of pre api 23 samples tests cl/777652802 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: Gray Mackall <34871572+gmackall@users.noreply.github.com>
This commit is contained in:
parent
4937e469d6
commit
1dfb59d281
@ -14,13 +14,12 @@ Future<void> main() async {
|
||||
try {
|
||||
await runProjectTest((FlutterProject flutterProject) async {
|
||||
section('APK contains plugin classes');
|
||||
await flutterProject.setMinSdkVersion(21);
|
||||
await flutterProject.addPlugin('google_maps_flutter:^2.2.1');
|
||||
await flutterProject.addPlugin('google_maps_flutter:^2.12.1');
|
||||
|
||||
await inDirectory(flutterProject.rootPath, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['apk', '--debug', '--target-platform=android-arm'],
|
||||
options: <String>['apk', '--debug', '--target-platform=android-arm64'],
|
||||
);
|
||||
final File apk = File(
|
||||
'${flutterProject.rootPath}/build/app/outputs/flutter-apk/app-debug.apk',
|
||||
@ -30,8 +29,8 @@ Future<void> main() async {
|
||||
}
|
||||
// https://github.com/flutter/flutter/issues/72185
|
||||
await checkApkContainsMethods(apk, <String>[
|
||||
'io.flutter.plugins.googlemaps.GoogleMapController void onFlutterViewAttached(android.view.View)',
|
||||
'io.flutter.plugins.googlemaps.GoogleMapController void onFlutterViewDetached()',
|
||||
'io.flutter.plugins.googlemaps.GoogleMapController android.view.View getView()',
|
||||
'io.flutter.plugins.googlemaps.GoogleMapController void dispose()',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -300,18 +300,6 @@ android {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setMinSdkVersion(int sdkVersion) async {
|
||||
final File buildScript = appBuildFile;
|
||||
|
||||
buildScript.openWrite(mode: FileMode.append).write('''
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = $sdkVersion
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> getPackages() async {
|
||||
await inDirectory(Directory(rootPath), () async {
|
||||
await flutter('pub', options: <String>['get']);
|
||||
|
||||
@ -43,7 +43,7 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = flutterVersionCode.toInteger()
|
||||
versionName = flutterVersionName
|
||||
|
||||
@ -24,7 +24,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "io.flutter.addtoapp"
|
||||
minSdk = 21
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@ -24,7 +24,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.flutter.add2app"
|
||||
minSdk = 21
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@ -24,7 +24,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.flutter.add2app"
|
||||
minSdk = 21
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@ -21,10 +21,10 @@ apply plugin: "com.android.library"
|
||||
|
||||
android {
|
||||
namespace "io.flutter.embedding"
|
||||
compileSdk 36
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@ -10,7 +10,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
public class Build {
|
||||
/** For use in place of the Android Build.VERSION_CODES class. */
|
||||
public static class API_LEVELS {
|
||||
@VisibleForTesting public static final int FLUTTER_MIN = 21;
|
||||
@VisibleForTesting public static final int FLUTTER_MIN = 24;
|
||||
/** Android 5.0 (Lollipop) */
|
||||
public static final int API_21 = 21;
|
||||
/** Android 5.1 (Lollipop MR1) */
|
||||
|
||||
@ -783,7 +783,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
result.setParent(rootAccessibilityView);
|
||||
}
|
||||
|
||||
if (semanticsNode.previousNodeId != -1 && Build.VERSION.SDK_INT >= API_LEVELS.API_22) {
|
||||
if (semanticsNode.previousNodeId != -1) {
|
||||
// Requires at least android api 22.
|
||||
result.setTraversalAfter(rootAccessibilityView, semanticsNode.previousNodeId);
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import io.flutter.Build.API_LEVELS;
|
||||
import io.flutter.FlutterInjector;
|
||||
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
@ -601,7 +602,7 @@ public class FlutterActivityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_28)
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_28)
|
||||
public void fullyDrawn_beforeAndroidQ() {
|
||||
Intent intent = FlutterActivityWithReportFullyDrawn.createDefaultIntent(ctx);
|
||||
ActivityController<FlutterActivityWithReportFullyDrawn> activityController =
|
||||
|
||||
@ -306,7 +306,7 @@ public class FlutterViewTest {
|
||||
// set to -1 values, so it is clear if the wrong algorithm is used.
|
||||
@Test
|
||||
@TargetApi(30)
|
||||
@Config(sdk = 30)
|
||||
@Config(sdk = API_LEVELS.API_30)
|
||||
public void reportSystemInsetWhenNotFullscreen() {
|
||||
// Without custom shadows, the default system ui visibility flags is 0.
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
@ -348,7 +348,7 @@ public class FlutterViewTest {
|
||||
// set to -1 values, so it is clear if the wrong algorithm is used.
|
||||
@Test
|
||||
@TargetApi(34)
|
||||
@Config(minSdk = 34)
|
||||
@Config(minSdk = API_LEVELS.API_34)
|
||||
public void reportSystemInsetWhenNotFullscreenForSystemBar() {
|
||||
// Without custom shadows, the default system ui visibility flags is 0.
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
@ -426,7 +426,7 @@ public class FlutterViewTest {
|
||||
// This test uses the pre-API 30 Algorithm for window insets.
|
||||
@Test
|
||||
@TargetApi(28)
|
||||
@Config(sdk = 28)
|
||||
@Config(sdk = API_LEVELS.API_28)
|
||||
public void reportSystemInsetWhenNotFullscreenLegacy() {
|
||||
// Without custom shadows, the default system ui visibility flags is 0.
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
@ -459,7 +459,7 @@ public class FlutterViewTest {
|
||||
@SuppressWarnings("deprecation")
|
||||
// getSystemUiVisibility, getWindowSystemUiVisibility, required to test pre api 30 behavior.
|
||||
@Test
|
||||
@Config(minSdk = 23, maxSdk = 29, qualifiers = "land")
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_29, qualifiers = "land")
|
||||
public void systemInsetHandlesFullscreenNavbarRight() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
setExpectedDisplayRotation(Surface.ROTATION_90);
|
||||
@ -521,7 +521,7 @@ public class FlutterViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_23, maxSdk = API_LEVELS.API_29, qualifiers = "land")
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_29, qualifiers = "land")
|
||||
public void calculateShouldZeroSidesInLandscapeRotation270API23Plus() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
setExpectedDisplayRotation(Surface.ROTATION_270);
|
||||
@ -531,7 +531,7 @@ public class FlutterViewTest {
|
||||
@SuppressWarnings("deprecation")
|
||||
// getSystemUiVisibility, getWindowSystemUiVisibility required to test pre api 30 behavior.
|
||||
@Test
|
||||
@Config(minSdk = 23, maxSdk = 29, qualifiers = "land")
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_29, qualifiers = "land")
|
||||
public void systemInsetHandlesFullscreenNavbarLeft() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
setExpectedDisplayRotation(Surface.ROTATION_270);
|
||||
@ -573,7 +573,7 @@ public class FlutterViewTest {
|
||||
// set to -1 values, so it is clear if the wrong algorithm is used.
|
||||
@Test
|
||||
@TargetApi(30)
|
||||
@Config(sdk = 30, qualifiers = "land")
|
||||
@Config(sdk = API_LEVELS.API_30, qualifiers = "land")
|
||||
public void systemInsetGetInsetsFullscreen() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
setExpectedDisplayRotation(Surface.ROTATION_270);
|
||||
@ -611,7 +611,7 @@ public class FlutterViewTest {
|
||||
// This test uses the pre-API 30 Algorithm for window insets.
|
||||
@Test
|
||||
@TargetApi(28)
|
||||
@Config(sdk = 28, qualifiers = "land")
|
||||
@Config(sdk = API_LEVELS.API_28, qualifiers = "land")
|
||||
public void systemInsetGetInsetsFullscreenLegacy() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
setExpectedDisplayRotation(Surface.ROTATION_270);
|
||||
@ -652,7 +652,7 @@ public class FlutterViewTest {
|
||||
// set to -1 values, so it is clear if the wrong algorithm is used.
|
||||
@Test
|
||||
@TargetApi(30)
|
||||
@Config(sdk = 30, qualifiers = "land")
|
||||
@Config(sdk = API_LEVELS.API_30, qualifiers = "land")
|
||||
public void systemInsetDisplayCutoutSimple() {
|
||||
FlutterView flutterView = spy(new FlutterView(ctx));
|
||||
assertEquals(0, flutterView.getSystemUiVisibility());
|
||||
@ -700,7 +700,7 @@ public class FlutterViewTest {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
@Config(minSdk = 28)
|
||||
@Config(minSdk = API_LEVELS.API_28)
|
||||
public void onApplyWindowInsetsSetsDisplayCutouts() {
|
||||
// Use an Activity context so that FlutterView.onAttachedToWindow completes.
|
||||
try (ActivityScenario<Activity> scenario = ActivityScenario.launch(Activity.class)) {
|
||||
@ -1030,7 +1030,7 @@ public class FlutterViewTest {
|
||||
|
||||
@Test
|
||||
@SuppressLint("PrivateApi")
|
||||
@Config(sdk = Build.VERSION_CODES.P)
|
||||
@Config(sdk = API_LEVELS.API_28)
|
||||
public void findViewByAccessibilityIdTraversal_returnsRootViewOnAndroid28() throws Exception {
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
|
||||
@ -1041,7 +1041,7 @@ public class FlutterViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = Build.VERSION_CODES.P)
|
||||
@Config(sdk = API_LEVELS.API_28)
|
||||
@SuppressLint("PrivateApi")
|
||||
public void findViewByAccessibilityIdTraversal_returnsChildViewOnAndroid28() throws Exception {
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
@ -1058,7 +1058,7 @@ public class FlutterViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = Build.VERSION_CODES.Q)
|
||||
@Config(sdk = API_LEVELS.API_29)
|
||||
@SuppressLint("PrivateApi")
|
||||
public void findViewByAccessibilityIdTraversal_returnsRootViewOnAndroid29() throws Exception {
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
@ -1126,7 +1126,7 @@ public class FlutterViewTest {
|
||||
// set to -1 values, so it is clear if the wrong algorithm is used.
|
||||
@Test
|
||||
@TargetApi(30)
|
||||
@Config(sdk = 30)
|
||||
@Config(sdk = API_LEVELS.API_30)
|
||||
public void setPaddingTopToZeroForFullscreenMode() {
|
||||
FlutterView flutterView = new FlutterView(ctx);
|
||||
FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni));
|
||||
@ -1163,7 +1163,7 @@ public class FlutterViewTest {
|
||||
@Test
|
||||
@TargetApi(28)
|
||||
@Config(
|
||||
sdk = 28,
|
||||
sdk = API_LEVELS.API_28,
|
||||
shadows = {
|
||||
FlutterViewTest.ShadowFullscreenView.class,
|
||||
})
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
package io.flutter.embedding.engine.loader;
|
||||
|
||||
import static android.os.Looper.getMainLooper;
|
||||
import static io.flutter.Build.API_LEVELS;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@ -16,13 +15,10 @@ import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@ -38,7 +34,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FlutterLoaderTest {
|
||||
@ -404,18 +399,4 @@ public class FlutterLoaderTest {
|
||||
List<String> arguments = Arrays.asList(shellArgsCaptor.getValue());
|
||||
assertTrue(arguments.contains(shaderModeArg));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TargetApi(API_LEVELS.API_23)
|
||||
@Config(sdk = API_LEVELS.API_23)
|
||||
public void itReportsFpsToVsyncWaiterAndroidM() {
|
||||
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
|
||||
FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
|
||||
|
||||
Context appContextSpy = spy(ctx);
|
||||
|
||||
assertFalse(flutterLoader.initialized());
|
||||
flutterLoader.startInitialization(appContextSpy);
|
||||
verify(appContextSpy, never()).getSystemService(anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,109 +247,6 @@ public class LocalizationPluginTest {
|
||||
assertEquals(result[2], "");
|
||||
}
|
||||
|
||||
// Tests the legacy pre API 24 algorithm.
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "es-rMX")
|
||||
public void computePlatformResolvedLocale_emptySupportedLocales_beforeAndroidN() {
|
||||
FlutterJNI flutterJNI = new FlutterJNI();
|
||||
DartExecutor dartExecutor = mock(DartExecutor.class);
|
||||
flutterJNI.setLocalizationPlugin(
|
||||
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
|
||||
String[] supportedLocales = new String[] {};
|
||||
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "")
|
||||
public void computePlatformResolvedLocale_selectFirstLocaleWhenNoUserSetting_beforeAndroidN() {
|
||||
FlutterJNI flutterJNI = new FlutterJNI();
|
||||
DartExecutor dartExecutor = mock(DartExecutor.class);
|
||||
flutterJNI.setLocalizationPlugin(
|
||||
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
|
||||
String[] supportedLocales =
|
||||
new String[] {
|
||||
"fr", "FR", "",
|
||||
"zh", "", "",
|
||||
"en", "CA", ""
|
||||
};
|
||||
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
|
||||
assertEquals(result.length, 3);
|
||||
assertEquals(result[0], "fr");
|
||||
assertEquals(result[1], "FR");
|
||||
assertEquals(result[2], "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "fr-rCH")
|
||||
public void computePlatformResolvedLocale_selectFirstLocaleWhenNoExactMatch_beforeAndroidN() {
|
||||
FlutterJNI flutterJNI = new FlutterJNI();
|
||||
DartExecutor dartExecutor = mock(DartExecutor.class);
|
||||
flutterJNI.setLocalizationPlugin(
|
||||
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
|
||||
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
|
||||
String[] supportedLocales =
|
||||
new String[] {
|
||||
"en", "", "",
|
||||
"de", "DE", "",
|
||||
"es", "ES", "",
|
||||
"fr", "FR", "",
|
||||
"it", "IT", ""
|
||||
};
|
||||
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
|
||||
assertEquals(result.length, 3);
|
||||
assertEquals(result[0], "en");
|
||||
assertEquals(result[1], "");
|
||||
assertEquals(result[2], "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "it-rIT")
|
||||
public void computePlatformResolvedLocale_selectExactMatchLocale_beforeAndroidN() {
|
||||
FlutterJNI flutterJNI = new FlutterJNI();
|
||||
DartExecutor dartExecutor = mock(DartExecutor.class);
|
||||
flutterJNI.setLocalizationPlugin(
|
||||
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
|
||||
|
||||
String[] supportedLocales =
|
||||
new String[] {
|
||||
"en", "", "",
|
||||
"de", "DE", "",
|
||||
"es", "ES", "",
|
||||
"fr", "FR", "",
|
||||
"it", "IT", ""
|
||||
};
|
||||
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
|
||||
assertEquals(result.length, 3);
|
||||
assertEquals(result[0], "it");
|
||||
assertEquals(result[1], "IT");
|
||||
assertEquals(result[2], "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "fr-rCH")
|
||||
public void computePlatformResolvedLocale_selectOnlyLanguageLocale_beforeAndroidN() {
|
||||
FlutterJNI flutterJNI = new FlutterJNI();
|
||||
DartExecutor dartExecutor = mock(DartExecutor.class);
|
||||
flutterJNI.setLocalizationPlugin(
|
||||
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
|
||||
|
||||
String[] supportedLocales =
|
||||
new String[] {
|
||||
"en", "", "",
|
||||
"de", "DE", "",
|
||||
"es", "ES", "",
|
||||
"fr", "FR", "",
|
||||
"fr", "", "",
|
||||
"it", "IT", ""
|
||||
};
|
||||
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
|
||||
assertEquals(result.length, 3);
|
||||
assertEquals(result[0], "fr");
|
||||
assertEquals(result[1], "");
|
||||
assertEquals(result[2], "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localeFromString_languageOnly() {
|
||||
Locale locale = LocalizationPlugin.localeFromString("en");
|
||||
|
||||
@ -17,6 +17,7 @@ import android.view.Display;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import io.flutter.Build.API_LEVELS;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
@ -25,7 +26,7 @@ import org.robolectric.annotation.Config;
|
||||
@TargetApi(API_LEVELS.API_28)
|
||||
public class SingleViewPresentationTest {
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_30)
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_30)
|
||||
public void returnsOuterContextInputMethodManager() {
|
||||
// There's a bug in Android Q caused by the IMM being instanced per display.
|
||||
// https://github.com/flutter/flutter/issues/38375. We need the context returned by
|
||||
@ -56,7 +57,7 @@ public class SingleViewPresentationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_30)
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN, maxSdk = API_LEVELS.API_30)
|
||||
public void returnsOuterContextInputMethodManager_createDisplayContext() {
|
||||
// The IMM should also persist across display contexts created from the base context.
|
||||
|
||||
|
||||
@ -65,9 +65,7 @@ public class PathUtilsTest {
|
||||
public void canGetCacheDir() {
|
||||
Context context = mock(Context.class);
|
||||
when(context.getCacheDir()).thenReturn(new File(APP_DATA_PATH + "/cache"));
|
||||
if (Build.VERSION.SDK_INT >= API_LEVELS.API_21) {
|
||||
when(context.getCodeCacheDir()).thenReturn(new File(APP_DATA_PATH + "/code_cache"));
|
||||
}
|
||||
when(context.getCodeCacheDir()).thenReturn(new File(APP_DATA_PATH + "/code_cache"));
|
||||
assertTrue(PathUtils.getCacheDirectory(context).startsWith(APP_DATA_PATH));
|
||||
}
|
||||
|
||||
@ -75,14 +73,10 @@ public class PathUtilsTest {
|
||||
public void canOnlyGetCachePathWhenDiskFullAndCacheDirNotCreated() {
|
||||
Context context = mock(Context.class);
|
||||
when(context.getCacheDir()).thenReturn(null);
|
||||
if (Build.VERSION.SDK_INT >= API_LEVELS.API_21) {
|
||||
when(context.getCodeCacheDir()).thenReturn(null);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= API_LEVELS.API_24) {
|
||||
when(context.getDataDir()).thenReturn(new File(APP_DATA_PATH));
|
||||
} else {
|
||||
when(context.getApplicationInfo().dataDir).thenReturn(APP_DATA_PATH);
|
||||
}
|
||||
// Requires at least api 21.
|
||||
when(context.getCodeCacheDir()).thenReturn(null);
|
||||
// Requires at least api 24.
|
||||
when(context.getDataDir()).thenReturn(new File(APP_DATA_PATH));
|
||||
assertEquals(PathUtils.getCacheDirectory(context), APP_DATA_PATH + "/cache");
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,8 +697,8 @@ public class AccessibilityBridgeTest {
|
||||
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("new_node2"));
|
||||
}
|
||||
|
||||
@Config(sdk = API_LEVELS.API_21)
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itCanPerformSetText() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
@ -736,8 +736,8 @@ public class AccessibilityBridgeTest {
|
||||
.dispatchSemanticsAction(1, AccessibilityBridge.Action.SET_TEXT, expectedText);
|
||||
}
|
||||
|
||||
@Config(sdk = API_LEVELS.API_21)
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itCanPredictSetText() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
@ -775,8 +775,8 @@ public class AccessibilityBridgeTest {
|
||||
assertEquals(nodeInfo.getText().toString(), expectedText);
|
||||
}
|
||||
|
||||
@Config(sdk = API_LEVELS.API_21)
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itBuildsAttributedString() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
@ -841,8 +841,8 @@ public class AccessibilityBridgeTest {
|
||||
assertEquals(actual.getSpanEnd(spellOutSpan), 9);
|
||||
}
|
||||
|
||||
@Config(sdk = API_LEVELS.API_21)
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itSetsTextCorrectly() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
@ -976,8 +976,8 @@ public class AccessibilityBridgeTest {
|
||||
assertEquals(actual.toString(), root.identifier);
|
||||
}
|
||||
|
||||
@Config(sdk = API_LEVELS.API_21)
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itCanCreateAccessibilityNodeInfoWithSetText() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
@ -1605,7 +1605,7 @@ public class AccessibilityBridgeTest {
|
||||
accessibilityBridge.performAction(
|
||||
1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
|
||||
AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
|
||||
// The seletction should be at the beginning of the third line.
|
||||
// The selection should be at the beginning of the third line.
|
||||
assertEquals(nodeInfo.getTextSelectionStart(), 21);
|
||||
assertEquals(nodeInfo.getTextSelectionEnd(), 21);
|
||||
|
||||
@ -1617,7 +1617,7 @@ public class AccessibilityBridgeTest {
|
||||
accessibilityBridge.performAction(
|
||||
1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
|
||||
nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
|
||||
// The seletction should be at the beginning of the second line.
|
||||
// The selection should be at the beginning of the second line.
|
||||
assertEquals(nodeInfo.getTextSelectionStart(), 11);
|
||||
assertEquals(nodeInfo.getTextSelectionEnd(), 11);
|
||||
}
|
||||
|
||||
@ -33,10 +33,10 @@ println "=========================================="
|
||||
|
||||
android {
|
||||
namespace 'io.flutter.app.test'
|
||||
compileSdk 36
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
||||
3
packages/flutter_tools/.gitignore
vendored
3
packages/flutter_tools/.gitignore
vendored
@ -4,3 +4,6 @@
|
||||
|
||||
!ide_templates/intellij/.idea
|
||||
!templates/**/.idea
|
||||
|
||||
# Do not need gradle cache of flutter gradle plugin code checked in.
|
||||
gradle/bin/**
|
||||
|
||||
@ -27,9 +27,15 @@ Tests can be run in Android Studio, or directly with Gradle: `./gradlew test`
|
||||
the engines `third_party` directory at
|
||||
`<flutter_root>/engine/src/flutter/third_party/gradle/bin/gradle`).
|
||||
|
||||
Alternatively, you can run all the tests in one file by passing in the fully qualified class name,
|
||||
e.g. `./gradlew test --tests com.flutter.gradle.BaseApplicationNameHandlerTest`, or one test in
|
||||
one file by passing in the fully qualified class name followed by the method name,
|
||||
If you can not run the test task try running `./gradlew tasks`. If that does not work then there is
|
||||
a configuration error. The most common one is using the wrong version of java. Java can be
|
||||
overridden by setting the `JAVA_HOME` environment variable.
|
||||
This example sets the java version to 17 downloaded with brew and then runs the tests:
|
||||
`JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home/ ./gradlew test`
|
||||
|
||||
You can run all the tests in one file by passing in the fully qualified class name,
|
||||
e.g. `./gradlew test --tests com.flutter.gradle.BaseApplicationNameHandlerTest`, or one test in
|
||||
one file by passing in the fully qualified class name followed by the method name,
|
||||
e.g `./gradlew test --tests "com.flutter.gradle.BaseApplicationNameHandlerTest.setBaseName respects Flutter tool property"`.
|
||||
|
||||
Sometimes changing a test name and then running it will cause an IDE error. To get Android Studio back
|
||||
|
||||
@ -105,11 +105,12 @@ object DependencyVersionChecker {
|
||||
@VisibleForTesting internal val errorKGPVersion: Version = Version(1, 7, 0)
|
||||
|
||||
// If this value is changed, then make sure to change the documentation on https://docs.flutter.dev/reference/supported-platforms
|
||||
// Non inclusive.
|
||||
@VisibleForTesting
|
||||
internal val warnMinSdkVersion: Int = 21
|
||||
internal val warnMinSdkVersion: Int = 24
|
||||
|
||||
@VisibleForTesting
|
||||
internal val errorMinSdkVersion: Int = 1
|
||||
internal val errorMinSdkVersion: Int = 23
|
||||
|
||||
/**
|
||||
* Checks if the project's Android build time dependencies are each within the respective
|
||||
|
||||
@ -23,7 +23,7 @@ open class FlutterExtension {
|
||||
val compileSdkVersion: Int = 36
|
||||
|
||||
/** Sets the minSdkVersion used by default in Flutter app projects. */
|
||||
val minSdkVersion: Int = 21
|
||||
val minSdkVersion: Int = 24
|
||||
|
||||
/**
|
||||
* Sets the targetSdkVersion used by default in Flutter app projects.
|
||||
|
||||
@ -228,7 +228,7 @@ class DependencyVersionCheckerTest {
|
||||
|
||||
@Test
|
||||
fun `min SDK version in warn range results in warning logs`() {
|
||||
val exampleWarnSDKVersion = 19
|
||||
val exampleWarnSDKVersion = 23
|
||||
val flavorName1 = "flavor1"
|
||||
val flavorName2 = "flavor2"
|
||||
val mockProject =
|
||||
@ -340,8 +340,8 @@ class DependencyVersionCheckerTest {
|
||||
|
||||
assertEquals(
|
||||
dependencyValidationException.message,
|
||||
"Error: Your project's minimum Android SDK (flavor='flavor') version (0) is lower than " +
|
||||
"Flutter's minimum supported version of 1. Please upgrade your minimum Android SDK " +
|
||||
"Error: Your project's minimum Android SDK (flavor='flavor') version ($version) is lower than " +
|
||||
"Flutter's minimum supported version of $errorMinSdkVersion. Please upgrade your minimum Android SDK " +
|
||||
"(flavor='flavor') version. \n" +
|
||||
"Alternatively, use the flag \"--android-skip-build-dependency-validation\" to " +
|
||||
"bypass this check.\n" +
|
||||
@ -358,7 +358,7 @@ class DependencyVersionCheckerTest {
|
||||
val mockExtraPropertiesExtension = mockk<ExtraPropertiesExtension>()
|
||||
val projectDir = "projectDir"
|
||||
val flavor = "flavor"
|
||||
val version = 20
|
||||
val version = 23
|
||||
|
||||
every { mockExtraPropertiesExtension.set(any(), any()) } returns Unit
|
||||
every { mockLogger.error(any()) } returns Unit
|
||||
@ -377,8 +377,8 @@ class DependencyVersionCheckerTest {
|
||||
assertEquals(
|
||||
warningMessageSlot.captured,
|
||||
"Warning: Flutter support for your project's minimum Android SDK (flavor='flavor') " +
|
||||
"version (20) will soon be dropped. Please upgrade your minimum Android SDK " +
|
||||
"(flavor='flavor') version to a version of at least 21 soon.\n" +
|
||||
"version ($version) will soon be dropped. Please upgrade your minimum Android SDK " +
|
||||
"(flavor='flavor') version to a version of at least $warnMinSdkVersion soon.\n" +
|
||||
"Alternatively, use the flag \"--android-skip-build-dependency-validation\" to " +
|
||||
"bypass this check.\n" +
|
||||
"\n" +
|
||||
@ -475,6 +475,9 @@ private object MockProjectFactory {
|
||||
val variant = mockk<Variant>()
|
||||
every { variant.name } returns it.flavor
|
||||
every { variant.minSdk } returns mockk { every { apiLevel } returns it.version }
|
||||
// Gradle prints a warning about this api being removed in gradle 9 but there is
|
||||
// no way to suppress individual warnings. We can remove this line when our tests
|
||||
// use gradle 9 or higher.
|
||||
every { variant.minSdkVersion } returns mockk { every { apiLevel } returns it.version }
|
||||
onVariantsFnSlot.captured.invoke(variant)
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ All of the Android SDK/NDK versions noted in `gradle_utils.dart`
|
||||
(`compileSdkVersion`, `minSdkVersion`, `targetSdkVersion`, `ndkVersion`)
|
||||
versions should match the values in Flutter Gradle Plugin (`FlutterExtension`),
|
||||
so updating any of these versions also requires an update in
|
||||
[flutter.groovy](../../../gradle/src/main/groovy/flutter.groovy).
|
||||
[FlutterExtension](../../../gradle/src/main/kotlin/FlutterExtention.kt).
|
||||
|
||||
When updating the Android `compileSdkVersion`, `minSdkVersion`, or
|
||||
`targetSdkVersion`, make sure that:
|
||||
@ -30,9 +30,11 @@ SDK versions are updated (you should see these fail if you do not fix them
|
||||
preemptively).
|
||||
|
||||
Also, make sure to also update to the same version in the following places:
|
||||
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/groovy/flutter.groovy`.
|
||||
- The version in the buildscript block in `packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts`.
|
||||
- The versions for error/warn in `packages/flutter_tools/gradle/src/main/kotlin/DependencyVersionChecker.kt`.
|
||||
- The version in the dependencies block in `packages/flutter_tools/gradle/build.gradle.kts`.
|
||||
- The flutter min version in `engine/src/flutter/shell/platform/android/io/flutter/Build.java`
|
||||
- The versions used when engine testing in `engine/src/flutter/shell/platform/android/test_runner/build.gradle`
|
||||
- The versions used when working in engine/shell in `engine/src/flutter/shell/platform/android/build.gradle`
|
||||
|
||||
#### Gradle
|
||||
When updating the Gradle version used in project templates
|
||||
|
||||
@ -51,7 +51,7 @@ const String templateKotlinGradlePluginVersion = '2.1.0';
|
||||
//
|
||||
// Please see the README before changing any of these values.
|
||||
const String compileSdkVersion = '36';
|
||||
const String minSdkVersion = '21';
|
||||
const String minSdkVersion = '24';
|
||||
const String targetSdkVersion = '36';
|
||||
const String ndkVersion = '27.0.12077973';
|
||||
|
||||
@ -158,9 +158,10 @@ final RegExp gradleOrgVersionMatch = RegExp(
|
||||
);
|
||||
|
||||
// This matches uncommented minSdkVersion lines in the module-level build.gradle
|
||||
// file which have minSdkVersion 16,17, 18, 19, or 20.
|
||||
// file which have minSdkVersion 16, 17, 18, 19, 20, 21, 22, 23 set with space sytax,
|
||||
// equals syntax and when using minSdk or minSdkVersion.
|
||||
final RegExp tooOldMinSdkVersionMatch = RegExp(
|
||||
r'(?<=^\s*)minSdkVersion (1[6789]|20)(?=\s*(?://|$))',
|
||||
r'(?<=^\s*)minSdk(Version)?\s=?\s?(1[6789]|2[0123])(?=\s*(?://|$))',
|
||||
multiLine: true,
|
||||
);
|
||||
|
||||
|
||||
@ -349,7 +349,7 @@ tasks.register("clean", Delete) {
|
||||
});
|
||||
});
|
||||
|
||||
group('migrate min sdk versions less than 21 to flutter.minSdkVersion '
|
||||
group('migrate min sdk versions less than 24 to flutter.minSdkVersion '
|
||||
'when in a FlutterProject that is an app', () {
|
||||
late MemoryFileSystem memoryFileSystem;
|
||||
late BufferLogger bufferLogger;
|
||||
@ -392,13 +392,43 @@ tasks.register("clean", Delete) {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('do nothing when >=api 21', () async {
|
||||
testWithoutContext('replace when api 21', () async {
|
||||
const String minSdkVersion21 = 'minSdkVersion 21';
|
||||
project.appGradleFile.writeAsStringSync(sampleModuleGradleBuildFile(minSdkVersion21));
|
||||
await migration.migrate();
|
||||
expect(
|
||||
project.appGradleFile.readAsStringSync(),
|
||||
sampleModuleGradleBuildFile(minSdkVersion21),
|
||||
sampleModuleGradleBuildFile(replacementMinSdkText),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('replace when api 22', () async {
|
||||
const String minSdkVersion20 = 'minSdkVersion = 22';
|
||||
project.appGradleFile.writeAsStringSync(sampleModuleGradleBuildFile(minSdkVersion20));
|
||||
await migration.migrate();
|
||||
expect(
|
||||
project.appGradleFile.readAsStringSync(),
|
||||
sampleModuleGradleBuildFile(replacementMinSdkText),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('replace when api 23', () async {
|
||||
const String minSdkVersion20 = 'minSdk = 23';
|
||||
project.appGradleFile.writeAsStringSync(sampleModuleGradleBuildFile(minSdkVersion20));
|
||||
await migration.migrate();
|
||||
expect(
|
||||
project.appGradleFile.readAsStringSync(),
|
||||
sampleModuleGradleBuildFile(replacementMinSdkText),
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('do nothing when >=api 24', () async {
|
||||
const String minSdkVersion24 = 'minSdkVersion 24';
|
||||
project.appGradleFile.writeAsStringSync(sampleModuleGradleBuildFile(minSdkVersion24));
|
||||
await migration.migrate();
|
||||
expect(
|
||||
project.appGradleFile.readAsStringSync(),
|
||||
sampleModuleGradleBuildFile(minSdkVersion24),
|
||||
);
|
||||
});
|
||||
|
||||
@ -415,7 +445,7 @@ tasks.register("clean", Delete) {
|
||||
testWithoutContext('avoid rewriting comments', () async {
|
||||
const String code =
|
||||
'// minSdkVersion 19 // old default\n'
|
||||
' minSdkVersion 23 // new version';
|
||||
' minSdkVersion 24 // new version';
|
||||
project.appGradleFile.writeAsStringSync(sampleModuleGradleBuildFile(code));
|
||||
await migration.migrate();
|
||||
expect(project.appGradleFile.readAsStringSync(), sampleModuleGradleBuildFile(code));
|
||||
@ -447,7 +477,7 @@ tasks.register("clean", Delete) {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('do nothing when minSdkVersion is set '
|
||||
testWithoutContext('migrate when minSdkVersion is set '
|
||||
'using = syntax', () async {
|
||||
const String equalsSyntaxMinSdkVersion19 = 'minSdkVersion = 19';
|
||||
project.appGradleFile.writeAsStringSync(
|
||||
@ -456,7 +486,7 @@ tasks.register("clean", Delete) {
|
||||
await migration.migrate();
|
||||
expect(
|
||||
project.appGradleFile.readAsStringSync(),
|
||||
sampleModuleGradleBuildFile(equalsSyntaxMinSdkVersion19),
|
||||
sampleModuleGradleBuildFile(replacementMinSdkText),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -138,7 +138,7 @@ class DeferredComponentModule {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user