Add functions to set / get SpellCheckerSubtype

Change-Id: I977326879fe201c4dee4a87da361217175eb6041
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03cba3d..15c57e6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3768,12 +3768,21 @@
 
 
         /**
-         * The {@link ComponentName} string of the service to be used as the spell checker
+         * The {@link ComponentName} string of the selected spell checker service which is
+         * one of the services managed by the text service manager.
+         *
+         * @hide
+         */
+        public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+
+        /**
+         * The {@link ComponentName} string of the selected subtype of the selected spell checker
          * service which is one of the services managed by the text service manager.
          *
          * @hide
          */
-        public static final String SPELL_CHECKER_SERVICE = "spell_checker_service";
+        public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
+                "selected_spell_checker_subtype";
 
         /**
          * What happens when the user presses the Power button while in-call
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index d60ce4f..bb13052 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -135,11 +135,40 @@
     public void setCurrentSpellChecker(SpellCheckerInfo sci) {
         try {
             if (sci == null) {
-                throw new NullPointerException("SpellCheckerInfo is null");
+                throw new NullPointerException("SpellCheckerInfo is null.");
             }
-            sService.setCurrentSpellChecker(sci.getId());
+            sService.setCurrentSpellChecker(null, sci.getId());
         } catch (RemoteException e) {
             Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
         }
     }
+
+    /**
+     * @hide
+     */
+    public SpellCheckerSubtype getCurrentSpellCheckerSubtype() {
+        try {
+            // Passing null as a locale for ICS
+            return sService.getCurrentSpellCheckerSubtype(null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
+        try {
+            if (subtype == null) {
+                throw new NullPointerException("SpellCheckerSubtype is null.");
+            }
+            sService.setCurrentSpellCheckerSubtype(null, subtype.hashCode());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setSpellCheckerSubtype:" + e);
+        }
+    }
+
+
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index bb4b2a3..cc30c176 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
 
 /**
  * Interface to the text service manager.
@@ -29,10 +30,12 @@
  */
 interface ITextServicesManager {
     SpellCheckerInfo getCurrentSpellChecker(String locale);
+    SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale);
     oneway void getSpellCheckerService(String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
             in ISpellCheckerSessionListener scListener, in Bundle bundle);
     oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
-    oneway void setCurrentSpellChecker(String sciId);
+    oneway void setCurrentSpellChecker(String locale, String sciId);
+    oneway void setCurrentSpellCheckerSubtype(String locale, int hashCode);
     SpellCheckerInfo[] getEnabledSpellCheckers();
 }
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 90824a6..18ddabd 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -41,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -174,7 +175,7 @@
         synchronized (mSpellCheckerMap) {
             String curSpellCheckerId =
                     Settings.Secure.getString(mContext.getContentResolver(),
-                            Settings.Secure.SPELL_CHECKER_SERVICE);
+                            Settings.Secure.SELECTED_SPELL_CHECKER);
             if (DBG) {
                 Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
             }
@@ -185,6 +186,35 @@
         }
     }
 
+    // TODO: Save SpellCheckerSubtype by supported languages.
+    @Override
+    public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale) {
+        synchronized (mSpellCheckerMap) {
+            final String subtypeHashCodeStr =
+                    Settings.Secure.getString(mContext.getContentResolver(),
+                            Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
+            if (DBG) {
+                Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr);
+            }
+            final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+            if (sci.getSubtypeCount() == 0) {
+                return null;
+            }
+            if (TextUtils.isEmpty(subtypeHashCodeStr)) {
+                // Return the first Subtype if there is no settings for the current subtype.
+                return sci.getSubtypeAt(0);
+            }
+            final int hashCode = Integer.valueOf(subtypeHashCodeStr);
+            for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+                final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
+                if (scs.hashCode() == hashCode) {
+                    return scs;
+                }
+            }
+            return sci.getSubtypeAt(0);
+        }
+    }
+
     @Override
     public void getSpellCheckerService(String sciId, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
@@ -301,7 +331,7 @@
     }
 
     @Override
-    public void setCurrentSpellChecker(String sciId) {
+    public void setCurrentSpellChecker(String locale, String sciId) {
         synchronized(mSpellCheckerMap) {
             if (mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -314,6 +344,20 @@
         }
     }
 
+    @Override
+    public void setCurrentSpellCheckerSubtype(String locale, int hashCode) {
+        synchronized(mSpellCheckerMap) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission "
+                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+            }
+            setCurrentSpellCheckerLocked(hashCode);
+        }
+    }
+
     private void setCurrentSpellCheckerLocked(String sciId) {
         if (DBG) {
             Slog.w(TAG, "setCurrentSpellChecker: " + sciId);
@@ -322,7 +366,32 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.SPELL_CHECKER_SERVICE, sciId);
+                    Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setCurrentSpellCheckerLocked(int hashCode) {
+        if (DBG) {
+            Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode);
+        }
+        final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+        if (sci == null) return;
+        boolean found = false;
+        for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+            if(sci.getSubtypeAt(i).hashCode() == hashCode) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            return;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
         } finally {
             Binder.restoreCallingIdentity(ident);
         }