The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.widget; |
| 18 | |
| 19 | import android.content.BroadcastReceiver; |
| 20 | import android.content.Context; |
| 21 | import android.content.Intent; |
| 22 | import android.content.IntentFilter; |
| 23 | import android.graphics.PixelFormat; |
| 24 | import android.graphics.Rect; |
| 25 | import android.os.Handler; |
| 26 | import android.os.Message; |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 27 | import android.util.Log; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 28 | import android.view.Gravity; |
| 29 | import android.view.KeyEvent; |
| 30 | import android.view.LayoutInflater; |
| 31 | import android.view.MotionEvent; |
| 32 | import android.view.View; |
| 33 | import android.view.ViewConfiguration; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 34 | import android.view.ViewGroup; |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 35 | import android.view.ViewParent; |
Dianne Hackborn | 6dd005b | 2011-07-18 13:22:50 -0700 | [diff] [blame] | 36 | import android.view.ViewRootImpl; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 37 | import android.view.WindowManager; |
| 38 | import android.view.View.OnClickListener; |
| 39 | import android.view.WindowManager.LayoutParams; |
| 40 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 41 | /* |
| 42 | * Implementation notes: |
| 43 | * - The zoom controls are displayed in their own window. |
| 44 | * (Easier for the client and better performance) |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 45 | * - This window is never touchable, and by default is not focusable. |
| 46 | * Its rect is quite big (fills horizontally) but has empty space between the |
| 47 | * edges and center. Touches there should be given to the owner. Instead of |
| 48 | * having the window touchable and dispatching these empty touch events to the |
| 49 | * owner, we set the window to not touchable and steal events from owner |
| 50 | * via onTouchListener. |
| 51 | * - To make the buttons clickable, it attaches an OnTouchListener to the owner |
| 52 | * view and does the hit detection locally (attaches when visible, detaches when invisible). |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 53 | * - When it is focusable, it forwards uninteresting events to the owner view's |
| 54 | * view hierarchy. |
| 55 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 56 | /** |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 57 | * The {@link ZoomButtonsController} handles showing and hiding the zoom |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 58 | * controls and positioning it relative to an owner view. It also gives the |
| 59 | * client access to the zoom controls container, allowing for additional |
| 60 | * accessory buttons to be shown in the zoom controls window. |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 61 | * <p> |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 62 | * Typically, clients should call {@link #setVisible(boolean) setVisible(true)} |
| 63 | * on a touch down or move (no need to call {@link #setVisible(boolean) |
| 64 | * setVisible(false)} since it will time out on its own). Also, whenever the |
| 65 | * owner cannot be zoomed further, the client should update |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 66 | * {@link #setZoomInEnabled(boolean)} and {@link #setZoomOutEnabled(boolean)}. |
| 67 | * <p> |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 68 | * If you are using this with a custom View, please call |
Steve Howard | 16bd937 | 2010-04-12 14:46:09 -0700 | [diff] [blame] | 69 | * {@link #setVisible(boolean) setVisible(false)} from |
| 70 | * {@link View#onDetachedFromWindow} and from {@link View#onVisibilityChanged} |
| 71 | * when <code>visibility != View.VISIBLE</code>. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 72 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 73 | */ |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 74 | public class ZoomButtonsController implements View.OnTouchListener { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 75 | |
| 76 | private static final String TAG = "ZoomButtonsController"; |
| 77 | |
| 78 | private static final int ZOOM_CONTROLS_TIMEOUT = |
| 79 | (int) ViewConfiguration.getZoomControlsTimeout(); |
| 80 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 81 | private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20; |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 82 | private int mTouchPaddingScaledSq; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 83 | |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 84 | private final Context mContext; |
| 85 | private final WindowManager mWindowManager; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 86 | private boolean mAutoDismissControls = true; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 87 | |
| 88 | /** |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 89 | * The view that is being zoomed by this zoom controller. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 90 | */ |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 91 | private final View mOwnerView; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 92 | |
| 93 | /** |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 94 | * The location of the owner view on the screen. This is recalculated |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 95 | * each time the zoom controller is shown. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 96 | */ |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 97 | private final int[] mOwnerViewRawLocation = new int[2]; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 98 | |
| 99 | /** |
| 100 | * The container that is added as a window. |
| 101 | */ |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 102 | private final FrameLayout mContainer; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 103 | private LayoutParams mContainerLayoutParams; |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 104 | private final int[] mContainerRawLocation = new int[2]; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 105 | |
| 106 | private ZoomControls mControls; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 107 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 108 | /** |
| 109 | * The view (or null) that should receive touch events. This will get set if |
| 110 | * the touch down hits the container. It will be reset on the touch up. |
| 111 | */ |
| 112 | private View mTouchTargetView; |
| 113 | /** |
| 114 | * The {@link #mTouchTargetView}'s location in window, set on touch down. |
| 115 | */ |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 116 | private final int[] mTouchTargetWindowLocation = new int[2]; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 117 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 118 | /** |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 119 | * If the zoom controller is dismissed but the user is still in a touch |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 120 | * interaction, we set this to true. This will ignore all touch events until |
| 121 | * up/cancel, and then set the owner's touch listener to null. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 122 | * <p> |
| 123 | * Otherwise, the owner view would get mismatched events (i.e., touch move |
| 124 | * even though it never got the touch down.) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 125 | */ |
| 126 | private boolean mReleaseTouchListenerOnUp; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 127 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 128 | /** Whether the container has been added to the window manager. */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 129 | private boolean mIsVisible; |
| 130 | |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 131 | private final Rect mTempRect = new Rect(); |
| 132 | private final int[] mTempIntArray = new int[2]; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 133 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 134 | private OnZoomListener mCallback; |
| 135 | |
| 136 | /** |
| 137 | * When showing the zoom, we add the view as a new window. However, there is |
| 138 | * logic that needs to know the size of the zoom which is determined after |
| 139 | * it's laid out. Therefore, we must post this logic onto the UI thread so |
| 140 | * it will be exceuted AFTER the layout. This is the logic. |
| 141 | */ |
| 142 | private Runnable mPostedVisibleInitializer; |
| 143 | |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 144 | private final IntentFilter mConfigurationChangedFilter = |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 145 | new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); |
| 146 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 147 | /** |
| 148 | * Needed to reposition the zoom controls after configuration changes. |
| 149 | */ |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 150 | private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 151 | @Override |
| 152 | public void onReceive(Context context, Intent intent) { |
| 153 | if (!mIsVisible) return; |
| 154 | |
| 155 | mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED); |
| 156 | mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED); |
| 157 | } |
| 158 | }; |
| 159 | |
| 160 | /** When configuration changes, this is called after the UI thread is idle. */ |
| 161 | private static final int MSG_POST_CONFIGURATION_CHANGED = 2; |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 162 | /** Used to delay the zoom controller dismissal. */ |
| 163 | private static final int MSG_DISMISS_ZOOM_CONTROLS = 3; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 164 | /** |
| 165 | * If setVisible(true) is called and the owner view's window token is null, |
| 166 | * we delay the setVisible(true) call until it is not null. |
| 167 | */ |
| 168 | private static final int MSG_POST_SET_VISIBLE = 4; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 169 | |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 170 | private final Handler mHandler = new Handler() { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 171 | @Override |
| 172 | public void handleMessage(Message msg) { |
| 173 | switch (msg.what) { |
| 174 | case MSG_POST_CONFIGURATION_CHANGED: |
| 175 | onPostConfigurationChanged(); |
| 176 | break; |
| 177 | |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 178 | case MSG_DISMISS_ZOOM_CONTROLS: |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 179 | setVisible(false); |
| 180 | break; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 181 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 182 | case MSG_POST_SET_VISIBLE: |
| 183 | if (mOwnerView.getWindowToken() == null) { |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 184 | // Doh, it is still null, just ignore the set visible call |
| 185 | Log.e(TAG, |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 186 | "Cannot make the zoom controller visible if the owner view is " + |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 187 | "not attached to a window."); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 188 | } else { |
| 189 | setVisible(true); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 190 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 191 | break; |
| 192 | } |
| 193 | |
| 194 | } |
| 195 | }; |
| 196 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 197 | /** |
| 198 | * Constructor for the {@link ZoomButtonsController}. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 199 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 200 | * @param ownerView The view that is being zoomed by the zoom controls. The |
| 201 | * zoom controls will be displayed aligned with this view. |
| 202 | */ |
| 203 | public ZoomButtonsController(View ownerView) { |
| 204 | mContext = ownerView.getContext(); |
| 205 | mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 206 | mOwnerView = ownerView; |
| 207 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 208 | mTouchPaddingScaledSq = (int) |
| 209 | (ZOOM_CONTROLS_TOUCH_PADDING * mContext.getResources().getDisplayMetrics().density); |
| 210 | mTouchPaddingScaledSq *= mTouchPaddingScaledSq; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 211 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 212 | mContainer = createContainer(); |
| 213 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 214 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 215 | /** |
| 216 | * Whether to enable the zoom in control. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 217 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 218 | * @param enabled Whether to enable the zoom in control. |
| 219 | */ |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 220 | public void setZoomInEnabled(boolean enabled) { |
| 221 | mControls.setIsZoomInEnabled(enabled); |
| 222 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 223 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 224 | /** |
| 225 | * Whether to enable the zoom out control. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 226 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 227 | * @param enabled Whether to enable the zoom out control. |
| 228 | */ |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 229 | public void setZoomOutEnabled(boolean enabled) { |
| 230 | mControls.setIsZoomOutEnabled(enabled); |
| 231 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 232 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 233 | /** |
| 234 | * Sets the delay between zoom callbacks as the user holds a zoom button. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 235 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 236 | * @param speed The delay in milliseconds between zoom callbacks. |
| 237 | */ |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 238 | public void setZoomSpeed(long speed) { |
| 239 | mControls.setZoomSpeed(speed); |
| 240 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 241 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 242 | private FrameLayout createContainer() { |
| 243 | LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 244 | // Controls are positioned BOTTOM | CENTER with respect to the owner view. |
Fabrice Di Meglio | aac0d4e | 2012-07-19 19:21:26 -0700 | [diff] [blame] | 245 | lp.gravity = Gravity.TOP | Gravity.START; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 246 | lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE | |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 247 | LayoutParams.FLAG_NOT_FOCUSABLE | |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 248 | LayoutParams.FLAG_LAYOUT_NO_LIMITS | |
| 249 | LayoutParams.FLAG_ALT_FOCUSABLE_IM; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 250 | lp.height = LayoutParams.WRAP_CONTENT; |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 251 | lp.width = LayoutParams.MATCH_PARENT; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 252 | lp.type = LayoutParams.TYPE_APPLICATION_PANEL; |
Dianne Hackborn | 9767e41 | 2009-09-15 18:45:34 -0700 | [diff] [blame] | 253 | lp.format = PixelFormat.TRANSLUCENT; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 254 | lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 255 | mContainerLayoutParams = lp; |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 256 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 257 | FrameLayout container = new Container(mContext); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 258 | container.setLayoutParams(lp); |
| 259 | container.setMeasureAllChildren(true); |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 260 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 261 | LayoutInflater inflater = (LayoutInflater) mContext |
| 262 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 263 | inflater.inflate(com.android.internal.R.layout.zoom_container, container); |
| 264 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 265 | mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls); |
| 266 | mControls.setOnZoomInClickListener(new OnClickListener() { |
| 267 | public void onClick(View v) { |
| 268 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
| 269 | if (mCallback != null) mCallback.onZoom(true); |
| 270 | } |
| 271 | }); |
| 272 | mControls.setOnZoomOutClickListener(new OnClickListener() { |
| 273 | public void onClick(View v) { |
| 274 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
| 275 | if (mCallback != null) mCallback.onZoom(false); |
| 276 | } |
| 277 | }); |
| 278 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 279 | return container; |
| 280 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 281 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 282 | /** |
| 283 | * Sets the {@link OnZoomListener} listener that receives callbacks to zoom. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 284 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 285 | * @param listener The listener that will be told to zoom. |
| 286 | */ |
| 287 | public void setOnZoomListener(OnZoomListener listener) { |
| 288 | mCallback = listener; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 289 | } |
| 290 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 291 | /** |
| 292 | * Sets whether the zoom controls should be focusable. If the controls are |
| 293 | * focusable, then trackball and arrow key interactions are possible. |
| 294 | * Otherwise, only touch interactions are possible. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 295 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 296 | * @param focusable Whether the zoom controls should be focusable. |
| 297 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 298 | public void setFocusable(boolean focusable) { |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 299 | int oldFlags = mContainerLayoutParams.flags; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 300 | if (focusable) { |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 301 | mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 302 | } else { |
| 303 | mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; |
| 304 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 305 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 306 | if ((mContainerLayoutParams.flags != oldFlags) && mIsVisible) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 307 | mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); |
| 308 | } |
| 309 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 310 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 311 | /** |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 312 | * Whether the zoom controls will be automatically dismissed after showing. |
| 313 | * |
| 314 | * @return Whether the zoom controls will be auto dismissed after showing. |
| 315 | */ |
| 316 | public boolean isAutoDismissed() { |
| 317 | return mAutoDismissControls; |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Sets whether the zoom controls will be automatically dismissed after |
| 322 | * showing. |
| 323 | */ |
| 324 | public void setAutoDismissed(boolean autoDismiss) { |
| 325 | if (mAutoDismissControls == autoDismiss) return; |
| 326 | mAutoDismissControls = autoDismiss; |
| 327 | } |
| 328 | |
| 329 | /** |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 330 | * Whether the zoom controls are visible to the user. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 331 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 332 | * @return Whether the zoom controls are visible to the user. |
| 333 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 334 | public boolean isVisible() { |
| 335 | return mIsVisible; |
| 336 | } |
| 337 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 338 | /** |
| 339 | * Sets whether the zoom controls should be visible to the user. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 340 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 341 | * @param visible Whether the zoom controls should be visible to the user. |
| 342 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 343 | public void setVisible(boolean visible) { |
| 344 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 345 | if (visible) { |
| 346 | if (mOwnerView.getWindowToken() == null) { |
| 347 | /* |
| 348 | * We need a window token to show ourselves, maybe the owner's |
| 349 | * window hasn't been created yet but it will have been by the |
| 350 | * time the looper is idle, so post the setVisible(true) call. |
| 351 | */ |
| 352 | if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) { |
| 353 | mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE); |
| 354 | } |
| 355 | return; |
| 356 | } |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 357 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 358 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
| 359 | } |
| 360 | |
| 361 | if (mIsVisible == visible) { |
| 362 | return; |
| 363 | } |
| 364 | mIsVisible = visible; |
| 365 | |
| 366 | if (visible) { |
| 367 | if (mContainerLayoutParams.token == null) { |
| 368 | mContainerLayoutParams.token = mOwnerView.getWindowToken(); |
| 369 | } |
| 370 | |
| 371 | mWindowManager.addView(mContainer, mContainerLayoutParams); |
| 372 | |
| 373 | if (mPostedVisibleInitializer == null) { |
| 374 | mPostedVisibleInitializer = new Runnable() { |
| 375 | public void run() { |
| 376 | refreshPositioningVariables(); |
| 377 | |
| 378 | if (mCallback != null) { |
| 379 | mCallback.onVisibilityChanged(true); |
| 380 | } |
| 381 | } |
| 382 | }; |
| 383 | } |
| 384 | |
| 385 | mHandler.post(mPostedVisibleInitializer); |
| 386 | |
| 387 | // Handle configuration changes when visible |
| 388 | mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter); |
| 389 | |
| 390 | // Steal touches events from the owner |
| 391 | mOwnerView.setOnTouchListener(this); |
| 392 | mReleaseTouchListenerOnUp = false; |
| 393 | |
| 394 | } else { |
| 395 | // Don't want to steal any more touches |
| 396 | if (mTouchTargetView != null) { |
| 397 | // We are still stealing the touch events for this touch |
| 398 | // sequence, so release the touch listener later |
| 399 | mReleaseTouchListenerOnUp = true; |
| 400 | } else { |
| 401 | mOwnerView.setOnTouchListener(null); |
| 402 | } |
| 403 | |
| 404 | // No longer care about configuration changes |
| 405 | mContext.unregisterReceiver(mConfigurationChangedReceiver); |
| 406 | |
| 407 | mWindowManager.removeView(mContainer); |
| 408 | mHandler.removeCallbacks(mPostedVisibleInitializer); |
| 409 | |
| 410 | if (mCallback != null) { |
| 411 | mCallback.onVisibilityChanged(false); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | } |
| 416 | |
| 417 | /** |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 418 | * Gets the container that is the parent of the zoom controls. |
| 419 | * <p> |
| 420 | * The client can add other views to this container to link them with the |
| 421 | * zoom controls. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 422 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 423 | * @return The container of the zoom controls. It will be a layout that |
| 424 | * respects the gravity of a child's layout parameters. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 425 | */ |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 426 | public ViewGroup getContainer() { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 427 | return mContainer; |
| 428 | } |
| 429 | |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 430 | /** |
| 431 | * Gets the view for the zoom controls. |
| 432 | * |
| 433 | * @return The zoom controls view. |
| 434 | */ |
| 435 | public View getZoomControls() { |
| 436 | return mControls; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 437 | } |
| 438 | |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 439 | private void dismissControlsDelayed(int delay) { |
| 440 | if (mAutoDismissControls) { |
| 441 | mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS); |
| 442 | mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 443 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 444 | } |
| 445 | |
| 446 | private void refreshPositioningVariables() { |
Owen Lin | 470681e | 2009-05-27 17:44:57 -0700 | [diff] [blame] | 447 | // if the mOwnerView is detached from window then skip. |
| 448 | if (mOwnerView.getWindowToken() == null) return; |
| 449 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 450 | // Position the zoom controls on the bottom of the owner view. |
| 451 | int ownerHeight = mOwnerView.getHeight(); |
| 452 | int ownerWidth = mOwnerView.getWidth(); |
| 453 | // The gap between the top of the owner and the top of the container |
| 454 | int containerOwnerYOffset = ownerHeight - mContainer.getHeight(); |
| 455 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 456 | // Calculate the owner view's bounds |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 457 | mOwnerView.getLocationOnScreen(mOwnerViewRawLocation); |
| 458 | mContainerRawLocation[0] = mOwnerViewRawLocation[0]; |
| 459 | mContainerRawLocation[1] = mOwnerViewRawLocation[1] + containerOwnerYOffset; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 460 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 461 | int[] ownerViewWindowLoc = mTempIntArray; |
| 462 | mOwnerView.getLocationInWindow(ownerViewWindowLoc); |
| 463 | |
| 464 | // lp.x and lp.y should be relative to the owner's window top-left |
| 465 | mContainerLayoutParams.x = ownerViewWindowLoc[0]; |
| 466 | mContainerLayoutParams.width = ownerWidth; |
| 467 | mContainerLayoutParams.y = ownerViewWindowLoc[1] + containerOwnerYOffset; |
| 468 | if (mIsVisible) { |
| 469 | mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); |
| 470 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 471 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 472 | } |
| 473 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 474 | /* This will only be called when the container has focus. */ |
| 475 | private boolean onContainerKey(KeyEvent event) { |
| 476 | int keyCode = event.getKeyCode(); |
| 477 | if (isInterestingKey(keyCode)) { |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 478 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 479 | if (keyCode == KeyEvent.KEYCODE_BACK) { |
Dianne Hackborn | 8d37426 | 2009-09-14 21:21:52 -0700 | [diff] [blame] | 480 | if (event.getAction() == KeyEvent.ACTION_DOWN |
| 481 | && event.getRepeatCount() == 0) { |
| 482 | if (mOwnerView != null) { |
| 483 | KeyEvent.DispatcherState ds = mOwnerView.getKeyDispatcherState(); |
| 484 | if (ds != null) { |
| 485 | ds.startTracking(event, this); |
| 486 | } |
| 487 | } |
| 488 | return true; |
| 489 | } else if (event.getAction() == KeyEvent.ACTION_UP |
| 490 | && event.isTracking() && !event.isCanceled()) { |
| 491 | setVisible(false); |
| 492 | return true; |
| 493 | } |
| 494 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 495 | } else { |
| 496 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
| 497 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 498 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 499 | // Let the container handle the key |
| 500 | return false; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 501 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 502 | } else { |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 503 | |
Jeff Brown | a175a5b | 2012-02-15 19:18:31 -0800 | [diff] [blame] | 504 | ViewRootImpl viewRoot = mOwnerView.getViewRootImpl(); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 505 | if (viewRoot != null) { |
keunyoung | 30f420f | 2013-08-02 14:23:10 -0700 | [diff] [blame] | 506 | viewRoot.dispatchInputEvent(event); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 507 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 508 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 509 | // We gave the key to the owner, don't let the container handle this key |
| 510 | return true; |
| 511 | } |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 512 | } |
| 513 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 514 | private boolean isInterestingKey(int keyCode) { |
| 515 | switch (keyCode) { |
| 516 | case KeyEvent.KEYCODE_DPAD_CENTER: |
| 517 | case KeyEvent.KEYCODE_DPAD_UP: |
| 518 | case KeyEvent.KEYCODE_DPAD_DOWN: |
| 519 | case KeyEvent.KEYCODE_DPAD_LEFT: |
| 520 | case KeyEvent.KEYCODE_DPAD_RIGHT: |
| 521 | case KeyEvent.KEYCODE_ENTER: |
| 522 | case KeyEvent.KEYCODE_BACK: |
| 523 | return true; |
| 524 | default: |
| 525 | return false; |
| 526 | } |
| 527 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 528 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 529 | /** |
| 530 | * @hide The ZoomButtonsController implements the OnTouchListener, but this |
| 531 | * does not need to be shown in its public API. |
| 532 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 533 | public boolean onTouch(View v, MotionEvent event) { |
| 534 | int action = event.getAction(); |
| 535 | |
Grace Kloba | d4d1d6e | 2010-01-18 10:43:59 -0800 | [diff] [blame] | 536 | if (event.getPointerCount() > 1) { |
| 537 | // ZoomButtonsController doesn't handle mutitouch. Give up control. |
| 538 | return false; |
| 539 | } |
| 540 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 541 | if (mReleaseTouchListenerOnUp) { |
The Android Open Source Project | b2a3dd8 | 2009-03-09 11:52:12 -0700 | [diff] [blame] | 542 | // The controls were dismissed but we need to throw away all events until the up |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 543 | if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { |
| 544 | mOwnerView.setOnTouchListener(null); |
| 545 | setTouchTargetView(null); |
| 546 | mReleaseTouchListenerOnUp = false; |
| 547 | } |
| 548 | |
| 549 | // Eat this event |
| 550 | return true; |
| 551 | } |
| 552 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 553 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
The Android Open Source Project | c39a6e0 | 2009-03-11 12:11:56 -0700 | [diff] [blame] | 554 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 555 | View targetView = mTouchTargetView; |
| 556 | |
| 557 | switch (action) { |
| 558 | case MotionEvent.ACTION_DOWN: |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 559 | targetView = findViewForTouch((int) event.getRawX(), (int) event.getRawY()); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 560 | setTouchTargetView(targetView); |
| 561 | break; |
| 562 | |
| 563 | case MotionEvent.ACTION_UP: |
| 564 | case MotionEvent.ACTION_CANCEL: |
| 565 | setTouchTargetView(null); |
| 566 | break; |
| 567 | } |
| 568 | |
| 569 | if (targetView != null) { |
| 570 | // The upperleft corner of the target view in raw coordinates |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 571 | int targetViewRawX = mContainerRawLocation[0] + mTouchTargetWindowLocation[0]; |
| 572 | int targetViewRawY = mContainerRawLocation[1] + mTouchTargetWindowLocation[1]; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 573 | |
| 574 | MotionEvent containerEvent = MotionEvent.obtain(event); |
| 575 | // Convert the motion event into the target view's coordinates (from |
| 576 | // owner view's coordinates) |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 577 | containerEvent.offsetLocation(mOwnerViewRawLocation[0] - targetViewRawX, |
| 578 | mOwnerViewRawLocation[1] - targetViewRawY); |
| 579 | /* Disallow negative coordinates (which can occur due to |
| 580 | * ZOOM_CONTROLS_TOUCH_PADDING) */ |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 581 | // These are floats because we need to potentially offset away this exact amount |
| 582 | float containerX = containerEvent.getX(); |
| 583 | float containerY = containerEvent.getY(); |
| 584 | if (containerX < 0 && containerX > -ZOOM_CONTROLS_TOUCH_PADDING) { |
| 585 | containerEvent.offsetLocation(-containerX, 0); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 586 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 587 | if (containerY < 0 && containerY > -ZOOM_CONTROLS_TOUCH_PADDING) { |
| 588 | containerEvent.offsetLocation(0, -containerY); |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 589 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 590 | boolean retValue = targetView.dispatchTouchEvent(containerEvent); |
| 591 | containerEvent.recycle(); |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 592 | return retValue; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 593 | |
| 594 | } else { |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 595 | return false; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 596 | } |
| 597 | } |
| 598 | |
| 599 | private void setTouchTargetView(View view) { |
| 600 | mTouchTargetView = view; |
| 601 | if (view != null) { |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 602 | view.getLocationInWindow(mTouchTargetWindowLocation); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 603 | } |
| 604 | } |
| 605 | |
| 606 | /** |
| 607 | * Returns the View that should receive a touch at the given coordinates. |
| 608 | * |
| 609 | * @param rawX The raw X. |
| 610 | * @param rawY The raw Y. |
| 611 | * @return The view that should receive the touches, or null if there is not one. |
| 612 | */ |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 613 | private View findViewForTouch(int rawX, int rawY) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 614 | // Reverse order so the child drawn on top gets first dibs. |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 615 | int containerCoordsX = rawX - mContainerRawLocation[0]; |
| 616 | int containerCoordsY = rawY - mContainerRawLocation[1]; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 617 | Rect frame = mTempRect; |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 618 | |
| 619 | View closestChild = null; |
| 620 | int closestChildDistanceSq = Integer.MAX_VALUE; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 621 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 622 | for (int i = mContainer.getChildCount() - 1; i >= 0; i--) { |
| 623 | View child = mContainer.getChildAt(i); |
| 624 | if (child.getVisibility() != View.VISIBLE) { |
| 625 | continue; |
| 626 | } |
| 627 | |
| 628 | child.getHitRect(frame); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 629 | if (frame.contains(containerCoordsX, containerCoordsY)) { |
| 630 | return child; |
| 631 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 632 | |
| 633 | int distanceX; |
| 634 | if (containerCoordsX >= frame.left && containerCoordsX <= frame.right) { |
| 635 | distanceX = 0; |
| 636 | } else { |
| 637 | distanceX = Math.min(Math.abs(frame.left - containerCoordsX), |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 638 | Math.abs(containerCoordsX - frame.right)); |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 639 | } |
| 640 | int distanceY; |
| 641 | if (containerCoordsY >= frame.top && containerCoordsY <= frame.bottom) { |
| 642 | distanceY = 0; |
| 643 | } else { |
| 644 | distanceY = Math.min(Math.abs(frame.top - containerCoordsY), |
| 645 | Math.abs(containerCoordsY - frame.bottom)); |
| 646 | } |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 647 | int distanceSq = distanceX * distanceX + distanceY * distanceY; |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 648 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 649 | if ((distanceSq < mTouchPaddingScaledSq) && |
| 650 | (distanceSq < closestChildDistanceSq)) { |
| 651 | closestChild = child; |
| 652 | closestChildDistanceSq = distanceSq; |
| 653 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 654 | } |
| 655 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 656 | return closestChild; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 657 | } |
| 658 | |
| 659 | private void onPostConfigurationChanged() { |
| 660 | dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); |
| 661 | refreshPositioningVariables(); |
| 662 | } |
| 663 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 664 | /** |
| 665 | * Interface that will be called when the user performs an interaction that |
| 666 | * triggers some action, for example zooming. |
| 667 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 668 | public interface OnZoomListener { |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 669 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 670 | /** |
| 671 | * Called when the zoom controls' visibility changes. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 672 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 673 | * @param visible Whether the zoom controls are visible. |
| 674 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 675 | void onVisibilityChanged(boolean visible); |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 676 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 677 | /** |
| 678 | * Called when the owner view needs to be zoomed. |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 679 | * |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 680 | * @param zoomIn The direction of the zoom: true to zoom in, false to zoom out. |
| 681 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 682 | void onZoom(boolean zoomIn); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 683 | } |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 684 | |
The Android Open Source Project | ba87e3e | 2009-03-13 13:04:22 -0700 | [diff] [blame] | 685 | private class Container extends FrameLayout { |
| 686 | public Container(Context context) { |
| 687 | super(context); |
| 688 | } |
| 689 | |
| 690 | /* |
| 691 | * Need to override this to intercept the key events. Otherwise, we |
| 692 | * would attach a key listener to the container but its superclass |
| 693 | * ViewGroup gives it to the focused View instead of calling the key |
| 694 | * listener, and so we wouldn't get the events. |
| 695 | */ |
| 696 | @Override |
| 697 | public boolean dispatchKeyEvent(KeyEvent event) { |
| 698 | return onContainerKey(event) ? true : super.dispatchKeyEvent(event); |
| 699 | } |
| 700 | } |
| 701 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 702 | } |