From 39a015453ca4889747733adb5a593bfa8bcde4fe Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 8 Sep 2022 17:48:15 -0400 Subject: [PATCH] [Java.Interop.Tools.JavaSource] Fix remaining parsing errors All 431 instances of `JavadocImport-Error` that would occur when generating API docs for Mono.Android.dll have been fixed. Problemetic scenarios that have been fixed include: * `@hide` and `@inheritDoc` tags with trailing content * `` or `` elements with no closing tag * `
` elements with an attribute and/or no closing elements
 * `` or `` elements inside of an unclosed `

` element * `` elements with mixed casing or spacing * `{@link foo` tags without a closing bracket * Any extra occurances of `@`, `{`, or `}` --- ...avadocToXmldocGrammar.BlockTagsBnfTerms.cs | 8 +- ...urceJavadocToXmldocGrammar.HtmlBnfTerms.cs | 34 +- ...vadocToXmldocGrammar.InlineTagsBnfTerms.cs | 26 +- ...cToXmldocGrammar.BlockTagsBnfTermsTests.cs | 8 + ...avadocToXmldocGrammar.HtmlBnfTermsTests.cs | 4 + .../SourceJavadocToXmldocParserTests.cs | 607 +++++++++++++++++- .../JavadocInfo.cs | 2 +- 7 files changed, 677 insertions(+), 12 deletions(-) diff --git a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.BlockTagsBnfTerms.cs b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.BlockTagsBnfTerms.cs index 54a0d1be0..283113df6 100644 --- a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.BlockTagsBnfTerms.cs +++ b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.BlockTagsBnfTerms.cs @@ -102,12 +102,16 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) }; // Ignore @hide tags - HideDeclaration.Rule = "@hide"; + HideDeclaration.Rule = "@hide" + | "@hide" + BlockValues + ; HideDeclaration.AstConfig.NodeCreator = (context, parseNode) => { FinishParse (context, parseNode); }; - InheritDocDeclaration.Rule = "@inheritDoc"; + InheritDocDeclaration.Rule = "@inheritDoc" + | "@inheritDoc" + BlockValues + ; InheritDocDeclaration.AstConfig.NodeCreator = (context, parseNode) => { if (!grammar.ShouldImport (ImportJavadoc.InheritDocTag)) { return; diff --git a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs index 84735783d..bf6bce2ac 100644 --- a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs +++ b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs @@ -23,6 +23,7 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) AllHtmlTerms.Rule = TopLevelInlineDeclaration | PBlockDeclaration | PreBlockDeclaration + | IgnorableElementDeclaration ; var inlineDeclaration = new NonTerminal ("", ConcatChildNodes) { @@ -59,11 +60,11 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) parseNode.AstNode = ""; }; - var fontstyle_tt = CreateHtmlToCrefElement (grammar, "tt", "c", InlineDeclarations); - var fontstyle_i = CreateHtmlToCrefElement (grammar, "i", "i", InlineDeclarations); + var fontstyle_tt = CreateHtmlToCrefElement (grammar, "tt", "c", InlineDeclarations, optionalEnd: true); + var fontstyle_i = CreateHtmlToCrefElement (grammar, "i", "i", InlineDeclarations, optionalEnd: true); var preText = new PreBlockDeclarationBodyTerminal (); - PreBlockDeclaration.Rule = CreateStartElement ("pre", grammar) + preText + CreateEndElement ("pre", grammar); + PreBlockDeclaration.Rule = CreateStartElementIgnoreAttribute ("pre") + preText + CreateEndElement ("pre", grammar, optional: true); PreBlockDeclaration.AstConfig.NodeCreator = (context, parseNode) => { if (!grammar.ShouldImport (ImportJavadoc.Remarks)) { parseNode.AstNode = ""; @@ -125,6 +126,16 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) parseNode.AstNode = astNodeElement; } }; + + // Start to trim out unusable HTML elements/tags, but not any inner values + IgnorableElementDeclaration.Rule = + CreateStartElementIgnoreAttribute ("a", "name") + InlineDeclarations + CreateEndElement ("a", grammar, optional: true) + | CreateStartElementIgnoreAttribute ("a", "id") + InlineDeclarations + CreateEndElement ("a", grammar, optional: true) + ; + IgnorableElementDeclaration.AstConfig.NodeCreator = (context, parseNode) => { + var aElementValue = new XText (parseNode.ChildNodes [1].AstNode.ToString ()); + parseNode.AstNode = aElementValue; + }; } static IEnumerable GetParagraphs (ParseTreeNodeList children) @@ -193,8 +204,9 @@ static IEnumerable GetParagraphs (ParseTreeNodeList children) public readonly NonTerminal PBlockDeclaration = new NonTerminal (nameof (PBlockDeclaration), ConcatChildNodes); public readonly NonTerminal PreBlockDeclaration = new NonTerminal (nameof (PreBlockDeclaration), ConcatChildNodes); public readonly NonTerminal InlineHyperLinkDeclaration = new NonTerminal (nameof (InlineHyperLinkDeclaration), ConcatChildNodes); + public readonly NonTerminal IgnorableElementDeclaration = new NonTerminal (nameof (IgnorableElementDeclaration), ConcatChildNodes); - public readonly Terminal InlineHyperLinkOpenTerm = new RegexBasedTerminal ("]*>") { + AstConfig = new AstNodeConfig { + NodeCreator = (context, parseNode) => parseNode.AstNode = "", + }, + }; + } + + static RegexBasedTerminal CreateStartElementIgnoreAttribute (string startElement) + { + return CreateStartElementIgnoreAttribute (startElement, ""); + } + static NonTerminal CreateEndElement (string endElement, Grammar grammar, bool optional = false) { var end = new NonTerminal (endElement, nodeCreator: (context, parseNode) => parseNode.AstNode = "") { diff --git a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.InlineTagsBnfTerms.cs b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.InlineTagsBnfTerms.cs index 3685ea7b6..e7c74d85c 100644 --- a/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.InlineTagsBnfTerms.cs +++ b/src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.InlineTagsBnfTerms.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.IO; using System.Linq; using System.Xml.Linq; @@ -28,6 +29,7 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) | LiteralDeclaration | SeeDeclaration | ValueDeclaration + | IgnorableDeclaration ; CodeDeclaration.Rule = grammar.ToTerm ("{@code") + InlineValue + "}"; @@ -54,12 +56,20 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) parseNode.AstNode = new XText ("To be added"); }; - LinkDeclaration.Rule = grammar.ToTerm ("{@link") + InlineValue + "}"; + LinkDeclaration.Rule = grammar.ToTerm ("{@link") + InlineValue + new NonTerminal ("Optional }", nodeCreator: (context, parseNode) => parseNode.AstNode = "") { + Rule = grammar.ToTerm ("}") | grammar.Empty, + }; LinkDeclaration.AstConfig.NodeCreator = (context, parseNode) => { // TODO: *everything*; {@link target label}, but target can contain spaces! // Also need to convert to appropriate CREF value, use code text for now. + // Also some {@link tags are missing a closing bracket, use plain text rather than throwing for now. var target = parseNode.ChildNodes [1].AstNode; - parseNode.AstNode = new XElement ("c", target); + var optionalBracketMatch = parseNode.ChildNodes [2]; + if (optionalBracketMatch.ChildNodes.Count > 0 && optionalBracketMatch.ChildNodes [0].Term.Name == "}") { + parseNode.AstNode = new XElement ("c", target); + } else { + parseNode.AstNode = new XText (target.ToString ()); + } }; LinkplainDeclaration.Rule = grammar.ToTerm ("{@linkplain") + InlineValue + "}"; @@ -97,6 +107,15 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) parseNode.AstNode = new XText ("To be added"); } }; + + // Inline content may contain reserved characters with no tags or special parsing rules, do not throw when encountering them + IgnorableDeclaration.Rule = grammar.ToTerm ("@ ") + | grammar.ToTerm ("{") + | grammar.ToTerm ("}") + ; + IgnorableDeclaration.AstConfig.NodeCreator = (context, parseNode) => { + parseNode.AstNode = new XText (parseNode.ChildNodes [0].Term.Name.Trim ()); + }; } public readonly NonTerminal AllInlineTerms = new NonTerminal (nameof (AllInlineTerms), ConcatChildNodes); @@ -129,6 +148,9 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar) // https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#value public readonly NonTerminal ValueDeclaration = new NonTerminal (nameof (ValueDeclaration)); + + public readonly NonTerminal IgnorableDeclaration = new NonTerminal (nameof (IgnorableDeclaration)); + } } } diff --git a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.BlockTagsBnfTermsTests.cs b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.BlockTagsBnfTermsTests.cs index 323fd8b34..ae4f3af4b 100644 --- a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.BlockTagsBnfTermsTests.cs +++ b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.BlockTagsBnfTermsTests.cs @@ -65,6 +65,11 @@ public void InheritDocDeclaration () Assert.IsFalse (r.HasErrors (), "@inheritDoc: " + DumpMessages (r, p)); // TODO: Enable after adding support for @inheritDoc Assert.IsNull (r.Root.AstNode, "@inheritDoc should be ignored, but node was not null."); + + r = p.Parse ("@inheritDoc With extra"); + Assert.IsFalse (r.HasErrors (), "@inheritDoc with trailing content " + DumpMessages (r, p)); + // TODO: Enable after adding support for @inheritDoc + Assert.IsNull (r.Root.AstNode, "@inheritDoc with trailing content should be ignored, but node was not null."); } [Test] @@ -75,6 +80,9 @@ public void HideDeclaration () var r = p.Parse ("@hide"); Assert.IsFalse (r.HasErrors (), "@hide: " + DumpMessages (r, p)); Assert.IsNull (r.Root.AstNode, "@hide should be ignored, but node was not null."); + r = p.Parse ("@hide Method is broken"); + Assert.IsFalse (r.HasErrors (), "@hide with trailing content: " + DumpMessages (r, p)); + Assert.IsNull (r.Root.AstNode, "@hide with trailing content should be ignored, but node was not null."); } [Test] diff --git a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs index 643782f69..43dba191f 100644 --- a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs +++ b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs @@ -46,6 +46,10 @@ public void PreBlockDeclaration () Assert.AreEqual ("this @contains <arbitrary/> text.", r.Root.AstNode.ToString ()); + r = p.Parse ("

