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