Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.wm;
     18 
     19 import org.junit.Before;
     20 import org.junit.Ignore;
     21 import org.junit.Test;
     22 import org.junit.runner.RunWith;
     23 
     24 import android.graphics.Rect;
     25 import android.support.test.filters.SmallTest;
     26 import android.support.test.runner.AndroidJUnit4;
     27 import android.util.DisplayMetrics;
     28 import android.util.Log;
     29 import android.view.Display;
     30 
     31 import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
     32 import static com.android.server.wm.WindowManagerService.dipToPixel;
     33 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
     34 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
     35 import static org.junit.Assert.assertEquals;
     36 import static org.junit.Assert.assertNull;
     37 import static org.junit.Assert.assertTrue;
     38 
     39 /**
     40  * Tests for the {@link TaskPositioner} class.
     41  *
     42  * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
     43  */
     44 @SmallTest
     45 @RunWith(AndroidJUnit4.class)
     46 public class TaskPositionerTests extends WindowTestsBase {
     47 
     48     private final boolean DEBUGGING = false;
     49     private final String TAG = "TaskPositionerTest";
     50 
     51     private final static int MOUSE_DELTA_X = 5;
     52     private final static int MOUSE_DELTA_Y = 5;
     53 
     54     private int mMinVisibleWidth;
     55     private int mMinVisibleHeight;
     56     private TaskPositioner mPositioner;
     57 
     58     @Before
     59     public void setUp() throws Exception {
     60         super.setUp();
     61 
     62         TaskPositioner.setFactory(null);
     63 
     64         final Display display = mDisplayContent.getDisplay();
     65         final DisplayMetrics dm = new DisplayMetrics();
     66         display.getMetrics(dm);
     67 
     68         // This should be the same calculation as the TaskPositioner uses.
     69         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
     70         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
     71 
     72         mPositioner = TaskPositioner.create(sWm);
     73         mPositioner.register(mDisplayContent);
     74     }
     75 
     76     @Test
     77     public void testOverrideFactory() throws Exception {
     78         final boolean[] created = new boolean[1];
     79         created[0] = false;
     80         TaskPositioner.setFactory(new TaskPositioner.Factory() {
     81             @Override
     82             public TaskPositioner create(WindowManagerService service) {
     83                 created[0] = true;
     84                 return null;
     85             }
     86         });
     87 
     88         assertNull(TaskPositioner.create(sWm));
     89         assertTrue(created[0]);
     90     }
     91 
     92     /**
     93      * This tests that free resizing will allow to change the orientation as well
     94      * as does some basic tests (e.g. dragging in Y only will keep X stable).
     95      */
     96     @Test
     97     @Ignore
     98     public void testBasicFreeWindowResizing() throws Exception {
     99         final Rect r = new Rect(100, 220, 700, 520);
    100         final int midY = (r.top + r.bottom) / 2;
    101 
    102         // Start a drag resize starting upper left.
    103         mPositioner.startDrag(true /*resizing*/,
    104                 false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
    105         assertBoundsEquals(r, mPositioner.getWindowDragBounds());
    106 
    107         // Drag to a good landscape size.
    108         mPositioner.resizeDrag(0.0f, 0.0f);
    109         assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
    110                 mPositioner.getWindowDragBounds());
    111 
    112         // Drag to a good portrait size.
    113         mPositioner.resizeDrag(400.0f, 0.0f);
    114         assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
    115                 mPositioner.getWindowDragBounds());
    116 
    117         // Drag to a too small size for the width.
    118         mPositioner.resizeDrag(2000.0f, r.top);
    119         assertBoundsEquals(
    120                 new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
    121                 mPositioner.getWindowDragBounds());
    122 
    123         // Drag to a too small size for the height.
    124         mPositioner.resizeDrag(r.left, 2000.0f);
    125         assertBoundsEquals(
    126                 new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
    127                 mPositioner.getWindowDragBounds());
    128 
    129         // Start a drag resize left and see that only the left coord changes..
    130         mPositioner.startDrag(true /*resizing*/,
    131                 false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
    132 
    133         // Drag to the left.
    134         mPositioner.resizeDrag(0.0f, midY);
    135         assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
    136                 mPositioner.getWindowDragBounds());
    137 
    138         // Drag to the right.
    139         mPositioner.resizeDrag(200.0f, midY);
    140         assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    141                 mPositioner.getWindowDragBounds());
    142 
    143         // Drag to the top
    144         mPositioner.resizeDrag(r.left, 0.0f);
    145         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    146                 mPositioner.getWindowDragBounds());
    147 
    148         // Drag to the bottom
    149         mPositioner.resizeDrag(r.left, 1000.0f);
    150         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    151                 mPositioner.getWindowDragBounds());
    152     }
    153 
    154     /**
    155      * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
    156      */
    157     @Test
    158     @Ignore
    159     public void testFreeWindowResizingTestAllEdges() throws Exception {
    160         final Rect r = new Rect(100, 220, 700, 520);
    161         final int midX = (r.left + r.right) / 2;
    162         final int midY = (r.top + r.bottom) / 2;
    163 
    164         // Drag upper left.
    165         mPositioner.startDrag(true /*resizing*/,
    166                 false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
    167         mPositioner.resizeDrag(0.0f, 0.0f);
    168         assertTrue(r.left != mPositioner.getWindowDragBounds().left);
    169         assertEquals(r.right, mPositioner.getWindowDragBounds().right);
    170         assertTrue(r.top != mPositioner.getWindowDragBounds().top);
    171         assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
    172 
    173         // Drag upper.
    174         mPositioner.startDrag(true /*resizing*/,
    175                 false /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
    176         mPositioner.resizeDrag(0.0f, 0.0f);
    177         assertEquals(r.left, mPositioner.getWindowDragBounds().left);
    178         assertEquals(r.right, mPositioner.getWindowDragBounds().right);
    179         assertTrue(r.top != mPositioner.getWindowDragBounds().top);
    180         assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
    181 
    182         // Drag upper right.
    183         mPositioner.startDrag(true /*resizing*/,
    184                 false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
    185         mPositioner.resizeDrag(r.right + 100, 0.0f);
    186         assertEquals(r.left, mPositioner.getWindowDragBounds().left);
    187         assertTrue(r.right != mPositioner.getWindowDragBounds().right);
    188         assertTrue(r.top != mPositioner.getWindowDragBounds().top);
    189         assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
    190 
    191         // Drag right.
    192         mPositioner.startDrag(true /*resizing*/,
    193                 false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, midY, r);
    194         mPositioner.resizeDrag(r.right + 100, 0.0f);
    195         assertEquals(r.left, mPositioner.getWindowDragBounds().left);
    196         assertTrue(r.right != mPositioner.getWindowDragBounds().right);
    197         assertEquals(r.top, mPositioner.getWindowDragBounds().top);
    198         assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
    199 
    200         // Drag bottom right.
    201         mPositioner.startDrag(true /*resizing*/,
    202                 false /*preserveOrientation*/,
    203                 r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y, r);
    204         mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
    205         assertEquals(r.left, mPositioner.getWindowDragBounds().left);
    206         assertTrue(r.right != mPositioner.getWindowDragBounds().right);
    207         assertEquals(r.top, mPositioner.getWindowDragBounds().top);
    208         assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
    209 
    210         // Drag bottom.
    211         mPositioner.startDrag(true /*resizing*/,
    212                 false /*preserveOrientation*/, midX, r.bottom + MOUSE_DELTA_Y, r);
    213         mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
    214         assertEquals(r.left, mPositioner.getWindowDragBounds().left);
    215         assertEquals(r.right, mPositioner.getWindowDragBounds().right);
    216         assertEquals(r.top, mPositioner.getWindowDragBounds().top);
    217         assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
    218 
    219         // Drag bottom left.
    220         mPositioner.startDrag(true /*resizing*/,
    221                 false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y, r);
    222         mPositioner.resizeDrag(0.0f, r.bottom + 100);
    223         assertTrue(r.left != mPositioner.getWindowDragBounds().left);
    224         assertEquals(r.right, mPositioner.getWindowDragBounds().right);
    225         assertEquals(r.top, mPositioner.getWindowDragBounds().top);
    226         assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
    227 
    228         // Drag left.
    229         mPositioner.startDrag(true /*resizing*/,
    230                 false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midX, r);
    231         mPositioner.resizeDrag(0.0f, r.bottom + 100);
    232         assertTrue(r.left != mPositioner.getWindowDragBounds().left);
    233         assertEquals(r.right, mPositioner.getWindowDragBounds().right);
    234         assertEquals(r.top, mPositioner.getWindowDragBounds().top);
    235         assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
    236     }
    237 
    238     /**
    239      * This tests that a constrained landscape window will keep the aspect and do the
    240      * right things upon resizing when dragged from the top left corner.
    241      */
    242     @Test
    243     @Ignore
    244     public void testLandscapePreservedWindowResizingDragTopLeft() throws Exception {
    245         final Rect r = new Rect(100, 220, 700, 520);
    246 
    247         mPositioner.startDrag(true /*resizing*/,
    248                 true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
    249         assertBoundsEquals(r, mPositioner.getWindowDragBounds());
    250 
    251         // Drag to a good landscape size.
    252         mPositioner.resizeDrag(0.0f, 0.0f);
    253         assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
    254                 mPositioner.getWindowDragBounds());
    255 
    256         // Drag to a good portrait size.
    257         mPositioner.resizeDrag(400.0f, 0.0f);
    258         int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT);
    259         assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom),
    260                 mPositioner.getWindowDragBounds());
    261 
    262         // Drag to a too small size for the width.
    263         mPositioner.resizeDrag(2000.0f, r.top);
    264         final int w = mMinVisibleWidth;
    265         final int h = Math.round(w / MIN_ASPECT);
    266         assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
    267                 mPositioner.getWindowDragBounds());
    268 
    269         // Drag to a too small size for the height.
    270         mPositioner.resizeDrag(r.left, 2000.0f);
    271         assertBoundsEquals(
    272                 new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
    273                 mPositioner.getWindowDragBounds());
    274     }
    275 
    276     /**
    277      * This tests that a constrained landscape window will keep the aspect and do the
    278      * right things upon resizing when dragged from the left corner.
    279      */
    280     @Test
    281     @Ignore
    282     public void testLandscapePreservedWindowResizingDragLeft() throws Exception {
    283         final Rect r = new Rect(100, 220, 700, 520);
    284         final int midY = (r.top + r.bottom) / 2;
    285 
    286         mPositioner.startDrag(true /*resizing*/,
    287                 true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
    288 
    289         // Drag to the left.
    290         mPositioner.resizeDrag(0.0f, midY);
    291         assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
    292                 mPositioner.getWindowDragBounds());
    293 
    294         // Drag to the right.
    295         mPositioner.resizeDrag(200.0f, midY);
    296         assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    297                 mPositioner.getWindowDragBounds());
    298 
    299         // Drag all the way to the right and see the height also shrinking.
    300         mPositioner.resizeDrag(2000.0f, midY);
    301         final int w = mMinVisibleWidth;
    302         final int h = Math.round((float)w / MIN_ASPECT);
    303         assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
    304                 mPositioner.getWindowDragBounds());
    305 
    306         // Drag to the top.
    307         mPositioner.resizeDrag(r.left, 0.0f);
    308         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    309                 mPositioner.getWindowDragBounds());
    310 
    311         // Drag to the bottom.
    312         mPositioner.resizeDrag(r.left, 1000.0f);
    313         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    314                 mPositioner.getWindowDragBounds());
    315     }
    316 
    317     /**
    318      * This tests that a constrained landscape window will keep the aspect and do the
    319      * right things upon resizing when dragged from the top corner.
    320      */
    321     @Test
    322     @Ignore
    323     public void testLandscapePreservedWindowResizingDragTop() throws Exception {
    324         final Rect r = new Rect(100, 220, 700, 520);
    325         final int midX = (r.left + r.right) / 2;
    326 
    327         mPositioner.startDrag(true /*resizing*/,
    328                 true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
    329 
    330         // Drag to the left (no change).
    331         mPositioner.resizeDrag(0.0f, r.top);
    332         assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
    333                 mPositioner.getWindowDragBounds());
    334 
    335         // Drag to the right (no change).
    336         mPositioner.resizeDrag(2000.0f, r.top);
    337         assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
    338                 mPositioner.getWindowDragBounds());
    339 
    340         // Drag to the top.
    341         mPositioner.resizeDrag(300.0f, 0.0f);
    342         int h = r.bottom - MOUSE_DELTA_Y;
    343         int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT));
    344         assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
    345                 mPositioner.getWindowDragBounds());
    346 
    347         // Drag to the bottom.
    348         mPositioner.resizeDrag(r.left, 1000.0f);
    349         h = mMinVisibleHeight;
    350         assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom),
    351                 mPositioner.getWindowDragBounds());
    352     }
    353 
    354     /**
    355      * This tests that a constrained portrait window will keep the aspect and do the
    356      * right things upon resizing when dragged from the top left corner.
    357      */
    358     @Test
    359     @Ignore
    360     public void testPortraitPreservedWindowResizingDragTopLeft() throws Exception {
    361         final Rect r = new Rect(330, 100, 630, 600);
    362 
    363         mPositioner.startDrag(true /*resizing*/,
    364                 true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
    365         assertBoundsEquals(r, mPositioner.getWindowDragBounds());
    366 
    367         // Drag to a good landscape size.
    368         mPositioner.resizeDrag(0.0f, 0.0f);
    369         int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT);
    370         assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom),
    371                 mPositioner.getWindowDragBounds());
    372 
    373         // Drag to a good portrait size.
    374         mPositioner.resizeDrag(500.0f, 0.0f);
    375         assertBoundsEquals(new Rect(500 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
    376                 mPositioner.getWindowDragBounds());
    377 
    378         // Drag to a too small size for the height and the the width shrinking.
    379         mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f);
    380         final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT));
    381         final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT));
    382         assertBoundsEquals(
    383                 new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
    384                 mPositioner.getWindowDragBounds());
    385     }
    386 
    387     /**
    388      * This tests that a constrained portrait window will keep the aspect and do the
    389      * right things upon resizing when dragged from the left corner.
    390      */
    391     @Test
    392     @Ignore
    393     public void testPortraitPreservedWindowResizingDragLeft() throws Exception {
    394         final Rect r = new Rect(330, 100, 630, 600);
    395         final int midY = (r.top + r.bottom) / 2;
    396 
    397         mPositioner.startDrag(true /*resizing*/,
    398                 true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
    399 
    400         // Drag to the left.
    401         mPositioner.resizeDrag(0.0f, midY);
    402         int w = r.right - MOUSE_DELTA_X;
    403         int h = Math.round(w * MIN_ASPECT);
    404         assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h),
    405                 mPositioner.getWindowDragBounds());
    406 
    407         // Drag to the right.
    408         mPositioner.resizeDrag(450.0f, midY);
    409         assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    410                 mPositioner.getWindowDragBounds());
    411 
    412         // Drag all the way to the right.
    413         mPositioner.resizeDrag(2000.0f, midY);
    414         w = mMinVisibleWidth;
    415         h = Math.max(Math.round((float)w * MIN_ASPECT), r.height());
    416         assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
    417                 mPositioner.getWindowDragBounds());
    418 
    419         // Drag to the top.
    420         mPositioner.resizeDrag(r.left, 0.0f);
    421         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    422                 mPositioner.getWindowDragBounds());
    423 
    424         // Drag to the bottom.
    425         mPositioner.resizeDrag(r.left, 1000.0f);
    426         assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
    427                 mPositioner.getWindowDragBounds());
    428     }
    429 
    430     /**
    431      * This tests that a constrained portrait window will keep the aspect and do the
    432      * right things upon resizing when dragged from the top corner.
    433      */
    434     @Test
    435     @Ignore
    436     public void testPortraitPreservedWindowResizingDragTop() throws Exception {
    437         final Rect r = new Rect(330, 100, 630, 600);
    438         final int midX = (r.left + r.right) / 2;
    439 
    440         mPositioner.startDrag(true /*resizing*/,
    441                 true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
    442 
    443         // Drag to the left (no change).
    444         mPositioner.resizeDrag(0.0f, r.top);
    445         assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
    446                 mPositioner.getWindowDragBounds());
    447 
    448         // Drag to the right (no change).
    449         mPositioner.resizeDrag(2000.0f, r.top);
    450         assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
    451                 mPositioner.getWindowDragBounds());
    452 
    453         // Drag to the top.
    454         mPositioner.resizeDrag(300.0f, 0.0f);
    455         int h = r.bottom - MOUSE_DELTA_Y;
    456         int w = Math.min(r.width(), Math.round(h / MIN_ASPECT));
    457         assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
    458                 mPositioner.getWindowDragBounds());
    459 
    460         // Drag to the bottom.
    461         mPositioner.resizeDrag(r.left, 1000.0f);
    462         h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT));
    463         w = Math.round(h / MIN_ASPECT);
    464         assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom),
    465                 mPositioner.getWindowDragBounds());
    466     }
    467 
    468     private void assertBoundsEquals(Rect expected, Rect actual) {
    469         if (DEBUGGING) {
    470             if (!expected.equals(actual)) {
    471                 Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
    472                         + ") " + Log.getStackTraceString(new Throwable()));
    473             }
    474         }
    475         assertEquals(expected.left, actual.left);
    476         assertEquals(expected.right, actual.right);
    477         assertEquals(expected.top, actual.top);
    478         assertEquals(expected.bottom, actual.bottom);
    479     }
    480 }
    481