Skip to content

Commit

Permalink
Android 10+ View.getSystemGestureExclusionRects (flutter#11451)
Browse files Browse the repository at this point in the history
* SystemGesture.getSystemGestureExclusionRects

* Add getSystemExclusionRects unit tests
  • Loading branch information
Shi-Hao Hong authored Sep 4, 2019
1 parent 27955f2 commit 898480f
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import io.flutter.Log;
Expand Down Expand Up @@ -145,7 +146,7 @@ private int decodeOrientations(@NonNull JSONArray encodedOrientations) throws JS
* @throws JSONException if {@code inputRects} does not contain expected keys and value types.
*/
@NonNull
private ArrayList<Rect> decodeRects(@NonNull JSONArray inputRects) throws JSONException {
private ArrayList<Rect> decodeExclusionRects(@NonNull JSONArray inputRects) throws JSONException {
ArrayList<Rect> exclusionRects = new ArrayList<Rect>();
for (int i = 0; i < inputRects.length(); i++) {
JSONObject rect = inputRects.getJSONObject(i);
Expand Down Expand Up @@ -173,6 +174,31 @@ private ArrayList<Rect> decodeRects(@NonNull JSONArray inputRects) throws JSONEx
return exclusionRects;
}

/**
* Encodes a List<Rect> provided by the Android host into an
* ArrayList<HashMap<String, Integer>>.
*
* Since View.getSystemGestureExclusionRects returns a list of Rects, these
* Rects need to be transformed into UTF-8 encoded JSON messages to be
* properly decoded by the Flutter framework.
*
* This method is used by the SystemGestures.getSystemGestureExclusionRects
* platform channel.
*/
private ArrayList<HashMap<String, Integer>> encodeExclusionRects(List<Rect> exclusionRects) {
ArrayList<HashMap<String, Integer>> encodedExclusionRects = new ArrayList<HashMap<String, Integer>>();
for (Rect rect : exclusionRects) {
HashMap<String, Integer> rectMap = new HashMap<String, Integer>();
rectMap.put("top", rect.top);
rectMap.put("right", rect.right);
rectMap.put("bottom", rect.bottom);
rectMap.put("left", rect.left);
encodedExclusionRects.add(rectMap);
}

return encodedExclusionRects;
}

@NonNull
private AppSwitcherDescription decodeAppSwitcherDescription(@NonNull JSONObject encodedDescription) throws JSONException {
int color = encodedDescription.getInt("primaryColor");
Expand Down Expand Up @@ -337,6 +363,12 @@ public interface PlatformMessageHandler {
*/
void setClipboardData(@NonNull String text);

/**
* The Flutter application would like to get the system gesture exclusion
* rects.
*/
List<Rect> getSystemGestureExclusionRects();

/**
* The Flutter application would like to set the system gesture exclusion
* rects through the given {@code rects}.
Expand Down Expand Up @@ -594,18 +626,6 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
platformMessageHandler.popSystemNavigator();
result.success(null);
break;
case "SystemGestures.setSystemGestureExclusionRects":
if (!(arguments instanceof JSONArray)) {
String inputTypeError = "Input type is incorrect. Ensure that a List<Map<String, int>> is passed as the input for SystemGestureExclusionRects.setSystemGestureExclusionRects.";
result.error("inputTypeError", inputTypeError, null);
break;
}

JSONArray inputRects = (JSONArray) arguments;
ArrayList<Rect> decodedRects = decodeRects(inputRects);
platformMessageHandler.setSystemGestureExclusionRects(decodedRects);
result.success(null);
break;
case "Clipboard.getData": {
String contentFormatName = (String) arguments;
ClipboardContentFormat clipboardFormat = null;
Expand Down Expand Up @@ -634,6 +654,30 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
result.success(null);
break;
}
case "SystemGestures.setSystemGestureExclusionRects":
if (!(arguments instanceof JSONArray)) {
String inputTypeError = "Input type is incorrect. Ensure that a List<Map<String, int>> is passed as the input for SystemGestureExclusionRects.setSystemGestureExclusionRects.";
result.error("inputTypeError", inputTypeError, null);
break;
}

JSONArray inputRects = (JSONArray) arguments;
ArrayList<Rect> decodedRects = decodeExclusionRects(inputRects);
platformMessageHandler.setSystemGestureExclusionRects(decodedRects);
result.success(null);
break;
case "SystemGestures.getSystemGestureExclusionRects": {
List<Rect> exclusionRects = platformMessageHandler.getSystemGestureExclusionRects();
if (exclusionRects == null) {
String incorrectApiLevel = "Exclusion rects only exist for Android API 29+.";
result.error("error", incorrectApiLevel, null);
break;
}

ArrayList<HashMap<String, Integer>> encodedExclusionRects = encodeExclusionRects(exclusionRects);
result.success(encodedExclusionRects);
break;
}
default:
result.notImplemented();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ public void setClipboardData(@NonNull String text) {
PlatformPlugin.this.setClipboardData(text);
}

@Override
public List<Rect> getSystemGestureExclusionRects() {
return PlatformPlugin.this.getSystemGestureExclusionRects();
}

@Override
public void setSystemGestureExclusionRects(@NonNull ArrayList rects) {
PlatformPlugin.this.setSystemGestureExclusionRects(rects);
Expand Down Expand Up @@ -281,6 +286,16 @@ private void setClipboardData(String text) {
clipboard.setPrimaryClip(clip);
}

private List<Rect> getSystemGestureExclusionRects() {
if (Build.VERSION.SDK_INT >= 29) {
Window window = activity.getWindow();
View view = window.getDecorView();
return view.getSystemGestureExclusionRects();
}

return null;
}

private void setSystemGestureExclusionRects(ArrayList<Rect> rects) {
if (Build.VERSION.SDK_INT < 29) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.graphics.Rect;

import java.util.ArrayList;
import java.util.HashMap;

import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
Expand All @@ -21,6 +22,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@Config(manifest=Config.NONE)
@RunWith(RobolectricTestRunner.class)
Expand Down Expand Up @@ -50,12 +52,12 @@ public void setSystemExclusionRectsSendsSuccessMessageToFramework() throws JSONE
Rect gestureRect = new Rect(left, top, right, bottom);
expectedDecodedRects.add(gestureRect);

MethodCall callSystemGestureExclusionRects = new MethodCall(
MethodCall callSetSystemGestureExclusionRects = new MethodCall(
"SystemGestures.setSystemGestureExclusionRects",
inputRects
);

platformChannel.parsingMethodCallHandler.onMethodCall(callSystemGestureExclusionRects, resultsMock);
platformChannel.parsingMethodCallHandler.onMethodCall(callSetSystemGestureExclusionRects, resultsMock);
verify(platformMessageHandler, times(1)).setSystemGestureExclusionRects(expectedDecodedRects);
verify(resultsMock, times(1)).success(null);
}
Expand All @@ -69,11 +71,11 @@ public void setSystemExclusionRectsRequiresJSONArrayInput() {

ResultsMock resultsMock = mock(ResultsMock.class);
String nonJsonInput = "Non-JSON";
MethodCall callSystemGestureExclusionRects = new MethodCall(
MethodCall callSetSystemGestureExclusionRects = new MethodCall(
"SystemGestures.setSystemGestureExclusionRects",
nonJsonInput
);
platformChannel.parsingMethodCallHandler.onMethodCall(callSystemGestureExclusionRects, resultsMock);
platformChannel.parsingMethodCallHandler.onMethodCall(callSetSystemGestureExclusionRects, resultsMock);

String inputTypeError = "Input type is incorrect. Ensure that a List<Map<String, int>> is passed as the input for SystemGestureExclusionRects.setSystemGestureExclusionRects.";
verify(resultsMock, times(1)).error(
Expand All @@ -100,11 +102,11 @@ public void setSystemExclusionRectsSendsJSONExceptionOnIncorrectDataShape() thro
JSONArray inputArray = new JSONArray();
inputArray.put(jsonObject);

MethodCall callSystemGestureExclusionRects = new MethodCall(
MethodCall callSetSystemGestureExclusionRects = new MethodCall(
"SystemGestures.setSystemGestureExclusionRects",
inputArray
);
platformChannel.parsingMethodCallHandler.onMethodCall(callSystemGestureExclusionRects, resultsMock);
platformChannel.parsingMethodCallHandler.onMethodCall(callSetSystemGestureExclusionRects, resultsMock);
verify(resultsMock, times(1)).error(
"error",
"JSON error: Incorrect JSON data shape. To set system gesture exclusion rects, \n" +
Expand All @@ -113,6 +115,67 @@ public void setSystemExclusionRectsSendsJSONExceptionOnIncorrectDataShape() thro
);
}

@Test
public void itSendsSuccessMessageToFrameworkWhenGettingSystemGestureExclusionRects() throws JSONException {
// --- Test Setup ---
DartExecutor dartExecutor = mock(DartExecutor.class);
PlatformChannel platformChannel = new PlatformChannel(dartExecutor);
PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class);
platformChannel.setPlatformMessageHandler(platformMessageHandler);
Result result = mock(Result.class);

// Fake API output setup
ArrayList<Rect> fakeExclusionRects = new ArrayList<Rect>();
Rect gestureRect = new Rect(0, 0, 500, 250);
fakeExclusionRects.add(gestureRect);
when(platformMessageHandler.getSystemGestureExclusionRects()).thenReturn(fakeExclusionRects);

// Parsed API output that should be passed to result.success()
ArrayList<HashMap<String, Integer>> expectedEncodedOutputRects = new ArrayList<HashMap<String, Integer>>();
HashMap<String, Integer> rectMap = new HashMap<String, Integer>();
rectMap.put("top", 0);
rectMap.put("right", 500);
rectMap.put("bottom", 250);
rectMap.put("left", 0);
expectedEncodedOutputRects.add(rectMap);
MethodCall callGetSystemGestureExclusionRects = new MethodCall(
"SystemGestures.getSystemGestureExclusionRects",
null
);

// --- Execute Test ---
platformChannel.parsingMethodCallHandler.onMethodCall(callGetSystemGestureExclusionRects, result);

// --- Verify Results ---
verify(result, times(1)).success(expectedEncodedOutputRects);
}

@Test
public void itSendsAPILevelErrorWhenAndroidVersionIsTooLowWhenGettingSystemGestureExclusionRects() {
// --- Test Setup ---
DartExecutor dartExecutor = mock(DartExecutor.class);
PlatformChannel platformChannel = new PlatformChannel(dartExecutor);
PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class);
platformChannel.setPlatformMessageHandler(platformMessageHandler);
when(platformMessageHandler.getSystemGestureExclusionRects()).thenReturn(null);
Result result = mock(Result.class);

MethodCall callGetSystemGestureExclusionRects = new MethodCall(
"SystemGestures.getSystemGestureExclusionRects",
null
);

// --- Execute Test ---
platformChannel.parsingMethodCallHandler.onMethodCall(callGetSystemGestureExclusionRects, result);

// --- Verify Results ---
verify(result, times(1)).error(
"error",
"Exclusion rects only exist for Android API 29+.",
null
);
}

private class ResultsMock implements Result {
@Override
public void success(Object result) {}
Expand Down

0 comments on commit 898480f

Please sign in to comment.