19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.BroadcastReceiver;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.IntentFilter;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PixelFormat;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
27ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectimport android.util.Log;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Gravity;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewConfiguration;
34c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.view.ViewGroup;
35ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectimport android.view.ViewParent;
366dd005b48138708762bfade0081d031a2a4a3822Dianne Hackbornimport android.view.ViewRootImpl;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View.OnClickListener;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager.LayoutParams;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
41ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project/*
42ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * Implementation notes:
43ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * - The zoom controls are displayed in their own window.
44ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project *   (Easier for the client and better performance)
45105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * - This window is never touchable, and by default is not focusable.
46105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   Its rect is quite big (fills horizontally) but has empty space between the
47105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   edges and center.  Touches there should be given to the owner.  Instead of
48105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   having the window touchable and dispatching these empty touch events to the
49105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   owner, we set the window to not touchable and steal events from owner
50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   via onTouchListener.
51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * - To make the buttons clickable, it attaches an OnTouchListener to the owner
52105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   view and does the hit detection locally (attaches when visible, detaches when invisible).
53ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * - When it is focusable, it forwards uninteresting events to the owner view's
54ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project *   view hierarchy.
55ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project */
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
57ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * The {@link ZoomButtonsController} handles showing and hiding the zoom
58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * controls and positioning it relative to an owner view. It also gives the
59105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * client access to the zoom controls container, allowing for additional
60105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * accessory buttons to be shown in the zoom controls window.
61ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * <p>
62105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Typically, clients should call {@link #setVisible(boolean) setVisible(true)}
63105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * on a touch down or move (no need to call {@link #setVisible(boolean)
64105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * setVisible(false)} since it will time out on its own). Also, whenever the
65105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * owner cannot be zoomed further, the client should update
66ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * {@link #setZoomInEnabled(boolean)} and {@link #setZoomOutEnabled(boolean)}.
67ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * <p>
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you are using this with a custom View, please call
6916bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * {@link #setVisible(boolean) setVisible(false)} from
7016bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * {@link View#onDetachedFromWindow} and from {@link View#onVisibilityChanged}
7116bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * when <code>visibility != View.VISIBLE</code>.
72105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
74ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectpublic class ZoomButtonsController implements View.OnTouchListener {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "ZoomButtonsController";
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TIMEOUT =
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            (int) ViewConfiguration.getZoomControlsTimeout();
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20;
82ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private int mTouchPaddingScaledSq;
83c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
84470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Context mContext;
85470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final WindowManager mWindowManager;
86105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private boolean mAutoDismissControls = true;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
89b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * The view that is being zoomed by this zoom controller.
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
91470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final View mOwnerView;
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
94ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The location of the owner view on the screen. This is recalculated
95b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * each time the zoom controller is shown.
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
97470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mOwnerViewRawLocation = new int[2];
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The container that is added as a window.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
102470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final FrameLayout mContainer;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private LayoutParams mContainerLayoutParams;
104470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mContainerRawLocation = new int[2];
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ZoomControls mControls;
107c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The view (or null) that should receive touch events. This will get set if
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the touch down hits the container. It will be reset on the touch up.
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mTouchTargetView;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The {@link #mTouchTargetView}'s location in window, set on touch down.
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
116470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTouchTargetWindowLocation = new int[2];
117105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
119b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * If the zoom controller is dismissed but the user is still in a touch
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interaction, we set this to true. This will ignore all touch events until
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * up/cancel, and then set the owner's touch listener to null.
122105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * <p>
123105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Otherwise, the owner view would get mismatched events (i.e., touch move
124105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * even though it never got the touch down.)
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mReleaseTouchListenerOnUp;
127c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
128ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /** Whether the container has been added to the window manager. */
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mIsVisible;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
131470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Rect mTempRect = new Rect();
132470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTempIntArray = new int[2];
133105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnZoomListener mCallback;
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * When showing the zoom, we add the view as a new window. However, there is
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * logic that needs to know the size of the zoom which is determined after
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it's laid out. Therefore, we must post this logic onto the UI thread so
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it will be exceuted AFTER the layout. This is the logic.
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Runnable mPostedVisibleInitializer;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
144470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final IntentFilter mConfigurationChangedFilter =
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
147ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
148ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Needed to reposition the zoom controls after configuration changes.
149ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
150470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onReceive(Context context, Intent intent) {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!mIsVisible) return;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED);
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED);
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** When configuration changes, this is called after the UI thread is idle. */
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
162b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    /** Used to delay the zoom controller dismissal. */
163b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    private static final int MSG_DISMISS_ZOOM_CONTROLS = 3;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If setVisible(true) is called and the owner view's window token is null,
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * we delay the setVisible(true) call until it is not null.
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_SET_VISIBLE = 4;
169c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
170470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Handler mHandler = new Handler() {
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleMessage(Message msg) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (msg.what) {
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_CONFIGURATION_CHANGED:
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    onPostConfigurationChanged();
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
178b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                case MSG_DISMISS_ZOOM_CONTROLS:
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setVisible(false);
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
181c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_SET_VISIBLE:
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mOwnerView.getWindowToken() == null) {
184ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        // Doh, it is still null, just ignore the set visible call
185ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        Log.e(TAG,
186b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                                "Cannot make the zoom controller visible if the owner view is " +
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "not attached to a window.");
188ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    } else {
189ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        setVisible(true);
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
197ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
198ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Constructor for the {@link ZoomButtonsController}.
199105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
200ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param ownerView The view that is being zoomed by the zoom controls. The
201ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *            zoom controls will be displayed aligned with this view.
202ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
203ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public ZoomButtonsController(View ownerView) {
204ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContext = ownerView.getContext();
205ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnerView = ownerView;
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
208ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq = (int)
209ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                (ZOOM_CONTROLS_TOUCH_PADDING * mContext.getResources().getDisplayMetrics().density);
210ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq *= mTouchPaddingScaledSq;
211105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainer = createContainer();
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
214c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
215ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
216ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom in control.
217105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
218ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom in control.
219ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
220b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomInEnabled(boolean enabled) {
221b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomInEnabled(enabled);
222b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
223c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
224ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
225ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom out control.
226105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
227ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom out control.
228ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
229b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomOutEnabled(boolean enabled) {
230b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomOutEnabled(enabled);
231b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
232c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
233ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
234ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the delay between zoom callbacks as the user holds a zoom button.
235105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
236ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param speed The delay in milliseconds between zoom callbacks.
237ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
238b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomSpeed(long speed) {
239b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setZoomSpeed(speed);
240b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
241c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private FrameLayout createContainer() {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
244ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Controls are positioned BOTTOM | CENTER with respect to the owner view.
245aac0d4ed026d1cfbcf3fa81c6e4eb96f4347ca17Fabrice Di Meglio        lp.gravity = Gravity.TOP | Gravity.START;
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
247b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                LayoutParams.FLAG_NOT_FOCUSABLE |
248105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_LAYOUT_NO_LIMITS |
249105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_ALT_FOCUSABLE_IM;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.height = LayoutParams.WRAP_CONTENT;
251980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy        lp.width = LayoutParams.MATCH_PARENT;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.type = LayoutParams.TYPE_APPLICATION_PANEL;
2539767e41d92bd6f4cf16111b3f911cef78c8b01ebDianne Hackborn        lp.format = PixelFormat.TRANSLUCENT;
254c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons;
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainerLayoutParams = lp;
256c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
257ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        FrameLayout container = new Container(mContext);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setLayoutParams(lp);
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setMeasureAllChildren(true);
260c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutInflater inflater = (LayoutInflater) mContext
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
263c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        inflater.inflate(com.android.internal.R.layout.zoom_container, container);
264c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls);
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomInClickListener(new OnClickListener() {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(true);
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomOutClickListener(new OnClickListener() {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(false);
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return container;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
281c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
282ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
283ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the {@link OnZoomListener} listener that receives callbacks to zoom.
284105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
285ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param listener The listener that will be told to zoom.
286ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
287ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public void setOnZoomListener(OnZoomListener listener) {
288ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mCallback = listener;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
291ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
292ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be focusable. If the controls are
293ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * focusable, then trackball and arrow key interactions are possible.
294ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Otherwise, only touch interactions are possible.
295105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
296ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param focusable Whether the zoom controls should be focusable.
297ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFocusable(boolean focusable) {
299ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int oldFlags = mContainerLayoutParams.flags;
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (focusable) {
301c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
305c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
306ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if ((mContainerLayoutParams.flags != oldFlags) && mIsVisible) {
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
311ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
312105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Whether the zoom controls will be automatically dismissed after showing.
313105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
314105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return Whether the zoom controls will be auto dismissed after showing.
315105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
316105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public boolean isAutoDismissed() {
317105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mAutoDismissControls;
318105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
319105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
320105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
321105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Sets whether the zoom controls will be automatically dismissed after
322105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * showing.
323105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
324105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public void setAutoDismissed(boolean autoDismiss) {
325105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls == autoDismiss) return;
326105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        mAutoDismissControls = autoDismiss;
327105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
328105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
329105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
330ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether the zoom controls are visible to the user.
331105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
332ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return Whether the zoom controls are visible to the user.
333ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isVisible() {
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mIsVisible;
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
338ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
339ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be visible to the user.
340105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
341ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param visible Whether the zoom controls should be visible to the user.
342ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setVisible(boolean visible) {
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mOwnerView.getWindowToken() == null) {
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                /*
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * We need a window token to show ourselves, maybe the owner's
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * window hasn't been created yet but it will have been by the
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * time the looper is idle, so post the setVisible(true) call.
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 */
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) {
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE);
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
357c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mIsVisible == visible) {
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIsVisible = visible;
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mContainerLayoutParams.token == null) {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mContainerLayoutParams.token = mOwnerView.getWindowToken();
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.addView(mContainer, mContainerLayoutParams);
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mPostedVisibleInitializer == null) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPostedVisibleInitializer = new Runnable() {
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    public void run() {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        refreshPositioningVariables();
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mCallback != null) {
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mCallback.onVisibilityChanged(true);
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                };
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.post(mPostedVisibleInitializer);
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Handle configuration changes when visible
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter);
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Steal touches events from the owner
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mOwnerView.setOnTouchListener(this);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mReleaseTouchListenerOnUp = false;
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Don't want to steal any more touches
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mTouchTargetView != null) {
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // We are still stealing the touch events for this touch
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // sequence, so release the touch listener later
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = true;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // No longer care about configuration changes
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.unregisterReceiver(mConfigurationChangedReceiver);
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.removeView(mContainer);
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeCallbacks(mPostedVisibleInitializer);
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mCallback != null) {
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mCallback.onVisibilityChanged(false);
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
418ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Gets the container that is the parent of the zoom controls.
419ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * <p>
420ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The client can add other views to this container to link them with the
421ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * zoom controls.
422105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
423ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return The container of the zoom controls. It will be a layout that
424ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *         respects the gravity of a child's layout parameters.
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
426c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    public ViewGroup getContainer() {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mContainer;
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
431105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Gets the view for the zoom controls.
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
433105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return The zoom controls view.
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
435105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public View getZoomControls() {
436105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mControls;
437105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
439105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private void dismissControlsDelayed(int delay) {
440105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls) {
441105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS);
442105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay);
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void refreshPositioningVariables() {
447470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        // if the mOwnerView is detached from window then skip.
448470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        if (mOwnerView.getWindowToken() == null) return;
449470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin
450ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Position the zoom controls on the bottom of the owner view.
451ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerHeight = mOwnerView.getHeight();
452ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerWidth = mOwnerView.getWidth();
453ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // The gap between the top of the owner and the top of the container
454ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerOwnerYOffset = ownerHeight - mContainer.getHeight();
455ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Calculate the owner view's bounds
457ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationOnScreen(mOwnerViewRawLocation);
458ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[0] = mOwnerViewRawLocation[0];
459ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[1] = mOwnerViewRawLocation[1] + containerOwnerYOffset;
460105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
461ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int[] ownerViewWindowLoc = mTempIntArray;
462ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationInWindow(ownerViewWindowLoc);
463ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
464ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // lp.x and lp.y should be relative to the owner's window top-left
465ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.x = ownerViewWindowLoc[0];
466ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.width = ownerWidth;
467ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.y = ownerViewWindowLoc[1] + containerOwnerYOffset;
468ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (mIsVisible) {
469ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
470ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
474ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /* This will only be called when the container has focus. */
475ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean onContainerKey(KeyEvent event) {
476ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int keyCode = event.getKeyCode();
477ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (isInterestingKey(keyCode)) {
478105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
479ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_BACK) {
4808d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                if (event.getAction() == KeyEvent.ACTION_DOWN
4818d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.getRepeatCount() == 0) {
4828d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    if (mOwnerView != null) {
4838d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        KeyEvent.DispatcherState ds = mOwnerView.getKeyDispatcherState();
4848d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        if (ds != null) {
4858d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                            ds.startTracking(event, this);
4868d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        }
4878d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    }
4888d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4898d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                } else if (event.getAction() == KeyEvent.ACTION_UP
4908d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.isTracking() && !event.isCanceled()) {
4918d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    setVisible(false);
4928d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4938d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                }
4948d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn
495ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            } else {
496ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
497ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
498105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
499ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // Let the container handle the key
500ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return false;
501105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
502ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        } else {
503105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
504a175a5b7ea3682cb58cca7f9726d0b8171cd549dJeff Brown            ViewRootImpl viewRoot = mOwnerView.getViewRootImpl();
505ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (viewRoot != null) {
506ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                viewRoot.dispatchKey(event);
507ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
508105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
509ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // We gave the key to the owner, don't let the container handle this key
510ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return true;
511ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
512b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
513b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project
514ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean isInterestingKey(int keyCode) {
515ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        switch (keyCode) {
516ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
517ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_UP:
518ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_DOWN:
519ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_LEFT:
520ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_RIGHT:
521ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_ENTER:
522ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_BACK:
523ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return true;
524ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            default:
525ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return false;
526ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
527ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
528105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
529ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
530ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @hide The ZoomButtonsController implements the OnTouchListener, but this
531ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *       does not need to be shown in its public API.
532ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouch(View v, MotionEvent event) {
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int action = event.getAction();
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
536d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba        if (event.getPointerCount() > 1) {
537d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba            // ZoomButtonsController doesn't handle mutitouch. Give up control.
538d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba            return false;
539d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba        }
540d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mReleaseTouchListenerOnUp) {
542b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project            // The controls were dismissed but we need to throw away all events until the up
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = false;
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Eat this event
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
554c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View targetView = mTouchTargetView;
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (action) {
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_DOWN:
559ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                targetView = findViewForTouch((int) event.getRawX(), (int) event.getRawY());
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(targetView);
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_UP:
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_CANCEL:
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (targetView != null) {
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // The upperleft corner of the target view in raw coordinates
571ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawX = mContainerRawLocation[0] + mTouchTargetWindowLocation[0];
572ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawY = mContainerRawLocation[1] + mTouchTargetWindowLocation[1];
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MotionEvent containerEvent = MotionEvent.obtain(event);
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Convert the motion event into the target view's coordinates (from
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // owner view's coordinates)
577ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            containerEvent.offsetLocation(mOwnerViewRawLocation[0] - targetViewRawX,
578ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    mOwnerViewRawLocation[1] - targetViewRawY);
579ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            /* Disallow negative coordinates (which can occur due to
580ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project             * ZOOM_CONTROLS_TOUCH_PADDING) */
581105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            // These are floats because we need to potentially offset away this exact amount
582105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerX = containerEvent.getX();
583105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerY = containerEvent.getY();
584105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerX < 0 && containerX > -ZOOM_CONTROLS_TOUCH_PADDING) {
585105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(-containerX, 0);
586ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
587105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerY < 0 && containerY > -ZOOM_CONTROLS_TOUCH_PADDING) {
588105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(0, -containerY);
589ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean retValue = targetView.dispatchTouchEvent(containerEvent);
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            containerEvent.recycle();
592105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return retValue;
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
595105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return false;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setTouchTargetView(View view) {
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTouchTargetView = view;
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
602ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            view.getLocationInWindow(mTouchTargetWindowLocation);
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the View that should receive a touch at the given coordinates.
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawX The raw X.
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawY The raw Y.
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The view that should receive the touches, or null if there is not one.
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
613ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private View findViewForTouch(int rawX, int rawY) {
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Reverse order so the child drawn on top gets first dibs.
615ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsX = rawX - mContainerRawLocation[0];
616ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsY = rawY - mContainerRawLocation[1];
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Rect frame = mTempRect;
618ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
619ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        View closestChild = null;
620ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int closestChildDistanceSq = Integer.MAX_VALUE;
621105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = mContainer.getChildCount() - 1; i >= 0; i--) {
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View child = mContainer.getChildAt(i);
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (child.getVisibility() != View.VISIBLE) {
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.getHitRect(frame);
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (frame.contains(containerCoordsX, containerCoordsY)) {
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return child;
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
632105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
633105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceX;
634105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsX >= frame.left && containerCoordsX <= frame.right) {
635105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = 0;
636105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
637105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = Math.min(Math.abs(frame.left - containerCoordsX),
638ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    Math.abs(containerCoordsX - frame.right));
639105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
640105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceY;
641105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsY >= frame.top && containerCoordsY <= frame.bottom) {
642105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = 0;
643105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
644105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = Math.min(Math.abs(frame.top - containerCoordsY),
645105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        Math.abs(containerCoordsY - frame.bottom));
646105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
647ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int distanceSq = distanceX * distanceX + distanceY * distanceY;
648105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
649ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if ((distanceSq < mTouchPaddingScaledSq) &&
650ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    (distanceSq < closestChildDistanceSq)) {
651ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChild = child;
652ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChildDistanceSq = distanceSq;
653ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
656ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        return closestChild;
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void onPostConfigurationChanged() {
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        refreshPositioningVariables();
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
664ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
665ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Interface that will be called when the user performs an interaction that
666ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * triggers some action, for example zooming.
667ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface OnZoomListener {
669105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
670ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
671ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the zoom controls' visibility changes.
672105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
673ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param visible Whether the zoom controls are visible.
674ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onVisibilityChanged(boolean visible);
676105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
677ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
678ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the owner view needs to be zoomed.
679105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
680ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param zoomIn The direction of the zoom: true to zoom in, false to zoom out.
681ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onZoom(boolean zoomIn);
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
684105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
685ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private class Container extends FrameLayout {
686ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public Container(Context context) {
687ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            super(context);
688ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
689ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
690ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /*
691ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Need to override this to intercept the key events. Otherwise, we
692ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * would attach a key listener to the container but its superclass
693ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * ViewGroup gives it to the focused View instead of calling the key
694ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * listener, and so we wouldn't get the events.
695ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
696ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        @Override
697ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public boolean dispatchKeyEvent(KeyEvent event) {
698ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return onContainerKey(event) ? true : super.dispatchKeyEvent(event);
699ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
700ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
701ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
703