API for stylus handwriting settings activity

Introduce  new attribute for InputMethodInfo to define settings activity
action for stylus handwriting settings. This allows apps/system components
to launch scribe specific settings.

Bug: 256045215
Test: atest InputMethodInfoTest
Change-Id: I8ce2073a1bb525e43a6ccc2ef5ef11d09029327d
diff --git a/core/api/current.txt b/core/api/current.txt
index eb2ba0e..efe04ab 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1489,6 +1489,7 @@
     field public static final int strokeLineJoin = 16843788; // 0x101040c
     field public static final int strokeMiterLimit = 16843789; // 0x101040d
     field public static final int strokeWidth = 16843783; // 0x1010407
+    field public static final int stylusHandwritingSettingsActivity;
     field public static final int subMenuArrow = 16844019; // 0x10104f3
     field public static final int submitBackground = 16843912; // 0x1010488
     field public static final int subtitle = 16843473; // 0x10102d1
@@ -54805,6 +54806,7 @@
   public final class InputMethodInfo implements android.os.Parcelable {
     ctor public InputMethodInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     ctor public InputMethodInfo(String, String, CharSequence, String);
+    method @Nullable public android.content.Intent createStylusHandwritingSettingsActivityIntent();
     method public int describeContents();
     method public void dump(android.util.Printer, String);
     method public android.content.ComponentName getComponent();
@@ -54823,6 +54825,7 @@
     method public boolean supportsStylusHandwriting();
     method public boolean suppressesSpellChecker();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0b4a708..d6ce1589 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3352,6 +3352,7 @@
   }
 
   public final class InputMethodInfo implements android.os.Parcelable {
+    ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String);
     ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
   }
 
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index b7da732..6a31818d 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -38,6 +39,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Printer;
 import android.util.Slog;
@@ -71,6 +73,10 @@
  * @attr ref android.R.styleable#InputMethod_configChanges
  */
 public final class InputMethodInfo implements Parcelable {
+
+    public static final String ACTION_STYLUS_HANDWRITING_SETTINGS =
+            "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
+
     static final String TAG = "InputMethodInfo";
 
     /**
@@ -152,6 +158,11 @@
      */
     private final boolean mSupportsStylusHandwriting;
 
+    /**
+     * The stylus handwriting setting activity's name, used by the system settings to
+     * launch the stylus handwriting specific setting activity of this input method.
+     */
+    private final String mStylusHandwritingSettingsActivityAttr;
 
     /**
      * @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
@@ -203,6 +214,7 @@
 
         PackageManager pm = context.getPackageManager();
         String settingsActivityComponent = null;
+        String stylusHandwritingSettingsActivity = null;
         boolean isVrOnly;
         int isDefaultResId = 0;
 
@@ -253,6 +265,9 @@
                     com.android.internal.R.styleable.InputMethod_configChanges, 0);
             mSupportsStylusHandwriting = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
+            stylusHandwritingSettingsActivity = sa.getString(
+                    com.android.internal.R.styleable
+                            .InputMethod_stylusHandwritingSettingsActivity);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -328,6 +343,7 @@
         }
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mSettingsActivityName = settingsActivityComponent;
+        mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivity;
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
@@ -354,6 +370,7 @@
         mSubtypes = new InputMethodSubtypeArray(source);
         mHandledConfigChanges = source.readInt();
         mSupportsStylusHandwriting = source.readBoolean();
+        mStylusHandwritingSettingsActivityAttr = source.readString8();
         mForceDefault = false;
     }
 
@@ -367,10 +384,28 @@
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
                 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
     /**
+     * Test API for creating a built-in input method to verify stylus handwriting.
+     * @hide
+     */
+    @TestApi
+    public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+            @NonNull CharSequence label, @NonNull String settingsActivity,
+            boolean supportStylusHandwriting,
+            @NonNull String stylusHandwritingSettingsActivityAttr) {
+        this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+                settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+                false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+                0 /* handledConfigChanges */, supportStylusHandwriting,
+                stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
+    }
+
+    /**
      * Temporary API for creating a built-in input method for test.
      * @hide
      */
