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;
3399441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikasimport android.view.View.OnClickListener;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewConfiguration;
35c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.view.ViewGroup;
366dd005b48138708762bfade0081d031a2a4a3822Dianne Hackbornimport android.view.ViewRootImpl;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager.LayoutParams;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
40ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project/*
41ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * Implementation notes:
42ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * - The zoom controls are displayed in their own window.
43ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project *   (Easier for the client and better performance)
44105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * - This window is never touchable, and by default is not focusable.
45105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   Its rect is quite big (fills horizontally) but has empty space between the
46105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   edges and center.  Touches there should be given to the owner.  Instead of
47105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   having the window touchable and dispatching these empty touch events to the
48105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   owner, we set the window to not touchable and steal events from owner
49105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   via onTouchListener.
50105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * - To make the buttons clickable, it attaches an OnTouchListener to the owner
51105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *   view and does the hit detection locally (attaches when visible, detaches when invisible).
52ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * - When it is focusable, it forwards uninteresting events to the owner view's
53ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project *   view hierarchy.
54ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project */
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
56ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * The {@link ZoomButtonsController} handles showing and hiding the zoom
57105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * controls and positioning it relative to an owner view. It also gives the
58105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * client access to the zoom controls container, allowing for additional
59105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * accessory buttons to be shown in the zoom controls window.
60ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * <p>
61105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * Typically, clients should call {@link #setVisible(boolean) setVisible(true)}
62105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * on a touch down or move (no need to call {@link #setVisible(boolean)
63105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * setVisible(false)} since it will time out on its own). Also, whenever the
64105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project * owner cannot be zoomed further, the client should update
65ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * {@link #setZoomInEnabled(boolean)} and {@link #setZoomOutEnabled(boolean)}.
66ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project * <p>
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If you are using this with a custom View, please call
6816bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * {@link #setVisible(boolean) setVisible(false)} from
6916bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * {@link View#onDetachedFromWindow} and from {@link View#onVisibilityChanged}
7016bd9372ee724b78d96fbddd551aa3e42a8451a6Steve Howard * when <code>visibility != View.VISIBLE</code>.
71105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *
72df103321813a4a518e44ae46f85a74f060cfc3d5Chet Haase * @deprecated This functionality and UI is better handled with custom views and layouts
73df103321813a4a518e44ae46f85a74f060cfc3d5Chet Haase * rather than a dedicated zoom-control widget
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
75df103321813a4a518e44ae46f85a74f060cfc3d5Chet Haase@Deprecated
76ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectpublic class ZoomButtonsController implements View.OnTouchListener {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "ZoomButtonsController";
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TIMEOUT =
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            (int) ViewConfiguration.getZoomControlsTimeout();
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20;
84ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private int mTouchPaddingScaledSq;
85c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
86470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Context mContext;
87470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final WindowManager mWindowManager;
88105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private boolean mAutoDismissControls = true;
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
91b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * The view that is being zoomed by this zoom controller.
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
93470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final View mOwnerView;
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
96ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The location of the owner view on the screen. This is recalculated
97b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * each time the zoom controller is shown.
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
99470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mOwnerViewRawLocation = new int[2];
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The container that is added as a window.
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
104470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final FrameLayout mContainer;
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private LayoutParams mContainerLayoutParams;
106470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mContainerRawLocation = new int[2];
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ZoomControls mControls;
109c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The view (or null) that should receive touch events. This will get set if
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the touch down hits the container. It will be reset on the touch up.
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mTouchTargetView;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The {@link #mTouchTargetView}'s location in window, set on touch down.
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
118470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTouchTargetWindowLocation = new int[2];
119105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
121b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * If the zoom controller is dismissed but the user is still in a touch
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interaction, we set this to true. This will ignore all touch events until
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * up/cancel, and then set the owner's touch listener to null.
124105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * <p>
125105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Otherwise, the owner view would get mismatched events (i.e., touch move
126105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * even though it never got the touch down.)
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mReleaseTouchListenerOnUp;
129c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
130ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /** Whether the container has been added to the window manager. */
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mIsVisible;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
133470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Rect mTempRect = new Rect();
134470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTempIntArray = new int[2];
135105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnZoomListener mCallback;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * When showing the zoom, we add the view as a new window. However, there is
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * logic that needs to know the size of the zoom which is determined after
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it's laid out. Therefore, we must post this logic onto the UI thread so
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it will be exceuted AFTER the layout. This is the logic.
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Runnable mPostedVisibleInitializer;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
146470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final IntentFilter mConfigurationChangedFilter =
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
149ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
150ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Needed to reposition the zoom controls after configuration changes.
151ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
152470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onReceive(Context context, Intent intent) {
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!mIsVisible) return;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED);
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED);
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** When configuration changes, this is called after the UI thread is idle. */
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
164b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    /** Used to delay the zoom controller dismissal. */
165b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    private static final int MSG_DISMISS_ZOOM_CONTROLS = 3;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If setVisible(true) is called and the owner view's window token is null,
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * we delay the setVisible(true) call until it is not null.
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_SET_VISIBLE = 4;
171c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
172470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Handler mHandler = new Handler() {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleMessage(Message msg) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (msg.what) {
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_CONFIGURATION_CHANGED:
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    onPostConfigurationChanged();
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
180b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                case MSG_DISMISS_ZOOM_CONTROLS:
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setVisible(false);
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
183c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_SET_VISIBLE:
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mOwnerView.getWindowToken() == null) {
186ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        // Doh, it is still null, just ignore the set visible call
187ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        Log.e(TAG,
188b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                                "Cannot make the zoom controller visible if the owner view is " +
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "not attached to a window.");
190ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    } else {
191ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        setVisible(true);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
200ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Constructor for the {@link ZoomButtonsController}.
201105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
202ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param ownerView The view that is being zoomed by the zoom controls. The
203ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *            zoom controls will be displayed aligned with this view.
204ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
205ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public ZoomButtonsController(View ownerView) {
206ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContext = ownerView.getContext();
207ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnerView = ownerView;
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
210ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq = (int)
211ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                (ZOOM_CONTROLS_TOUCH_PADDING * mContext.getResources().getDisplayMetrics().density);
212ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq *= mTouchPaddingScaledSq;
213105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainer = createContainer();
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
216c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
217ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
218ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom in control.
219105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
220ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom in control.
221ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
222b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomInEnabled(boolean enabled) {
223b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomInEnabled(enabled);
224b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
225c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
226ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
227ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom out control.
228105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
229ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom out control.
230ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
231b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomOutEnabled(boolean enabled) {
232b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomOutEnabled(enabled);
233b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
234c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
235ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
236ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the delay between zoom callbacks as the user holds a zoom button.
237105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
238ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param speed The delay in milliseconds between zoom callbacks.
239ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
240b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomSpeed(long speed) {
241b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setZoomSpeed(speed);
242b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
243c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private FrameLayout createContainer() {
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
246ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Controls are positioned BOTTOM | CENTER with respect to the owner view.
247aac0d4ed026d1cfbcf3fa81c6e4eb96f4347ca17Fabrice Di Meglio        lp.gravity = Gravity.TOP | Gravity.START;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
249b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                LayoutParams.FLAG_NOT_FOCUSABLE |
250105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_LAYOUT_NO_LIMITS |
251105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_ALT_FOCUSABLE_IM;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.height = LayoutParams.WRAP_CONTENT;
253980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy        lp.width = LayoutParams.MATCH_PARENT;
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.type = LayoutParams.TYPE_APPLICATION_PANEL;
2559767e41d92bd6f4cf16111b3f911cef78c8b01ebDianne Hackborn        lp.format = PixelFormat.TRANSLUCENT;
256c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainerLayoutParams = lp;
258c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
259ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        FrameLayout container = new Container(mContext);
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setLayoutParams(lp);
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setMeasureAllChildren(true);
262c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutInflater inflater = (LayoutInflater) mContext
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
265c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        inflater.inflate(com.android.internal.R.layout.zoom_container, container);
266c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2678e1a72964517bfd01d8e650453ef41e22f770f21Alan Viverette        mControls = container.findViewById(com.android.internal.R.id.zoomControls);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomInClickListener(new OnClickListener() {
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(true);
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomOutClickListener(new OnClickListener() {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(false);
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return container;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
283c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
284ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
285ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the {@link OnZoomListener} listener that receives callbacks to zoom.
286105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
287ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param listener The listener that will be told to zoom.
288ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
289ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public void setOnZoomListener(OnZoomListener listener) {
290ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mCallback = listener;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
293ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
294ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be focusable. If the controls are
295ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * focusable, then trackball and arrow key interactions are possible.
296ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Otherwise, only touch interactions are possible.
297105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
298ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param focusable Whether the zoom controls should be focusable.
299ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFocusable(boolean focusable) {
301ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int oldFlags = mContainerLayoutParams.flags;
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (focusable) {
303c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
307c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
308ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if ((mContainerLayoutParams.flags != oldFlags) && mIsVisible) {
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
313ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
314105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Whether the zoom controls will be automatically dismissed after showing.
315105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
316105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return Whether the zoom controls will be auto dismissed after showing.
317105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
318105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public boolean isAutoDismissed() {
319105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mAutoDismissControls;
320105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
321105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
322105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
323105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Sets whether the zoom controls will be automatically dismissed after
324105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * showing.
325105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
326105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public void setAutoDismissed(boolean autoDismiss) {
327105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls == autoDismiss) return;
328105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        mAutoDismissControls = autoDismiss;
329105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
330105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
331105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
332ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether the zoom controls are visible to the user.
333105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
334ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return Whether the zoom controls are visible to the user.
335ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isVisible() {
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mIsVisible;
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
340ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
341ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be visible to the user.
342105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
343ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param visible Whether the zoom controls should be visible to the user.
344ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setVisible(boolean visible) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mOwnerView.getWindowToken() == null) {
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                /*
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * We need a window token to show ourselves, maybe the owner's
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * window hasn't been created yet but it will have been by the
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * time the looper is idle, so post the setVisible(true) call.
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 */
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) {
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE);
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
359c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mIsVisible == visible) {
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIsVisible = visible;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mContainerLayoutParams.token == null) {
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mContainerLayoutParams.token = mOwnerView.getWindowToken();
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.addView(mContainer, mContainerLayoutParams);
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mPostedVisibleInitializer == null) {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPostedVisibleInitializer = new Runnable() {
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    public void run() {
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        refreshPositioningVariables();
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mCallback != null) {
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mCallback.onVisibilityChanged(true);
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                };
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.post(mPostedVisibleInitializer);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Handle configuration changes when visible
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter);
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Steal touches events from the owner
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mOwnerView.setOnTouchListener(this);
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mReleaseTouchListenerOnUp = false;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Don't want to steal any more touches
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mTouchTargetView != null) {
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // We are still stealing the touch events for this touch
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // sequence, so release the touch listener later
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = true;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // No longer care about configuration changes
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.unregisterReceiver(mConfigurationChangedReceiver);
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4095e641d653629e4e2192340e68402ed2f1d385bedWale Ogunwale            mWindowManager.removeViewImmediate(mContainer);
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeCallbacks(mPostedVisibleInitializer);
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mCallback != null) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mCallback.onVisibilityChanged(false);
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
420ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Gets the container that is the parent of the zoom controls.
421ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * <p>
422ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The client can add other views to this container to link them with the
423ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * zoom controls.
424105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
425ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return The container of the zoom controls. It will be a layout that
426ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *         respects the gravity of a child's layout parameters.
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
428c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    public ViewGroup getContainer() {
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mContainer;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
433105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Gets the view for the zoom controls.
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
435105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return The zoom controls view.
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
437105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public View getZoomControls() {
438105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mControls;
439105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
441105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private void dismissControlsDelayed(int delay) {
442105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls) {
443105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS);
444105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay);
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void refreshPositioningVariables() {
449470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        // if the mOwnerView is detached from window then skip.
450470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        if (mOwnerView.getWindowToken() == null) return;
451470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin
452ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Position the zoom controls on the bottom of the owner view.
453ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerHeight = mOwnerView.getHeight();
454ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerWidth = mOwnerView.getWidth();
455ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // The gap between the top of the owner and the top of the container
456ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerOwnerYOffset = ownerHeight - mContainer.getHeight();
457ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Calculate the owner view's bounds
459ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationOnScreen(mOwnerViewRawLocation);
460ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[0] = mOwnerViewRawLocation[0];
461ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[1] = mOwnerViewRawLocation[1] + containerOwnerYOffset;
462105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
463ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int[] ownerViewWindowLoc = mTempIntArray;
464ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationInWindow(ownerViewWindowLoc);
465ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
466ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // lp.x and lp.y should be relative to the owner's window top-left
467ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.x = ownerViewWindowLoc[0];
468ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.width = ownerWidth;
469ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.y = ownerViewWindowLoc[1] + containerOwnerYOffset;
470ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (mIsVisible) {
471ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
472ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
476ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /* This will only be called when the container has focus. */
477ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean onContainerKey(KeyEvent event) {
478ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int keyCode = event.getKeyCode();
479ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (isInterestingKey(keyCode)) {
480105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
481ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_BACK) {
4828d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                if (event.getAction() == KeyEvent.ACTION_DOWN
4838d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.getRepeatCount() == 0) {
4848d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    if (mOwnerView != null) {
4858d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        KeyEvent.DispatcherState ds = mOwnerView.getKeyDispatcherState();
4868d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        if (ds != null) {
4878d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                            ds.startTracking(event, this);
4888d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        }
4898d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    }
4908d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4918d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                } else if (event.getAction() == KeyEvent.ACTION_UP
4928d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.isTracking() && !event.isCanceled()) {
4938d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    setVisible(false);
4948d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4958d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                }
4965e641d653629e4e2192340e68402ed2f1d385bedWale Ogunwale
497ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            } else {
498ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
499ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
500105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
501ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // Let the container handle the key
502ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return false;
503105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
504ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        } else {
505105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
506a175a5b7ea3682cb58cca7f9726d0b8171cd549dJeff Brown            ViewRootImpl viewRoot = mOwnerView.getViewRootImpl();
507ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (viewRoot != null) {
50830f420fd6a74ffa28b351b4aba74d44f5ea48ddakeunyoung                viewRoot.dispatchInputEvent(event);
509ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
510105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
511ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // We gave the key to the owner, don't let the container handle this key
512ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return true;
513ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
514b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
515b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project
516ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean isInterestingKey(int keyCode) {
517ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        switch (keyCode) {
518ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
519ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_UP:
520ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_DOWN:
521ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_LEFT:
522ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_RIGHT:
523ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_ENTER:
524ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_BACK:
525ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return true;
526ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            default:
527ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return false;
528ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
529ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
530105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
531ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
532ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @hide The ZoomButtonsController implements the OnTouchListener, but this
533ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *       does not need to be shown in its public API.
534ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouch(View v, MotionEvent event) {
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int action = event.getAction();
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
538d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba        if (event.getPointerCount() > 1) {
539d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba            // ZoomButtonsController doesn't handle mutitouch. Give up control.
540d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba            return false;
541d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba        }
542d4d1d6eca0ad195c1ab64c0f4dd38ae328b2b042Grace Kloba
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mReleaseTouchListenerOnUp) {
544b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project            // The controls were dismissed but we need to throw away all events until the up
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = false;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Eat this event
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
556c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View targetView = mTouchTargetView;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (action) {
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_DOWN:
561ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                targetView = findViewForTouch((int) event.getRawX(), (int) event.getRawY());
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(targetView);
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_UP:
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_CANCEL:
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (targetView != null) {
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // The upperleft corner of the target view in raw coordinates
573ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawX = mContainerRawLocation[0] + mTouchTargetWindowLocation[0];
574ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawY = mContainerRawLocation[1] + mTouchTargetWindowLocation[1];
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MotionEvent containerEvent = MotionEvent.obtain(event);
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Convert the motion event into the target view's coordinates (from
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // owner view's coordinates)
579ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            containerEvent.offsetLocation(mOwnerViewRawLocation[0] - targetViewRawX,
580ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    mOwnerViewRawLocation[1] - targetViewRawY);
581ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            /* Disallow negative coordinates (which can occur due to
582ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project             * ZOOM_CONTROLS_TOUCH_PADDING) */
583105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            // These are floats because we need to potentially offset away this exact amount
584105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerX = containerEvent.getX();
585105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerY = containerEvent.getY();
586105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerX < 0 && containerX > -ZOOM_CONTROLS_TOUCH_PADDING) {
587105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(-containerX, 0);
588ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
589105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerY < 0 && containerY > -ZOOM_CONTROLS_TOUCH_PADDING) {
590105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(0, -containerY);
591ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean retValue = targetView.dispatchTouchEvent(containerEvent);
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            containerEvent.recycle();
594105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return retValue;
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
597105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return false;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setTouchTargetView(View view) {
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTouchTargetView = view;
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
604ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            view.getLocationInWindow(mTouchTargetWindowLocation);
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the View that should receive a touch at the given coordinates.
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawX The raw X.
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawY The raw Y.
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The view that should receive the touches, or null if there is not one.
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
615ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private View findViewForTouch(int rawX, int rawY) {
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Reverse order so the child drawn on top gets first dibs.
617ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsX = rawX - mContainerRawLocation[0];
618ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsY = rawY - mContainerRawLocation[1];
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Rect frame = mTempRect;
620ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
621ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        View closestChild = null;
622ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int closestChildDistanceSq = Integer.MAX_VALUE;
623105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = mContainer.getChildCount() - 1; i >= 0; i--) {
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View child = mContainer.getChildAt(i);
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (child.getVisibility() != View.VISIBLE) {
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.getHitRect(frame);
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (frame.contains(containerCoordsX, containerCoordsY)) {
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return child;
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
634105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
635105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceX;
636105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsX >= frame.left && containerCoordsX <= frame.right) {
637105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = 0;
638105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
639105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = Math.min(Math.abs(frame.left - containerCoordsX),
640ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    Math.abs(containerCoordsX - frame.right));
641105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
642105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceY;
643105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsY >= frame.top && containerCoordsY <= frame.bottom) {
644105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = 0;
645105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
646105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = Math.min(Math.abs(frame.top - containerCoordsY),
647105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        Math.abs(containerCoordsY - frame.bottom));
648105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
649ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int distanceSq = distanceX * distanceX + distanceY * distanceY;
650105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
651ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if ((distanceSq < mTouchPaddingScaledSq) &&
652ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    (distanceSq < closestChildDistanceSq)) {
653ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChild = child;
654ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChildDistanceSq = distanceSq;
655ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
658ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        return closestChild;
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void onPostConfigurationChanged() {
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        refreshPositioningVariables();
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
666ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
667ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Interface that will be called when the user performs an interaction that
668ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * triggers some action, for example zooming.
669ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface OnZoomListener {
671105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
672ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
673ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the zoom controls' visibility changes.
674105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
675ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param visible Whether the zoom controls are visible.
676ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onVisibilityChanged(boolean visible);
678105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
679ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
680ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the owner view needs to be zoomed.
681105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
682ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param zoomIn The direction of the zoom: true to zoom in, false to zoom out.
683ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onZoom(boolean zoomIn);
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
686105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
687ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private class Container extends FrameLayout {
688ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public Container(Context context) {
689ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            super(context);
690ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
691ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
692ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /*
693ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Need to override this to intercept the key events. Otherwise, we
694ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * would attach a key listener to the container but its superclass
695ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * ViewGroup gives it to the focused View instead of calling the key
696ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * listener, and so we wouldn't get the events.
697ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
698ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        @Override
699ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public boolean dispatchKeyEvent(KeyEvent event) {
700ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return onContainerKey(event) ? true : super.dispatchKeyEvent(event);
701ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
702ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
703ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
705