maui-linux/Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs

346 строки
10 KiB
C#

using System;
using System.ComponentModel;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.OS;
using Android.Views;
using Android.Widget;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
namespace Xamarin.Forms.Platform.Android
{
public class FormsApplicationActivity : Activity, IDeviceInfoProvider, IStartActivityForResult
{
public delegate bool BackButtonPressedEventHandler(object sender, EventArgs e);
readonly ConcurrentDictionary<int, Action<Result, Intent>> _activityResultCallbacks = new ConcurrentDictionary<int, Action<Result, Intent>>();
Application _application;
Platform _canvas;
AndroidApplicationLifecycleState _currentState;
LinearLayout _layout;
int _nextActivityResultCallbackKey;
AndroidApplicationLifecycleState _previousState;
protected FormsApplicationActivity()
{
_previousState = AndroidApplicationLifecycleState.Uninitialized;
_currentState = AndroidApplicationLifecycleState.Uninitialized;
}
public event EventHandler ConfigurationChanged;
int IStartActivityForResult.RegisterActivityResultCallback(Action<Result, Intent> callback)
{
int requestCode = _nextActivityResultCallbackKey;
while (!_activityResultCallbacks.TryAdd(requestCode, callback))
{
_nextActivityResultCallbackKey += 1;
requestCode = _nextActivityResultCallbackKey;
}
_nextActivityResultCallbackKey += 1;
return requestCode;
}
void IStartActivityForResult.UnregisterActivityResultCallback(int requestCode)
{
Action<Result, Intent> callback;
_activityResultCallbacks.TryRemove(requestCode, out callback);
}
public static event BackButtonPressedEventHandler BackPressed;
public override void OnBackPressed()
{
if (BackPressed != null && BackPressed(this, EventArgs.Empty))
return;
base.OnBackPressed();
}
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
EventHandler handler = ConfigurationChanged;
if (handler != null)
handler(this, new EventArgs());
}
// FIXME: THIS SHOULD NOT BE MANDATORY
// or
// This should be specified in an interface and formalized, perhaps even provide a stock AndroidActivity users
// can derive from to avoid having to do any work.
public override bool OnOptionsItemSelected(IMenuItem item)
{
if (item.ItemId == global::Android.Resource.Id.Home)
_canvas.SendHomeClicked();
return base.OnOptionsItemSelected(item);
}
public override bool OnPrepareOptionsMenu(IMenu menu)
{
_canvas.PrepareMenu(menu);
return base.OnPrepareOptionsMenu(menu);
}
[Obsolete("Please use protected LoadApplication (Application app) instead")]
public void SetPage(Page page)
{
var application = new DefaultApplication { MainPage = page };
LoadApplication(application);
}
protected void LoadApplication(Application application)
{
if (application == null)
throw new ArgumentNullException("application");
(application as IApplicationController)?.SetAppIndexingProvider(new AndroidAppIndexProvider(this));
_application = application;
Xamarin.Forms.Application.Current = application;
application.PropertyChanged += AppOnPropertyChanged;
SetMainPage();
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
Action<Result, Intent> callback;
if (_activityResultCallbacks.TryGetValue(requestCode, out callback))
callback(resultCode, data);
}
protected override void OnCreate(Bundle savedInstanceState)
{
Window.RequestFeature(WindowFeatures.IndeterminateProgress);
base.OnCreate(savedInstanceState);
SetSoftInputMode();
_layout = new LinearLayout(BaseContext);
SetContentView(_layout);
Xamarin.Forms.Application.ClearCurrent();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnCreate;
OnStateChanged();
}
protected override void OnDestroy()
{
// may never be called
base.OnDestroy();
MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName);
MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName);
MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName);
if (_canvas != null)
((IDisposable)_canvas).Dispose();
}
protected override void OnPause()
{
_layout.HideKeyboard(true);
// Stop animations or other ongoing actions that could consume CPU
// Commit unsaved changes, build only if users expect such changes to be permanently saved when thy leave such as a draft email
// Release system resources, such as broadcast receivers, handles to sensors (like GPS), or any resources that may affect battery life when your activity is paused.
// Avoid writing to permanent storage and CPU intensive tasks
base.OnPause();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnPause;
OnStateChanged();
}
protected override void OnRestart()
{
base.OnRestart();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnRestart;
OnStateChanged();
}
protected override void OnResume()
{
// counterpart to OnPause
base.OnResume();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnResume;
OnStateChanged();
}
protected override void OnStart()
{
base.OnStart();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnStart;
OnStateChanged();
}
// Scenarios that stop and restart you app
// -- Switches from your app to another app, activity restarts when clicking on the app again.
// -- Action in your app that starts a new Activity, the current activity is stopped and the second is created, pressing back restarts the activity
// -- The user recieves a phone call while using your app on his or her phone
protected override void OnStop()
{
// writing to storage happens here!
// full UI obstruction
// users focus in another activity
// perform heavy load shutdown operations
// clean up resources
// clean up everything that may leak memory
base.OnStop();
_previousState = _currentState;
_currentState = AndroidApplicationLifecycleState.OnStop;
OnStateChanged();
}
void AppOnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "MainPage")
InternalSetPage(_application.MainPage);
if (args.PropertyName == PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName)
SetSoftInputMode();
}
void InternalSetPage(Page page)
{
if (!Forms.IsInitialized)
throw new InvalidOperationException("Call Forms.Init (Activity, Bundle) before this");
if (_canvas != null)
{
_canvas.SetPage(page);
return;
}
var busyCount = 0;
MessagingCenter.Subscribe(this, Page.BusySetSignalName, (Page sender, bool enabled) =>
{
busyCount = Math.Max(0, enabled ? busyCount + 1 : busyCount - 1);
UpdateProgressBarVisibility(busyCount > 0);
});
UpdateProgressBarVisibility(busyCount > 0);
MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments arguments) =>
{
AlertDialog alert = new AlertDialog.Builder(this).Create();
alert.SetTitle(arguments.Title);
alert.SetMessage(arguments.Message);
if (arguments.Accept != null)
alert.SetButton((int)DialogButtonType.Positive, arguments.Accept, (o, args) => arguments.SetResult(true));
alert.SetButton((int)DialogButtonType.Negative, arguments.Cancel, (o, args) => arguments.SetResult(false));
alert.CancelEvent += (o, args) => { arguments.SetResult(false); };
alert.Show();
});
MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments arguments) =>
{
var builder = new AlertDialog.Builder(this);
builder.SetTitle(arguments.Title);
string[] items = arguments.Buttons.ToArray();
builder.SetItems(items, (sender2, args) => { arguments.Result.TrySetResult(items[args.Which]); });
if (arguments.Cancel != null)
builder.SetPositiveButton(arguments.Cancel, delegate { arguments.Result.TrySetResult(arguments.Cancel); });
if (arguments.Destruction != null)
builder.SetNegativeButton(arguments.Destruction, delegate { arguments.Result.TrySetResult(arguments.Destruction); });
AlertDialog dialog = builder.Create();
builder.Dispose();
//to match current functionality of renderer we set cancelable on outside
//and return null
dialog.SetCanceledOnTouchOutside(true);
dialog.CancelEvent += (sender3, e) => { arguments.SetResult(null); };
dialog.Show();
});
_canvas = new Platform(this);
if (_application != null)
_application.Platform = _canvas;
_canvas.SetPage(page);
_layout.AddView(_canvas.GetViewGroup());
}
async void OnStateChanged()
{
if (_application == null)
return;
if (_previousState == AndroidApplicationLifecycleState.OnCreate && _currentState == AndroidApplicationLifecycleState.OnStart)
_application.SendStart();
else if (_previousState == AndroidApplicationLifecycleState.OnStop && _currentState == AndroidApplicationLifecycleState.OnRestart)
_application.SendResume();
else if (_previousState == AndroidApplicationLifecycleState.OnPause && _currentState == AndroidApplicationLifecycleState.OnStop)
await _application.SendSleepAsync();
}
void SetMainPage()
{
InternalSetPage(_application.MainPage);
}
void SetSoftInputMode()
{
SoftInput adjust = SoftInput.AdjustPan;
if (Xamarin.Forms.Application.Current != null)
{
var elementValue = Xamarin.Forms.Application.Current.OnThisPlatform().GetWindowSoftInputModeAdjust();
switch (elementValue)
{
default:
case WindowSoftInputModeAdjust.Pan:
adjust = SoftInput.AdjustPan;
break;
case WindowSoftInputModeAdjust.Resize:
adjust = SoftInput.AdjustResize;
break;
}
}
Window.SetSoftInputMode(adjust);
}
void UpdateProgressBarVisibility(bool isBusy)
{
if (!Forms.SupportsProgress)
return;
#pragma warning disable 612, 618
SetProgressBarIndeterminate(true);
SetProgressBarIndeterminateVisibility(isBusy);
#pragma warning restore 612, 618
}
internal class DefaultApplication : Application
{
}
}
}