@@ -383,6 +418,7 @@
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
                 false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -397,6 +433,7 @@
                 true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
                 false /* isVrOnly */, 0 /* handledconfigChanges */,
                 false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -410,6 +447,7 @@
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
                 supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
                 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -421,6 +459,7 @@
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
             boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting,
+            String stylusHandwritingSettingsActivityAttr,
             boolean supportsInlineSuggestionsWithTouchExploration) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
@@ -439,6 +478,7 @@
         mIsVrOnly = isVrOnly;
         mHandledConfigChanges = handledConfigChanges;
         mSupportsStylusHandwriting = supportsStylusHandwriting;
+        mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr;
     }
 
     private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -528,6 +568,7 @@
      *
      * <p>A null will be returned if there is no settings activity associated
      * with the input method.</p>
+     * @see #createStylusHandwritingSettingsActivityIntent()
      */
     public String getSettingsActivity() {
         return mSettingsActivityName;
@@ -600,11 +641,40 @@
     /**
      * Returns if IME supports handwriting using stylus input.
      * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+     * @see #createStylusHandwritingSettingsActivityIntent()
      */
     public boolean supportsStylusHandwriting() {
         return mSupportsStylusHandwriting;
     }
 
+    /**
+     * Returns {@link Intent} for stylus handwriting settings activity with
+     * {@link Intent#getAction() Intent action} {@link #ACTION_STYLUS_HANDWRITING_SETTINGS}
+     * if IME {@link #supportsStylusHandwriting() supports stylus handwriting} or else
+     * <code>null</code> if there are no associated settings for stylus handwriting / handwriting
+     * is not supported.
+     *
+     * <p>To launch stylus settings, use this method to get the {@link android.content.Intent} to
+     * launch the stylus handwriting settings activity.</p>
+     * <p>e.g.<pre><code>startActivity(createStylusHandwritingSettingsActivityIntent());</code>
+     * </pre></p>
+     *
+     * @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity
+     * @see #getSettingsActivity()
+     * @see #supportsStylusHandwriting()
+     */
+    @Nullable
+    public Intent createStylusHandwritingSettingsActivityIntent() {
+        if (TextUtils.isEmpty(mStylusHandwritingSettingsActivityAttr)
+                || !mSupportsStylusHandwriting) {
+            return null;
+        }
+        // TODO(b/210039666): consider returning null if component is not enabled.
+        return new Intent(ACTION_STYLUS_HANDWRITING_SETTINGS).setComponent(
+                new ComponentName(getServiceInfo().packageName,
+                        mStylusHandwritingSettingsActivityAttr));
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
@@ -615,7 +685,9 @@
                 + mSupportsInlineSuggestionsWithTouchExploration
                 + " mSuppressesSpellChecker=" + mSuppressesSpellChecker
                 + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
-                + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
+                + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting
+                + " mStylusHandwritingSettingsActivityAttr="
+                        + mStylusHandwritingSettingsActivityAttr);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
         pw.println(prefix + "Service:");
@@ -730,6 +802,7 @@
         mSubtypes.writeToParcel(dest);
         dest.writeInt(mHandledConfigChanges);
         dest.writeBoolean(mSupportsStylusHandwriting);
+        dest.writeString8(mStylusHandwritingSettingsActivityAttr);
     }
 
     /**
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1b6f88f..5851c4e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3763,6 +3763,9 @@
             {@link android.inputmethodservice.InputMethodService#onFinishInput()}.
         -->
         <attr name="supportsStylusHandwriting" format="boolean" />
+        <!-- SClass name of an activity that allows the user to modify the stylus handwriting
+            settings for this service -->
+        <attr name="stylusHandwritingSettingsActivity" format="string" />
 
     </declare-styleable>
 
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index f4b49e6..58b8601 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -128,6 +128,7 @@
     <public name="isCredential"/>
     <public name="searchResultHighlightColor" />
     <public name="focusedSearchResultHighlightColor" />
+    <public name="stylusHandwritingSettingsActivity" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index e7d7d640..7bef55e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -62,6 +62,8 @@
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
         assertThat(imi.isInlineSuggestionsEnabled(), is(false));
         assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
+        assertThat(imi.supportsStylusHandwriting(), is(false));
+        assertThat(imi.createStylusHandwritingSettingsActivityIntent(), equalTo(null));
     }
 
     @Test