Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b29d045 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Consolidate the language-switching logic

This CL does not change the existing behavior.
In I84291fd4a7d6192b, the IME rotation group is logically
devided into two groups, one is for IMEs that are declared as
supportsSwitchingToNextInputMethod == true, and the other is
IMEs that are not declared so. The problem is that the logic
was implemented with a single ime/subtype list where these two
kinds of IMEs are completely mixed. This makes the code
unnecessarily complex.
With this CL, these two rotation groups are actually managed
as two different collections separately. This allows us to
simplify the rotation logic as well as its test cases.
This CL is also a groundwork to implement smarter
language-switching logic that is applied to
language-switching-aware IMEs only.

BUG: 7043015
Change-Id: I7f08ec299ec41d614e2cd3912320687db1576e80
parent b284e30b
Loading
Loading
Loading
Loading
+102 −38
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.inputmethod;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -211,53 +212,110 @@ public class InputMethodSubtypeSwitchingController {
        }
    }

    private final InputMethodSettings mSettings;
    private InputMethodAndSubtypeList mSubtypeList;
    private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
        return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
                subtype.hashCode()) : NOT_A_SUBTYPE_ID;
    }

    @VisibleForTesting
    public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList,
            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
    private static class StaticRotationList {
        private final List<ImeSubtypeListItem> mImeSubtypeList;
        public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
            mImeSubtypeList = imeSubtypeList;
        }

        /**
         * Returns the index of the specified input method and subtype in the given list.
         * @param imi The {@link InputMethodInfo} to be searched.
         * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
         * does not have a subtype.
         * @return The index in the given list. -1 if not found.
         */
        private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
            final int currentSubtypeId = calculateSubtypeId(imi, subtype);
            final int N = mImeSubtypeList.size();
            for (int i = 0; i < N; ++i) {
                final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
                // Skip until the current IME/subtype is found.
                if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
                    return i;
                }
            }
            return -1;
        }

        public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
                InputMethodInfo imi, InputMethodSubtype subtype) {
            if (imi == null) {
                return null;
            }
        if (imList.size() <= 1) {
            if (mImeSubtypeList.size() <= 1) {
                return null;
            }
        // Here we have two rotation groups, depending on the returned boolean value of
        // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}.
        final boolean expectedValueOfSupportsSwitchingToNextInputMethod =
                imi.supportsSwitchingToNextInputMethod();
        final int N = imList.size();
        final int currentSubtypeId =
                subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
                        subtype.hashCode()) : NOT_A_SUBTYPE_ID;
        for (int i = 0; i < N; ++i) {
            final ImeSubtypeListItem isli = imList.get(i);
            // Skip until the current IME/subtype is found.
            if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) {
                continue;
            }
            // Found the current IME/subtype. Start searching the next IME/subtype from here.
            for (int j = 0; j < N - 1; ++j) {
                final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
                // Skip if the candidate doesn't belong to the expected rotation group.
                if (expectedValueOfSupportsSwitchingToNextInputMethod !=
                        candidate.mImi.supportsSwitchingToNextInputMethod()) {
                    continue;
            final int currentIndex = getIndex(imi, subtype);
            if (currentIndex < 0) {
                return null;
            }
            final int N = mImeSubtypeList.size();
            for (int offset = 1; offset < N; ++offset) {
                // Start searching the next IME/subtype from the next of the current index.
                final int candidateIndex = (currentIndex + offset) % N;
                final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
                // Skip if searching inside the current IME only, but the candidate is not
                // the current IME.
                if (onlyCurrentIme && !candidate.mImi.equals(imi)) {
                if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
                    continue;
                }
                return candidate;
            }
            // No appropriate IME/subtype is found in the list. Give up.
            return null;
        }
        // The current IME/subtype is not found in the list. Give up.
    }

    @VisibleForTesting
    public static class ControllerImpl {
        private final StaticRotationList mSwitchingAwareSubtypeList;
        private final StaticRotationList mSwitchingUnawareSubtypeList;

        public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
            mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
                    true /* supportsSwitchingToNextInputMethod */));
            mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
                    false /* supportsSwitchingToNextInputMethod */));
        }

        public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
                InputMethodSubtype subtype) {
            if (imi == null) {
                return null;
            }
            if (imi.supportsSwitchingToNextInputMethod()) {
                return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
                        subtype);
            } else {
                return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
                        subtype);
            }
        }

        private static List<ImeSubtypeListItem> filterImeSubtypeList(
                final List<ImeSubtypeListItem> items,
                final boolean supportsSwitchingToNextInputMethod) {
            final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
            final int ALL_ITEMS_COUNT = items.size();
            for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
                final ImeSubtypeListItem item = items.get(i);
                if (item.mImi.supportsSwitchingToNextInputMethod() ==
                        supportsSwitchingToNextInputMethod) {
                    result.add(item);
                }
            }
            return result;
        }
    }

    private final InputMethodSettings mSettings;
    private InputMethodAndSubtypeList mSubtypeList;
    private ControllerImpl mController;

    private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
        mSettings = settings;
@@ -276,12 +334,18 @@ public class InputMethodSubtypeSwitchingController {

    public void resetCircularListLocked(Context context) {
        mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
        mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
    }

    public ImeSubtypeListItem getNextInputMethodLocked(
            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
        return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
                onlyCurrentIme, imi, subtype);
    public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
            InputMethodSubtype subtype) {
        if (mController == null) {
            if (DEBUG) {
                Log.e(TAG, "mController shouldn't be null.");
            }
            return null;
        }
        return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
    }

    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
