Support sentence level spell check in sample code of the spell checker

Bug: 6136149

Change-Id: I1519258dd3ce95ad01e50a75f802469737bef3c4
diff --git a/samples/SpellChecker/HelloSpellChecker/Android.mk b/samples/SpellChecker/HelloSpellChecker/Android.mk
index 1c76f23..5138ce9 100755
--- a/samples/SpellChecker/HelloSpellChecker/Android.mk
+++ b/samples/SpellChecker/HelloSpellChecker/Android.mk
@@ -5,6 +5,7 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+# TODO: Change sdk version to 16
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := HelloSpellChecker
diff --git a/samples/SpellChecker/HelloSpellChecker/src/com/example/android/hellospellchecker/HelloSpellCheckerActivity.java b/samples/SpellChecker/HelloSpellChecker/src/com/example/android/hellospellchecker/HelloSpellCheckerActivity.java
index 6550981..f88b023 100644
--- a/samples/SpellChecker/HelloSpellChecker/src/com/example/android/hellospellchecker/HelloSpellCheckerActivity.java
+++ b/samples/SpellChecker/HelloSpellChecker/src/com/example/android/hellospellchecker/HelloSpellCheckerActivity.java
@@ -18,8 +18,10 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SpellCheckerSession;
 import android.view.textservice.SuggestionsInfo;
 import android.view.textservice.TextInfo;
@@ -30,8 +32,10 @@
 
 public class HelloSpellCheckerActivity extends Activity implements SpellCheckerSessionListener {
     private static final String TAG = HelloSpellCheckerActivity.class.getSimpleName();
+    private static final int NOT_A_LENGTH = -1;
     private TextView mMainView;
     private SpellCheckerSession mScs;
+
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -40,18 +44,36 @@
         mMainView = (TextView)findViewById(R.id.main);
     }
 
+    private boolean isSentenceSpellCheckSupported() {
+        if (mScs == null || Build.VERSION.SDK_INT < 16) {
+            return false;
+        }
+        // Note that isSentenceSpellCheckSupported works on JB or later.
+        return mScs.isSentenceSpellCheckSupported();
+    }
+
     @Override
     public void onResume() {
         super.onResume();
         final TextServicesManager tsm = (TextServicesManager) getSystemService(
                 Context.TEXT_SERVICES_MANAGER_SERVICE);
         mScs = tsm.newSpellCheckerSession(null, null, this, true);
+
         if (mScs != null) {
             // Instantiate TextInfo for each query
             // TextInfo can be passed a sequence number and a cookie number to identify the result
-            mScs.getSuggestions(new TextInfo("tgis"), 3);
-            mScs.getSuggestions(new TextInfo("hllo"), 3);
-            mScs.getSuggestions(new TextInfo("helloworld"), 3);
+            if (isSentenceSpellCheckSupported()) {
+                // Note that getSentenceSuggestions works on JB or later.
+                Log.d(TAG, "Sentence spellchecking supported.");
+                mScs.getSentenceSuggestions(new TextInfo[] {new TextInfo("tgisis")}, 3);
+                mScs.getSentenceSuggestions(new TextInfo[] {new TextInfo(
+                        "I wold like to here form you")}, 3);
+                mScs.getSentenceSuggestions(new TextInfo[] {new TextInfo("hell othere")}, 3);
+            } else {
+                mScs.getSuggestions(new TextInfo("tgis"), 3);
+                mScs.getSuggestions(new TextInfo("hllo"), 3);
+                mScs.getSuggestions(new TextInfo("helloworld"), 3);
+            }
         } else {
             Log.e(TAG, "Couldn't obtain the spell checker service.");
         }
@@ -65,17 +87,67 @@
         }
     }
 
