1 /* 2 * Copyright (C) 2010 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.systemui.statusbar.tablet; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.content.ClipData; 23 import android.content.ClipDescription; 24 import android.content.Context; 25 import android.graphics.Bitmap; 26 import android.graphics.Canvas; 27 import android.graphics.Paint; 28 import android.graphics.PixelFormat; 29 import android.graphics.Point; 30 import android.util.AttributeSet; 31 import android.util.Slog; 32 import android.view.DragEvent; 33 import android.view.Gravity; 34 import android.view.MotionEvent; 35 import android.view.View; 36 import android.view.WindowManager; 37 import android.view.WindowManagerImpl; 38 import android.widget.FrameLayout; 39 import android.widget.ImageView; 40 import android.widget.TextView; 41 42 import com.android.systemui.R; 43 44 public class ShirtPocket extends ImageView { 45 private static final boolean DEBUG = false; 46 private static final String TAG = "StatusBar/ShirtPocket"; 47 48 private ClipData mClipping = null; 49 50 private ImageView mPreviewIcon; 51 52 public static class DropZone extends View { 53 ShirtPocket mPocket; DropZone(Context context, AttributeSet attrs)54 public DropZone(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 } setPocket(ShirtPocket p)57 public void setPocket(ShirtPocket p) { 58 mPocket = p; 59 } 60 onAttachedToWindow()61 public void onAttachedToWindow() { 62 super.onAttachedToWindow(); 63 if (mPocket.holding()) { 64 show(false); 65 } else { 66 hide(false); 67 } 68 } 69 70 // Drag API notes: we must be visible to receive drag events show(boolean animate)71 private void show(boolean animate) { 72 setTranslationY(0f); 73 if (animate) { 74 setAlpha(0f); 75 ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).start(); 76 } else { 77 setAlpha(1f); 78 } 79 } 80 hide(boolean animate)81 private void hide(boolean animate) { 82 AnimatorListenerAdapter onEnd = new AnimatorListenerAdapter() { 83 @Override 84 public void onAnimationEnd(Animator _a) { 85 DropZone.this.setTranslationY(getHeight() + 2); 86 DropZone.this.setAlpha(0f); 87 } 88 }; 89 if (animate) { 90 Animator a = ObjectAnimator.ofFloat(this, "alpha", getAlpha(), 0f); 91 a.addListener(onEnd); 92 a.start(); 93 } else { 94 onEnd.onAnimationEnd(null); 95 } 96 } 97 98 @Override onDragEvent(DragEvent event)99 public boolean onDragEvent(DragEvent event) { 100 if (DEBUG) Slog.d(TAG, "onDragEvent: " + event); 101 switch (event.getAction()) { 102 // We want to appear whenever a potential drag takes off from anywhere in the UI. 103 case DragEvent.ACTION_DRAG_STARTED: 104 show(true); 105 break; 106 case DragEvent.ACTION_DRAG_ENTERED: 107 if (DEBUG) Slog.d(TAG, "entered!"); 108 // XXX: TODO 109 break; 110 case DragEvent.ACTION_DRAG_EXITED: 111 if (DEBUG) Slog.d(TAG, "exited!"); 112 break; 113 case DragEvent.ACTION_DROP: 114 if (DEBUG) Slog.d(TAG, "dropped!"); 115 mPocket.stash(event.getClipData()); 116 break; 117 case DragEvent.ACTION_DRAG_ENDED: 118 hide(true); 119 break; 120 } 121 return true; // we want everything, thank you 122 } 123 } 124 ShirtPocket(Context context, AttributeSet attrs)125 public ShirtPocket(Context context, AttributeSet attrs) { 126 super(context, attrs); 127 } 128 129 // TODO: "pin area" panel, dragging things out 130 ObjectAnimator mAnimHide, mAnimShow; 131 onAttachedToWindow()132 protected void onAttachedToWindow() { 133 } 134 holding()135 public boolean holding() { 136 return (mClipping != null); 137 } 138 stash(ClipData clipping)139 private void stash(ClipData clipping) { 140 mClipping = clipping; 141 if (mClipping != null) { 142 setVisibility(View.VISIBLE); 143 Bitmap icon = mClipping.getIcon(); 144 // mDescription.setText(mClipping.getDescription().getLabel()); 145 if (icon != null) { 146 setImageBitmap(icon); 147 } else { 148 if (mClipping.getItemCount() > 0) { 149 // TODO: figure out how to visualize every kind of ClipData! 150 //mAltText.setText(mClipping.getItemAt(0).coerceToText(getContext())); 151 } 152 } 153 } else { 154 setVisibility(View.GONE); 155 } 156 } 157 158 @Override onTouchEvent(MotionEvent ev)159 public boolean onTouchEvent(MotionEvent ev) { 160 final int action = ev.getAction(); 161 if (action == MotionEvent.ACTION_DOWN) { 162 final ClipData clip = mClipping; 163 if (clip != null) { 164 final Bitmap icon = clip.getIcon(); 165 DragShadowBuilder shadow; 166 if (icon != null) { 167 shadow = new DragShadowBuilder(this) { 168 public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { 169 shadowSize.set(icon.getWidth(), icon.getHeight()); 170 shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2); 171 } 172 public void onDrawShadow(Canvas canvas) { 173 canvas.drawBitmap(icon, 0, 0, new Paint()); 174 } 175 }; 176 } else { 177 // uhhh, what now? 178 shadow = new DragShadowBuilder(this); 179 } 180 181 startDrag(clip, shadow, null, 0); 182 183 // TODO: only discard the clipping if it was accepted 184 stash(null); 185 186 return true; 187 } 188 } 189 return false; 190 } 191 192 /* 193 private boolean isInViewContentArea(View v, int x, int y) { 194 final int l = v.getPaddingLeft(); 195 final int r = v.getWidth() - v.getPaddingRight(); 196 final int t = v.getPaddingTop(); 197 final int b = v.getHeight() - v.getPaddingBottom(); 198 return x >= l && x < r && y >= t && y < b; 199 } 200 201 View.OnTouchListener mWindowTouchListener = new View.OnTouchListener() { 202 public boolean onTouch(View v, MotionEvent ev) { 203 final int action = ev.getAction(); 204 if (action == MotionEvent.ACTION_OUTSIDE 205 || (action == MotionEvent.ACTION_DOWN 206 && !isInViewContentArea(mWindow, (int)ev.getX(), (int)ev.getY()))) { 207 hideWindow(); 208 return true; 209 } else if (action == MotionEvent.ACTION_DOWN) { 210 final ClipData clip = mClipping; 211 if (clip != null) { 212 final Bitmap icon = clip.getIcon(); 213 DragShadowBuilder shadow; 214 if (icon != null) { 215 shadow = new DragShadowBuilder(v) { 216 public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { 217 shadowSize.set(icon.getWidth(), icon.getHeight()); 218 shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2); 219 } 220 public void onDrawShadow(Canvas canvas) { 221 canvas.drawBitmap(icon, 0, 0, new Paint()); 222 } 223 }; 224 } else { 225 // uhhh, what now? 226 shadow = new DragShadowBuilder(mWindow.findViewById(R.id.preview)); 227 } 228 229 v.startDrag(clip, shadow, null, 0); 230 231 // TODO: only discard the clipping if it was accepted 232 stash(null); 233 234 return true; 235 } 236 } 237 return false; 238 } 239 }; 240 */ 241 } 242 243