+120 −119
Original line number Diff line number Diff line
@@ -25,8 +25,9 @@ import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;

import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.internal.inputmethod.InputMethodUtils;

import java.util.ArrayList;
import java.util.Arrays;
@@ -39,6 +40,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
    private static final boolean DUMMY_FORCE_DEFAULT = false;
    private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
    private static final String SYSTEM_LOCALE = "en_US";
    private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;

    private static InputMethodSubtype createDummySubtype(final String locale) {
        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
@@ -64,142 +66,141 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
        si.exported = true;
        si.nonLocalizedLabel = imeLabel;
        ri.serviceInfo = si;
        final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
        List<InputMethodSubtype> subtypes = null;
        if (subtypeLocales != null) {
            subtypes = new ArrayList<InputMethodSubtype>();
            for (String subtypeLocale : subtypeLocales) {
                subtypes.add(createDummySubtype(subtypeLocale));
            }
        }
        final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
        if (subtypes == null) {
            items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
                    NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
        } else {
            for (int i = 0; i < subtypes.size(); ++i) {
                final String subtypeLocale = subtypeLocales.get(i);
                items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
                        SYSTEM_LOCALE));
            }
        }
    }

    private static List<ImeSubtypeListItem> createTestData() {
    private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
        addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
                Arrays.asList("en_US", "es_US", "fr"),
        addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
                true /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
        addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
                Arrays.asList("en_UK", "hi"),
                false /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
                Arrays.asList("ja_JP"),
        addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
                false /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
                true /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
                Arrays.asList("ja_JP"),
        addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
                Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
        return items;
    }

    private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
        addDummyImeSubtypeListItems(items,
                "UnknownIme", "UnknownIme",
                Arrays.asList("en_US", "hi"),
                true /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items,
                "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
                Arrays.asList("en_US"),
                false /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
                "UnknownSubtypeUnawareIme", null,
                false /* supportsSwitchingToNextInputMethod*/);
        return items;
    }

    @SmallTest
    public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
        final List<ImeSubtypeListItem> imList = createTestData();

        final boolean ONLY_CURRENT_IME = false;
        ImeSubtypeListItem currentIme;
        ImeSubtypeListItem nextIme;

        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
        currentIme = imList.get(0);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(1), nextIme);
        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
        currentIme = imList.get(1);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(2), nextIme);
        // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP"
        currentIme = imList.get(2);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(5), nextIme);
        // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
        currentIme = imList.get(5);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(0), nextIme);

        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
        currentIme = imList.get(3);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(4), nextIme);
        // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP"
        currentIme = imList.get(4);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(6), nextIme);
        // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK"
        currentIme = imList.get(6);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(3), nextIme);
    private void assertNextInputMethod(final ControllerImpl controller,
            final boolean onlyCurrentIme,
            final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
        InputMethodSubtype subtype = null;
        if (currentItem.mSubtypeName != null) {
            subtype = createDummySubtype(currentItem.mSubtypeName.toString());
        }
        final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
                currentItem.mImi, subtype);
        assertEquals(nextItem, nextIme);
    }

    @SmallTest
    public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
        final List<ImeSubtypeListItem> imList = createTestData();

        final boolean ONLY_CURRENT_IME = true;
        ImeSubtypeListItem currentIme;
        ImeSubtypeListItem nextIme;

        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
        currentIme = imList.get(0);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(1), nextIme);
        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
        currentIme = imList.get(1);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(2), nextIme);
        // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
        currentIme = imList.get(2);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(0), nextIme);

        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
        currentIme = imList.get(3);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(4), nextIme);
        // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
        currentIme = imList.get(4);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(3), nextIme);

        // "switchAwareJapaneseIme/ja_JP" -> null
        currentIme = imList.get(5);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertNull(nextIme);

        // "nonSwitchAwareJapaneseIme/ja_JP" -> null
        currentIme = imList.get(6);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertNull(nextIme);
    public void testControllerImpl() throws Exception {
        final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
        final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
        final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
        final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
        final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);

        final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
        final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
        final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
        final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
        final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
        final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
        final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
        final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);

        final ControllerImpl controller = new ControllerImpl(enabledItems);

        // switching-aware loop
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                latinIme_en_US, latinIme_fr);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                latinIme_fr, japaneseIme_ja_JP);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                japaneseIme_ja_JP, latinIme_en_US);

        // switching-unaware loop
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                switchingUnawarelatinIme_hi, subtypeUnawareIme);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                subtypeUnawareIme, switchUnawareJapaneseIme_ja_JP);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                switchUnawareJapaneseIme_ja_JP, switchingUnawarelatinIme_en_UK);

        // test onlyCurrentIme == true
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                latinIme_en_US, latinIme_fr);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                latinIme_fr, latinIme_en_US);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                switchingUnawarelatinIme_hi, switchingUnawarelatinIme_en_UK);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                subtypeUnawareIme, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                japaneseIme_ja_JP, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                switchUnawareJapaneseIme_ja_JP, null);

        // Make sure that disabled IMEs are not accepted.
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                disabledIme_en_US, null);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                disabledIme_hi, null);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                disabledSwitchingUnawareIme, null);
        assertNextInputMethod(controller, false /* onlyCurrentIme */,
                disabledSubtypeUnawareIme, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                disabledIme_en_US, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                disabledIme_hi, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                disabledSwitchingUnawareIme, null);
        assertNextInputMethod(controller, true /* onlyCurrentIme */,
                disabledSubtypeUnawareIme, null);
    }
 }