1 /* 2 * Copyright (C) 2011 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 com.android.launcher2; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.content.res.TypedArray; 22 import android.graphics.Bitmap; 23 import android.graphics.Canvas; 24 import android.graphics.Paint; 25 import android.graphics.PorterDuff; 26 import android.graphics.PorterDuffXfermode; 27 import android.graphics.Rect; 28 import android.graphics.drawable.Drawable; 29 import android.util.AttributeSet; 30 import android.util.DisplayMetrics; 31 import android.view.FocusFinder; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.accessibility.AccessibilityManager; 35 import android.widget.FrameLayout; 36 37 import com.android.launcher.R; 38 39 public class Cling extends FrameLayout { 40 41 static final String WORKSPACE_CLING_DISMISSED_KEY = "cling.workspace.dismissed"; 42 static final String ALLAPPS_CLING_DISMISSED_KEY = "cling.allapps.dismissed"; 43 static final String FOLDER_CLING_DISMISSED_KEY = "cling.folder.dismissed"; 44 45 private static String WORKSPACE_PORTRAIT = "workspace_portrait"; 46 private static String WORKSPACE_LANDSCAPE = "workspace_landscape"; 47 private static String WORKSPACE_LARGE = "workspace_large"; 48 private static String WORKSPACE_CUSTOM = "workspace_custom"; 49 50 private static String ALLAPPS_PORTRAIT = "all_apps_portrait"; 51 private static String ALLAPPS_LANDSCAPE = "all_apps_landscape"; 52 private static String ALLAPPS_LARGE = "all_apps_large"; 53 54 private static String FOLDER_PORTRAIT = "folder_portrait"; 55 private static String FOLDER_LANDSCAPE = "folder_landscape"; 56 private static String FOLDER_LARGE = "folder_large"; 57 58 private Launcher mLauncher; 59 private boolean mIsInitialized; 60 private String mDrawIdentifier; 61 private Drawable mBackground; 62 private Drawable mPunchThroughGraphic; 63 private Drawable mHandTouchGraphic; 64 private int mPunchThroughGraphicCenterRadius; 65 private int mAppIconSize; 66 private int mButtonBarHeight; 67 private float mRevealRadius; 68 private int[] mPositionData; 69 70 private Paint mErasePaint; 71 72 public Cling(Context context) { 73 this(context, null, 0); 74 } 75 76 public Cling(Context context, AttributeSet attrs) { 77 this(context, attrs, 0); 78 } 79 80 public Cling(Context context, AttributeSet attrs, int defStyle) { 81 super(context, attrs, defStyle); 82 83 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0); 84 mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier); 85 a.recycle(); 86 } 87 88 void init(Launcher l, int[] positionData) { 89 if (!mIsInitialized) { 90 mLauncher = l; 91 mPositionData = positionData; 92 93 Resources r = getContext().getResources(); 94 95 mPunchThroughGraphic = r.getDrawable(R.drawable.cling); 96 mPunchThroughGraphicCenterRadius = 97 r.getDimensionPixelSize(R.dimen.clingPunchThroughGraphicCenterRadius); 98 mAppIconSize = r.getDimensionPixelSize(R.dimen.app_icon_size); 99 mRevealRadius = r.getDimensionPixelSize(R.dimen.reveal_radius) * 1f; 100 mButtonBarHeight = r.getDimensionPixelSize(R.dimen.button_bar_height); 101 102 mErasePaint = new Paint(); 103 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); 104 mErasePaint.setColor(0xFFFFFF); 105 mErasePaint.setAlpha(0); 106 107 mIsInitialized = true; 108 } 109 } 110 111 void cleanup() { 112 mBackground = null; 113 mPunchThroughGraphic = null; 114 mHandTouchGraphic = null; 115 mIsInitialized = false; 116 } 117 118 public String getDrawIdentifier() { 119 return mDrawIdentifier; 120 } 121 122 private int[] getPunchThroughPositions() { 123 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)) { 124 return new int[]{getMeasuredWidth() / 2, getMeasuredHeight() - (mButtonBarHeight / 2)}; 125 } else if (mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)) { 126 return new int[]{getMeasuredWidth() - (mButtonBarHeight / 2), getMeasuredHeight() / 2}; 127 } else if (mDrawIdentifier.equals(WORKSPACE_LARGE)) { 128 final float scale = LauncherApplication.getScreenDensity(); 129 final int cornerXOffset = (int) (scale * 15); 130 final int cornerYOffset = (int) (scale * 10); 131 return new int[]{getMeasuredWidth() - cornerXOffset, cornerYOffset}; 132 } else if (mDrawIdentifier.equals(ALLAPPS_PORTRAIT) || 133 mDrawIdentifier.equals(ALLAPPS_LANDSCAPE) || 134 mDrawIdentifier.equals(ALLAPPS_LARGE)) { 135 return mPositionData; 136 } 137 return new int[]{-1, -1}; 138 } 139 140 @Override 141 public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) { 142 if (descendant.includeForAccessibility()) { 143 return descendant; 144 } 145 return null; 146 } 147 148 @Override 149 public View focusSearch(int direction) { 150 return this.focusSearch(null, direction); 151 } 152 153 @Override 154 public View focusSearch(View focused, int direction) { 155 return FocusFinder.getInstance().findNextFocus(this, focused, direction); 156 } 157 158 @Override 159 public boolean onHoverEvent(MotionEvent event) { 160 return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) 161 || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) 162 || mDrawIdentifier.equals(WORKSPACE_LARGE) 163 || mDrawIdentifier.equals(ALLAPPS_PORTRAIT) 164 || mDrawIdentifier.equals(ALLAPPS_LANDSCAPE) 165 || mDrawIdentifier.equals(ALLAPPS_LARGE) 166 || mDrawIdentifier.equals(WORKSPACE_CUSTOM)); 167 } 168 169 @Override 170 public boolean onTouchEvent(android.view.MotionEvent event) { 171 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || 172 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || 173 mDrawIdentifier.equals(WORKSPACE_LARGE) || 174 mDrawIdentifier.equals(ALLAPPS_PORTRAIT) || 175 mDrawIdentifier.equals(ALLAPPS_LANDSCAPE) || 176 mDrawIdentifier.equals(ALLAPPS_LARGE)) { 177 178 int[] positions = getPunchThroughPositions(); 179 for (int i = 0; i < positions.length; i += 2) { 180 double diff = Math.sqrt(Math.pow(event.getX() - positions[i], 2) + 181 Math.pow(event.getY() - positions[i + 1], 2)); 182 if (diff < mRevealRadius) { 183 return false; 184 } 185 } 186 } else if (mDrawIdentifier.equals(FOLDER_PORTRAIT) || 187 mDrawIdentifier.equals(FOLDER_LANDSCAPE) || 188 mDrawIdentifier.equals(FOLDER_LARGE)) { 189 Folder f = mLauncher.getWorkspace().getOpenFolder(); 190 if (f != null) { 191 Rect r = new Rect(); 192 f.getHitRect(r); 193 if (r.contains((int) event.getX(), (int) event.getY())) { 194 return false; 195 } 196 } 197 } 198 return true; 199 }; 200 201 @Override 202 protected void dispatchDraw(Canvas canvas) { 203 if (mIsInitialized) { 204 DisplayMetrics metrics = new DisplayMetrics(); 205 mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics); 206 207 // Initialize the draw buffer (to allow punching through) 208 Bitmap b = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), 209 Bitmap.Config.ARGB_8888); 210 Canvas c = new Canvas(b); 211 212 // Draw the background 213 if (mBackground == null) { 214 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || 215 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || 216 mDrawIdentifier.equals(WORKSPACE_LARGE)) { 217 mBackground = getResources().getDrawable(R.drawable.bg_cling1); 218 } else if (mDrawIdentifier.equals(ALLAPPS_PORTRAIT) || 219 mDrawIdentifier.equals(ALLAPPS_LANDSCAPE) || 220 mDrawIdentifier.equals(ALLAPPS_LARGE)) { 221 mBackground = getResources().getDrawable(R.drawable.bg_cling2); 222 } else if (mDrawIdentifier.equals(FOLDER_PORTRAIT) || 223 mDrawIdentifier.equals(FOLDER_LANDSCAPE)) { 224 mBackground = getResources().getDrawable(R.drawable.bg_cling3); 225 } else if (mDrawIdentifier.equals(FOLDER_LARGE)) { 226 mBackground = getResources().getDrawable(R.drawable.bg_cling4); 227 } else if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) { 228 mBackground = getResources().getDrawable(R.drawable.bg_cling5); 229 } 230 } 231 if (mBackground != null) { 232 mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); 233 mBackground.draw(c); 234 } else { 235 c.drawColor(0x99000000); 236 } 237 238 int cx = -1; 239 int cy = -1; 240 float scale = mRevealRadius / mPunchThroughGraphicCenterRadius; 241 int dw = (int) (scale * mPunchThroughGraphic.getIntrinsicWidth()); 242 int dh = (int) (scale * mPunchThroughGraphic.getIntrinsicHeight()); 243 244 // Determine where to draw the punch through graphic 245 int[] positions = getPunchThroughPositions(); 246 for (int i = 0; i < positions.length; i += 2) { 247 cx = positions[i]; 248 cy = positions[i + 1]; 249 if (cx > -1 && cy > -1) { 250 c.drawCircle(cx, cy, mRevealRadius, mErasePaint); 251 mPunchThroughGraphic.setBounds(cx - dw/2, cy - dh/2, cx + dw/2, cy + dh/2); 252 mPunchThroughGraphic.draw(c); 253 } 254 } 255 256 // Draw the hand graphic in All Apps 257 if (mDrawIdentifier.equals(ALLAPPS_PORTRAIT) || 258 mDrawIdentifier.equals(ALLAPPS_LANDSCAPE) || 259 mDrawIdentifier.equals(ALLAPPS_LARGE)) { 260 if (mHandTouchGraphic == null) { 261 mHandTouchGraphic = getResources().getDrawable(R.drawable.hand); 262 } 263 int offset = mAppIconSize / 4; 264 mHandTouchGraphic.setBounds(cx + offset, cy + offset, 265 cx + mHandTouchGraphic.getIntrinsicWidth() + offset, 266 cy + mHandTouchGraphic.getIntrinsicHeight() + offset); 267 mHandTouchGraphic.draw(c); 268 } 269 270 canvas.drawBitmap(b, 0, 0, null); 271 c.setBitmap(null); 272 b = null; 273 } 274 275 // Draw the rest of the cling 276 super.dispatchDraw(canvas); 277 }; 278 } 279