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

Commit acdabb25 authored by Yuichiro Hanada's avatar Yuichiro Hanada
Browse files

Add AsyncResultHolder.

Change-Id: Icfa685bcda2f5c74f5649f09098d00b4bd321c5a
parent 1d4a07f6
Loading
Loading
Loading
Loading
+7 −19
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import com.android.inputmethod.latin.settings.SettingsActivity;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -107,8 +108,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Locale;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Input method implementation for Qwerty'ish keyboard.
@@ -2428,31 +2427,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
            return;
        }

        final CountDownLatch latch = new CountDownLatch(1);
        final SuggestedWords[] suggestedWordsArray = new SuggestedWords[1];
        final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>();
        getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING,
                new OnGetSuggestedWordsCallback() {
                    @Override
                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
                        suggestedWordsArray[0] = suggestedWords;
                        latch.countDown();
                        holder.set(suggestedWords);
                    }
                }
        );

        // TODO: Quit blocking the main thread.
        try {
            // Wait for the result of getSuggestedWords
            // We set the time out to avoid ANR.
            latch.await(GET_SUGGESTED_WORDS_TIMEOUT, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            // TODO: Cancel all pending "getSuggestedWords" tasks when it failed. We may want to add
            // "onGetSuggestionFailed" to "OnGetSuggestedWordsCallback".
            Log.e(TAG, "InterruptedException while waiting for getSuggestedWords.", e);
            return;
        }
        if (suggestedWordsArray[0] != null) {
            showSuggestionStrip(suggestedWordsArray[0]);
        // This line may cause the current thread to wait.
        final SuggestedWords suggestedWords = holder.get(null, GET_SUGGESTED_WORDS_TIMEOUT);
        if (suggestedWords != null) {
            showSuggestionStrip(suggestedWords);
        }
    }

+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.inputmethod.latin.utils;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * This class is a holder of a result of asynchronous computation.
 *
 * @param <E> the type of the result.
 */
public class AsyncResultHolder<E> {

    private final Object mLock = new Object();

    private E mResult;
    private final CountDownLatch mLatch;

    public AsyncResultHolder() {
        mLatch = new CountDownLatch(1);
    }

    /**
     * Sets the result value to this holder.
     *
     * @param result the value which is set.
     */
    public void set(final E result) {
        synchronized(mLock) {
            if (mLatch.getCount() > 0) {
                mResult = result;
                mLatch.countDown();
            }
        }
    }

    /**
     * Gets the result value held in this holder.
     * Causes the current thread to wait unless the value is set or the specified time is elapsed.
     *
     * @param defaultValue the default value.
     * @param timeOut the time to wait.
     * @return if the result is set until the time limit then the result, otherwise defaultValue.
     */
    public E get(final E defaultValue, final long timeOut) {
        try {
            if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) {
                return mResult;
            } else {
                return defaultValue;
            }
        } catch (InterruptedException e) {
            return defaultValue;
        }
    }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.inputmethod.latin.utils;

import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;

@MediumTest
public class AsyncResultHolderTests extends AndroidTestCase {
    private static final String TAG = AsyncResultHolderTests.class.getSimpleName();

    private static final int TIMEOUT_IN_MILLISECONDS = 500;
    private static final int MARGIN_IN_MILLISECONDS = 250;
    private static final int DEFAULT_VALUE = 2;
    private static final int SET_VALUE = 1;

    private <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value,
            final long time) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    Log.d(TAG, "Exception while sleeping", e);
                }
                holder.set(value);
            }
        }).start();
    }

    public void testGetWithoutSet() {
        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
        assertEquals(DEFAULT_VALUE, resultValue);
    }

    public void testGetBeforeSet() {
        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
        setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS);
        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
        assertEquals(DEFAULT_VALUE, resultValue);
    }

    public void testGetAfterSet() {
        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
        holder.set(SET_VALUE);
        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
        assertEquals(SET_VALUE, resultValue);
    }

    public void testGetBeforeTimeout() {
        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
        setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS);
        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
        assertEquals(SET_VALUE, resultValue);
    }
}