ZoomButtonsController.java revision 8d37426c754e9822feaa8c6cc0b7c13e8523e217
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;
36ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectimport android.view.ViewRoot;
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
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #setVisible(boolean) setVisible(false)} from the
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link View#onDetachedFromWindow}.
71105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project *
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
73ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Projectpublic class ZoomButtonsController implements View.OnTouchListener {
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "ZoomButtonsController";
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TIMEOUT =
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            (int) ViewConfiguration.getZoomControlsTimeout();
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20;
81ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private int mTouchPaddingScaledSq;
82c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
83470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Context mContext;
84470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final WindowManager mWindowManager;
85105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private boolean mAutoDismissControls = true;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
88b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * The view that is being zoomed by this zoom controller.
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
90470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final View mOwnerView;
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
93ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The location of the owner view on the screen. This is recalculated
94b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * each time the zoom controller is shown.
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
96470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mOwnerViewRawLocation = new int[2];
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The container that is added as a window.
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
101470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final FrameLayout mContainer;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private LayoutParams mContainerLayoutParams;
103470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mContainerRawLocation = new int[2];
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ZoomControls mControls;
106c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The view (or null) that should receive touch events. This will get set if
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the touch down hits the container. It will be reset on the touch up.
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mTouchTargetView;
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The {@link #mTouchTargetView}'s location in window, set on touch down.
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
115470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTouchTargetWindowLocation = new int[2];
116105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
118b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project     * If the zoom controller is dismissed but the user is still in a touch
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interaction, we set this to true. This will ignore all touch events until
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * up/cancel, and then set the owner's touch listener to null.
121105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * <p>
122105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Otherwise, the owner view would get mismatched events (i.e., touch move
123105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * even though it never got the touch down.)
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mReleaseTouchListenerOnUp;
126c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
127ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /** Whether the container has been added to the window manager. */
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mIsVisible;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
130470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Rect mTempRect = new Rect();
131470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final int[] mTempIntArray = new int[2];
132105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnZoomListener mCallback;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * When showing the zoom, we add the view as a new window. However, there is
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * logic that needs to know the size of the zoom which is determined after
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it's laid out. Therefore, we must post this logic onto the UI thread so
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it will be exceuted AFTER the layout. This is the logic.
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Runnable mPostedVisibleInitializer;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
143470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final IntentFilter mConfigurationChangedFilter =
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
146ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
147ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Needed to reposition the zoom controls after configuration changes.
148ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
149470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onReceive(Context context, Intent intent) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!mIsVisible) return;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED);
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED);
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** When configuration changes, this is called after the UI thread is idle. */
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
161b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    /** Used to delay the zoom controller dismissal. */
162b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    private static final int MSG_DISMISS_ZOOM_CONTROLS = 3;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If setVisible(true) is called and the owner view's window token is null,
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * we delay the setVisible(true) call until it is not null.
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_POST_SET_VISIBLE = 4;
168c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
169470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin    private final Handler mHandler = new Handler() {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleMessage(Message msg) {
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (msg.what) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_CONFIGURATION_CHANGED:
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    onPostConfigurationChanged();
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
177b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                case MSG_DISMISS_ZOOM_CONTROLS:
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setVisible(false);
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
180c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case MSG_POST_SET_VISIBLE:
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mOwnerView.getWindowToken() == null) {
183ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        // Doh, it is still null, just ignore the set visible call
184ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        Log.e(TAG,
185b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                                "Cannot make the zoom controller visible if the owner view is " +
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "not attached to a window.");
187ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    } else {
188ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                        setVisible(true);
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
196ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
197ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Constructor for the {@link ZoomButtonsController}.
198105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
199ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param ownerView The view that is being zoomed by the zoom controls. The
200ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *            zoom controls will be displayed aligned with this view.
201ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
202ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public ZoomButtonsController(View ownerView) {
203ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContext = ownerView.getContext();
204ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOwnerView = ownerView;
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
207ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq = (int)
208ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                (ZOOM_CONTROLS_TOUCH_PADDING * mContext.getResources().getDisplayMetrics().density);
209ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mTouchPaddingScaledSq *= mTouchPaddingScaledSq;
210105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainer = createContainer();
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
213c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
214ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
215ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom in control.
216105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
217ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom in control.
218ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
219b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomInEnabled(boolean enabled) {
220b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomInEnabled(enabled);
221b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
222c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
223ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
224ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether to enable the zoom out control.
225105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
226ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param enabled Whether to enable the zoom out control.
227ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
228b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomOutEnabled(boolean enabled) {
229b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setIsZoomOutEnabled(enabled);
230b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
231c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
232ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
233ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the delay between zoom callbacks as the user holds a zoom button.
234105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
235ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param speed The delay in milliseconds between zoom callbacks.
236ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
237b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    public void setZoomSpeed(long speed) {
238b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project        mControls.setZoomSpeed(speed);
239b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
240c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private FrameLayout createContainer() {
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
243ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Controls are positioned BOTTOM | CENTER with respect to the owner view.
244ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        lp.gravity = Gravity.TOP | Gravity.LEFT;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
246b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                LayoutParams.FLAG_NOT_FOCUSABLE |
247105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_LAYOUT_NO_LIMITS |
248105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                LayoutParams.FLAG_ALT_FOCUSABLE_IM;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.height = LayoutParams.WRAP_CONTENT;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.width = LayoutParams.FILL_PARENT;
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.type = LayoutParams.TYPE_APPLICATION_PANEL;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lp.format = PixelFormat.TRANSPARENT;
253c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons;
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContainerLayoutParams = lp;
255c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
256ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        FrameLayout container = new Container(mContext);
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setLayoutParams(lp);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        container.setMeasureAllChildren(true);
259c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        LayoutInflater inflater = (LayoutInflater) mContext
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
262c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        inflater.inflate(com.android.internal.R.layout.zoom_container, container);
263c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls);
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomInClickListener(new OnClickListener() {
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(true);
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mControls.setOnZoomOutClickListener(new OnClickListener() {
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onClick(View v) {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mCallback != null) mCallback.onZoom(false);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return container;
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
280c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
281ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
282ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets the {@link OnZoomListener} listener that receives callbacks to zoom.
283105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
284ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param listener The listener that will be told to zoom.
285ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
286ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    public void setOnZoomListener(OnZoomListener listener) {
287ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mCallback = listener;
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
290ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
291ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be focusable. If the controls are
292ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * focusable, then trackball and arrow key interactions are possible.
293ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Otherwise, only touch interactions are possible.
294105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
295ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param focusable Whether the zoom controls should be focusable.
296ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFocusable(boolean focusable) {
298ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int oldFlags = mContainerLayoutParams.flags;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (focusable) {
300c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
304c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
305ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if ((mContainerLayoutParams.flags != oldFlags) && mIsVisible) {
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
310ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
311105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Whether the zoom controls will be automatically dismissed after showing.
312105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
313105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return Whether the zoom controls will be auto dismissed after showing.
314105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
315105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public boolean isAutoDismissed() {
316105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mAutoDismissControls;
317105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
318105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
319105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
320105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Sets whether the zoom controls will be automatically dismissed after
321105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * showing.
322105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     */
323105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public void setAutoDismissed(boolean autoDismiss) {
324105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls == autoDismiss) return;
325105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        mAutoDismissControls = autoDismiss;
326105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
327105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
328105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    /**
329ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Whether the zoom controls are visible to the user.
330105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
331ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return Whether the zoom controls are visible to the user.
332ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isVisible() {
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mIsVisible;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
337ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
338ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Sets whether the zoom controls should be visible to the user.
339105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
340ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @param visible Whether the zoom controls should be visible to the user.
341ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setVisible(boolean visible) {
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mOwnerView.getWindowToken() == null) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                /*
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * We need a window token to show ourselves, maybe the owner's
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * window hasn't been created yet but it will have been by the
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 * time the looper is idle, so post the setVisible(true) call.
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 */
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) {
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
356c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mIsVisible == visible) {
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIsVisible = visible;
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (visible) {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mContainerLayoutParams.token == null) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mContainerLayoutParams.token = mOwnerView.getWindowToken();
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.addView(mContainer, mContainerLayoutParams);
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mPostedVisibleInitializer == null) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPostedVisibleInitializer = new Runnable() {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    public void run() {
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        refreshPositioningVariables();
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mCallback != null) {
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mCallback.onVisibilityChanged(true);
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
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            mHandler.post(mPostedVisibleInitializer);
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Handle configuration changes when visible
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Steal touches events from the owner
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mOwnerView.setOnTouchListener(this);
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mReleaseTouchListenerOnUp = false;
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Don't want to steal any more touches
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mTouchTargetView != null) {
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // We are still stealing the touch events for this touch
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // sequence, so release the touch listener later
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = true;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // No longer care about configuration changes
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.unregisterReceiver(mConfigurationChangedReceiver);
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWindowManager.removeView(mContainer);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeCallbacks(mPostedVisibleInitializer);
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mCallback != null) {
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mCallback.onVisibilityChanged(false);
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
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    /**
417ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Gets the container that is the parent of the zoom controls.
418ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * <p>
419ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * The client can add other views to this container to link them with the
420ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * zoom controls.
421105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     *
422ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @return The container of the zoom controls. It will be a layout that
423ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *         respects the gravity of a child's layout parameters.
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
425c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    public ViewGroup getContainer() {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mContainer;
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
430105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * Gets the view for the zoom controls.
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
432105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project     * @return The zoom controls view.
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
434105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public View getZoomControls() {
435105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return mControls;
436105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
438105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private void dismissControlsDelayed(int delay) {
439105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (mAutoDismissControls) {
440105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS);
441105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay);
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void refreshPositioningVariables() {
446470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        // if the mOwnerView is detached from window then skip.
447470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin        if (mOwnerView.getWindowToken() == null) return;
448470681e0ff7a9f629ccfc3a36b51550c5a4d32fcOwen Lin
449ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // Position the zoom controls on the bottom of the owner view.
450ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerHeight = mOwnerView.getHeight();
451ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int ownerWidth = mOwnerView.getWidth();
452ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // The gap between the top of the owner and the top of the container
453ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerOwnerYOffset = ownerHeight - mContainer.getHeight();
454ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Calculate the owner view's bounds
456ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationOnScreen(mOwnerViewRawLocation);
457ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[0] = mOwnerViewRawLocation[0];
458ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerRawLocation[1] = mOwnerViewRawLocation[1] + containerOwnerYOffset;
459105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
460ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int[] ownerViewWindowLoc = mTempIntArray;
461ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mOwnerView.getLocationInWindow(ownerViewWindowLoc);
462ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
463ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        // lp.x and lp.y should be relative to the owner's window top-left
464ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.x = ownerViewWindowLoc[0];
465ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.width = ownerWidth;
466ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        mContainerLayoutParams.y = ownerViewWindowLoc[1] + containerOwnerYOffset;
467ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (mIsVisible) {
468ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
469ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
473ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /* This will only be called when the container has focus. */
474ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean onContainerKey(KeyEvent event) {
475ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int keyCode = event.getKeyCode();
476ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (isInterestingKey(keyCode)) {
477105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
478ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_BACK) {
4798d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                if (event.getAction() == KeyEvent.ACTION_DOWN
4808d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.getRepeatCount() == 0) {
4818d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    if (mOwnerView != null) {
4828d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        KeyEvent.DispatcherState ds = mOwnerView.getKeyDispatcherState();
4838d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        if (ds != null) {
4848d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                            ds.startTracking(event, this);
4858d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        }
4868d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    }
4878d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4888d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                } else if (event.getAction() == KeyEvent.ACTION_UP
4898d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                        && event.isTracking() && !event.isCanceled()) {
4908d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    setVisible(false);
4918d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                    return true;
4928d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn                }
4938d37426c754e9822feaa8c6cc0b7c13e8523e217Dianne Hackborn
494ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            } else {
495ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
496ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
497105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
498ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // Let the container handle the key
499ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return false;
500105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
501ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        } else {
502105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
503ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            ViewRoot viewRoot = getOwnerViewRoot();
504ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if (viewRoot != null) {
505ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                viewRoot.dispatchKey(event);
506ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
507105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
508ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            // We gave the key to the owner, don't let the container handle this key
509ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return true;
510ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
511b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project    }
512b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project
513ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private boolean isInterestingKey(int keyCode) {
514ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        switch (keyCode) {
515ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
516ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_UP:
517ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_DOWN:
518ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_LEFT:
519ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_DPAD_RIGHT:
520ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_ENTER:
521ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            case KeyEvent.KEYCODE_BACK:
522ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return true;
523ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            default:
524ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                return false;
525ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
526ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
527105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
528ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private ViewRoot getOwnerViewRoot() {
529ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        View rootViewOfOwner = mOwnerView.getRootView();
530ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (rootViewOfOwner == null) {
531ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return null;
532ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
533105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
534ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        ViewParent parentOfRootView = rootViewOfOwner.getParent();
535ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        if (parentOfRootView instanceof ViewRoot) {
536ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return (ViewRoot) parentOfRootView;
537ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        } else {
538ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return null;
539ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
540ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
541ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
542ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
543ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * @hide The ZoomButtonsController implements the OnTouchListener, but this
544ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     *       does not need to be shown in its public API.
545ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouch(View v, MotionEvent event) {
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int action = event.getAction();
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mReleaseTouchListenerOnUp) {
550b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project            // The controls were dismissed but we need to throw away all events until the up
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOwnerView.setOnTouchListener(null);
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mReleaseTouchListenerOnUp = false;
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Eat this event
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
562c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View targetView = mTouchTargetView;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (action) {
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_DOWN:
567ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                targetView = findViewForTouch((int) event.getRawX(), (int) event.getRawY());
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(targetView);
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_UP:
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_CANCEL:
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                setTouchTargetView(null);
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (targetView != null) {
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // The upperleft corner of the target view in raw coordinates
579ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawX = mContainerRawLocation[0] + mTouchTargetWindowLocation[0];
580ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int targetViewRawY = mContainerRawLocation[1] + mTouchTargetWindowLocation[1];
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MotionEvent containerEvent = MotionEvent.obtain(event);
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Convert the motion event into the target view's coordinates (from
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // owner view's coordinates)
585ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            containerEvent.offsetLocation(mOwnerViewRawLocation[0] - targetViewRawX,
586ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    mOwnerViewRawLocation[1] - targetViewRawY);
587ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            /* Disallow negative coordinates (which can occur due to
588ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project             * ZOOM_CONTROLS_TOUCH_PADDING) */
589105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            // These are floats because we need to potentially offset away this exact amount
590105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerX = containerEvent.getX();
591105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            float containerY = containerEvent.getY();
592105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerX < 0 && containerX > -ZOOM_CONTROLS_TOUCH_PADDING) {
593105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(-containerX, 0);
594ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
595105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerY < 0 && containerY > -ZOOM_CONTROLS_TOUCH_PADDING) {
596105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                containerEvent.offsetLocation(0, -containerY);
597ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean retValue = targetView.dispatchTouchEvent(containerEvent);
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            containerEvent.recycle();
600105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return retValue;
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
603105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return false;
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setTouchTargetView(View view) {
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTouchTargetView = view;
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
610ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            view.getLocationInWindow(mTouchTargetWindowLocation);
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the View that should receive a touch at the given coordinates.
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawX The raw X.
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param rawY The raw Y.
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The view that should receive the touches, or null if there is not one.
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
621ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private View findViewForTouch(int rawX, int rawY) {
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Reverse order so the child drawn on top gets first dibs.
623ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsX = rawX - mContainerRawLocation[0];
624ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int containerCoordsY = rawY - mContainerRawLocation[1];
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Rect frame = mTempRect;
626ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
627ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        View closestChild = null;
628ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        int closestChildDistanceSq = Integer.MAX_VALUE;
629105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = mContainer.getChildCount() - 1; i >= 0; i--) {
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View child = mContainer.getChildAt(i);
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (child.getVisibility() != View.VISIBLE) {
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            child.getHitRect(frame);
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (frame.contains(containerCoordsX, containerCoordsY)) {
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return child;
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
640105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
641105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceX;
642105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsX >= frame.left && containerCoordsX <= frame.right) {
643105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = 0;
644105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
645105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceX = Math.min(Math.abs(frame.left - containerCoordsX),
646ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    Math.abs(containerCoordsX - frame.right));
647105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
648105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int distanceY;
649105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            if (containerCoordsY >= frame.top && containerCoordsY <= frame.bottom) {
650105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = 0;
651105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            } else {
652105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                distanceY = Math.min(Math.abs(frame.top - containerCoordsY),
653105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        Math.abs(containerCoordsY - frame.bottom));
654105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
655ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            int distanceSq = distanceX * distanceX + distanceY * distanceY;
656105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
657ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            if ((distanceSq < mTouchPaddingScaledSq) &&
658ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                    (distanceSq < closestChildDistanceSq)) {
659ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChild = child;
660ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                closestChildDistanceSq = distanceSq;
661ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            }
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
664ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        return closestChild;
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void onPostConfigurationChanged() {
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        refreshPositioningVariables();
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
672ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    /**
673ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * Interface that will be called when the user performs an interaction that
674ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     * triggers some action, for example zooming.
675ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project     */
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface OnZoomListener {
677105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
678ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
679ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the zoom controls' visibility changes.
680105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
681ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param visible Whether the zoom controls are visible.
682ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onVisibilityChanged(boolean visible);
684105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
685ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /**
686ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Called when the owner view needs to be zoomed.
687105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project         *
688ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * @param zoomIn The direction of the zoom: true to zoom in, false to zoom out.
689ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onZoom(boolean zoomIn);
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
692105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
693ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    private class Container extends FrameLayout {
694ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public Container(Context context) {
695ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            super(context);
696ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
697ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
698ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        /*
699ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * Need to override this to intercept the key events. Otherwise, we
700ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * would attach a key listener to the container but its superclass
701ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * ViewGroup gives it to the focused View instead of calling the key
702ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         * listener, and so we wouldn't get the events.
703ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project         */
704ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        @Override
705ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        public boolean dispatchKeyEvent(KeyEvent event) {
706ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project            return onContainerKey(event) ? true : super.dispatchKeyEvent(event);
707ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project        }
708ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project    }
709ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
711