Added Android code
[wl-app.git] / Android / folioreader / src / main / java / com / folioreader / model / quickaction / QuickAction.java
1 package com.folioreader.model.quickaction;
2
3 import com.folioreader.R;
4
5 import android.content.Context;
6 import android.graphics.Rect;
7 import android.graphics.drawable.Drawable;
8 import android.view.Gravity;
9 import android.view.LayoutInflater;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.view.ViewGroup;
13 import android.view.ViewGroup.LayoutParams;
14 import android.widget.ImageView;
15 import android.widget.PopupWindow.OnDismissListener;
16 import android.widget.RelativeLayout;
17 import android.widget.ScrollView;
18 import android.widget.TextView;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
23 /**
24  * QuickAction dialog, shows action list as icon and text like the one in Gallery3D app. Currently
25  * supports vertical
26  * and horizontal layout.
27  *
28  * @author Lorensius W. L. T <lorenz@londatiga.net>
29  *
30  *         Contributors:
31  *         - Kevin Peck <kevinwpeck@gmail.com>
32  */
33 public class QuickAction extends PopupWindows implements OnDismissListener {
34     private View mRootView;
35     private ImageView mArrowUp;
36     private ImageView mArrowDown;
37     private LayoutInflater mInflater;
38     private ViewGroup mTrack;
39     private ScrollView mScroller;
40     private OnActionItemClickListener mItemClickListener;
41     private OnDismissListener mDismissListener;
42
43     private List<ActionItem> mActionItems = new ArrayList<ActionItem>();
44
45     private boolean mDidAction;
46
47     private int mChildPos;
48     private int mInsertPos;
49     private int mAnimStyle;
50     private int mOrientation;
51     private int mRootWidth = 0;
52
53     public static final int HORIZONTAL = 0;
54     public static final int VERTICAL = 1;
55
56     public static final int ANIM_GROW_FROM_LEFT = 1;
57     public static final int ANIM_GROW_FROM_RIGHT = 2;
58     public static final int ANIM_GROW_FROM_CENTER = 3;
59     public static final int ANIM_REFLECT = 4;
60     public static final int ANIM_AUTO = 5;
61
62     /**
63      * Constructor for default vertical layout
64      *
65      * @param context Context
66      */
67     public QuickAction(Context context) {
68         this(context, VERTICAL);
69     }
70
71     /**
72      * Constructor allowing orientation override
73      *
74      * @param context     Context
75      * @param orientation Layout orientation, can be vartical or horizontal
76      */
77     public QuickAction(Context context, int orientation) {
78         super(context);
79
80         mOrientation = orientation;
81
82         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
83
84         if (mOrientation == HORIZONTAL) {
85             setRootViewId(R.layout.popup_horizontal);
86         } else {
87             setRootViewId(R.layout.popup_vertical);
88         }
89
90         mAnimStyle = ANIM_AUTO;
91         mChildPos = 0;
92     }
93
94     /**
95      * Get action item at an index
96      *
97      * @param index Index of item (position from callback)
98      * @return Action Item at the position
99      */
100     public ActionItem getActionItem(int index) {
101         return mActionItems.get(index);
102     }
103
104     /**
105      * Set root view.
106      *
107      * @param id Layout resource id
108      */
109     public void setRootViewId(int id) {
110         mRootView = (ViewGroup) mInflater.inflate(id, null);
111         mTrack = (ViewGroup) mRootView.findViewById(R.id.tracks);
112
113         mArrowDown = (ImageView) mRootView.findViewById(R.id.arrow_down);
114         mArrowUp = (ImageView) mRootView.findViewById(R.id.arrow_up);
115
116         mScroller = (ScrollView) mRootView.findViewById(R.id.scroller);
117
118         //This was previously defined on show()
119         // method, moved here to prevent force close that occured
120         //when tapping fastly on a view to show quickaction dialog.
121         //Thanx to zammbi (github.com/zammbi)
122         mRootView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
123                 LayoutParams.WRAP_CONTENT));
124
125         setContentView(mRootView);
126     }
127
128     /**
129      * Set animation style
130      *
131      * @param mAnimStyle animation style, default is set to ANIM_AUTO
132      */
133     public void setAnimStyle(int mAnimStyle) {
134         this.mAnimStyle = mAnimStyle;
135     }
136
137     /**
138      * Set listener for action item clicked.
139      *
140      * @param listener Listener
141      */
142     public void setOnActionItemClickListener(OnActionItemClickListener listener) {
143         mItemClickListener = listener;
144     }
145
146     /**
147      * Add action item
148      *
149      * @param action {@link ActionItem}
150      */
151     public void addActionItem(ActionItem action) {
152         mActionItems.add(action);
153
154         String title = action.getTitle();
155         Drawable icon = action.getIcon();
156
157         View container;
158
159         if (mOrientation == HORIZONTAL) {
160             container = mInflater.inflate(R.layout.action_item_horizontal, null);
161         } else {
162             container = mInflater.inflate(R.layout.action_item_vertical, null);
163         }
164
165         ImageView img = (ImageView) container.findViewById(R.id.iv_icon);
166         TextView text = (TextView) container.findViewById(R.id.tv_title);
167
168         if (icon != null) {
169             img.setImageDrawable(icon);
170         } else {
171             img.setVisibility(View.GONE);
172         }
173
174         if (title != null) {
175             text.setText(title);
176         } else {
177             text.setVisibility(View.GONE);
178         }
179
180         final int pos = mChildPos;
181         final int actionId = action.getActionId();
182
183         container.setOnClickListener(new OnClickListener() {
184             @Override
185             public void onClick(View v) {
186                 if (mItemClickListener != null) {
187                     mItemClickListener.onItemClick(QuickAction.this, pos, actionId);
188                 }
189
190                 if (!getActionItem(pos).isSticky()) {
191                     mDidAction = true;
192
193                     dismiss();
194                 }
195             }
196         });
197
198         container.setFocusable(true);
199         container.setClickable(true);
200
201         if (mOrientation == HORIZONTAL && mChildPos != 0) {
202             View separator = mInflater.inflate(R.layout.horiz_separator, null);
203
204             RelativeLayout.LayoutParams params =
205                     new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
206                             LayoutParams.FILL_PARENT);
207
208             separator.setLayoutParams(params);
209             separator.setPadding(5, 0, 5, 0);
210
211             mTrack.addView(separator, mInsertPos);
212
213             mInsertPos++;
214         }
215
216         mTrack.addView(container, mInsertPos);
217
218         mChildPos++;
219         mInsertPos++;
220     }
221
222     /**
223      * Show quickaction popup. Popup is automatically positioned, on top or bottom of anchor view.
224      */
225     public void show(View anchor) {
226         preShow();
227
228         int xPos, yPos, arrowPos;
229
230         mDidAction = false;
231
232         int[] location = new int[2];
233
234         anchor.getLocationOnScreen(location);
235
236         Rect anchorRect = new Rect(location[0], location[1], location[0]
237                 + anchor.getWidth(), location[1]
238                 + anchor.getHeight());
239
240         mRootView.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
241
242         int rootHeight = mRootView.getMeasuredHeight();
243
244         if (mRootWidth == 0) {
245             mRootWidth = mRootView.getMeasuredWidth();
246         }
247
248         int screenWidth = mWindowManager.getDefaultDisplay().getWidth();
249         int screenHeight = mWindowManager.getDefaultDisplay().getHeight();
250
251         //automatically get X coord of popup (top left)
252         if ((anchorRect.left + mRootWidth) > screenWidth) {
253             xPos = anchorRect.left - (mRootWidth - anchor.getWidth());
254             xPos = (xPos < 0) ? 0 : xPos;
255
256             arrowPos = anchorRect.centerX() - xPos;
257
258         } else {
259             if (anchor.getWidth() > mRootWidth) {
260                 xPos = anchorRect.centerX() - (mRootWidth / 2);
261             } else {
262                 xPos = anchorRect.left;
263             }
264
265             arrowPos = anchorRect.centerX() - xPos;
266         }
267
268         int dyTop = anchorRect.top;
269         int dyBottom = screenHeight - anchorRect.bottom;
270
271         boolean onTop = (dyTop > dyBottom) ? true : false;
272
273         if (onTop) {
274             if (rootHeight > dyTop) {
275                 yPos = 15;
276                 LayoutParams l = mScroller.getLayoutParams();
277                 l.height = dyTop - anchor.getHeight();
278             } else {
279                 yPos = anchorRect.top - rootHeight;
280             }
281         } else {
282             yPos = anchorRect.bottom;
283
284             if (rootHeight > dyBottom) {
285                 LayoutParams l = mScroller.getLayoutParams();
286                 l.height = dyBottom;
287             }
288         }
289
290         showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), arrowPos);
291
292         setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);
293
294         mWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, xPos, yPos);
295     }
296
297     public void show(View anchor, int width, int height) {
298         preShow();
299
300         int xPos, yPos, arrowPos;
301         int[] location = new int[2];
302
303         anchor.getLocationOnScreen(location);
304
305         Rect anchorRect =
306                 new Rect(location[0], location[1], location[0] + width, location[1] + height);
307
308         mDidAction = false;
309
310         mRootView.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
311
312         int rootHeight = mRootView.getMeasuredHeight();
313
314         if (mRootWidth == 0) {
315             mRootWidth = mRootView.getMeasuredWidth();
316         }
317
318         int screenWidth = mWindowManager.getDefaultDisplay().getWidth();
319         int screenHeight = mWindowManager.getDefaultDisplay().getHeight();
320
321         //automatically get X coord of popup (top left)
322         if ((anchorRect.left + mRootWidth) > screenWidth) {
323             xPos = anchorRect.left - (mRootWidth - width);
324             xPos = (xPos < 0) ? 0 : xPos;
325
326             arrowPos = anchorRect.centerX() - xPos;
327
328         } else {
329             if (width > mRootWidth) {
330                 xPos = anchorRect.centerX() - (mRootWidth / 2);
331             } else {
332                 xPos = anchorRect.left;
333             }
334
335             arrowPos = anchorRect.centerX() - xPos;
336         }
337
338         int dyTop = anchorRect.top;
339         int dyBottom = screenHeight - anchorRect.bottom;
340
341         boolean onTop = (dyTop > dyBottom) ? true : false;
342
343         if (onTop) {
344             if (rootHeight > dyTop) {
345                 yPos = 15;
346                 LayoutParams l = mScroller.getLayoutParams();
347                 l.height = dyTop - height;
348             } else {
349                 yPos = anchorRect.top - rootHeight;
350             }
351         } else {
352             yPos = anchorRect.bottom;
353
354             if (rootHeight > dyBottom) {
355                 LayoutParams l = mScroller.getLayoutParams();
356                 l.height = dyBottom;
357             }
358         }
359
360         showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), arrowPos);
361
362         setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);
363
364         mWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, xPos, yPos);
365     }
366
367     /**
368      * Set animation style
369      *
370      * @param screenWidth screen width
371      * @param requestedX  distance from left edge
372      * @param onTop       flag to indicate where the popup should be displayed. Set TRUE if
373      *                    displayed on top of anchor view
374      *                    and vice versa
375      */
376     private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) {
377         int arrowPos = requestedX - mArrowUp.getMeasuredWidth() / 2;
378
379         switch (mAnimStyle) {
380             case ANIM_GROW_FROM_LEFT:
381                 mWindow.setAnimationStyle((onTop) ?
382                         R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);
383                 break;
384
385             case ANIM_GROW_FROM_RIGHT:
386                 mWindow.setAnimationStyle((onTop) ?
387                         R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);
388                 break;
389
390             case ANIM_GROW_FROM_CENTER:
391                 mWindow.setAnimationStyle((onTop) ?
392                         R.style.Animations_PopUpMenu_Center :
393                         R.style.Animations_PopDownMenu_Center);
394                 break;
395
396             case ANIM_REFLECT:
397                 mWindow.setAnimationStyle((onTop) ?
398                         R.style.Animations_PopUpMenu_Reflect :
399                         R.style.Animations_PopDownMenu_Reflect);
400                 break;
401
402             case ANIM_AUTO:
403                 if (arrowPos <= screenWidth / 4) {
404                     mWindow.setAnimationStyle((onTop) ?
405                             R.style.Animations_PopUpMenu_Left :
406                             R.style.Animations_PopDownMenu_Left);
407                 } else if (arrowPos > screenWidth / 4 &&
408                         arrowPos < 3 * (screenWidth / 4)) {
409                     mWindow.setAnimationStyle((onTop) ?
410                             R.style.Animations_PopUpMenu_Center :
411                             R.style.Animations_PopDownMenu_Center);
412                 } else {
413                     mWindow.setAnimationStyle((onTop) ?
414                             R.style.Animations_PopUpMenu_Right :
415                             R.style.Animations_PopDownMenu_Right);
416                 }
417
418                 break;
419         }
420     }
421
422     /**
423      * Show arrow
424      *
425      * @param whichArrow arrow type resource id
426      * @param requestedX distance from left screen
427      */
428     private void showArrow(int whichArrow, int requestedX) {
429         final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;
430         final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;
431
432         final int arrowWidth = mArrowUp.getMeasuredWidth();
433
434         showArrow.setVisibility(View.VISIBLE);
435
436         ViewGroup.MarginLayoutParams param =
437                 (ViewGroup.MarginLayoutParams) showArrow.getLayoutParams();
438
439         param.leftMargin = requestedX - arrowWidth / 2;
440
441         hideArrow.setVisibility(View.INVISIBLE);
442     }
443
444     /**
445      * Set listener for window dismissed. This listener will only be fired if the quicakction dialog
446      * is dismissed
447      * by clicking outside the dialog or clicking on sticky item.
448      */
449     public void setOnDismissListener(QuickAction.OnDismissListener listener) {
450         setOnDismissListener(this);
451
452         mDismissListener = listener;
453     }
454
455     @Override
456     public void onDismiss() {
457         if (!mDidAction && mDismissListener != null) {
458             mDismissListener.onDismiss();
459         }
460     }
461
462     /**
463      * Listener for item click
464      */
465     public interface OnActionItemClickListener {
466         public abstract void onItemClick(QuickAction source, int pos, int actionId);
467     }
468
469     /**
470      * Listener for window dismiss
471      */
472     public interface OnDismissListener {
473         public abstract void onDismiss();
474     }
475 }