├── 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