Survivalcraft API 1.8.2.3 v1.8.2.3
Survivalcraft 2.4
载入中...
搜索中...
未找到
EngineActivity.cs
浏览该文件的文档.
1#if ANDROID
2#pragma warning disable CA1416
3using Android.App;
4using Android.Content;
5using Android.Content.PM;
6using Android.Database;
7using Android.Graphics;
8using Android.Media;
9using Android.OS;
10using Android.Provider;
11using Android.Runtime;
12using Android.Views;
13using AndroidX.Core.View;
14using Engine.Input;
15using Silk.NET.Windowing.Sdl.Android;
16using Environment = System.Environment;
17using Insets = AndroidX.Core.Graphics.Insets;
18using Path = System.IO.Path;
19using Stream = Android.Media.Stream;
20using Uri = Android.Net.Uri;
21// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
22
23namespace Engine {
24 public class EngineActivity : SilkActivity {
25 internal static EngineActivity m_activity;
26
27 public event Action Paused;
28
29 public event Action Resumed;
30
31 public event Action Destroyed;
32
33 public event Action<Intent> NewIntent;
34
35 public event Func<KeyEvent, bool> OnDispatchKeyEvent;
36
37 public static string BasePath = RunPath.AndroidFilePath;
38 public static string ConfigPath = RunPath.AndroidFilePath;
39
40 const int PickFileRequestCode = 1001;
41 TaskCompletionSource<(System.IO.Stream Stream, string FileName)> filePickTcs;
42
43 AudioManager AudioManager {
44 get {
45 if (field == null) {
46 field = GetAudioManager();
47 }
48 return field;
49 }
50 }
51
52 public EngineActivity() => m_activity = this;
53
54 protected override void OnCreate(Bundle savedInstanceState) {
55 RequestWindowFeature(WindowFeatures.NoTitle);
56 base.OnCreate(savedInstanceState);
57 Window?.AddFlags(WindowManagerFlags.Fullscreen | WindowManagerFlags.TranslucentStatus | WindowManagerFlags.TranslucentNavigation);
58 EnableImmersiveMode();
59 VolumeControlStream = Stream.Music;
60 RequestedOrientation = ScreenOrientation.SensorLandscape;
61 if (Build.VERSION.SdkInt >= (BuildVersionCodes)28 && Window != null) {
62 ViewCompat.SetOnApplyWindowInsetsListener(Window.DecorView, new ApplyWindowInsetsListener());
63 }
64 }
65
66 public void Vibrate(long ms) {
67 if (Build.VERSION.SdkInt >= (BuildVersionCodes)26) {
68 (GetSystemService("vibrator") as Vibrator)?.Vibrate(VibrationEffect.CreateOneShot(ms, VibrationEffect.DefaultAmplitude));
69 }
70 }
71
72 public void OpenLink(string link) {
73 StartActivity(new Intent(Intent.ActionView, Uri.Parse(link)));
74 }
75
76 public void OpenFile(string path, string chooserTitle = null, string mimeType = null) {
77 string processedAndroidFilePath = Storage.ProcessPath(RunPath.AndroidFilePath, false, false);
78 if (!path.StartsWith(processedAndroidFilePath)) {
79 throw new ArgumentException($"Open {path} failed, because it is not in {processedAndroidFilePath}.");
80 }
81 Java.IO.File file = new(path);
82 if (!file.Exists()) {
83 throw new FileNotFoundException($"Open {path} failed, because it is not exists.");
84 }
85 Uri uri = Build.VERSION.SdkInt >= BuildVersionCodes.N
86 ? AndroidX.Core.Content.FileProvider.GetUriForFile(this, $"{PackageName}.fileprovider", file)
87 : Uri.FromFile(file);
88 Intent intent = new(Intent.ActionView);
89 mimeType ??= Android.Webkit.MimeTypeMap.Singleton?.GetMimeTypeFromExtension(Storage.GetExtension(path));
90 if (mimeType == null) {
91 intent.SetData(uri);
92 }
93 else {
94 intent.SetDataAndType(uri, mimeType);
95 }
96 intent.AddFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.NewTask);
97 if (Application.Context.PackageManager?.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly).Any() ?? false) {
98 StartActivity(Intent.CreateChooser(intent, chooserTitle ?? Storage.GetFileName(path)));
99 }
100 else {
101 throw new InvalidOperationException($"Open {path} failed, because no app can open it.");
102 }
103 }
104
105 public void ShareFile(string path, string chooserTitle = null, string mimeType = null) {
106 string processedAndroidFilePath = Storage.ProcessPath(RunPath.AndroidFilePath, false, false);
107 if (!path.StartsWith(processedAndroidFilePath)) {
108 throw new ArgumentException($"Share {path} failed, because it is not in {processedAndroidFilePath}.");
109 }
110 Java.IO.File file = new(path);
111 if (!file.Exists()) {
112 throw new FileNotFoundException($"Share {path} failed, because it does not exist.");
113 }
114 Uri uri = Build.VERSION.SdkInt >= BuildVersionCodes.N
115 ? AndroidX.Core.Content.FileProvider.GetUriForFile(this, $"{PackageName}.fileprovider", file)
116 : Uri.FromFile(file);
117 Intent intent = new(Intent.ActionSend);
118 mimeType ??= Android.Webkit.MimeTypeMap.Singleton?.GetMimeTypeFromExtension(Storage.GetExtension(path)) ?? "*/*";
119 intent.SetType(mimeType);
120 intent.PutExtra(Intent.ExtraStream, uri);
121 intent.AddFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.NewTask);
122 StartActivity(Intent.CreateChooser(intent, chooserTitle ?? Storage.GetFileName(path)));
123 }
124
125 public Task<(System.IO.Stream Stream, string FileName)> ChooseFileAsync(string chooserTitle = null) {
126 Intent intent = new(Intent.ActionOpenDocument);
127 intent.AddCategory(Intent.CategoryOpenable);
128 intent.SetType("*/*");
129 filePickTcs = new TaskCompletionSource<(System.IO.Stream, string)>();
130 StartActivityForResult(string.IsNullOrEmpty(chooserTitle) ? intent : Intent.CreateChooser(intent, chooserTitle), PickFileRequestCode);
131 return filePickTcs.Task;
132 }
133
134 protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) {
135 base.OnActivityResult(requestCode, resultCode, data);
136 if (requestCode == PickFileRequestCode) {
137 if (resultCode == Result.Ok
138 && data != null) {
139 try {
140 System.IO.Stream stream = GetStreamFromUri(data.Data, out string fileName);
141 filePickTcs?.TrySetResult((stream, fileName));
142 }
143 catch (Exception ex) {
144 filePickTcs?.TrySetException(ex);
145 }
146 }
147 else {
148 filePickTcs?.TrySetResult((null, null));
149 }
150 }
151 }
152
153 protected override void OnPause() {
154 base.OnPause();
155 Paused?.Invoke();
156 }
157
158 protected override void OnResume() {
159 base.OnResume();
160 Resumed?.Invoke();
161 }
162
163 protected override void OnNewIntent(Intent intent) {
164 base.OnNewIntent(intent);
165 NewIntent?.Invoke(intent);
166 }
167
168 protected override void OnRun() { }
169
170 protected override void OnDestroy() {
171 try {
172 base.OnDestroy();
173 Destroyed?.Invoke();
174 }
175 finally {
176 Thread.Sleep(250);
177 Environment.Exit(0);
178 }
179 }
180
181 public override bool DispatchTouchEvent(MotionEvent e) {
182 if (e == null) {
183 return true;
184 }
185 if ((e.Source & InputSourceType.Touchscreen) == InputSourceType.Touchscreen) {
186 Touch.HandleTouchEvent(e);
187 }
188 else if ((e.Source & InputSourceType.Mouse) == InputSourceType.Mouse
189 || (e.Source & InputSourceType.ClassPointer) == InputSourceType.ClassPointer
190 || (e.Source & InputSourceType.MouseRelative) == InputSourceType.MouseRelative) {
191 Mouse.HandleMotionEvent(e);
192 }
193 return true;
194 }
195
196 public override bool DispatchKeyEvent(KeyEvent e) {
197 if (e == null) {
198 return true;
199 }
200 /*Debug.WriteLine(
201 $"[DispatchKeyEvent]action:{e.Action} keyCode:{e.KeyCode} unicodeChar:{e.UnicodeChar} flags:{e.Flags} metaState:{e.MetaState} source:{e.Source} deviceId:{e.DeviceId}"
202 );*/
203 bool handled = false;
204 Delegate[] invocationList = OnDispatchKeyEvent?.GetInvocationList();
205 if (invocationList != null) {
206 foreach (Delegate invocation in invocationList) {
207 handled |= (bool)invocation.DynamicInvoke(e)!;
208 }
209 }
210 if (!handled) {
211 _ = e.Action switch {
212 KeyEventActions.Down => OnKeyDown(e.KeyCode, e),
213 KeyEventActions.Up => OnKeyUp(e.KeyCode, e),
214 _ => false
215 };
216 }
217 return true;
218 }
219
220 public override bool OnKeyDown(Keycode keyCode, KeyEvent e) {
221 switch (keyCode) {
222 case Keycode.VolumeUp:
223 AudioManager?.AdjustStreamVolume(Stream.Music, Adjust.Raise, VolumeNotificationFlags.ShowUi);
224 EnableImmersiveMode();
225 break;
226 case Keycode.VolumeDown:
227 AudioManager?.AdjustStreamVolume(Stream.Music, Adjust.Lower, VolumeNotificationFlags.ShowUi);
228 EnableImmersiveMode();
229 break;
230 }
231 if (e == null) {
232 return true;
233 }
234 if ((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad
235 || (e.Source & InputSourceType.Joystick) == InputSourceType.Joystick) {
236 GamePad.HandleKeyEvent(e);
237 }
238 else {
239 Keyboard.HandleKeyEvent(e);
240 }
241 return true;
242 }
243
244 AudioManager GetAudioManager() => Build.VERSION.SdkInt >= (BuildVersionCodes)21 ? GetSystemService("audio") as AudioManager : null;
245
246 public override bool OnKeyUp(Keycode keyCode, KeyEvent e) {
247 if (e == null) {
248 return true;
249 }
250 if ((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad
251 || (e.Source & InputSourceType.Joystick) == InputSourceType.Joystick) {
252 GamePad.HandleKeyEvent(e);
253 }
254 else {
255 Keyboard.HandleKeyEvent(e);
256 }
257 return true;
258 }
259
260 public override bool DispatchGenericMotionEvent(MotionEvent e) {
261 if (e == null) {
262 return true;
263 }
264 //Debug.WriteLine($"[OnGenericMotionEvent]source:{e.Source} action:{e.Action}");
265 if (((e.Source & InputSourceType.Gamepad) == InputSourceType.Gamepad || (e.Source & InputSourceType.Joystick) == InputSourceType.Joystick)
266 && e.Action == MotionEventActions.Move) {
267 GamePad.HandleMotionEvent(e);
268 }
269 if ((e.Source & InputSourceType.Mouse) == InputSourceType.Mouse
270 || (e.Source & InputSourceType.ClassPointer) == InputSourceType.ClassPointer
271 || (e.Source & InputSourceType.MouseRelative) == InputSourceType.MouseRelative) {
272 Mouse.HandleMotionEvent(e);
273 }
274 return true;
275 }
276
277 public void EnableImmersiveMode() {
278 if (Window != null) {
279 switch (Build.VERSION.SdkInt) {
280 case >= (BuildVersionCodes)30:
281 IWindowInsetsController insetsController = Window.InsetsController;
282 if (insetsController != null) {
283 insetsController.Hide(WindowInsets.Type.SystemBars());
284 insetsController.SystemBarsBehavior = (int)WindowInsetsControllerBehavior.ShowTransientBarsBySwipe;
285 }
286 break;
287#pragma warning disable CA1422
288 case > (BuildVersionCodes)19:
289 Window.DecorView.SystemUiFlags = SystemUiFlags.Fullscreen
290 | SystemUiFlags.HideNavigation
291 | SystemUiFlags.Immersive
292 | SystemUiFlags.ImmersiveSticky; break;
293#pragma warning restore CA1422
294 }
295 }
296 }
297
298 public void GetGlEsVersion(out int major, out int minor) {
299 try {
300 int reqGlEsVersion = ((ActivityManager)GetSystemService(ActivityService))?.DeviceConfigurationInfo?.ReqGlEsVersion ?? 0x20000;
301 major = reqGlEsVersion >> 16;
302 minor = reqGlEsVersion & 0xFFFF;
303 }
304 catch {
305 major = 2;
306 minor = 0;
307 }
308 }
309
310 public System.IO.Stream GetStreamFromUri(Uri uri, out string fileName) {
311 System.IO.Stream stream = null;
312 fileName = null;
313 try {
314 using (ICursor cursor = ContentResolver?.Query(uri, null, null, null, null)) {
315 if (cursor != null
316 && cursor.MoveToFirst()) {
317 int nameIndex = cursor.GetColumnIndex(IOpenableColumns.DisplayName);
318 if (nameIndex >= 0) {
319 fileName = cursor.GetString(nameIndex);
320 }
321 }
322 }
323 stream = ContentResolver?.OpenInputStream(uri);
324 }
325 catch {
326 // ignored
327 }
328 if (string.IsNullOrEmpty(fileName)) {
329 fileName = Path.GetFileName(uri.Path);
330 }
331 return stream;
332 }
333
334 public class ApplyWindowInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener {
335 public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets) {
336 IList<Rect> boundingRects = insets?.DisplayCutout?.BoundingRects;
337 if (boundingRects == null
338 || boundingRects.Count == 0) {
339 return WindowInsetsCompat.Consumed;
340 }
341 bool hasWideNotch = false;
342 if (boundingRects.Count >= 2) {
343 hasWideNotch = true;
344 }
345 else {
346 Rect rect = boundingRects[0];
347 if (Math.Max(rect.Width(), rect.Height()) > 200) {
348 hasWideNotch = true;
349 }
350 }
351 Insets cutoutInsets = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout());
352 if (cutoutInsets != null) {
353 Engine.Window.DisplayCutoutInsetsChangedHandler(
354 new Vector4(cutoutInsets.Left, cutoutInsets.Top, cutoutInsets.Right, cutoutInsets.Bottom),
355 hasWideNotch
356 );
357 }
358 return WindowInsetsCompat.Consumed;
359 }
360 }
361 }
362}
363#endif
Android.Net.Uri Uri
System.Environment Environment