ColorSpace cs = ColorSpace.get(ColorSpace.Named.DCI_P3);");
+			Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
+			Assert.AreEqual ($"ColorSpace cs = ColorSpace.get(ColorSpace.Named.DCI_P3);",
+					r.Root.AstNode.ToString ());
 		}
 
 		[Test]
diff --git a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs
index 14255216e..650cd4fc1 100644
--- a/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs
+++ b/tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs
@@ -184,21 +184,27 @@ more description here.
 			},
 			new ParseResult {
 				Javadoc = @"See accept(2).  Insert
-more description here.
+more description {e.g. something} here.  Include @ character.
 How about another link accept(2)
 @param manifest The value of the {@code
+HREF = ""{@docRoot}guide/topics/manifest/manifest-element.html#vcode"">{@code
 android:versionCode} manifest attribute.
 @param empty
+@param options Additional options.
+See {@link foo()}
+bar()} for more details.
 @return the return value
 ",
 				FullXml = $@"
   The value of the android:versionCode manifest attribute.
   empty
+  Additional options.
+See foo()
+bar()}} for more details.
   See accept(2).
   
     See accept(2).  Insert
-more description here.
+more description {{e.g. something}} here.  Include @ character.
 How about another link accept(2)
   
   the return value
@@ -206,8 +212,23 @@ more description here.
 				IntelliSenseXml = $@"
   The value of the android:versionCode manifest attribute.
   empty
