A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using Android.Content;
6using Android.Runtime;
7using Android.Text;
8using Android.Util;
9using Android.Views;
10using Android.Views.InputMethods;
11using osu.Framework.Android.Input;
12using osuTK.Graphics;
13
14namespace osu.Framework.Android
15{
16 public class AndroidGameView : osuTK.Android.AndroidGameView
17 {
18 public AndroidGameHost Host { get; private set; }
19
20 private readonly Game game;
21
22 public new event Action<Keycode, KeyEvent> KeyDown;
23 public new event Action<Keycode, KeyEvent> KeyUp;
24 public event Action<Keycode, KeyEvent> KeyLongPress;
25 public event Action<string> CommitText;
26 public event Action<AndroidGameHost> HostStarted;
27
28 public AndroidGameView(Context context, Game game)
29 : base(context)
30 {
31 this.game = game;
32
33 init();
34 }
35
36 public AndroidGameView(Context context, IAttributeSet attrs)
37 : base(context, attrs)
38 {
39 init();
40 }
41
42 public AndroidGameView(IntPtr handle, JniHandleOwnership transfer)
43 : base(handle, transfer)
44 {
45 init();
46 }
47
48 private void init()
49 {
50 AutoSetContextOnRenderFrame = true;
51 ContextRenderingApi = GLVersion.ES3;
52
53 // enable soft and hardware keyboard
54 // this needs to happen in the constructor
55 Focusable = true;
56 FocusableInTouchMode = true;
57 }
58
59 protected override void CreateFrameBuffer()
60 {
61 try
62 {
63 base.CreateFrameBuffer();
64 Log.Verbose("AndroidGameView", "Successfully created the framebuffer");
65 }
66 catch (Exception e)
67 {
68 Log.Verbose("AndroidGameView", "{0}", e);
69 throw new InvalidOperationException("Can't load egl, aborting", e);
70 }
71 }
72
73 public bool OnCommitText(string text)
74 {
75 CommitText?.Invoke(text);
76 return false;
77 }
78
79 public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent e)
80 {
81 switch (keyCode)
82 {
83 // Do not consume Volume keys, so the system can handle them
84 case Keycode.VolumeDown:
85 case Keycode.VolumeUp:
86 case Keycode.VolumeMute:
87 return false;
88
89 default:
90 KeyDown?.Invoke(keyCode, e);
91 return true;
92 }
93 }
94
95 public override bool OnKeyLongPress([GeneratedEnum] Keycode keyCode, KeyEvent e)
96 {
97 KeyLongPress?.Invoke(keyCode, e);
98 return true;
99 }
100
101 public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent e)
102 {
103 KeyUp?.Invoke(keyCode, e);
104 return true;
105 }
106
107 protected override void OnLoad(EventArgs e)
108 {
109 base.OnLoad(e);
110
111 RenderGame();
112 }
113
114 [STAThread]
115 public void RenderGame()
116 {
117 Host = new AndroidGameHost(this);
118 Host.ExceptionThrown += handleException;
119 Host.Run(game);
120 HostStarted.Invoke(Host);
121 }
122
123 private bool handleException(Exception ex)
124 {
125 // suppress exceptions related to MobileAuthenticatedStream disposal
126 // (see: https://github.com/ppy/osu/issues/6264 and linked related mono/xamarin issues)
127 // to be removed when upstream fixes come in
128 return ex is AggregateException ae
129 && ae.InnerException is ObjectDisposedException ode
130 && ode.ObjectName == "MobileAuthenticatedStream";
131 }
132
133 public override bool OnCheckIsTextEditor() => true;
134
135 public override IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
136 {
137 outAttrs.ImeOptions = ImeFlags.NoExtractUi;
138 outAttrs.InputType = InputTypes.Null;
139 return new AndroidInputConnection(this, true);
140 }
141 }
142}