2016-03-22 23:02:25 +03:00
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 ;
2016-08-30 20:46:14 +03:00
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific ;
2016-03-22 23:02:25 +03:00
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" ) ;
2016-05-25 16:50:21 +03:00
( application as IApplicationController ) ? . SetAppIndexingProvider ( new AndroidAppIndexProvider ( this ) ) ;
2016-03-22 23:02:25 +03:00
_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 ) ;
2016-08-30 20:46:14 +03:00
SetSoftInputMode ( ) ;
2016-03-22 23:02:25 +03:00
_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 ) ;
2016-08-30 20:46:14 +03:00
if ( args . PropertyName = = PlatformConfiguration . AndroidSpecific . Application . WindowSoftInputModeAdjustProperty . PropertyName )
SetSoftInputMode ( ) ;
2016-03-22 23:02:25 +03:00
}
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 ( ) ) ;
}
2016-04-13 19:28:48 +03:00
async void OnStateChanged ( )
2016-03-22 23:02:25 +03:00
{
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 )
2016-04-13 19:28:48 +03:00
await _application . SendSleepAsync ( ) ;
2016-03-22 23:02:25 +03:00
}
void SetMainPage ( )
{
InternalSetPage ( _application . MainPage ) ;
}
2016-08-30 20:46:14 +03:00
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 ) ;
}
2016-03-22 23:02:25 +03:00
void UpdateProgressBarVisibility ( bool isBusy )
{
if ( ! Forms . SupportsProgress )
return ;
2016-09-14 00:52:47 +03:00
#pragma warning disable 612 , 618
SetProgressBarIndeterminate ( true ) ;
2016-03-22 23:02:25 +03:00
SetProgressBarIndeterminateVisibility ( isBusy ) ;
2016-09-14 00:52:47 +03:00
#pragma warning restore 612 , 618
2016-03-22 23:02:25 +03:00
}
internal class DefaultApplication : Application
{
}
}
}