+  Additional options.
+See foo()
+bar()}} for more details.
   See accept(2).
   the return value
+",
+			},
+			new ParseResult {
+				Javadoc = "Summary with broken {@link AccessibilityService#takeScreenshot(int, Executor, Consumer) link.",
+				FullXml = @"
+  Summary with broken AccessibilityService#takeScreenshot(int, Executor, Consumer) link.
+  
+    Summary with broken AccessibilityService#takeScreenshot(int, Executor, Consumer) link.
+  
+",
+				IntelliSenseXml = @"
+  Summary with broken AccessibilityService#takeScreenshot(int, Executor, Consumer) link.
 ",
 			},
 		};
@@ -217,5 +238,585 @@ public class ParseResult {
 			public  string  FullXml;
 			public  string  IntelliSenseXml;
 		}
+
+		[Test]
+		public void ParseActivityDoc ()
+		{
+			ParseTree parseTree;
+			var p = new SourceJavadocToXmldocParser (new XmldocSettings {
+				Style = XmldocStyle.Full,
+				DocRootValue = DocRootPrefixActual,
+			});
+			p.TryParse (activityJavaDoc, null, out parseTree);
+			Assert.IsFalse (parseTree.HasErrors (), DumpMessages (parseTree, p));
+		}
+
+		string activityJavaDoc = @"An activity is a single, focused thing that the user can do.  Almost all
+  activities interact with the user, so the Activity class takes care of
+  creating a window for you in which you can place your UI with
+  {@link #setContentView}.  While activities are often presented to the user
+  as full-screen windows, they can also be used in other ways: as floating
+  windows (via a theme with {@link android.R.attr#windowIsFloating} set),
+  
+  Multi-Window mode or embedded into other windows.
+  
+  There are two methods almost all subclasses of Activity will implement:
+  
+  
    +
  • {@link #onCreate} is where you initialize your activity. Most + importantly, here you will usually call {@link #setContentView(int)} + with a layout resource defining your UI, and using {@link #findViewById} + to retrieve the widgets in that UI that you need to interact with + programmatically. + +
  • {@link #onPause} is where you deal with the user pausing active + interaction with the activity. Any changes made by the user should at + this point be committed (usually to the + {@link android.content.ContentProvider} holding the data). In this + state the activity is still visible on screen. +
+ +

To be of use with {@link android.content.Context#startActivity Context.startActivity()}, all + activity classes must have a corresponding + {@link android.R.styleable#AndroidManifestActivity <activity>} + declaration in their package's AndroidManifest.xml.

+ +

Topics covered here: +

    +
  1. Fragments +
  2. Activity Lifecycle +
  3. Configuration Changes +
  4. Starting Activities and Getting Results +
  5. Saving Persistent State +
  6. Permissions +
  7. Process Lifecycle +
+ +
+

Developer Guides

+

The Activity class is an important part of an application's overall lifecycle, + and the way activities are launched and put together is a fundamental + part of the platform's application model. For a detailed perspective on the structure of an + Android application and how activities behave, please read the + Application Fundamentals and + Tasks and Back Stack + developer guides.

+ +

You can also find a detailed discussion about how to create activities in the + Activities + developer guide.

+
+ + +

Fragments

+ +

The {@link androidx.fragment.app.FragmentActivity} subclass + can make use of the {@link androidx.fragment.app.Fragment} class to better + modularize their code, build more sophisticated user interfaces for larger + screens, and help scale their application between small and large screens.

+ +

For more information about using fragments, read the + Fragments developer guide.

+ + +

Activity Lifecycle

+ +

Activities in the system are managed as + + activity stacks. When a new activity is started, it is usually placed on the top of the + current stack and becomes the running activity -- the previous activity always remains + below it in the stack, and will not come to the foreground again until + the new activity exits. There can be one or multiple activity stacks visible + on screen.

+ +

An activity has essentially four states:

+
    +
  • If an activity is in the foreground of the screen (at the highest position of the topmost + stack), it is active or running. This is usually the activity that the + user is currently interacting with.
  • +
  • If an activity has lost focus but is still presented to the user, it is visible. + It is possible if a new non-full-sized or transparent activity has focus on top of your + activity, another activity has higher position in multi-window mode, or the activity + itself is not focusable in current windowing mode. Such activity is completely alive (it + maintains all state and member information and remains attached to the window manager). +
  • If an activity is completely obscured by another activity, + it is stopped or hidden. It still retains all state and member + information, however, it is no longer visible to the user so its window is hidden + and it will often be killed by the system when memory is needed elsewhere.
  • +
  • The system can drop the activity from memory by either asking it to finish, + or simply killing its process, making it destroyed. When it is displayed again + to the user, it must be completely restarted and restored to its previous state.
  • +
+ +

The following diagram shows the important state paths of an Activity. + The square rectangles represent callback methods you can implement to + perform operations when the Activity moves between states. The colored + ovals are major states the Activity can be in.

+ +

+ +

There are three key loops you may be interested in monitoring within your + activity: + +

    +
  • The entire lifetime of an activity happens between the first call + to {@link android.app.Activity#onCreate} through to a single final call + to {@link android.app.Activity#onDestroy}. An activity will do all setup + of ""global"" state in onCreate(), and release all remaining resources in + onDestroy(). For example, if it has a thread running in the background + to download data from the network, it may create that thread in onCreate() + and then stop the thread in onDestroy(). + +
  • The visible lifetime of an activity happens between a call to + {@link android.app.Activity#onStart} until a corresponding call to + {@link android.app.Activity#onStop}. During this time the user can see the + activity on-screen, though it may not be in the foreground and interacting + with the user. Between these two methods you can maintain resources that + are needed to show the activity to the user. For example, you can register + a {@link android.content.BroadcastReceiver} in onStart() to monitor for changes + that impact your UI, and unregister it in onStop() when the user no + longer sees what you are displaying. The onStart() and onStop() methods + can be called multiple times, as the activity becomes visible and hidden + to the user. + +
  • The foreground lifetime of an activity happens between a call to + {@link android.app.Activity#onResume} until a corresponding call to + {@link android.app.Activity#onPause}. During this time the activity is + in visible, active and interacting with the user. An activity + can frequently go between the resumed and paused states -- for example when + the device goes to sleep, when an activity result is delivered, when a new + intent is delivered -- so the code in these methods should be fairly + lightweight. +
+ +

The entire lifecycle of an activity is defined by the following + Activity methods. All of these are hooks that you can override + to do appropriate work when the activity changes state. All + activities will implement {@link android.app.Activity#onCreate} + to do their initial setup; many will also implement + {@link android.app.Activity#onPause} to commit changes to data and + prepare to pause interacting with the user, and {@link android.app.Activity#onStop} + to handle no longer being visible on screen. You should always + call up to your superclass when implementing these methods.

+ +

+
+  public class Activity extends ApplicationContext {
+      protected void onCreate(Bundle savedInstanceState);
+  
+      protected void onStart();
+  
+      protected void onRestart();
+  
+      protected void onResume();
+  
+      protected void onPause();
+  
+      protected void onStop();
+  
+      protected void onDestroy();
+  }
+  
+ +

In general the movement through an activity's lifecycle looks like + this:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Method Description Killable? Next
{@link android.app.Activity#onCreate onCreate()}Called when the activity is first created. + This is where you should do all of your normal static set up: + create views, bind data to lists, etc. This method also + provides you with a Bundle containing the activity's previously + frozen state, if there was one. +

Always followed by onStart().

NoonStart()
    {@link android.app.Activity#onRestart onRestart()}Called after your activity has been stopped, prior to it being + started again. +

Always followed by onStart()

NoonStart()
{@link android.app.Activity#onStart onStart()}Called when the activity is becoming visible to the user. +

Followed by onResume() if the activity comes + to the foreground, or onStop() if it becomes hidden.

NoonResume() or onStop()
    {@link android.app.Activity#onResume onResume()}Called when the activity will start + interacting with the user. At this point your activity is at + the top of its activity stack, with user input going to it. +

Always followed by onPause().

NoonPause()
{@link android.app.Activity#onPause onPause()}Called when the activity loses foreground state, is no longer focusable or before + transition to stopped/hidden or destroyed state. The activity is still visible to + user, so it's recommended to keep it visually active and continue updating the UI. + Implementations of this method must be very quick because + the next activity will not be resumed until this method returns. +

Followed by either onResume() if the activity + returns back to the front, or onStop() if it becomes + invisible to the user.

Pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB}onResume() or
+ onStop()
{@link android.app.Activity#onStop onStop()}Called when the activity is no longer visible to the user. This may happen either + because a new activity is being started on top, an existing one is being brought in + front of this one, or this one is being destroyed. This is typically used to stop + animations and refreshing the UI, etc. +

Followed by either onRestart() if + this activity is coming back to interact with the user, or + onDestroy() if this activity is going away.

YesonRestart() or
+ onDestroy()
{@link android.app.Activity#onDestroy onDestroy()}The final call you receive before your + activity is destroyed. This can happen either because the + activity is finishing (someone called {@link Activity#finish} on + it), or because the system is temporarily destroying this + instance of the activity to save space. You can distinguish + between these two scenarios with the {@link + Activity#isFinishing} method.Yesnothing
+ +

Note the ""Killable"" column in the above table -- for those methods that + are marked as being killable, after that method returns the process hosting the + activity may be killed by the system at any time without another line + of its code being executed. Because of this, you should use the + {@link #onPause} method to write any persistent data (such as user edits) + to storage. In addition, the method + {@link #onSaveInstanceState(Bundle)} is called before placing the activity + in such a background state, allowing you to save away any dynamic instance + state in your activity into the given Bundle, to be later received in + {@link #onCreate} if the activity needs to be re-created. + See the Process Lifecycle + section for more information on how the lifecycle of a process is tied + to the activities it is hosting. Note that it is important to save + persistent data in {@link #onPause} instead of {@link #onSaveInstanceState} + because the latter is not part of the lifecycle callbacks, so will not + be called in every situation as described in its documentation.

+ +

Be aware that these semantics will change slightly between + applications targeting platforms starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB} + vs. those targeting prior platforms. Starting with Honeycomb, an application + is not in the killable state until its {@link #onStop} has returned. This + impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be + safely called after {@link #onPause()}) and allows an application to safely + wait until {@link #onStop()} to save persistent state.

+ +

For applications targeting platforms starting with + {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)} + will always be called after {@link #onStop}, so an application may safely + perform fragment transactions in {@link #onStop} and will be able to save + persistent state later.

+ +

For those methods that are not marked as being killable, the activity's + process will not be killed by the system starting from the time the method + is called and continuing after it returns. Thus an activity is in the killable + state, for example, between after onStop() to the start of + onResume(). Keep in mind that under extreme memory pressure the + system can kill the application process at any time.

+ + +

Configuration Changes

+ +

If the configuration of the device (as defined by the + {@link Configuration Resources.Configuration} class) changes, + then anything displaying a user interface will need to update to match that + configuration. Because Activity is the primary mechanism for interacting + with the user, it includes special support for handling configuration + changes.

+ +

Unless you specify otherwise, a configuration change (such as a change + in screen orientation, language, input devices, etc) will cause your + current activity to be destroyed, going through the normal activity + lifecycle process of {@link #onPause}, + {@link #onStop}, and {@link #onDestroy} as appropriate. If the activity + had been in the foreground or visible to the user, once {@link #onDestroy} is + called in that instance then a new instance of the activity will be + created, with whatever savedInstanceState the previous instance had generated + from {@link #onSaveInstanceState}.

+ +

This is done because any application resource, + including layout files, can change based on any configuration value. Thus + the only safe way to handle a configuration change is to re-retrieve all + resources, including layouts, drawables, and strings. Because activities + must already know how to save their state and re-create themselves from + that state, this is a convenient way to have an activity restart itself + with a new configuration.

+ +

In some special cases, you may want to bypass restarting of your + activity based on one or more types of configuration changes. This is + done with the {@link android.R.attr#configChanges android:configChanges} + attribute in its manifest. For any types of configuration changes you say + that you handle there, you will receive a call to your current activity's + {@link #onConfigurationChanged} method instead of being restarted. If + a configuration change involves any that you do not handle, however, the + activity will still be restarted and {@link #onConfigurationChanged} + will not be called.

+ + +

Starting Activities and Getting Results

+ +

The {@link android.app.Activity#startActivity} + method is used to start a + new activity, which will be placed at the top of the activity stack. It + takes a single argument, an {@link android.content.Intent Intent}, + which describes the activity + to be executed.

+ +

Sometimes you want to get a result back from an activity when it + ends. For example, you may start an activity that lets the user pick + a person in a list of contacts; when it ends, it returns the person + that was selected. To do this, you call the + {@link android.app.Activity#startActivityForResult(Intent, int)} + version with a second integer parameter identifying the call. The result + will come back through your {@link android.app.Activity#onActivityResult} + method.

+ +

When an activity exits, it can call + {@link android.app.Activity#setResult(int)} + to return data back to its parent. It must always supply a result code, + which can be the standard results RESULT_CANCELED, RESULT_OK, or any + custom values starting at RESULT_FIRST_USER. In addition, it can optionally + return back an Intent containing any additional data it wants. All of this + information appears back on the + parent's Activity.onActivityResult(), along with the integer + identifier it originally supplied.

+ +

If a child activity fails for any reason (such as crashing), the parent + activity will receive a result with the code RESULT_CANCELED.

+ +
+  public class MyActivity extends Activity {
+      ...
+  
+      static final int PICK_CONTACT_REQUEST = 0;
+  
+      public boolean onKeyDown(int keyCode, KeyEvent event) {
+          if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+              // When the user center presses, let them pick a contact.
+              startActivityForResult(
+                  new Intent(Intent.ACTION_PICK,
+                  new Uri(""content://contacts"")),
+                  PICK_CONTACT_REQUEST);
+             return true;
+          }
+          return false;
+      }
+  
+      protected void onActivityResult(int requestCode, int resultCode,
+              Intent data) {
+          if (requestCode == PICK_CONTACT_REQUEST) {
+              if (resultCode == RESULT_OK) {
+                  // A contact was picked.  Here we will just display it
+                  // to the user.
+                  startActivity(new Intent(Intent.ACTION_VIEW, data));
+              }
+          }
+      }
+  }
+  
+ + +

Saving Persistent State

+ +

There are generally two kinds of persistent state that an activity + will deal with: shared document-like data (typically stored in a SQLite + database using a {@linkplain android.content.ContentProvider content provider}) + and internal state such as user preferences.

+ +

For content provider data, we suggest that activities use an + ""edit in place"" user model. That is, any edits a user makes are effectively + made immediately without requiring an additional confirmation step. + Supporting this model is generally a simple matter of following two rules:

+ +
    +
  • When creating a new document, the backing database entry or file for + it is created immediately. For example, if the user chooses to write + a new email, a new entry for that email is created as soon as they + start entering data, so that if they go to any other activity after + that point this email will now appear in the list of drafts.

    +
  • When an activity's onPause() method is called, it should + commit to the backing content provider or file any changes the user + has made. This ensures that those changes will be seen by any other + activity that is about to run. You will probably want to commit + your data even more aggressively at key times during your + activity's lifecycle: for example before starting a new + activity, before finishing your own activity, when the user + switches between input fields, etc.

    +
+ +

This model is designed to prevent data loss when a user is navigating + between activities, and allows the system to safely kill an activity (because + system resources are needed somewhere else) at any time after it has been + stopped (or paused on platform versions before {@link android.os.Build.VERSION_CODES#HONEYCOMB}). + Note this implies that the user pressing BACK from your activity does not + mean ""cancel"" -- it means to leave the activity with its current contents + saved away. Canceling edits in an activity must be provided through + some other mechanism, such as an explicit ""revert"" or ""undo"" option.

+ +

See the {@linkplain android.content.ContentProvider content package} for + more information about content providers. These are a key aspect of how + different activities invoke and propagate data between themselves.

+ +

The Activity class also provides an API for managing internal persistent state + associated with an activity. This can be used, for example, to remember + the user's preferred initial display in a calendar (day view or week view) + or the user's default home page in a web browser.

+ +

Activity persistent state is managed + with the method {@link #getPreferences}, + allowing you to retrieve and + modify a set of name/value pairs associated with the activity. To use + preferences that are shared across multiple application components + (activities, receivers, services, providers), you can use the underlying + {@link Context#getSharedPreferences Context.getSharedPreferences()} method + to retrieve a preferences + object stored under a specific name. + (Note that it is not possible to share settings data across application + packages -- for that you will need a content provider.)

+ +

Here is an excerpt from a calendar activity that stores the user's + preferred view mode in its persistent settings:

+ +
+  public class CalendarActivity extends Activity {
+      ...
+  
+      static final int DAY_VIEW_MODE = 0;
+      static final int WEEK_VIEW_MODE = 1;
+  
+      private SharedPreferences mPrefs;
+      private int mCurViewMode;
+  
+      protected void onCreate(Bundle savedInstanceState) {
+          super.onCreate(savedInstanceState);
+  
+          mPrefs = getSharedPreferences(getLocalClassName(), MODE_PRIVATE);
+          mCurViewMode = mPrefs.getInt(""view_mode"", DAY_VIEW_MODE);
+      }
+  
+      protected void onPause() {
+          super.onPause();
+  
+          SharedPreferences.Editor ed = mPrefs.edit();
+          ed.putInt(""view_mode"", mCurViewMode);
+          ed.commit();
+      }
+  }
+  
+ + +

Permissions

+ +

The ability to start a particular Activity can be enforced when it is + declared in its + manifest's {@link android.R.styleable#AndroidManifestActivity <activity>} + tag. By doing so, other applications will need to declare a corresponding + {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} + element in their own manifest to be able to start that activity. + +

When starting an Activity you can set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION + Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION + Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the + Activity access to the specific URIs in the Intent. Access will remain + until the Activity has finished (it will remain across the hosting + process being killed and other temporary destruction). As of + {@link android.os.Build.VERSION_CODES#GINGERBREAD}, if the Activity + was already created and a new Intent is being delivered to + {@link #onNewIntent(Intent)}, any newly granted URI permissions will be added + to the existing ones it holds. + +

See the Security and Permissions + document for more information on permissions and security in general. + + +

Process Lifecycle

+ +

The Android system attempts to keep an application process around for as + long as possible, but eventually will need to remove old processes when + memory runs low. As described in Activity + Lifecycle, the decision about which process to remove is intimately + tied to the state of the user's interaction with it. In general, there + are four states a process can be in based on the activities running in it, + listed here in order of importance. The system will kill less important + processes (the last ones) before it resorts to killing more important + processes (the first ones). + +

    +
  1. The foreground activity (the activity at the top of the screen + that the user is currently interacting with) is considered the most important. + Its process will only be killed as a last resort, if it uses more memory + than is available on the device. Generally at this point the device has + reached a memory paging state, so this is required in order to keep the user + interface responsive. +

  2. A visible activity (an activity that is visible to the user + but not in the foreground, such as one sitting behind a foreground dialog + or next to other activities in multi-window mode) + is considered extremely important and will not be killed unless that is + required to keep the foreground activity running. +

  3. A background activity (an activity that is not visible to + the user and has been stopped) is no longer critical, so the system may + safely kill its process to reclaim memory for other foreground or + visible processes. If its process needs to be killed, when the user navigates + back to the activity (making it visible on the screen again), its + {@link #onCreate} method will be called with the savedInstanceState it had previously + supplied in {@link #onSaveInstanceState} so that it can restart itself in the same + state as the user last left it. +

  4. An empty process is one hosting no activities or other + application components (such as {@link Service} or + {@link android.content.BroadcastReceiver} classes). These are killed very + quickly by the system as memory becomes low. For this reason, any + background operation you do outside of an activity must be executed in the + context of an activity BroadcastReceiver or Service to ensure that the system + knows it needs to keep your process around. +

+ +

Sometimes an Activity may need to do a long-running operation that exists + independently of the activity lifecycle itself. An example may be a camera + application that allows you to upload a picture to a web site. The upload + may take a long time, and the application should allow the user to leave + the application while it is executing. To accomplish this, your Activity + should start a {@link Service} in which the upload takes place. This allows + the system to properly prioritize your process (considering it to be more + important than other non-visible applications) for the duration of the + upload, independent of whether the original activity is paused, stopped, + or finished. +"; + } } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs index 1bfc1a5b2..c142a930c 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs @@ -172,7 +172,7 @@ public IEnumerable ParseJavadoc () nodes = parser.TryParse (Javadoc, fileName: null, out tree); } catch (Exception e) { - Console.Error.WriteLine ($"## Exception translating remarks: {e.ToString ()}"); + Console.Error.WriteLine ($"## Exception translating remarks for {MemberDescription}:\n{e.ToString ()}"); } if (tree != null && tree.HasErrors ()) {