This source file includes following definitions.
- handleDirtyBitmap
- explicitlyDirtyBitmap
- getBitmap
- getCanvas
- prepareForLayout
- onMeasure
- onLayout
- damageRect
- swapBuffers
- onKeyDown
- onKeyMultiple
- onKeyUp
- onFocusChanged
- onGenericMotionEvent
- onTouchEvent
- moveChildToBack
- raise
- lower
- onCreateContextMenu
- popupMenu
- cancelPopupMenu
- onDetachedFromWindow
- onAttachedToWindow
- showOnScreenKeyboard
- hideOnScreenKeyboard
- onCreateInputConnection
- onCheckIsTextEditor
- isOpaque
- setICMode
- getICMode
- onGlobalLayout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.gnu.emacs;
21
22 import android.content.Context;
23
24 import android.text.InputType;
25
26 import android.view.ContextMenu;
27 import android.view.View;
28 import android.view.KeyEvent;
29 import android.view.MotionEvent;
30 import android.view.ViewGroup;
31 import android.view.ViewTreeObserver;
32
33 import android.view.inputmethod.EditorInfo;
34 import android.view.inputmethod.InputConnection;
35 import android.view.inputmethod.InputMethodManager;
36
37 import android.graphics.Bitmap;
38 import android.graphics.Canvas;
39 import android.graphics.Rect;
40 import android.graphics.Region;
41 import android.graphics.Paint;
42
43 import android.os.Build;
44 import android.util.Log;
45
46
47
48
49
50
51
52
53
54 public final class EmacsView extends ViewGroup
55 implements ViewTreeObserver.OnGlobalLayoutListener
56 {
57 public static final String TAG = "EmacsView";
58
59
60 public EmacsWindow window;
61
62
63 public Bitmap bitmap;
64
65
66 public Canvas canvas;
67
68
69 public Region damageRegion;
70
71
72 private EmacsSurfaceView surfaceView;
73
74
75
76 public boolean mustReportLayout;
77
78
79
80 private boolean bitmapDirty;
81
82
83 private boolean popupActive;
84
85
86 private EmacsContextMenu contextMenu;
87
88
89 private int measuredWidth, measuredHeight;
90
91
92 private Object dimensionsLock;
93
94
95 private long lastClipSerial;
96
97
98 public InputMethodManager imManager;
99
100
101 public boolean isAttachedToWindow;
102
103
104
105 public boolean isCurrentlyTextEditor;
106
107
108 private EmacsInputConnection inputConnection;
109
110
111
112 private int icMode;
113
114
115
116 public long icSerial;
117
118
119 public volatile long icGeneration;
120
121 public
122 EmacsView (EmacsWindow window)
123 {
124 super (EmacsService.SERVICE);
125
126 Object tem;
127 Context context;
128
129 this.window = window;
130 this.damageRegion = new Region ();
131
132 setFocusable (true);
133 setFocusableInTouchMode (true);
134
135
136 this.surfaceView = new EmacsSurfaceView (this);
137 addView (this.surfaceView);
138
139
140 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
141 setDefaultFocusHighlightEnabled (false);
142
143
144 context = getContext ();
145 tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
146 imManager = (InputMethodManager) tem;
147
148
149 getViewTreeObserver ().addOnGlobalLayoutListener (this);
150
151
152 this.dimensionsLock = new Object ();
153 }
154
155 private void
156 handleDirtyBitmap ()
157 {
158 Bitmap oldBitmap;
159 int measuredWidth, measuredHeight;
160
161 synchronized (dimensionsLock)
162 {
163
164 measuredWidth = this.measuredWidth;
165 measuredHeight = this.measuredHeight;
166 }
167
168 if (measuredWidth == 0 || measuredHeight == 0)
169 return;
170
171 if (!isAttachedToWindow)
172 return;
173
174
175
176
177 if (bitmap != null
178 && (bitmap.getWidth () == measuredWidth
179 && bitmap.getHeight () == measuredHeight))
180 {
181 bitmapDirty = false;
182 return;
183 }
184
185
186 oldBitmap = bitmap;
187
188
189 bitmap
190 = Bitmap.createBitmap (measuredWidth,
191 measuredHeight,
192 Bitmap.Config.ARGB_8888);
193 bitmap.eraseColor (window.background | 0xff000000);
194
195
196 canvas = new Canvas (bitmap);
197 canvas.save ();
198
199
200
201 lastClipSerial = 0;
202
203
204 if (oldBitmap != null)
205 canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
206
207 bitmapDirty = false;
208
209
210
211 if (oldBitmap != null)
212 oldBitmap.recycle ();
213
214
215
216 Runtime.getRuntime ().gc ();
217 }
218
219 public synchronized void
220 explicitlyDirtyBitmap ()
221 {
222 bitmapDirty = true;
223 }
224
225 public synchronized Bitmap
226 getBitmap ()
227 {
228 if (bitmapDirty || bitmap == null)
229 handleDirtyBitmap ();
230
231 return bitmap;
232 }
233
234 public synchronized Canvas
235 getCanvas (EmacsGC gc)
236 {
237 int i;
238
239 if (bitmapDirty || bitmap == null)
240 handleDirtyBitmap ();
241
242 if (canvas == null)
243 return null;
244
245
246 if (gc.clipRectID != lastClipSerial)
247 {
248 canvas.restore ();
249 canvas.save ();
250
251 if (gc.real_clip_rects != null)
252 {
253 for (i = 0; i < gc.real_clip_rects.length; ++i)
254 canvas.clipRect (gc.real_clip_rects[i]);
255 }
256
257 lastClipSerial = gc.clipRectID;
258 }
259
260 return canvas;
261 }
262
263 public void
264 prepareForLayout (int wantedWidth, int wantedHeight)
265 {
266 synchronized (dimensionsLock)
267 {
268 measuredWidth = wantedWidth;
269 measuredHeight = wantedWidth;
270 }
271 }
272
273 @Override
274 protected void
275 onMeasure (int widthMeasureSpec, int heightMeasureSpec)
276 {
277 Rect measurements;
278 int width, height;
279
280
281
282 measurements = window.getGeometry ();
283
284 width = measurements.width ();
285 height = measurements.height ();
286
287
288
289
290 if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY)
291 width = MeasureSpec.getSize (widthMeasureSpec);
292 else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST
293 && width > MeasureSpec.getSize (widthMeasureSpec))
294 width = MeasureSpec.getSize (widthMeasureSpec);
295
296 if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY)
297 height = MeasureSpec.getSize (heightMeasureSpec);
298 else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
299 && height > MeasureSpec.getSize (heightMeasureSpec))
300 height = MeasureSpec.getSize (heightMeasureSpec);
301
302 super.setMeasuredDimension (width, height);
303 }
304
305
306
307
308
309 @Override
310 protected void
311 onLayout (boolean changed, int left, int top, int right,
312 int bottom)
313 {
314 int count, i, oldMeasuredWidth, oldMeasuredHeight;
315 View child;
316 Rect windowRect;
317 boolean needExpose;
318
319 count = getChildCount ();
320 needExpose = false;
321
322 synchronized (dimensionsLock)
323 {
324
325 oldMeasuredWidth = measuredWidth;
326 oldMeasuredHeight = measuredHeight;
327
328
329 measuredWidth = right - left;
330 measuredHeight = bottom - top;
331 }
332
333
334
335
336 if (changed && (right - left != oldMeasuredWidth
337 || bottom - top != oldMeasuredHeight))
338 {
339 explicitlyDirtyBitmap ();
340
341
342
343 if (right - left > oldMeasuredWidth
344 || bottom - top > oldMeasuredHeight)
345 needExpose = true;
346 }
347
348 for (i = 0; i < count; ++i)
349 {
350 child = getChildAt (i);
351
352 Log.d (TAG, "onLayout: " + child);
353
354 if (child == surfaceView)
355 child.layout (0, 0, right - left, bottom - top);
356 else if (child.getVisibility () != GONE)
357 {
358 if (!(child instanceof EmacsView))
359 continue;
360
361
362
363 windowRect = ((EmacsView) child).window.getGeometry ();
364 child.layout (windowRect.left, windowRect.top,
365 windowRect.right, windowRect.bottom);
366 }
367 }
368
369
370
371 if (changed || mustReportLayout)
372 {
373 mustReportLayout = false;
374 window.viewLayout (left, top, right, bottom);
375 }
376
377 if (needExpose)
378 EmacsNative.sendExpose (this.window.handle, 0, 0,
379 right - left, bottom - top);
380 }
381
382 public void
383 damageRect (Rect damageRect)
384 {
385 EmacsService.checkEmacsThread ();
386 damageRegion.union (damageRect);
387 }
388
389
390
391
392 public void
393 swapBuffers ()
394 {
395 Canvas canvas;
396 Rect damageRect;
397 Bitmap bitmap;
398
399
400
401 EmacsService.checkEmacsThread ();
402
403 damageRect = null;
404
405
406
407 if (damageRegion.isEmpty ())
408 return;
409
410
411
412 damageRect = damageRegion.getBounds ();
413 damageRegion.setEmpty ();
414
415 bitmap = getBitmap ();
416
417
418
419 surfaceView.setBitmap (bitmap, damageRect);
420 }
421
422 @Override
423 public boolean
424 onKeyDown (int keyCode, KeyEvent event)
425 {
426 if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
427 || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
428 || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
429 && !EmacsNative.shouldForwardMultimediaButtons ())
430 return false;
431
432 window.onKeyDown (keyCode, event);
433 return true;
434 }
435
436 @Override
437 public boolean
438 onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
439 {
440 if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
441 || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
442 || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
443 && !EmacsNative.shouldForwardMultimediaButtons ())
444 return false;
445
446 window.onKeyDown (keyCode, event);
447 return true;
448 }
449
450 @Override
451 public boolean
452 onKeyUp (int keyCode, KeyEvent event)
453 {
454 if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
455 || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
456 || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
457 && !EmacsNative.shouldForwardMultimediaButtons ())
458 return false;
459
460 window.onKeyUp (keyCode, event);
461 return true;
462 }
463
464 @Override
465 public void
466 onFocusChanged (boolean gainFocus, int direction,
467 Rect previouslyFocusedRect)
468 {
469 window.onFocusChanged (gainFocus);
470 super.onFocusChanged (gainFocus, direction,
471 previouslyFocusedRect);
472 }
473
474 @Override
475 public boolean
476 onGenericMotionEvent (MotionEvent motion)
477 {
478 return window.onGenericMotionEvent (motion);
479 }
480
481 @Override
482 public boolean
483 onTouchEvent (MotionEvent motion)
484 {
485 return window.onTouchEvent (motion);
486 }
487
488 private void
489 moveChildToBack (View child)
490 {
491 int index;
492
493 index = indexOfChild (child);
494
495 if (index > 0)
496 {
497 detachViewFromParent (index);
498
499
500 attachViewToParent (child, 1,
501 child.getLayoutParams());
502 }
503 }
504
505
506
507
508 public void
509 raise ()
510 {
511 EmacsView parent;
512
513 parent = (EmacsView) getParent ();
514
515 Log.d (TAG, "raise: parent " + parent);
516
517 if (parent.indexOfChild (this)
518 == parent.getChildCount () - 1)
519 return;
520
521 parent.bringChildToFront (this);
522 }
523
524 public void
525 lower ()
526 {
527 EmacsView parent;
528
529 parent = (EmacsView) getParent ();
530
531 Log.d (TAG, "lower: parent " + parent);
532
533 if (parent.indexOfChild (this) == 1)
534 return;
535
536 parent.moveChildToBack (this);
537 }
538
539 @Override
540 protected void
541 onCreateContextMenu (ContextMenu menu)
542 {
543 if (contextMenu == null)
544 return;
545
546 contextMenu.expandTo (menu, this);
547 }
548
549 public boolean
550 popupMenu (EmacsContextMenu menu, int xPosition,
551 int yPosition, boolean force)
552 {
553 if (popupActive && !force)
554 return false;
555
556 contextMenu = menu;
557 popupActive = true;
558
559 Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
560 + ", " + yPosition + " " + force);
561
562
563
564 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
565 return showContextMenu ((float) xPosition, (float) yPosition);
566 else
567 return showContextMenu ();
568 }
569
570 public void
571 cancelPopupMenu ()
572 {
573 if (!popupActive)
574 throw new IllegalStateException ("cancelPopupMenu called without"
575 + " popupActive set");
576
577 contextMenu = null;
578 popupActive = false;
579
580
581
582
583
584 for (EmacsWindowAttachmentManager.WindowConsumer consumer
585 : EmacsWindowAttachmentManager.MANAGER.consumers)
586 {
587 if (consumer instanceof EmacsActivity)
588 ((EmacsActivity) consumer).closeContextMenu ();
589 }
590 }
591
592 @Override
593 public synchronized void
594 onDetachedFromWindow ()
595 {
596 isAttachedToWindow = false;
597
598
599
600 if (bitmap != null)
601 bitmap.recycle ();
602
603 bitmap = null;
604 canvas = null;
605 surfaceView.setBitmap (null, null);
606
607
608 Runtime.getRuntime ().gc ();
609
610 super.onDetachedFromWindow ();
611 }
612
613 @Override
614 public synchronized void
615 onAttachedToWindow ()
616 {
617 isAttachedToWindow = true;
618
619
620
621 bitmapDirty = true;
622
623 synchronized (dimensionsLock)
624 {
625
626 EmacsNative.sendExpose (this.window.handle, 0, 0,
627 measuredWidth, measuredHeight);
628 }
629
630 super.onAttachedToWindow ();
631 }
632
633 public void
634 showOnScreenKeyboard ()
635 {
636
637
638
639 imManager.showSoftInput (this, 0);
640 isCurrentlyTextEditor = true;
641 }
642
643 public void
644 hideOnScreenKeyboard ()
645 {
646 imManager.hideSoftInputFromWindow (this.getWindowToken (),
647 0);
648 isCurrentlyTextEditor = false;
649 }
650
651 @Override
652 public InputConnection
653 onCreateInputConnection (EditorInfo info)
654 {
655 int mode;
656 int[] selection;
657
658
659 mode = getICMode ();
660
661
662
663 info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
664 info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
665
666
667 info.inputType = InputType.TYPE_CLASS_TEXT;
668
669
670
671
672 if (mode == EmacsService.IC_MODE_NULL)
673 {
674 info.inputType = InputType.TYPE_NULL;
675 return null;
676 }
677
678
679
680
681
682 icSerial = icGeneration;
683
684
685
686 EmacsNative.clearInputFlags (window.handle);
687
688
689
690
691
692
693
694
695 selection = EmacsService.viewGetSelection (window.handle);
696
697 if (selection != null)
698 Log.d (TAG, "onCreateInputConnection: current selection is: "
699 + selection[0] + ", by " + selection[1]);
700 else
701 {
702 Log.d (TAG, "onCreateInputConnection: current selection could"
703 + " not be retrieved.");
704
705
706
707
708
709 selection = new int[] { 0, 0, };
710 EmacsNative.requestSelectionUpdate (window.handle);
711 }
712
713 if (mode == EmacsService.IC_MODE_ACTION)
714 info.imeOptions |= EditorInfo.IME_ACTION_DONE;
715
716
717 info.initialSelStart = selection[0];
718 info.initialSelEnd = selection[1];
719
720
721
722 if (inputConnection == null)
723 inputConnection = new EmacsInputConnection (this);
724 else
725
726 inputConnection.reset ();
727
728
729 return inputConnection;
730 }
731
732 @Override
733 public synchronized boolean
734 onCheckIsTextEditor ()
735 {
736
737
738 return isCurrentlyTextEditor;
739 }
740
741 @Override
742 public boolean
743 isOpaque ()
744 {
745
746
747
748 return true;
749 }
750
751 public synchronized void
752 setICMode (int icMode)
753 {
754 this.icMode = icMode;
755 }
756
757 public synchronized int
758 getICMode ()
759 {
760 return icMode;
761 }
762
763 @Override
764 public void
765 onGlobalLayout ()
766 {
767 int[] locations;
768
769
770
771
772 locations = new int[2];
773 getLocationInWindow (locations);
774 window.notifyContentRectPosition (locations[0],
775 locations[1]);
776 }
777 };