1 /* 2 * Copyright (C) 2013 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.gallery3d.filtershow.state; 18 19 import android.animation.LayoutTransition; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.database.DataSetObserver; 23 import android.graphics.Canvas; 24 import android.graphics.Point; 25 import android.graphics.Rect; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.GestureDetector; 29 import android.view.MotionEvent; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.Adapter; 33 import android.widget.LinearLayout; 34 import com.android.gallery3d.R; 35 import com.android.gallery3d.filtershow.FilterShowActivity; 36 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; 37 import com.android.gallery3d.filtershow.filters.FilterRepresentation; 38 import com.android.gallery3d.filtershow.imageshow.MasterImage; 39 40 public class StatePanelTrack extends LinearLayout implements PanelTrack { 41 42 private static final String LOGTAG = "StatePanelTrack"; 43 private Point mTouchPoint; 44 private StateView mCurrentView; 45 private StateView mCurrentSelectedView; 46 private boolean mExited = false; 47 private boolean mStartedDrag = false; 48 private StateAdapter mAdapter; 49 private DragListener mDragListener = new DragListener(this); 50 private float mDeleteSlope = 0.2f; 51 private GestureDetector mGestureDetector; 52 private int mElemWidth; 53 private int mElemHeight; 54 private int mElemSize; 55 private int mElemEndSize; 56 private int mEndElemWidth; 57 private int mEndElemHeight; 58 private long mTouchTime; 59 private int mMaxTouchDelay = 300; // 300ms delay for touch 60 private static final boolean ALLOWS_DRAG = false; 61 private static final boolean ALLOWS_DUPLICATES = false; 62 private DataSetObserver mObserver = new DataSetObserver() { 63 @Override 64 public void onChanged() { 65 super.onChanged(); 66 fillContent(false); 67 } 68 69 @Override 70 public void onInvalidated() { 71 super.onInvalidated(); 72 fillContent(false); 73 } 74 }; 75 76 public StatePanelTrack(Context context, AttributeSet attrs) { 77 super(context, attrs); 78 TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.StatePanelTrack); 79 mElemSize = a.getDimensionPixelSize(R.styleable.StatePanelTrack_elemSize, 0); 80 mElemEndSize = a.getDimensionPixelSize(R.styleable.StatePanelTrack_elemEndSize, 0); 81 if (getOrientation() == LinearLayout.HORIZONTAL) { 82 mElemWidth = mElemSize; 83 mElemHeight = LayoutParams.MATCH_PARENT; 84 mEndElemWidth = mElemEndSize; 85 mEndElemHeight = LayoutParams.MATCH_PARENT; 86 } else { 87 mElemWidth = LayoutParams.MATCH_PARENT; 88 mElemHeight = mElemSize; 89 mEndElemWidth = LayoutParams.MATCH_PARENT; 90 mEndElemHeight = mElemEndSize; 91 } 92 GestureDetector.SimpleOnGestureListener simpleOnGestureListener 93 = new GestureDetector.SimpleOnGestureListener(){ 94 @Override 95 public void onLongPress(MotionEvent e) { 96 longPress(e); 97 } 98 @Override 99 public boolean onDoubleTap(MotionEvent e) { 100 addDuplicate(e); 101 return true; 102 } 103 }; 104 mGestureDetector = new GestureDetector(context, simpleOnGestureListener); 105 } 106 107 private void addDuplicate(MotionEvent e) { 108 if (!ALLOWS_DUPLICATES) { 109 return; 110 } 111 if (mCurrentSelectedView == null) { 112 return; 113 } 114 int pos = findChild(mCurrentSelectedView); 115 if (pos != -1) { 116 mAdapter.insert(new State(mCurrentSelectedView.getState()), pos); 117 fillContent(true); 118 } 119 } 120 121 private void longPress(MotionEvent e) { 122 if (!ALLOWS_DUPLICATES) { 123 return; 124 } 125 View view = findChildAt((int) e.getX(), (int) e.getY()); 126 if (view == null) { 127 return; 128 } 129 if (view instanceof StateView) { 130 StateView stateView = (StateView) view; 131 stateView.setDuplicateButton(true); 132 } 133 } 134 135 public void setAdapter(StateAdapter adapter) { 136 mAdapter = adapter; 137 mAdapter.registerDataSetObserver(mObserver); 138 mAdapter.setOrientation(getOrientation()); 139 fillContent(false); 140 requestLayout(); 141 } 142 143 public StateView findChildWithState(State state) { 144 for (int i = 0; i < getChildCount(); i++) { 145 StateView view = (StateView) getChildAt(i); 146 if (view.getState() == state) { 147 return view; 148 } 149 } 150 return null; 151 } 152 153 public void fillContent(boolean animate) { 154 if (!animate) { 155 this.setLayoutTransition(null); 156 } 157 int n = mAdapter.getCount(); 158 for (int i = 0; i < getChildCount(); i++) { 159 StateView child = (StateView) getChildAt(i); 160 child.resetPosition(); 161 if (!mAdapter.contains(child.getState())) { 162 removeView(child); 163 } 164 } 165 LayoutParams params = new LayoutParams(mElemWidth, mElemHeight); 166 for (int i = 0; i < n; i++) { 167 State s = mAdapter.getItem(i); 168 if (findChildWithState(s) == null) { 169 View view = mAdapter.getView(i, null, this); 170 addView(view, i, params); 171 } 172 } 173 174 for (int i = 0; i < n; i++) { 175 State state = mAdapter.getItem(i); 176 StateView view = (StateView) getChildAt(i); 177 view.setState(state); 178 if (i == 0) { 179 view.setType(StateView.BEGIN); 180 } else if (i == n - 1) { 181 view.setType(StateView.END); 182 } else { 183 view.setType(StateView.DEFAULT); 184 } 185 view.resetPosition(); 186 } 187 188 if (!animate) { 189 this.setLayoutTransition(new LayoutTransition()); 190 } 191 } 192 193 public void onTouch(MotionEvent event, StateView view) { 194 if (!view.isDraggable()) { 195 return; 196 } 197 mCurrentView = view; 198 if (mCurrentSelectedView == mCurrentView) { 199 return; 200 } 201 if (mCurrentSelectedView != null) { 202 mCurrentSelectedView.setSelected(false); 203 } 204 // We changed the current view -- let's reset the 205 // gesture detector. 206 MotionEvent cancelEvent = MotionEvent.obtain(event); 207 cancelEvent.setAction(MotionEvent.ACTION_CANCEL); 208 mGestureDetector.onTouchEvent(cancelEvent); 209 mCurrentSelectedView = mCurrentView; 210 // We have to send the event to the gesture detector 211 mGestureDetector.onTouchEvent(event); 212 mTouchTime = System.currentTimeMillis(); 213 } 214 215 @Override 216 public boolean onInterceptTouchEvent(MotionEvent event) { 217 if (mCurrentView != null) { 218 return true; 219 } 220 return false; 221 } 222 223 @Override 224 public boolean onTouchEvent(MotionEvent event) { 225 if (mCurrentView == null) { 226 return false; 227 } 228 if (mTouchTime == 0) { 229 mTouchTime = System.currentTimeMillis(); 230 } 231 mGestureDetector.onTouchEvent(event); 232 if (mTouchPoint == null) { 233 mTouchPoint = new Point(); 234 mTouchPoint.x = (int) event.getX(); 235 mTouchPoint.y = (int) event.getY(); 236 } 237 238 if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { 239 float translation = event.getY() - mTouchPoint.y; 240 float alpha = 1.0f - (Math.abs(translation) / mCurrentView.getHeight()); 241 if (getOrientation() == LinearLayout.VERTICAL) { 242 translation = event.getX() - mTouchPoint.x; 243 alpha = 1.0f - (Math.abs(translation) / mCurrentView.getWidth()); 244 mCurrentView.setTranslationX(translation); 245 } else { 246 mCurrentView.setTranslationY(translation); 247 } 248 mCurrentView.setBackgroundAlpha(alpha); 249 if (ALLOWS_DRAG && alpha < 0.7) { 250 setOnDragListener(mDragListener); 251 DragShadowBuilder shadowBuilder = new DragShadowBuilder(mCurrentView); 252 mCurrentView.startDrag(null, shadowBuilder, mCurrentView, 0); 253 mStartedDrag = true; 254 } 255 } 256 if (!mExited && mCurrentView != null 257 && mCurrentView.getBackgroundAlpha() > mDeleteSlope 258 && event.getActionMasked() == MotionEvent.ACTION_UP 259 && System.currentTimeMillis() - mTouchTime < mMaxTouchDelay) { 260 FilterRepresentation representation = mCurrentView.getState().getFilterRepresentation(); 261 mCurrentView.setSelected(true); 262 if (representation != MasterImage.getImage().getCurrentFilterRepresentation()) { 263 FilterShowActivity activity = (FilterShowActivity) getContext(); 264 activity.showRepresentation(representation); 265 mCurrentView.setSelected(false); 266 } 267 } 268 if (event.getActionMasked() == MotionEvent.ACTION_UP 269 || (!mStartedDrag && event.getActionMasked() == MotionEvent.ACTION_CANCEL)) { 270 checkEndState(); 271 if (mCurrentView != null) { 272 FilterRepresentation representation = mCurrentView.getState().getFilterRepresentation(); 273 if (representation.getEditorId() == ImageOnlyEditor.ID) { 274 mCurrentView.setSelected(false); 275 } 276 } 277 } 278 return true; 279 } 280 281 public void checkEndState() { 282 mTouchPoint = null; 283 mTouchTime = 0; 284 if (mExited || mCurrentView.getBackgroundAlpha() < mDeleteSlope) { 285 int origin = findChild(mCurrentView); 286 if (origin != -1) { 287 State current = mAdapter.getItem(origin); 288 FilterRepresentation currentRep = MasterImage.getImage().getCurrentFilterRepresentation(); 289 FilterRepresentation removedRep = current.getFilterRepresentation(); 290 mAdapter.remove(current); 291 fillContent(true); 292 if (currentRep != null && removedRep != null 293 && currentRep.getFilterClass() == removedRep.getFilterClass()) { 294 FilterShowActivity activity = (FilterShowActivity) getContext(); 295 activity.backToMain(); 296 return; 297 } 298 } 299 } else { 300 mCurrentView.setBackgroundAlpha(1.0f); 301 mCurrentView.setTranslationX(0); 302 mCurrentView.setTranslationY(0); 303 } 304 if (mCurrentSelectedView != null) { 305 mCurrentSelectedView.invalidate(); 306 } 307 if (mCurrentView != null) { 308 mCurrentView.invalidate(); 309 } 310 mCurrentView = null; 311 mExited = false; 312 mStartedDrag = false; 313 } 314 315 public View findChildAt(int x, int y) { 316 Rect frame = new Rect(); 317 int scrolledXInt = getScrollX() + x; 318 int scrolledYInt = getScrollY() + y; 319 for (int i = 0; i < getChildCount(); i++) { 320 View child = getChildAt(i); 321 child.getHitRect(frame); 322 if (frame.contains(scrolledXInt, scrolledYInt)) { 323 return child; 324 } 325 } 326 return null; 327 } 328 329 public int findChild(View view) { 330 for (int i = 0; i < getChildCount(); i++) { 331 View child = getChildAt(i); 332 if (child == view) { 333 return i; 334 } 335 } 336 return -1; 337 } 338 339 public StateView getCurrentView() { 340 return mCurrentView; 341 } 342 343 public void setCurrentView(View currentView) { 344 mCurrentView = (StateView) currentView; 345 } 346 347 public void setExited(boolean value) { 348 mExited = value; 349 } 350 351 public Point getTouchPoint() { 352 return mTouchPoint; 353 } 354 355 public Adapter getAdapter() { 356 return mAdapter; 357 } 358 } 359