├── assets ├── icon.png └── icon.afdesign ├── TestApp ├── Resources │ ├── mipmap-hdpi │ │ └── Icon.png │ ├── mipmap-mdpi │ │ └── Icon.png │ ├── mipmap-xhdpi │ │ └── Icon.png │ ├── mipmap-xxhdpi │ │ └── Icon.png │ ├── mipmap-xxxhdpi │ │ └── Icon.png │ ├── values │ │ └── Strings.xml │ ├── layout │ │ └── Main.axml │ └── AboutResources.txt ├── Properties │ ├── AndroidManifest.xml │ └── AssemblyInfo.cs ├── Assets │ └── AboutAssets.txt ├── MainActivity.cs └── TestApp.csproj ├── ActivityTask ├── Resources │ └── values │ │ └── Strings.xml ├── ActivityIdProvider.cs ├── ActivityTask.cs ├── Properties │ └── AssemblyInfo.cs ├── ActivityScopeRetriever.cs ├── readme.txt ├── ActivityScopeMethodBuilder.cs ├── ActivityScope.cs └── ActivityTask.csproj ├── ActivityTask.sln ├── README.md ├── .gitignore └── LICENSE /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/icon.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/assets/icon.afdesign -------------------------------------------------------------------------------- /TestApp/Resources/mipmap-hdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/TestApp/Resources/mipmap-hdpi/Icon.png -------------------------------------------------------------------------------- /TestApp/Resources/mipmap-mdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/TestApp/Resources/mipmap-mdpi/Icon.png -------------------------------------------------------------------------------- /TestApp/Resources/mipmap-xhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/TestApp/Resources/mipmap-xhdpi/Icon.png -------------------------------------------------------------------------------- /TestApp/Resources/mipmap-xxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/TestApp/Resources/mipmap-xxhdpi/Icon.png -------------------------------------------------------------------------------- /TestApp/Resources/mipmap-xxxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garuma/LibActivityTask/HEAD/TestApp/Resources/mipmap-xxxhdpi/Icon.png -------------------------------------------------------------------------------- /ActivityTask/Resources/values/Strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ActivityTask 4 | 5 | -------------------------------------------------------------------------------- /ActivityTask/ActivityIdProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Neteril.Android 5 | { 6 | static class ActivityIdProvider 7 | { 8 | static long IdentityTagPool; 9 | 10 | public static long GetNextId () => Interlocked.Increment (ref IdentityTagPool); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TestApp/Resources/values/Strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | This label will show first Step 1 and then Step 2, try to rotate your screen or hit the home button in between. 4 | ActivityTaskTest 5 | 6 | -------------------------------------------------------------------------------- /TestApp/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /TestApp/Resources/layout/Main.axml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | -------------------------------------------------------------------------------- /TestApp/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with your package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /ActivityTask/ActivityTask.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.ComponentModel; 3 | using System.Threading.Tasks; 4 | 5 | namespace Neteril.Android 6 | { 7 | [AsyncMethodBuilder (typeof (ActivityScopeMethodBuilder))] 8 | public class ActivityTask 9 | { 10 | TaskCompletionSource completion; 11 | 12 | internal TaskCompletionSource Completion => completion ?? (completion = new TaskCompletionSource ()); 13 | internal Task CompletionTask => Completion.Task; 14 | 15 | public TaskAwaiter GetAwaiter () 16 | { 17 | return CompletionTask.GetAwaiter (); 18 | } 19 | 20 | public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext) 21 | { 22 | return CompletionTask.ConfigureAwait (continueOnCapturedContext); 23 | } 24 | 25 | [EditorBrowsable (EditorBrowsableState.Never)] 26 | public static ActivityScopeMethodBuilder CreateAsyncMethodBuilder () => ActivityScopeMethodBuilder.Create (); 27 | } 28 | 29 | struct VoidTaskResult { }; 30 | } 31 | -------------------------------------------------------------------------------- /TestApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Android.App; 4 | 5 | // Information about this assembly is defined by the following attributes. 6 | // Change them to the values specific to your project. 7 | 8 | [assembly: AssemblyTitle("ActivityTaskTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("")] 13 | [assembly: AssemblyCopyright("${AuthorCopyright}")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 20 | 21 | [assembly: AssemblyVersion("1.0.0")] 22 | 23 | // The following attributes are used to specify the signing key for the assembly, 24 | // if desired. See the Mono documentation for more information about signing. 25 | 26 | //[assembly: AssemblyDelaySign(false)] 27 | //[assembly: AssemblyKeyFile("")] 28 | -------------------------------------------------------------------------------- /ActivityTask/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("ActivityTask")] 8 | [assembly: AssemblyDescription("Android-friendly async methods")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("(c) Jérémie Laval")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.0")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | -------------------------------------------------------------------------------- /TestApp/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Widget; 3 | using Android.OS; 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | using Neteril.Android; 9 | 10 | namespace ActivityTaskTest 11 | { 12 | [Activity(Label = "ActivityTaskTest", MainLauncher = true, Icon = "@mipmap/icon")] 13 | public class MainActivity : Activity 14 | { 15 | static bool launched = false; 16 | 17 | protected override async void OnCreate(Bundle savedInstanceState) 18 | { 19 | base.OnCreate(savedInstanceState); 20 | 21 | // Set our view from the "main" layout resource 22 | SetContentView(Resource.Layout.Main); 23 | 24 | if (!launched) 25 | { 26 | launched = true; 27 | using (var scope = ActivityScope.Of(this)) 28 | await DoAsyncStuff(scope); 29 | } 30 | } 31 | 32 | TextView MyLabel(Activity activity) => activity.FindViewById(Resource.Id.myLabel); 33 | 34 | async ActivityTask DoAsyncStuff(ActivityScope scope) 35 | { 36 | await Task.Delay(3000); // Medium network call 37 | MyLabel(scope).Text = "Step 1"; 38 | await Task.Delay(5000); // Big network call 39 | MyLabel(scope).Text = "Step 2"; 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /ActivityTask.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{42B1AC60-D6E0-4FDC-B4EA-EB72E2970111}" 5 | EndProject 6 | Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "ActivityTask", "ActivityTask\ActivityTask.csproj", "{9F488BC8-17DE-46D0-85E3-582C3ACB90B8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {42B1AC60-D6E0-4FDC-B4EA-EB72E2970111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {42B1AC60-D6E0-4FDC-B4EA-EB72E2970111}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {42B1AC60-D6E0-4FDC-B4EA-EB72E2970111}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {42B1AC60-D6E0-4FDC-B4EA-EB72E2970111}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {9F488BC8-17DE-46D0-85E3-582C3ACB90B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {9F488BC8-17DE-46D0-85E3-582C3ACB90B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {9F488BC8-17DE-46D0-85E3-582C3ACB90B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {9F488BC8-17DE-46D0-85E3-582C3ACB90B8}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | EndGlobal 24 | -------------------------------------------------------------------------------- /ActivityTask/ActivityScopeRetriever.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace Neteril.Android 8 | { 9 | static class ActivityScopeRetriever where TStateMachine : IAsyncStateMachine 10 | { 11 | delegate ActivityScope GetActivityScopeDelegate (TStateMachine stateMachine); 12 | static GetActivityScopeDelegate getter; 13 | 14 | internal static ActivityScope GetScopeFromStateMachine (ref TStateMachine stateMachine) 15 | { 16 | if (getter != null) 17 | return getter (stateMachine); 18 | var field = typeof (TStateMachine).GetFields ()?.FirstOrDefault (f => f.FieldType == typeof (ActivityScope)); 19 | if (field == null) 20 | return null; 21 | var dynamicGetter = new DynamicMethod ("__MagicSpecialGetScope" + typeof (TStateMachine).Name, 22 | typeof (ActivityScope), 23 | new[] { typeof (TStateMachine) }, 24 | restrictedSkipVisibility: true); 25 | var generator = dynamicGetter.GetILGenerator (); 26 | generator.Emit (OpCodes.Ldarg_0); 27 | generator.Emit (OpCodes.Ldfld, field); 28 | generator.Emit (OpCodes.Ret); 29 | getter = (GetActivityScopeDelegate)dynamicGetter.CreateDelegate (typeof (GetActivityScopeDelegate)); 30 | return getter (stateMachine); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /TestApp/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.axml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable/ 12 | icon.png 13 | 14 | layout/ 15 | main.axml 16 | 17 | values/ 18 | strings.xml 19 | 20 | In order to get the build system to recognize Android resources, set the build action to 21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 22 | instead operate on resource IDs. When you compile an Android application that uses resources, 23 | the build system will package the resources for distribution and generate a class called "R" 24 | (this is an Android convention) that contains the tokens for each one of the resources 25 | included. For example, for the above Resources layout, this is what the R class would expose: 26 | 27 | public class R { 28 | public class drawable { 29 | public const int icon = 0x123; 30 | } 31 | 32 | public class layout { 33 | public const int main = 0x456; 34 | } 35 | 36 | public class strings { 37 | public const int first_string = 0xabc; 38 | public const int second_string = 0xbcd; 39 | } 40 | } 41 | 42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first 44 | string in the dictionary file values/strings.xml. 45 | -------------------------------------------------------------------------------- /ActivityTask/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | Source is at https://github.com/garuma/LibActivityTask 3 | 4 | This small NuGet gives you a way to create asynchronous methods in Android 5 | that handles two things for you: 6 | 7 | - Activity instance re-creation 8 | - Activity lifecycle events 9 | 10 | The first one is common with configuration changes like the user rotating 11 | the device screen. In this case by default Android will re-create your 12 | activity which means that your async operation previously captured instance 13 | is now likely defunct. This library solves this problem by introducing 14 | the `ActivityScope` class, once initialized with an Activity instance it 15 | becomes a replacement for it that will keep track of the latest incarnation 16 | of your activity. 17 | 18 | The second one manifests itself when your activity is put on hold, this can 19 | happen when the user press the home button or if you are launching another 20 | activity. In this case, the library introduce the `ActivityTask` type that 21 | should be used as the return type of your async method (acting like a normal 22 | Task) would. When you do so, the async method builder associated with it will 23 | handle lifecycle events for you (using the previously mentioned `ActivityScope`) 24 | and schedule your await continuations only when the activity is alive. For this 25 | to work, the async method needs to be passed the instance of the `ActivityScope` 26 | from the caller. 27 | 28 | Below is a small example of how you can use the library for those two things: 29 | 30 | ``` 31 | using Neteril.Android; 32 | 33 | static bool alreadyExecuted = false; 34 | 35 | protected override async void OnCreate(Bundle savedInstanceState) 36 | { 37 | base.OnCreate(savedInstanceState); 38 | SetContentView(Resource.Layout.Main); 39 | 40 | if (!alreadyExecuted) 41 | { 42 | alreadyExecuted = true; 43 | using (var scope = ActivityScope.Of(this)) 44 | await DoAsyncStuff(scope); 45 | } 46 | } 47 | 48 | TextView MyLabel(Activity activity) => activity.FindViewById