Merge "Removing RWC#getValidLaunchRootTaskInTaskDisplayArea" into tm-dev
This commit is contained in:
@@ -18,15 +18,9 @@ package com.android.server.wm;
|
|||||||
|
|
||||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||||
import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
|
import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
|
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
|
||||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
|
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
|
||||||
@@ -2791,35 +2785,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
Task rootTask = null;
|
Task rootTask = null;
|
||||||
|
|
||||||
// Next preference for root task goes to the taskDisplayArea candidate.
|
// Next preference for root task goes to the taskDisplayArea candidate.
|
||||||
if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
|
if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null
|
||||||
|
&& canLaunchOnDisplay(r, launchParams.mPreferredTaskDisplayArea.getDisplayId())) {
|
||||||
taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
|
taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
|
||||||
}
|
}
|
||||||
|
if (taskDisplayArea == null && displayId != INVALID_DISPLAY
|
||||||
if (taskDisplayArea == null && displayId != INVALID_DISPLAY) {
|
&& canLaunchOnDisplay(r, displayId)) {
|
||||||
final DisplayContent displayContent = getDisplayContent(displayId);
|
taskDisplayArea = getDisplayContent(displayId).getDefaultTaskDisplayArea();
|
||||||
if (displayContent != null) {
|
|
||||||
taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskDisplayArea != null) {
|
if (taskDisplayArea != null) {
|
||||||
final int tdaDisplayId = taskDisplayArea.getDisplayId();
|
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
|
||||||
if (canLaunchOnDisplay(r, tdaDisplayId)) {
|
sourceTask, launchParams, launchFlags, activityType, onTop);
|
||||||
if (r != null) {
|
|
||||||
final Task result = getValidLaunchRootTaskInTaskDisplayArea(
|
|
||||||
taskDisplayArea, r, candidateTask, options, launchParams);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Falling back to default task container
|
|
||||||
taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea();
|
|
||||||
rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
|
|
||||||
sourceTask, launchParams, launchFlags, activityType, onTop);
|
|
||||||
if (rootTask != null) {
|
|
||||||
return rootTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give preference to the root task and display of the input task and activity if they
|
// Give preference to the root task and display of the input task and activity if they
|
||||||
@@ -2869,103 +2845,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
return r.canBeLaunchedOnDisplay(displayId);
|
return r.canBeLaunchedOnDisplay(displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a topmost root task on the display area, that is a valid launch root task for
|
|
||||||
* specified activity. If there is no such root task, new dynamic root task can be created.
|
|
||||||
*
|
|
||||||
* @param taskDisplayArea Target display area.
|
|
||||||
* @param r Activity that should be launched there.
|
|
||||||
* @param candidateTask The possible task the activity might be put in.
|
|
||||||
* @return Existing root task if there is a valid one, new dynamic root task if it is valid
|
|
||||||
* or null.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
Task getValidLaunchRootTaskInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea,
|
|
||||||
@NonNull ActivityRecord r, @Nullable Task candidateTask,
|
|
||||||
@Nullable ActivityOptions options,
|
|
||||||
@Nullable LaunchParamsController.LaunchParams launchParams) {
|
|
||||||
if (!r.canBeLaunchedOnDisplay(taskDisplayArea.getDisplayId())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If {@code r} is already in target display area and its task is the same as the candidate
|
|
||||||
// task, the intention should be getting a launch root task for the reusable activity, so we
|
|
||||||
// can use the existing root task.
|
|
||||||
if (candidateTask != null) {
|
|
||||||
final TaskDisplayArea attachedTaskDisplayArea = candidateTask.getDisplayArea();
|
|
||||||
if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) {
|
|
||||||
return candidateTask.getRootTask();
|
|
||||||
}
|
|
||||||
// Or the candidate task is already a root task that can be reused by reparenting
|
|
||||||
// it to the target display.
|
|
||||||
if (candidateTask.isRootTask()) {
|
|
||||||
final Task rootTask = candidateTask.getRootTask();
|
|
||||||
rootTask.reparent(taskDisplayArea, true /* onTop */);
|
|
||||||
return rootTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int windowingMode;
|
|
||||||
if (launchParams != null) {
|
|
||||||
// When launch params is not null, we always defer to its windowing mode. Sometimes
|
|
||||||
// it could be unspecified, which indicates it should inherit windowing mode from
|
|
||||||
// display.
|
|
||||||
windowingMode = launchParams.mWindowingMode;
|
|
||||||
} else {
|
|
||||||
windowingMode = options != null ? options.getLaunchWindowingMode()
|
|
||||||
: r.getWindowingMode();
|
|
||||||
}
|
|
||||||
windowingMode = taskDisplayArea.validateWindowingMode(windowingMode, r, candidateTask);
|
|
||||||
|
|
||||||
// Return the topmost valid root task on the display.
|
|
||||||
final int targetWindowingMode = windowingMode;
|
|
||||||
final Task topmostValidRootTask = taskDisplayArea.getRootTask(rootTask ->
|
|
||||||
isValidLaunchRootTask(rootTask, r, targetWindowingMode));
|
|
||||||
if (topmostValidRootTask != null) {
|
|
||||||
return topmostValidRootTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no valid root task on the secondary display area - check if new dynamic root
|
|
||||||
// task will do.
|
|
||||||
if (taskDisplayArea != getDisplayContent(taskDisplayArea.getDisplayId())
|
|
||||||
.getDefaultTaskDisplayArea()) {
|
|
||||||
final int activityType =
|
|
||||||
options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
|
|
||||||
? options.getLaunchActivityType() : r.getActivityType();
|
|
||||||
return taskDisplayArea.createRootTask(
|
|
||||||
windowingMode, activityType, true /*onTop*/, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can probably be consolidated into getLaunchRootTask()...
|
|
||||||
private boolean isValidLaunchRootTask(Task task, ActivityRecord r, int windowingMode) {
|
|
||||||
switch (task.getActivityType()) {
|
|
||||||
case ACTIVITY_TYPE_HOME:
|
|
||||||
return r.isActivityTypeHome();
|
|
||||||
case ACTIVITY_TYPE_RECENTS:
|
|
||||||
return r.isActivityTypeRecents();
|
|
||||||
case ACTIVITY_TYPE_ASSISTANT:
|
|
||||||
return r.isActivityTypeAssistant();
|
|
||||||
case ACTIVITY_TYPE_DREAM:
|
|
||||||
return r.isActivityTypeDream();
|
|
||||||
}
|
|
||||||
if (task.mCreatedByOrganizer) {
|
|
||||||
// Don't launch directly into task created by organizer...but why can't we?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// There is a 1-to-1 relationship between root task and task when not in
|
|
||||||
// primary split-windowing mode.
|
|
||||||
if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|
|
||||||
&& r.supportsSplitScreenWindowingModeInDisplayArea(task.getDisplayArea())
|
|
||||||
&& (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|
|
||||||
|| windowingMode == WINDOWING_MODE_UNDEFINED)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
|
int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
|
||||||
@Nullable Task task) {
|
@Nullable Task task) {
|
||||||
// Preference is given to the activity type for the activity then the task since the type
|
// Preference is given to the activity type for the activity then the task since the type
|
||||||
|
|||||||
@@ -941,36 +941,32 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
|
|||||||
Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
|
Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
|
||||||
@Nullable Task candidateTask, @Nullable Task sourceTask,
|
@Nullable Task candidateTask, @Nullable Task sourceTask,
|
||||||
@Nullable ActivityOptions options, int launchFlags) {
|
@Nullable ActivityOptions options, int launchFlags) {
|
||||||
|
final int resolvedWindowingMode =
|
||||||
|
windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode;
|
||||||
// Need to pass in a determined windowing mode to see if a new root task should be created,
|
// Need to pass in a determined windowing mode to see if a new root task should be created,
|
||||||
// so use its parent's windowing mode if it is undefined.
|
// so use its parent's windowing mode if it is undefined.
|
||||||
if (!alwaysCreateRootTask(
|
if (!alwaysCreateRootTask(resolvedWindowingMode, activityType)) {
|
||||||
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(),
|
Task rootTask = getRootTask(resolvedWindowingMode, activityType);
|
||||||
activityType)) {
|
|
||||||
Task rootTask = getRootTask(windowingMode, activityType);
|
|
||||||
if (rootTask != null) {
|
if (rootTask != null) {
|
||||||
return rootTask;
|
return rootTask;
|
||||||
}
|
}
|
||||||
} else if (candidateTask != null) {
|
} else if (candidateTask != null) {
|
||||||
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
|
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
|
||||||
final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options,
|
final Task launchRootTask = getLaunchRootTask(resolvedWindowingMode, activityType,
|
||||||
sourceTask, launchFlags);
|
options, sourceTask, launchFlags);
|
||||||
if (launchRootTask != null) {
|
if (launchRootTask != null) {
|
||||||
if (candidateTask.getParent() == null) {
|
if (candidateTask.getParent() == null) {
|
||||||
launchRootTask.addChild(candidateTask, position);
|
launchRootTask.addChild(candidateTask, position);
|
||||||
} else if (candidateTask.getParent() != launchRootTask) {
|
} else if (candidateTask.getParent() != launchRootTask) {
|
||||||
candidateTask.reparent(launchRootTask, position);
|
candidateTask.reparent(launchRootTask, position);
|
||||||
}
|
}
|
||||||
} else if (candidateTask.getDisplayArea() != this || !candidateTask.isRootTask()) {
|
} else if (candidateTask.getDisplayArea() != this) {
|
||||||
if (candidateTask.getParent() == null) {
|
if (candidateTask.getParent() == null) {
|
||||||
addChild(candidateTask, position);
|
addChild(candidateTask, position);
|
||||||
} else {
|
} else {
|
||||||
candidateTask.reparent(this, onTop);
|
candidateTask.reparent(this, onTop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
|
|
||||||
if (candidateTask.getWindowingMode() != windowingMode) {
|
|
||||||
candidateTask.setWindowingMode(windowingMode);
|
|
||||||
}
|
|
||||||
return candidateTask.getRootTask();
|
return candidateTask.getRootTask();
|
||||||
}
|
}
|
||||||
return new Task.Builder(mAtmService)
|
return new Task.Builder(mAtmService)
|
||||||
|
|||||||
@@ -157,12 +157,26 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
|
|||||||
// display.
|
// display.
|
||||||
if (launchMode == WINDOWING_MODE_UNDEFINED
|
if (launchMode == WINDOWING_MODE_UNDEFINED
|
||||||
&& canInheritWindowingModeFromSource(display, source)) {
|
&& canInheritWindowingModeFromSource(display, source)) {
|
||||||
launchMode = source.getWindowingMode();
|
// The source's windowing mode may be different from its task, e.g. activity is set
|
||||||
|
// to fullscreen and its task is pinned windowing mode when the activity is entering
|
||||||
|
// pip.
|
||||||
|
launchMode = source.getTask().getWindowingMode();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
appendLog("inherit-from-source="
|
appendLog("inherit-from-source="
|
||||||
+ WindowConfiguration.windowingModeToString(launchMode));
|
+ WindowConfiguration.windowingModeToString(launchMode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If the launch windowing mode is still undefined, inherit from the target task if the
|
||||||
|
// task is already on the right display area (otherwise, the task may be on a different
|
||||||
|
// display area that has incompatible windowing mode).
|
||||||
|
if (launchMode == WINDOWING_MODE_UNDEFINED
|
||||||
|
&& task != null && task.getTaskDisplayArea() == suggestedDisplayArea) {
|
||||||
|
launchMode = task.getWindowingMode();
|
||||||
|
if (DEBUG) {
|
||||||
|
appendLog("inherit-from-task="
|
||||||
|
+ WindowConfiguration.windowingModeToString(launchMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
// hasInitialBounds is set if either activity options or layout has specified bounds. If
|
// hasInitialBounds is set if either activity options or layout has specified bounds. If
|
||||||
// that's set we'll skip some adjustments later to avoid overriding the initial bounds.
|
// that's set we'll skip some adjustments later to avoid overriding the initial bounds.
|
||||||
boolean hasInitialBounds = false;
|
boolean hasInitialBounds = false;
|
||||||
|
|||||||
@@ -925,14 +925,10 @@ public class ActivityStarterTests extends WindowTestsBase {
|
|||||||
any(), anyBoolean(), anyBoolean(), eq(false));
|
any(), anyBoolean(), anyBoolean(), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActivityRecord createSingleTaskActivityOn(Task stack) {
|
private ActivityRecord createSingleTaskActivityOn(Task task) {
|
||||||
final ComponentName componentName = ComponentName.createRelative(
|
final ComponentName componentName = ComponentName.createRelative(
|
||||||
DEFAULT_COMPONENT_PACKAGE_NAME,
|
DEFAULT_COMPONENT_PACKAGE_NAME,
|
||||||
DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
|
DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
|
||||||
final Task task = new TaskBuilder(mSupervisor)
|
|
||||||
.setComponent(componentName)
|
|
||||||
.setParentTaskFragment(stack)
|
|
||||||
.build();
|
|
||||||
return new ActivityBuilder(mAtm)
|
return new ActivityBuilder(mAtm)
|
||||||
.setComponent(componentName)
|
.setComponent(componentName)
|
||||||
.setLaunchMode(LAUNCH_SINGLE_TASK)
|
.setLaunchMode(LAUNCH_SINGLE_TASK)
|
||||||
|
|||||||
@@ -938,7 +938,33 @@ public class RootWindowContainerTests extends WindowTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetValidLaunchRootTaskOnDisplayWithCandidateRootTask() {
|
public void testGetLaunchRootTaskOnSecondaryTaskDisplayArea() {
|
||||||
|
// Adding another TaskDisplayArea to the default display.
|
||||||
|
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
|
||||||
|
final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(display,
|
||||||
|
mWm, "TDA", FEATURE_VENDOR_FIRST);
|
||||||
|
display.addChild(taskDisplayArea, POSITION_BOTTOM);
|
||||||
|
|
||||||
|
// Making sure getting the root task from the preferred TDA
|
||||||
|
LaunchParamsController.LaunchParams launchParams =
|
||||||
|
new LaunchParamsController.LaunchParams();
|
||||||
|
launchParams.mPreferredTaskDisplayArea = taskDisplayArea;
|
||||||
|
Task root = mRootWindowContainer.getLaunchRootTask(null /* r */, null /* options */,
|
||||||
|
null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams,
|
||||||
|
0 /* launchParams */);
|
||||||
|
assertEquals(taskDisplayArea, root.getTaskDisplayArea());
|
||||||
|
|
||||||
|
// Making sure still getting the root task from the preferred TDA when passing in a
|
||||||
|
// launching activity.
|
||||||
|
ActivityRecord r = new ActivityBuilder(mAtm).build();
|
||||||
|
root = mRootWindowContainer.getLaunchRootTask(r, null /* options */,
|
||||||
|
null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams,
|
||||||
|
0 /* launchParams */);
|
||||||
|
assertEquals(taskDisplayArea, root.getTaskDisplayArea());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetOrCreateRootTaskOnDisplayWithCandidateRootTask() {
|
||||||
// Create a root task with an activity on secondary display.
|
// Create a root task with an activity on secondary display.
|
||||||
final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
|
final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
|
||||||
600).build();
|
600).build();
|
||||||
@@ -947,9 +973,9 @@ public class RootWindowContainerTests extends WindowTestsBase {
|
|||||||
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
|
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||||
|
|
||||||
// Make sure the root task is valid and can be reused on default display.
|
// Make sure the root task is valid and can be reused on default display.
|
||||||
final Task rootTask = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea(
|
final Task rootTask = mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootTask(
|
||||||
mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
|
activity, null /* options */, task, null /* sourceTask */, null /* launchParams */,
|
||||||
null /* options */, null /* launchParams */);
|
0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||||
assertEquals(task, rootTask);
|
assertEquals(task, rootTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
|
|||||||
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
|
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
|
||||||
WINDOWING_MODE_FULLSCREEN);
|
WINDOWING_MODE_FULLSCREEN);
|
||||||
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
|
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
|
||||||
source.setWindowingMode(WINDOWING_MODE_FREEFORM);
|
source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
|
||||||
|
|
||||||
assertEquals(RESULT_CONTINUE,
|
assertEquals(RESULT_CONTINUE,
|
||||||
new CalculateRequestBuilder().setSource(source).calculate());
|
new CalculateRequestBuilder().setSource(source).calculate());
|
||||||
@@ -951,7 +951,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
|
|||||||
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
|
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
|
||||||
WINDOWING_MODE_FULLSCREEN);
|
WINDOWING_MODE_FULLSCREEN);
|
||||||
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
|
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
|
||||||
source.setWindowingMode(WINDOWING_MODE_FREEFORM);
|
source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
|
||||||
|
|
||||||
final ActivityOptions options = ActivityOptions.makeBasic();
|
final ActivityOptions options = ActivityOptions.makeBasic();
|
||||||
final Rect expected = new Rect(0, 0, 150, 150);
|
final Rect expected = new Rect(0, 0, 150, 150);
|
||||||
|
|||||||
Reference in New Issue
Block a user