Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 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 android.view;
     18 
     19 import android.graphics.PixelFormat;
     20 import android.os.IBinder;
     21 import android.util.AndroidRuntimeException;
     22 import android.util.Config;
     23 import android.util.Log;
     24 import android.view.WindowManager;
     25 import android.view.inputmethod.InputMethodManager;
     26 
     27 final class WindowLeaked extends AndroidRuntimeException {
     28     public WindowLeaked(String msg) {
     29         super(msg);
     30     }
     31 }
     32 
     33 /**
     34  * Low-level communication with the global system window manager.  It implements
     35  * the ViewManager interface, allowing you to add any View subclass as a
     36  * top-level window on the screen. Additional window manager specific layout
     37  * parameters are defined for control over how windows are displayed.
     38  * It also implemens the WindowManager interface, allowing you to control the
     39  * displays attached to the device.
     40  *
     41  * <p>Applications will not normally use WindowManager directly, instead relying
     42  * on the higher-level facilities in {@link android.app.Activity} and
     43  * {@link android.app.Dialog}.
     44  *
     45  * <p>Even for low-level window manager access, it is almost never correct to use
     46  * this class.  For example, {@link android.app.Activity#getWindowManager}
     47  * provides a ViewManager for adding windows that are associated with that
     48  * activity -- the window manager will not normally allow you to add arbitrary
     49  * windows that are not associated with an activity.
     50  *
     51  * @hide
     52  */
     53 public class WindowManagerImpl implements WindowManager {
     54     /**
     55      * The user is navigating with keys (not the touch screen), so
     56      * navigational focus should be shown.
     57      */
     58     public static final int RELAYOUT_IN_TOUCH_MODE = 0x1;
     59     /**
     60      * This is the first time the window is being drawn,
     61      * so the client must call drawingFinished() when done
     62      */
     63     public static final int RELAYOUT_FIRST_TIME = 0x2;
     64 
     65     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
     66     public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_IN_TOUCH_MODE;
     67 
     68     public static final int ADD_OKAY = 0;
     69     public static final int ADD_BAD_APP_TOKEN = -1;
     70     public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
     71     public static final int ADD_NOT_APP_TOKEN = -3;
     72     public static final int ADD_APP_EXITING = -4;
     73     public static final int ADD_DUPLICATE_ADD = -5;
     74     public static final int ADD_STARTING_NOT_NEEDED = -6;
     75     public static final int ADD_MULTIPLE_SINGLETON = -7;
     76     public static final int ADD_PERMISSION_DENIED = -8;
     77 
     78     public static WindowManagerImpl getDefault()
     79     {
     80         return mWindowManager;
     81     }
     82 
     83     public void addView(View view)
     84     {
     85         addView(view, new WindowManager.LayoutParams(
     86             WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
     87     }
     88 
     89     public void addView(View view, ViewGroup.LayoutParams params)
     90     {
     91         addView(view, params, false);
     92     }
     93 
     94     public void addViewNesting(View view, ViewGroup.LayoutParams params)
     95     {
     96         addView(view, params, false);
     97     }
     98 
     99     private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
    100     {
    101         if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
    102 
    103         if (!(params instanceof WindowManager.LayoutParams)) {
    104             throw new IllegalArgumentException(
    105                     "Params must be WindowManager.LayoutParams");
    106         }
    107 
    108         final WindowManager.LayoutParams wparams
    109                 = (WindowManager.LayoutParams)params;
    110 
    111         ViewRoot root;
    112         View panelParentView = null;
    113 
    114         synchronized (this) {
    115             // Here's an odd/questionable case: if someone tries to add a
    116             // view multiple times, then we simply bump up a nesting count
    117             // and they need to remove the view the corresponding number of
    118             // times to have it actually removed from the window manager.
    119             // This is useful specifically for the notification manager,
    120             // which can continually add/remove the same view as a
    121             // notification gets updated.
    122             int index = findViewLocked(view, false);
    123             if (index >= 0) {
    124                 if (!nest) {
    125                     throw new IllegalStateException("View " + view
    126                             + " has already been added to the window manager.");
    127                 }
    128                 root = mRoots[index];
    129                 root.mAddNesting++;
    130                 // Update layout parameters.
    131                 view.setLayoutParams(wparams);
    132                 root.setLayoutParams(wparams, true);
    133                 return;
    134             }
    135 
    136             // If this is a panel window, then find the window it is being
    137             // attached to for future reference.
    138             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
    139                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    140                 final int count = mViews != null ? mViews.length : 0;
    141                 for (int i=0; i<count; i++) {
    142                     if (mRoots[i].mWindow.asBinder() == wparams.token) {
    143                         panelParentView = mViews[i];
    144                     }
    145                 }
    146             }
    147 
    148             root = new ViewRoot(view.getContext());
    149             root.mAddNesting = 1;
    150 
    151             view.setLayoutParams(wparams);
    152 
    153             if (mViews == null) {
    154                 index = 1;
    155                 mViews = new View[1];
    156                 mRoots = new ViewRoot[1];
    157                 mParams = new WindowManager.LayoutParams[1];
    158             } else {
    159                 index = mViews.length + 1;
    160                 Object[] old = mViews;
    161                 mViews = new View[index];
    162                 System.arraycopy(old, 0, mViews, 0, index-1);
    163                 old = mRoots;
    164                 mRoots = new ViewRoot[index];
    165                 System.arraycopy(old, 0, mRoots, 0, index-1);
    166                 old = mParams;
    167                 mParams = new WindowManager.LayoutParams[index];
    168                 System.arraycopy(old, 0, mParams, 0, index-1);
    169             }
    170             index--;
    171 
    172             mViews[index] = view;
    173             mRoots[index] = root;
    174             mParams[index] = wparams;
    175         }
    176         // do this last because it fires off messages to start doing things
    177         root.setView(view, wparams, panelParentView);
    178     }
    179 
    180     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    181         if (!(params instanceof WindowManager.LayoutParams)) {
    182             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    183         }
    184 
    185         final WindowManager.LayoutParams wparams
    186                 = (WindowManager.LayoutParams)params;
    187 
    188         view.setLayoutParams(wparams);
    189 
    190         synchronized (this) {
    191             int index = findViewLocked(view, true);
    192             ViewRoot root = mRoots[index];
    193             mParams[index] = wparams;
    194             root.setLayoutParams(wparams, false);
    195         }
    196     }
    197 
    198     public void removeView(View view) {
    199         synchronized (this) {
    200             int index = findViewLocked(view, true);
    201             View curView = removeViewLocked(index);
    202             if (curView == view) {
    203                 return;
    204             }
    205 
    206             throw new IllegalStateException("Calling with view " + view
    207                     + " but the ViewRoot is attached to " + curView);
    208         }
    209     }
    210 
    211     public void removeViewImmediate(View view) {
    212         synchronized (this) {
    213             int index = findViewLocked(view, true);
    214             ViewRoot root = mRoots[index];
    215             View curView = root.getView();
    216 
    217             root.mAddNesting = 0;
    218             root.die(true);
    219             finishRemoveViewLocked(curView, index);
    220             if (curView == view) {
    221                 return;
    222             }
    223 
    224             throw new IllegalStateException("Calling with view " + view
    225                     + " but the ViewRoot is attached to " + curView);
    226         }
    227     }
    228 
    229     View removeViewLocked(int index) {
    230         ViewRoot root = mRoots[index];
    231         View view = root.getView();
    232 
    233         // Don't really remove until we have matched all calls to add().
    234         root.mAddNesting--;
    235         if (root.mAddNesting > 0) {
    236             return view;
    237         }
    238 
    239         InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
    240         if (imm != null) {
    241             imm.windowDismissed(mViews[index].getWindowToken());
    242         }
    243         root.die(false);
    244         finishRemoveViewLocked(view, index);
    245         return view;
    246     }
    247 
    248     void finishRemoveViewLocked(View view, int index) {
    249         final int count = mViews.length;
    250 
    251         // remove it from the list
    252         View[] tmpViews = new View[count-1];
    253         removeItem(tmpViews, mViews, index);
    254         mViews = tmpViews;
    255 
    256         ViewRoot[] tmpRoots = new ViewRoot[count-1];
    257         removeItem(tmpRoots, mRoots, index);
    258         mRoots = tmpRoots;
    259 
    260         WindowManager.LayoutParams[] tmpParams
    261                 = new WindowManager.LayoutParams[count-1];
    262         removeItem(tmpParams, mParams, index);
    263         mParams = tmpParams;
    264 
    265         view.assignParent(null);
    266         // func doesn't allow null...  does it matter if we clear them?
    267         //view.setLayoutParams(null);
    268     }
    269 
    270     public void closeAll(IBinder token, String who, String what) {
    271         synchronized (this) {
    272             if (mViews == null)
    273                 return;
    274 
    275             int count = mViews.length;
    276             //Log.i("foo", "Closing all windows of " + token);
    277             for (int i=0; i<count; i++) {
    278                 //Log.i("foo", "@ " + i + " token " + mParams[i].token
    279                 //        + " view " + mRoots[i].getView());
    280                 if (token == null || mParams[i].token == token) {
    281                     ViewRoot root = mRoots[i];
    282                     root.mAddNesting = 1;
    283 
    284                     //Log.i("foo", "Force closing " + root);
    285                     if (who != null) {
    286                         WindowLeaked leak = new WindowLeaked(
    287                                 what + " " + who + " has leaked window "
    288                                 + root.getView() + " that was originally added here");
    289                         leak.setStackTrace(root.getLocation().getStackTrace());
    290                         Log.e("WindowManager", leak.getMessage(), leak);
    291                     }
    292 
    293                     removeViewLocked(i);
    294                     i--;
    295                     count--;
    296                 }
    297             }
    298         }
    299     }
    300 
    301     public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
    302         ViewParent vp = view.getParent();
    303         while (vp != null && !(vp instanceof ViewRoot)) {
    304             vp = vp.getParent();
    305         }
    306 
    307         if (vp == null) return null;
    308 
    309         ViewRoot vr = (ViewRoot)vp;
    310 
    311         int N = mRoots.length;
    312         for (int i = 0; i < N; ++i) {
    313             if (mRoots[i] == vr) {
    314                 return mParams[i];
    315             }
    316         }
    317 
    318         return null;
    319     }
    320 
    321     public void closeAll() {
    322         closeAll(null, null, null);
    323     }
    324 
    325     public Display getDefaultDisplay() {
    326         return new Display(Display.DEFAULT_DISPLAY);
    327     }
    328 
    329     private View[] mViews;
    330     private ViewRoot[] mRoots;
    331     private WindowManager.LayoutParams[] mParams;
    332 
    333     private static void removeItem(Object[] dst, Object[] src, int index)
    334     {
    335         if (dst.length > 0) {
    336             if (index > 0) {
    337                 System.arraycopy(src, 0, dst, 0, index);
    338             }
    339             if (index < dst.length) {
    340                 System.arraycopy(src, index+1, dst, index, src.length-index-1);
    341             }
    342         }
    343     }
    344 
    345     private int findViewLocked(View view, boolean required)
    346     {
    347         synchronized (this) {
    348             final int count = mViews != null ? mViews.length : 0;
    349             for (int i=0; i<count; i++) {
    350                 if (mViews[i] == view) {
    351                     return i;
    352                 }
    353             }
    354             if (required) {
    355                 throw new IllegalArgumentException(
    356                         "View not attached to window manager");
    357             }
    358             return -1;
    359         }
    360     }
    361 
    362     private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
    363 }
    364