1 package org.coolreader.crengine;
2 
3 import java.util.ArrayList;
4 
5 import org.coolreader.CoolReader;
6 import org.coolreader.R;
7 
8 import android.graphics.Bitmap;
9 import android.content.res.TypedArray;
10 import android.graphics.Canvas;
11 import android.graphics.ColorMatrix;
12 import android.graphics.ColorMatrixColorFilter;
13 import android.graphics.Paint;
14 import android.graphics.Rect;
15 import android.graphics.drawable.Drawable;
16 import android.view.ContextMenu;
17 import android.view.Gravity;
18 import android.view.KeyEvent;
19 import android.view.LayoutInflater;
20 import android.view.MotionEvent;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.view.WindowManager;
24 import android.widget.ImageButton;
25 import android.widget.ImageView;
26 import android.widget.LinearLayout;
27 import android.widget.PopupWindow;
28 import android.widget.ScrollView;
29 import android.widget.TextView;
30 
31 public class CRToolBar extends ViewGroup {
32 	private static final Logger log = L.create("tb");
33 
34 	final private BaseActivity activity;
35 	private ArrayList<ReaderAction> actions = new ArrayList<>();
36 	private ArrayList<ReaderAction> iconActions = new ArrayList<>();
37 	private int buttonHeight;
38 	private int buttonWidth;
39 	private int itemHeight; // multiline mode, line height
40 	private int visibleButtonCount;
41 	private int visibleNonButtonCount;
42 	private boolean isVertical;
43 	private boolean isMultiline;
44 	final private int preferredItemHeight;
45 	private int BUTTON_SPACING = 4;
46 	private int BAR_SPACING = 4;
47 	private int buttonAlpha = 0xFF;
48 	private int windowDividerHeight = 0; // for popup window, height of divider below buttons
49 	private ImageButton overflowButton;
50 	private LayoutInflater inflater;
51 	private PopupWindow popup;
52 	private int popupLocation = Settings.VIEWER_TOOLBAR_BOTTOM;
53 	private int maxMultilineLines = 3;
54 	private int optionAppearance = 0;
55 	private float toolbarScale = 1.0f;
56 	private boolean grayIcons = false;
57 
setPopup(PopupWindow popup, int popupLocation)58 	private void setPopup(PopupWindow popup, int popupLocation) {
59 		this.popup = popup;
60 		this.popupLocation = popupLocation;
61 	}
62 
63 	private ArrayList<ReaderAction> itemsOverflow = new ArrayList<>();
64 
setButtonAlpha(int alpha)65 	public void setButtonAlpha(int alpha) {
66 		this.buttonAlpha = alpha;
67 		if (isShown()) {
68 			requestLayout();
69 			invalidate();
70 		}
71 	}
72 
setVertical(boolean vertical)73 	public void setVertical(boolean vertical) {
74 		this.isVertical = vertical;
75 		if (isVertical) {
76 			//setPadding(BUTTON_SPACING, BUTTON_SPACING, BAR_SPACING, BUTTON_SPACING);
77 			setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT));
78 		} else {
79 			//setPadding(BUTTON_SPACING, BAR_SPACING, BUTTON_SPACING, BUTTON_SPACING);
80 			setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
81 		}
82 	}
isVertical()83 	public boolean isVertical() {
84 		return this.isVertical;
85 	}
86 
CRToolBar(BaseActivity context)87 	public CRToolBar(BaseActivity context) {
88 		super(context);
89 		this.activity = context;
90 		this.preferredItemHeight = context.getPreferredItemHeight();
91 	}
92 
inflateItem(ReaderAction action)93 	private LinearLayout inflateItem(ReaderAction action) {
94 		final LinearLayout view = (LinearLayout)inflater.inflate(R.layout.popup_toolbar_item, null);
95 		ImageView icon = view.findViewById(R.id.action_icon);
96 		TextView label = view.findViewById(R.id.action_label);
97 		icon.setImageResource(action != null ? action.iconId : Utils.resolveResourceIdByAttr(activity, R.attr.cr3_button_more_drawable, R.drawable.cr3_button_more));
98 		//icon.setMinimumHeight(buttonHeight);
99 		icon.setMinimumWidth(buttonWidth);
100 		Utils.setContentDescription(icon, activity.getString(action != null ? action.nameId : R.string.btn_toolbar_more));
101 		label.setText(action != null ? action.nameId : R.string.btn_toolbar_more);
102 		view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
103 		return view;
104 	}
105 
CRToolBar(BaseActivity context, ArrayList<ReaderAction> actions, boolean multiline)106 	public CRToolBar(BaseActivity context, ArrayList<ReaderAction> actions, boolean multiline) {
107 		super(context);
108 		this.activity = context;
109 		this.actions = actions;
110 		this.isMultiline = multiline;
111 		this.preferredItemHeight = activity.getPreferredItemHeight(); //context.getPreferredItemHeight();
112 		this.inflater = LayoutInflater.from(activity);
113 		this.windowDividerHeight = multiline ? 8 : 0;
114 		context.getWindow().getAttributes();
115 		if (context.isSmartphone()) {
116 			BUTTON_SPACING = 3;
117 			BAR_SPACING = 0; //3;
118 		} else {
119 			BUTTON_SPACING = preferredItemHeight / 20;
120 			BAR_SPACING = 0; //preferredItemHeight / 20;
121 		}
122 		calcLayout();
123 		L.d("CRToolBar preferredItemHeight=" + preferredItemHeight + " buttonWidth=" + buttonWidth + " buttonHeight=" + buttonHeight + " buttonSpacing=" + BUTTON_SPACING);
124 	}
125 
calcLayout()126 	private void calcLayout() {
127 		if (activity instanceof CoolReader) {
128 			//Properties settings = ((CoolReader)activity).getReaderView().getSettings();
129 			//this.optionAppearance = settings.getInt(ReaderView.PROP_TOOLBAR_APPEARANCE, 0);
130 			optionAppearance = Integer.parseInt(((CoolReader)activity).getToolbarAppearance());
131 			toolbarScale = 1.0f;
132 			grayIcons = false;
133 			switch (optionAppearance) {
134 				case Settings.VIEWER_TOOLBAR_100:           // 0
135 				case Settings.VIEWER_TOOLBAR_100_gray:      // 1
136 					toolbarScale = 1.0f;
137 					break;
138 				case Settings.VIEWER_TOOLBAR_75:            // 2
139 					toolbarScale = 0.75f;
140 					break;
141 				case Settings.VIEWER_TOOLBAR_75_gray:       // 3
142 					toolbarScale = 0.75f;
143 					grayIcons = true;
144 					break;
145 				case Settings.VIEWER_TOOLBAR_50:            // 4
146 					toolbarScale = 0.5f;
147 					break;
148 				case Settings.VIEWER_TOOLBAR_50_gray:       // 5
149 					toolbarScale = 0.5f;
150 					grayIcons = true;
151 					break;
152 			}
153 		}
154 		int sz = (int)((float)preferredItemHeight * toolbarScale); //(activity.isSmartphone() ? preferredItemHeight * 6 / 10 - BUTTON_SPACING : preferredItemHeight);
155 		buttonWidth = buttonHeight = sz - BUTTON_SPACING;
156 		if (isMultiline)
157 			buttonHeight = sz / 2;
158 		for (int i=0; i<actions.size(); i++) {
159 			ReaderAction item = actions.get(i);
160 			int iconId = item.iconId;
161 			if (iconId == 0) {
162 				itemsOverflow.add(item);
163 				visibleNonButtonCount++;
164 				continue;
165 			}
166 			iconActions.add(item);
167 			Drawable d = activity.getResources().getDrawable(iconId);
168 			visibleButtonCount++;
169 			int w = d.getIntrinsicWidth();// * dpi / 160;
170 			int h = d.getIntrinsicHeight();// * dpi / 160;
171 			if (buttonWidth < w) {
172 				buttonWidth = w;
173 			}
174 			if (buttonHeight < h) {
175 				buttonHeight = h;
176 			}
177 		}
178 		if (isMultiline) {
179 			LinearLayout item = inflateItem(iconActions.get(0));
180 			itemHeight = item.getMeasuredHeight() + BUTTON_SPACING;
181 		}
182 	}
183 
184 	private OnActionHandler onActionHandler;
185 
setOnActionHandler(OnActionHandler handler)186 	public void setOnActionHandler(OnActionHandler handler) {
187 		this.onActionHandler = handler;
188 	}
189 
190 	private OnOverflowHandler onOverflowHandler;
191 
setOnOverflowHandler(OnOverflowHandler handler)192 	public void setOnOverflowHandler(OnOverflowHandler handler) {
193 		this.onOverflowHandler = handler;
194 	}
195 
196 	public interface OnActionHandler {
onActionSelected(ReaderAction item)197 		boolean onActionSelected(ReaderAction item);
198 	}
199 	public interface OnOverflowHandler {
onOverflowActions(ArrayList<ReaderAction> actions)200 		boolean onOverflowActions(ArrayList<ReaderAction> actions);
201 	}
202 
203 	@Override
onCreateContextMenu(ContextMenu menu)204 	protected void onCreateContextMenu(ContextMenu menu) {
205 		int order = 0;
206 		for (ReaderAction action : itemsOverflow) {
207 			menu.add(0, action.menuItemId, order++, action.nameId);
208 		}
209 	}
210 
allActionsHaveIcon(ArrayList<ReaderAction> list)211 	private static boolean allActionsHaveIcon(ArrayList<ReaderAction> list) {
212 		for (ReaderAction item : list) {
213 			if (item.iconId == 0)
214 				return false;
215 		}
216 		return true;
217 	}
218 
showOverflowMenu()219 	public void showOverflowMenu() {
220 		if (itemsOverflow.size() > 0) {
221 			if (onOverflowHandler != null)
222 				onOverflowHandler.onOverflowActions(itemsOverflow);
223 			else {
224 				if (!isMultiline && visibleNonButtonCount == 0) {
225 					showPopup(activity, activity.getContentView(), actions, onActionHandler, onOverflowHandler, actions.size(), Settings.VIEWER_TOOLBAR_TOP);
226 				} else {
227 					if (allActionsHaveIcon(itemsOverflow)) {
228 						if (popup != null)
229 							popup.dismiss();
230 						showPopup(activity, activity.getContentView(), itemsOverflow, onActionHandler, onOverflowHandler, actions.size(), isMultiline ? popupLocation : Settings.VIEWER_TOOLBAR_BOTTOM);
231 					} else
232 						activity.showActionsPopupMenu(itemsOverflow, onActionHandler);
233 				}
234 			}
235 //			PopupMenu menu = new PopupMenu(activity, this);
236 //			int order = 0;
237 //			for (ReaderAction action : itemsOverflow) {
238 //				menu.getMenu().add(0, action.menuItemId, order++, action.nameId);
239 //			}
240 //			menu.show();
241 //			showContextMenuForChild(overflowButton);
242 		}
243 	}
244 
245 //	private void onMoreButtonClick() {
246 //		showOverflowMenu();
247 //	}
248 
onButtonClick(ReaderAction item)249 	private void onButtonClick(ReaderAction item) {
250 		if (onActionHandler != null)
251 			onActionHandler.onActionSelected(item);
252 	}
253 
setButtonImageResource(ImageButton ib, int resId)254 	private void setButtonImageResource(ImageButton ib, int resId) {
255 		if (optionAppearance == Settings.VIEWER_TOOLBAR_100) {
256 			ib.setImageResource(resId);
257 			return;
258 		}
259 		Drawable dr = getResources().getDrawable(resId);
260 		int iWidth = dr.getIntrinsicWidth();
261 		iWidth = (int) ((float) iWidth * this.toolbarScale);
262 		int iHeight = dr.getIntrinsicHeight();
263 		iHeight = (int) ((float) iHeight * this.toolbarScale);
264 		Bitmap bitmap = Bitmap.createBitmap(iWidth, iHeight, Bitmap.Config.ARGB_8888);
265 		Canvas canvas = new Canvas(bitmap);
266 		dr.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
267 		dr.draw(canvas);
268 		if (this.grayIcons) {
269 			Bitmap bmpGrayscale = Bitmap.createBitmap(iWidth, iHeight, Bitmap.Config.ARGB_8888);
270 			Canvas c = new Canvas(bmpGrayscale);
271 			Paint paint = new Paint();
272 			ColorMatrix cm = new ColorMatrix();
273 			cm.setSaturation(0);
274 			ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
275 			paint.setColorFilter(f);
276 			c.drawBitmap(bitmap, 0, 0, paint);
277 			ib.setImageBitmap(bmpGrayscale);
278 		} else {
279 			ib.setImageBitmap(bitmap);
280 		}
281 	}
282 
addButton(Rect rect, final ReaderAction item, boolean left)283 	private ImageButton addButton(Rect rect, final ReaderAction item, boolean left) {
284 		Rect rc = new Rect(rect);
285 		if (isVertical) {
286 			if (left) {
287 				rc.bottom = rc.top + buttonHeight;
288 				rect.top += buttonHeight + BUTTON_SPACING;
289 			} else {
290 				rc.top = rc.bottom - buttonHeight;
291 				rect.bottom -= buttonHeight + BUTTON_SPACING;
292 			}
293 		} else {
294 			if (left) {
295 				rc.right = rc.left + buttonWidth;
296 				rect.left += buttonWidth + BUTTON_SPACING;
297 			} else {
298 				rc.left = rc.right - buttonWidth;
299 				rect.right -= buttonWidth + BUTTON_SPACING;
300 			}
301 		}
302 		if (rc.isEmpty())
303 			return null;
304 		ImageButton ib = new ImageButton(getContext());
305 		if (item != null) {
306 			setButtonImageResource(ib,item.iconId);
307 			Utils.setContentDescription(ib, getContext().getString(item.nameId));
308 			ib.setTag(item);
309 		} else {
310 			setButtonImageResource(ib,Utils.resolveResourceIdByAttr(activity, R.attr.cr3_button_more_drawable, R.drawable.cr3_button_more));
311 			Utils.setContentDescription(ib, getContext().getString(R.string.btn_toolbar_more));
312 		}
313 		TypedArray a = activity.getTheme().obtainStyledAttributes( new int[] { R.attr.cr3_toolbar_button_background_drawable } );
314 		int cr3_toolbar_button_background = a.getResourceId(0, 0);
315 		a.recycle();
316 		if (0 == cr3_toolbar_button_background)
317 			cr3_toolbar_button_background = R.drawable.cr3_toolbar_button_background;
318 		ib.setBackgroundResource(cr3_toolbar_button_background);
319 		ib.layout(rc.left, rc.top, rc.right, rc.bottom);
320 		if (item == null)
321 			overflowButton = ib;
322 		ib.setOnClickListener(v -> {
323 			if (item != null)
324 				onButtonClick(item);
325 			else
326 				showOverflowMenu();
327 		});
328 		ib.setAlpha(nightMode ? 0x60 : buttonAlpha);
329 		addView(ib);
330 		return ib;
331 	}
332 
333 	private Rect layoutRect = new Rect();
334 	private Rect layoutLineRect = new Rect();
335 	private Rect layoutItemRect = new Rect();
336 	@Override
onLayout(boolean changed, int left, int top, int right, int bottom)337 	protected void onLayout(boolean changed, int left, int top, int right,
338 			int bottom) {
339 		right -= left;
340 		bottom -= top;
341 		left = top = 0;
342 		//calcLayout();
343 		removeAllViews();
344 		overflowButton = null;
345 
346 		if (isMultiline) {
347 			itemsOverflow.clear();
348 			int lastButtonIndex = -1;
349 
350         	int lineCount = calcLineCount(right);
351         	int btnCount = iconActions.size() + (visibleNonButtonCount > 0 ? 1 : 0);
352         	int buttonsPerLine = (btnCount + lineCount - 1) / lineCount;
353 
354         	int y0 = 0;
355         	if (popupLocation == Settings.VIEWER_TOOLBAR_BOTTOM) {
356 	    		View separator = new View(activity);
357 	    		separator.setBackgroundResource(activity.getCurrentTheme().getBrowserStatusBackground());
358 	    		addView(separator);
359 	    		separator.layout(left, top, right, top + windowDividerHeight);
360 	    		y0 = windowDividerHeight + 4;
361         	}
362 
363 
364 //        	ScrollView scroll = new ScrollView(activity);
365 //        	scroll.setLayoutParams(new LayoutParams(right, bottom));
366 //        	AbsoluteLayout content = new AbsoluteLayout(activity);
367 
368         	layoutRect.set(left + getPaddingLeft() + BUTTON_SPACING, top + getPaddingTop() + BUTTON_SPACING, right - getPaddingRight() - BUTTON_SPACING, bottom - getPaddingBottom() - BUTTON_SPACING - y0);
369     		int lineH = itemHeight; //rect.height() / lineCount;
370     		int spacing = 0;
371     		int maxLines = bottom / lineH;
372     		if (maxLines > maxMultilineLines)
373     			maxLines = maxMultilineLines;
374         	for (int currentLine = 0; currentLine < lineCount && currentLine < maxLines; currentLine++) {
375         		int startBtn = currentLine * buttonsPerLine;
376         		int endBtn = (currentLine + 1) * buttonsPerLine;
377         		if (endBtn > btnCount)
378         			endBtn = btnCount;
379         		int currentLineButtons = endBtn - startBtn;
380         		layoutLineRect.set(layoutRect);
381         		layoutLineRect.top += currentLine * lineH + spacing + y0;
382         		layoutLineRect.bottom = layoutLineRect.top + lineH - spacing;
383         		int itemWidth = layoutLineRect.width() / currentLineButtons;
384         		for (int i = 0; i < currentLineButtons; i++) {
385         			layoutItemRect.set(layoutLineRect);
386         			layoutItemRect.left += i * itemWidth + spacing;
387         			layoutItemRect.right = layoutItemRect.left + itemWidth - spacing;
388         			final ReaderAction action = (visibleNonButtonCount > 0 && i + startBtn == iconActions.size()) || (lineCount > maxLines && currentLine == maxLines - 1 && i == currentLineButtons - 1) ? null : iconActions.get(startBtn + i);
389         			if (action != null)
390         				lastButtonIndex = startBtn + i;
391         			log.v("item=" + layoutItemRect);
392         			LinearLayout item = inflateItem(action);
393         			//item.setLayoutParams(new LinearLayout.LayoutParams(itemRect.width(), itemRect.height()));
394         			item.measure(MeasureSpec.makeMeasureSpec(layoutItemRect.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(layoutItemRect.height(), MeasureSpec.EXACTLY));
395         			item.layout(layoutItemRect.left, layoutItemRect.top, layoutItemRect.right, layoutItemRect.bottom);
396         			//item.forceLayout();
397         			addView(item);
398         			item.setOnClickListener(v -> {
399 						if (action != null)
400 							onButtonClick(action);
401 						else
402 							showOverflowMenu();
403 					});
404         		}
405 //        		addView(scroll);
406         	}
407         	if (popupLocation != Settings.VIEWER_TOOLBAR_BOTTOM) {
408 	    		View separator = new View(activity);
409 	    		separator.setBackgroundResource(activity.getCurrentTheme().getBrowserStatusBackground());
410 	    		addView(separator);
411 	    		separator.layout(left, bottom - windowDividerHeight, right, bottom);
412         	}
413     		//popup.
414     		if (lastButtonIndex > 0)
415     			for (int i=lastButtonIndex + 1; i < actions.size(); i++)
416     				itemsOverflow.add(actions.get(i));
417         	return;
418 		}
419 
420 //		View divider = new View(getContext());
421 //		addView(divider);
422 //		if (isVertical()) {
423 //			divider.setBackgroundResource(R.drawable.divider_light_vertical_tiled);
424 //			divider.layout(right - 8, top, right, bottom);
425 //		} else {
426 //			divider.setBackgroundResource(R.drawable.divider_light_tiled);
427 //			divider.layout(left, bottom - 8, right, bottom);
428 //		}
429 
430 		visibleButtonCount = 0;
431 		for (int i=0; i<actions.size(); i++) {
432 			if (actions.get(i).iconId != 0)
433 				visibleButtonCount++;
434 		}
435 
436 
437 		Rect rect = new Rect(left + getPaddingLeft() + BUTTON_SPACING, top + getPaddingTop() + BUTTON_SPACING, right - getPaddingRight() - BUTTON_SPACING, bottom - getPaddingBottom() - BUTTON_SPACING);
438 		if (rect.isEmpty())
439 			return;
440 		ArrayList<ReaderAction> itemsToShow = new ArrayList<ReaderAction>();
441 		itemsOverflow.clear();
442 		int maxButtonCount = 1;
443 		if (isVertical) {
444 			rect.right -= BAR_SPACING;
445 			int maxHeight = bottom - top - getPaddingTop() - getPaddingBottom() + BUTTON_SPACING;
446 			maxButtonCount = maxHeight / (buttonHeight + BUTTON_SPACING);
447 		} else {
448 			rect.bottom -= BAR_SPACING;
449 			int maxWidth = right - left - getPaddingLeft() - getPaddingRight() + BUTTON_SPACING;
450 			maxButtonCount = maxWidth / (buttonWidth + BUTTON_SPACING);
451 		}
452 		int count = 0;
453 		boolean addEllipsis = visibleButtonCount > maxButtonCount || visibleNonButtonCount > 0;
454 		if (addEllipsis) {
455 			addButton(rect, null, false);
456 			maxButtonCount--;
457 		}
458 		for (int i = 0; i < actions.size(); i++) {
459 			ReaderAction item = actions.get(i);
460 			if (count >= maxButtonCount) {
461 				itemsOverflow.add(item);
462 				continue;
463 			}
464 			if (item.iconId == 0) {
465 				itemsOverflow.add(item);
466 				continue;
467 			}
468 			itemsToShow.add(item);
469 			count++;
470 			addButton(rect, item, true);
471 		}
472 	}
473 
474 	@Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)475 	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
476 //        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
477 //        if (widthMode != MeasureSpec.EXACTLY) {
478 //            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
479 //                    "with android:layout_width=\"match_parent\" (or fill_parent)");
480 //        }
481 
482 //        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
483 //        if (heightMode != MeasureSpec.AT_MOST) {
484 //            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
485 //                    "with android:layout_height=\"wrap_content\"");
486 //        }
487 
488         if (isVertical) {
489 	        int contentHeight = MeasureSpec.getSize(heightMeasureSpec);
490 	        int maxWidth = buttonWidth + BUTTON_SPACING + BUTTON_SPACING + BAR_SPACING + getPaddingLeft() + getPaddingRight();
491 	        setMeasuredDimension(maxWidth, contentHeight);
492         } else {
493 	        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
494 	        if (isMultiline) {
495 		        int contentHeight = MeasureSpec.getSize(heightMeasureSpec);
496 	        	int lineCount = calcLineCount(contentWidth);
497 	        	if (lineCount > maxMultilineLines)
498 	        		lineCount = maxMultilineLines;
499 	        	int h = lineCount * itemHeight + BAR_SPACING + BAR_SPACING + windowDividerHeight + 4;
500 //	        	if (h > contentHeight - itemHeight)
501 //	        		h = contentHeight - itemHeight;
502 	        	setMeasuredDimension(contentWidth, h);
503 	        } else {
504 	        	setMeasuredDimension(contentWidth, buttonHeight + BUTTON_SPACING * 2 + BAR_SPACING);
505 	        }
506         }
507 	}
508 
calcLineCount(int contentWidth)509 	protected int calcLineCount(int contentWidth) {
510 		if (!isMultiline)
511 			return 1;
512     	int lineCount = 1;
513     	int btnCount = iconActions.size() + (visibleNonButtonCount > 0 ? 1 : 0);
514     	int minLineItemCount = 3;
515     	int maxLineItemCount = contentWidth / (preferredItemHeight * 3 / 2);
516     	if (maxLineItemCount < minLineItemCount)
517     		maxLineItemCount = minLineItemCount;
518 
519     	for (;;) {
520 	    	if (btnCount <= maxLineItemCount * lineCount)
521 	    		return lineCount;
522 	    	lineCount++;
523     	}
524 	}
525 
526 	@Override
onSizeChanged(int w, int h, int oldw, int oldh)527 	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
528 		super.onSizeChanged(w, h, oldw, oldh);
529 		log.v("CRToolBar.onSizeChanged(" + w + ", " + h + ")");
530 	}
531 
532 
533 
534 	@Override
onDraw(Canvas canvas)535 	protected void onDraw(Canvas canvas) {
536 		log.v("CRToolBar.onDraw(" + getWidth() + ", " + getHeight() + ")");
537 		super.onDraw(canvas);
538 	}
showAsPopup(View anchor, OnActionHandler onActionHandler, OnOverflowHandler onOverflowHandler)539 	public PopupWindow showAsPopup(View anchor, OnActionHandler onActionHandler, OnOverflowHandler onOverflowHandler) {
540 		return showPopup(activity, anchor, actions, onActionHandler, onOverflowHandler, 3, Settings.VIEWER_TOOLBAR_BOTTOM);
541 	}
542 
setMaxLines(int maxLines)543 	private void setMaxLines(int maxLines) {
544 		this.maxMultilineLines = maxLines;
545 	}
546 
showPopup(BaseActivity context, View anchor, ArrayList<ReaderAction> actions, final OnActionHandler onActionHandler, final OnOverflowHandler onOverflowHandler, int maxLines, int popupLocation)547 	public static PopupWindow showPopup(BaseActivity context, View anchor, ArrayList<ReaderAction> actions, final OnActionHandler onActionHandler, final OnOverflowHandler onOverflowHandler, int maxLines, int popupLocation) {
548 		final ScrollView scroll = new ScrollView(context);
549 		final CRToolBar tb = new CRToolBar(context, actions, true);
550 		tb.setMaxLines(maxLines);
551 		tb.setOnActionHandler(onActionHandler);
552 		tb.setVertical(false);
553 		tb.measure(MeasureSpec.makeMeasureSpec(anchor.getWidth(), MeasureSpec.EXACTLY), ViewGroup.LayoutParams.WRAP_CONTENT);
554 		int w = tb.getMeasuredWidth();
555 		int h = tb.getMeasuredHeight();
556 		scroll.addView(tb);
557 		scroll.setLayoutParams(new LayoutParams(w, h/2));
558 		scroll.setVerticalFadingEdgeEnabled(true);
559 		scroll.setFadingEdgeLength(h / 10);
560 		final PopupWindow popup = new PopupWindow(context);
561 		tb.setPopup(popup, popupLocation);
562 		ReaderAction longMenuAction = null;
563 		for (ReaderAction action : actions) {
564 			if (action.activateWithLongMenuKey())
565 				longMenuAction = action;
566 		}
567 		final ReaderAction foundLongMenuAction = longMenuAction;
568 
569 		popup.setTouchInterceptor((v, event) -> {
570 			if ( event.getAction()==MotionEvent.ACTION_OUTSIDE ) {
571 				popup.dismiss();
572 				return true;
573 			}
574 			return false;
575 		});
576 		tb.setOnActionHandler(item -> {
577 			popup.dismiss();
578 			return onActionHandler.onActionSelected(item);
579 		});
580 		if (onOverflowHandler != null)
581 			tb.setOnOverflowHandler(actions1 -> {
582 				popup.dismiss();
583 				return onOverflowHandler.onOverflowActions(actions1);
584 			});
585 		// close on menu or back keys
586 		tb.setFocusable(true);
587 		tb.setFocusableInTouchMode(true);
588 		tb.setOnKeyListener((view, keyCode, event) -> {
589 			if (event.getAction() == KeyEvent.ACTION_DOWN) {
590 				if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
591 					//popup.dismiss();
592 					return true;
593 				}
594 			} else if (event.getAction() == KeyEvent.ACTION_UP) {
595 				if (keyCode == KeyEvent.KEYCODE_MENU && foundLongMenuAction != null && event.getDownTime() >= 500) {
596 					popup.dismiss();
597 					return onActionHandler.onActionSelected(foundLongMenuAction);
598 				}
599 				if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
600 					popup.dismiss();
601 					return true;
602 				}
603 			}
604 			return false;
605 		});
606 		//popup.setBackgroundDrawable(new BitmapDrawable());
607 		popup.setWidth(WindowManager.LayoutParams.FILL_PARENT);
608 		int hh = h;
609 		int maxh = anchor.getHeight();
610 		if (hh > maxh - context.getPreferredItemHeight())
611 			hh = maxh - context.getPreferredItemHeight() * 3 / 2;
612 		popup.setHeight(hh);
613 		popup.setFocusable(true);
614 		popup.setFocusable(true);
615 		popup.setTouchable(true);
616 		popup.setOutsideTouchable(true);
617 		popup.setContentView(scroll);
618 		InterfaceTheme theme = context.getCurrentTheme();
619 		Drawable bg;
620 		if (theme.getPopupToolbarBackground() != 0)
621 			bg = context.getResources().getDrawable(theme.getPopupToolbarBackground());
622 		else
623 			bg = Utils.solidColorDrawable(theme.getPopupToolbarBackgroundColor());
624 		popup.setBackgroundDrawable(bg);
625 		int [] location = new int[2];
626 		anchor.getLocationOnScreen(location);
627 		int popupY = location[1];
628 		if (popupLocation == Settings.VIEWER_TOOLBAR_BOTTOM)
629 			popup.showAtLocation(anchor, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0); //location[0], popupY - anchor.getHeight());
630 		else
631 			popup.showAtLocation(anchor, Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); //, location[0], popupY);
632 		return popup;
633 	}
634 
635 	private boolean nightMode;
updateNightMode(boolean nightMode)636 	public void updateNightMode(boolean nightMode) {
637 		if (this.nightMode != nightMode) {
638 			this.nightMode = nightMode;
639 			if (isShown()) {
640 				requestLayout();
641 				invalidate();
642 			}
643 		}
644 	}
645 
onThemeChanged(InterfaceTheme theme)646 	public void onThemeChanged(InterfaceTheme theme) {
647 		//buttonAlpha = theme.getToolbarButtonAlpha();
648 		//textColor = theme.getStatusTextColor();
649 		if (isShown()) {
650 			requestLayout();
651 			invalidate();
652 		}
653 	}
654 }
655