1

I am trying to implement a spell checker service as described here called SampleSpellCheckerService but it seems the tutorial is incomplete and the source code for it does not seem to be available.

I am struggling with how to get a session from my spell checker service in the setSuggestionsFor() method of my activity, as highlighted here:

public class SpellCheckerSettingsActivity extends AppCompatActivity implements SpellCheckerSession.SpellCheckerSessionListener {

    private static final String LOG_TAG = SpellCheckerSettingsActivity.class.getSimpleName();

    private TextView textView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spell_checker_settings);

        final EditText editText = (EditText)findViewById(R.id.editText);

        textView = (TextView)findViewById(R.id.textView);

        Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                fetchSuggestionsFor(editText.getText().toString());
            }
        });

        startService(new Intent(this, SampleSpellCheckerService.class));

    }

    private void fetchSuggestionsFor(String input){

        Log.d(LOG_TAG, "fetchSuggestionsFor(\"" + input + "\")");

        /***************************************************
         * 
         * This line is invalid. What do I replace it with?
         * 
         ***************************************************/
        SpellCheckerSession session = SampleSpellCheckerService.getSession();

        TextInfo[] textInfos = new TextInfo[]{ new TextInfo(input) };
        int suggestionsLimit = 5;
        session.getSentenceSuggestions(textInfos, suggestionsLimit);

    }

    @Override
    public void onGetSuggestions(SuggestionsInfo[] results) {

        Log.d(LOG_TAG, "onGetSuggestions(" + results + ")");

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText("Suggestions obtained (TODO - get from results[])");
            }
        });

    }

    @Override
    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {

        Log.d(LOG_TAG, "onGetSentenceSuggestions(" + results + ")");

        if (results != null) {
            final StringBuffer sb = new StringBuffer("");
            for (SentenceSuggestionsInfo result : results) {
                int n = result.getSuggestionsCount();
                for (int i = 0; i < n; i++) {
                    int m = result.getSuggestionsInfoAt(i).getSuggestionsCount();

                    for (int k = 0; k < m; k++) {
                        sb.append(result.getSuggestionsInfoAt(i).getSuggestionAt(k))
                                .append("\n");
                    }
                    sb.append("\n");
                }
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textView.setText(sb.toString());
                }
            });
        }

    }

    @Override
    public void onDestroy() {
        stopService(new Intent(this, SampleSpellCheckerService.class));
        super.onDestroy();
    }
}

So what is the correct way to get a session from SampleSpellCheckerService?

For completeness, here is my spell checker service class:

public class SampleSpellCheckerService extends SpellCheckerService {

    public static final String LOG_TAG = SampleSpellCheckerService.class.getSimpleName();

    public SampleSpellCheckerService() {
        Log.d(LOG_TAG, "SampleSpellCheckerService");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Log.d(LOG_TAG, "SampleSpellCheckerService.onCreate");
    }

    @Override
    public Session createSession() {

        Log.d(LOG_TAG, "createSession");

        return new AndroidSpellCheckerSession();
    }

    private static class AndroidSpellCheckerSession extends SpellCheckerService.Session {

        @Override
        public void onCreate() {

            Log.d(LOG_TAG, "AndroidSpellCheckerSession.onCreate");

        }



        @Override
        public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {

            Log.d(LOG_TAG, "onGetSentenceSuggestionsMultiple");

            SentenceSuggestionsInfo[] suggestionsInfos = null;
            //suggestionsInfo = new SuggestionsInfo();
            //... // look up suggestions for TextInfo
            return suggestionsInfos;
        }

        @Override
        public SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit) {

            Log.d(LOG_TAG, "onGetSuggestions");

            SuggestionsInfo suggestionsInfo = null;
            //suggestionsInfo = new SuggestionsInfo();
            //... // look up suggestions for TextInfo
            return suggestionsInfo;
        }

        @Override
        public void onCancel() {
            Log.d(LOG_TAG, "onCancel");
        }


    }
}

