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