+    private void dumpSuggestionsInfoInternal(
+            final StringBuilder sb, final SuggestionsInfo si, final int length, final int offset) {
+        // Returned suggestions are contained in SuggestionsInfo
+        final int len = si.getSuggestionsCount();
+        sb.append('\n');
+        for (int j = 0; j < len; ++j) {
+            if (j != 0) {
+                sb.append(", ");
+            }
+            sb.append(si.getSuggestionAt(j));
+        }
+        sb.append(" (" + len + ")");
+        if (length != NOT_A_LENGTH) {
+            sb.append(" length = " + length + ", offset = " + offset);
+        }
+    }
+
+    /**
+     * Callback for {@link SpellCheckerSession#getSuggestions(TextInfo, int)}
+     * and {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}
+     * @param results an array of {@link SuggestionsInfo}s.
+     * These results are suggestions for {@link TextInfo}s queried by
+     * {@link SpellCheckerSession#getSuggestions(TextInfo, int)} or
+     * {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}
+     */
     @Override
     public void onGetSuggestions(final SuggestionsInfo[] arg0) {
+        Log.d(TAG, "onGetSuggestions");
         final StringBuilder sb = new StringBuilder();
         for (int i = 0; i < arg0.length; ++i) {
-            // Returned suggestions are contained in SuggestionsInfo
-            final int len = arg0[i].getSuggestionsCount();
-            sb.append('\n');
-            for (int j = 0; j < len; ++j) {
-                sb.append("," + arg0[i].getSuggestionAt(j));
+            dumpSuggestionsInfoInternal(sb, arg0[i], 0, NOT_A_LENGTH);
+        }
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mMainView.append(sb.toString());
             }
-            sb.append(" (" + len + ")");
+        });
+    }
+
+    /**
+     * Callback for {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}
+     * @param results an array of {@link SentenceSuggestionsInfo}s.
+     * These results are suggestions for {@link TextInfo}s
+     * queried by {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}.
+     */
+    @Override
+    public void onGetSentenceSuggestions(final SentenceSuggestionsInfo[] arg0) {
+        if (!isSentenceSpellCheckSupported()) {
+            Log.e(TAG, "Sentence spell check is not supported on this platform, "
+                    + "but accidentially called.");
+            return;
+        }
+        Log.d(TAG, "onGetSentenceSuggestions");
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < arg0.length; ++i) {
+            final SentenceSuggestionsInfo ssi = arg0[i];
+            for (int j = 0; j < ssi.getSuggestionsCount(); ++j) {
+                dumpSuggestionsInfoInternal(
+                        sb, ssi.getSuggestionsInfoAt(j), ssi.getOffsetAt(j), ssi.getLengthAt(j));
+            }
         }
         runOnUiThread(new Runnable() {
             @Override
diff --git a/samples/SpellChecker/SampleSpellCheckerService/Android.mk b/samples/SpellChecker/SampleSpellCheckerService/Android.mk
index adf65d9..4f6b421 100755
--- a/samples/SpellChecker/SampleSpellCheckerService/Android.mk
+++ b/samples/SpellChecker/SampleSpellCheckerService/Android.mk
@@ -5,8 +5,9 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+# TODO: Change sdk version to 16
 LOCAL_SDK_VERSION := current
 
-LOCAL_PACKAGE_NAME := SampleSpellChecker
+LOCAL_PACKAGE_NAME := SampleSpellCheckerService
 
 include $(BUILD_PACKAGE)
diff --git a/samples/SpellChecker/SampleSpellCheckerService/res/xml/spellchecker.xml b/samples/SpellChecker/SampleSpellCheckerService/res/xml/spellchecker.xml
index f4601d2..b3a078a 100644
--- a/samples/SpellChecker/SampleSpellCheckerService/res/xml/spellchecker.xml
+++ b/samples/SpellChecker/SampleSpellCheckerService/res/xml/spellchecker.xml
@@ -22,7 +22,8 @@
 
 <spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
         android:label="@string/spellchecker_name"
-        android:settingsActivity="com.example.android.samplespellcheckerservice.SpellCheckerSettingsActivity">
+        android:settingsActivity="com.example.android.samplespellcheckerservice.SpellCheckerSettingsActivity"
+        android:supportsSentenceSpellCheck="true">
     <subtype
             android:label="@string/subtype_generic"
             android:subtypeLocale="en"
diff --git a/samples/SpellChecker/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java b/samples/SpellChecker/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java
index dc85587..8778cc0 100644
--- a/samples/SpellChecker/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java
+++ b/samples/SpellChecker/SampleSpellCheckerService/src/com/example/android/samplespellcheckerservice/SampleSpellCheckerService.java
@@ -16,20 +16,31 @@
 
 package com.example.android.samplespellcheckerservice;
 
+import android.os.Build;
 import android.service.textservice.SpellCheckerService;
 import android.util.Log;
+import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SuggestionsInfo;
 import android.view.textservice.TextInfo;
 
+import java.util.ArrayList;
+
 public class SampleSpellCheckerService extends SpellCheckerService {
     private static final String TAG = SampleSpellCheckerService.class.getSimpleName();
     private static final boolean DBG = true;
+
     @Override
     public Session createSession() {
         return new AndroidSpellCheckerSession();
     }
 
     private static class AndroidSpellCheckerSession extends Session {
+
+        private boolean isSentenceSpellCheckApiSupported() {
+            // Note that the sentence level spell check APIs work on JB or later.
+            return Build.VERSION.SDK_INT >= 16;
+        }
+
         private String mLocale;
         @Override
         public void onCreate() {
@@ -51,5 +62,60 @@
             return new SuggestionsInfo(flags,
                     new String[] {"aaa", "bbb", "Candidate for " + input, mLocale});
         }
+
+        @Override
+        public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(
+                TextInfo[] textInfos, int suggestionsLimit) {
+            if (!isSentenceSpellCheckApiSupported()) {
+                Log.e(TAG, "Sentence spell check is not supported on this platform, "
+                        + "but accidentially called.");
+                return null;
+            }
+            final ArrayList<SentenceSuggestionsInfo> retval =
+                    new ArrayList<SentenceSuggestionsInfo>();
+            for (int i = 0; i < textInfos.length; ++i) {
+                final TextInfo ti = textInfos[i];
+                if (DBG) {
+                    Log.d(TAG, "onGetSentenceSuggestionsMultiple: " + ti.getText());
+                }
+                final String input = ti.getText();
+                final int length = input.length();
+                final SuggestionsInfo[] sis;
+                final int[] lengths;
+                final int[] offsets;
+                if (input.equalsIgnoreCase("I wold like to here form you")) {
+                    // Return sentence level suggestion for this fixed input
+                    final int flags0 = SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO;
+                    final int flags1 = SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS
+                            | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO;
+                    final int flags2 = flags1;
+                    final SuggestionsInfo si0 = new SuggestionsInfo(
+                            flags0, new String[] { "would" });
+                    final SuggestionsInfo si1 = new SuggestionsInfo(
+                            flags1, new String[] { "hear" });
+                    final SuggestionsInfo si2 = new SuggestionsInfo(
+                            flags2, new String[] { "from" });
+                    sis = new SuggestionsInfo[] {si0, si1, si2};
+                    offsets = new int[] { 2, 15, 20 };
+                    lengths = new int[] { 4, 4, 4 };
+                } else {
+                    // Just a fake logic:
+                    // length <= 3 for short words that we assume are in the fake dictionary
+                    // length > 20 for too long words that we assume can't be recognized
+                    // (such as CJK words)
+                    final int flags = length <= 3 ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+                            : length <= 20 ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0;
+                    final SuggestionsInfo si = new SuggestionsInfo(flags,
+                            new String[] {"aaa", "bbb", "Candidate for " + input, mLocale});
+                    sis = new SuggestionsInfo[] { si };
+                    offsets = new int[] { 0 };
+                    lengths = new int[] { ti.getText().length() };
+                }
+                final SentenceSuggestionsInfo ssi =
+                        new SentenceSuggestionsInfo(sis, lengths, offsets);
+                retval.add(ssi);
+            }
+            return retval.toArray(new SentenceSuggestionsInfo[0]);
+        }
     }
 }