Here is my manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">

    <permission android:name="android.permission.BIND_TEXT_SERVICE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service
            android:name="com.example.SampleSpellCheckerService"
            android:label="@string/app_name"
            android:enabled="true"
            android:permission="android.permission.BIND_TEXT_SERVICE">
            <intent-filter>
                <action android:name="android.service.textservice.SpellCheckerService" />
            </intent-filter>

            <meta-data
                android:name="android.view.textservice.scs"
                android:resource="@xml/spellchecker" />
        </service>

        <activity android:name="com.example.SpellCheckerSettingsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

And here is my spellchecker.xml:

<?xml version="1.0" encoding="utf-8"?>
<spell-checker
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/spellchecker_name"
    android:settingsActivity="com.example.SpellCheckerSettingsActivity">
    <subtype
        android:label="@string/subtype_generic"
        android:subtypeLocale="en" />
    />
    <subtype
        android:label="@string/subtype_generic"
        android:subtypeLocale="en_GB" />
    />
</spell-checker>

NB - I am testing with a Samsung device.

ban-geoengineering
  • 18,324
  • 27
  • 171
  • 253

1 Answers1

1

As far as I can see from the docs and some sample code, there seems to be some misconception of the android Spell Checking API, that result in your error.

As far as I can tell you can't call your service directly since the APIs goal is for you to define a spellchecker that the user has to select from the system settings first. Basically you mixed up the settings activity (that is displayed for service related settings) with a test activity for your service.

Some better tutorials are written in the android dev blog and here, some sample code for a testing client and an rudimentary example service could be found between the mirrored android samples on github.

What you got so far is the sample service (though the linked samples provide some more code to see how the methods could be implemented), you have your spellchecker.xml needed for locale definition and the spellchecker name appearing in the settings, you already have a settings activity (as defined in your spellchecker.xml, but not needed as long as you don't need any preferences) and you have an activity implementing your SpellCheckerSessionListener (although you named it as settings activity).

What you'd still need to do, is go to your settings -> Language & keyboard -> activate Spell checker and choose your spell checker.

To get a session from that spellchecker you can then make a call to the API with

        final TextServicesManager tsm = (TextServicesManager) getSystemService(
            Context.TEXT_SERVICES_MANAGER_SERVICE);
    mScs = tsm.newSpellCheckerSession(null, null, this, true);

as seen in the samples.

Edit: if you don't need any settings for your service, you can remove the xml attribute from your xml:

 android:settingsActivity="com.example.SpellCheckerSettingsActivity"
Christian R.
  • 1,528
  • 9
  • 16
  • Thanks. Expecting the user to faff around with their settings is not a viable solution for me. Can you recommend a better way for me to implement my custom spell checker on a SearchView? The requirement is that if the user types the product name incorrectly (eg, *Badli Spelt Product*), they will be offered the actual product (eg, my *Badly Spelled* product). Would I need to write this functionality from scratch? (Or maybe there is a free, reliable, web-based API I can use?) – ban-geoengineering Dec 16 '16 at 08:40
  • IMHO: the whole spellchecking API is just some kind of step child. on my HTC neither the google keyboard nor the htc sense use the custom spellchecker, but both use their own - that and that another app that wants to use your spellchecker has to implement a specific interface makes this kind of useless for the user. for an application specific spellcheck (like you want it) I'd drop the whole spellchecking API and just go with a textwatcher – Christian R. Dec 16 '16 at 15:15
  • Thanks, but how would a TextWatcher help re spell checking? – ban-geoengineering Dec 16 '16 at 15:18
  • a good solution for that might be use case dependent. basic idea is to add a `TextWatcher` implementation as `EditText.addListener()`. In `onAfterTextChanged` you'll get the input string from the textview and can check for spelling errors. then you'll need some kind of display for the suggestions - either a list directly below the input or (if a list would mess with your interface to much) a floating context menu (https://developer.android.com/guide/topics/ui/menus.html#context-menu) linked to your edittext. – Christian R. Dec 16 '16 at 15:33
  • another way (without the whole spellchecking, which might be a lot of work) that you can go is the autocomplete textview, see https://developer.android.com/training/keyboard-input/style.html#AutoComplete - that doesn't use spell correction, but uses the whole list of possible inputs. – Christian R. Dec 16 '16 at 15:39
  • Thanks. I already have suggestions implemented. The problem is that the user is just entering incorrectly-spelled words and not seeing (or ignoring) the suggestions. – ban-geoengineering Dec 22 '16 at 11:05