diff options
Diffstat (limited to 'Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs')
-rw-r--r-- | Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs b/Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs new file mode 100644 index 00000000..c05c79f6 --- /dev/null +++ b/Xamarin.Forms.Platform.Android/FormsApplicationActivity.cs @@ -0,0 +1,319 @@ +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; + +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 = 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); + + _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); + } + + 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); + + if (!Forms.SupportsProgress) + return; + + SetProgressBarIndeterminate(true); + 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()); + } + + 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) + _application.SendSleepAsync().Wait(); + } + + void SetMainPage() + { + InternalSetPage(_application.MainPage); + } + + void UpdateProgressBarVisibility(bool isBusy) + { + if (!Forms.SupportsProgress) + return; + SetProgressBarIndeterminateVisibility(isBusy); + } + + internal class DefaultApplication : Application + { + } + } +}
\ No newline at end of file |