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

Commit f642aa2a authored by Romain Guy's avatar Romain Guy
Browse files

Removes gestures from ListView.

parent be19df0a
Loading
Loading
Loading
Loading
+0 −279
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 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 android.gesture;

import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

import static android.gesture.GestureConstants.LOG_TAG;

public class LetterRecognizer {
    static final String GESTURE_FILE_NAME = "letters.gestures";

    private final static int ADJUST_RANGE = 3;

    private SigmoidUnit[] mHiddenLayer;
    private SigmoidUnit[] mOutputLayer;

    private final String[] mClasses;

    private final int mPatchSize;

    private GestureLibrary mGestureStore;

    private final Comparator<Prediction> mComparator = new PredictionComparator();

    private static class SigmoidUnit {
        final float[] mWeights;

        SigmoidUnit(float[] weights) {
            mWeights = weights;
        }

        private float compute(float[] inputs) {
            float sum = 0;

            final int count = inputs.length;
            final float[] weights = mWeights;

            for (int i = 0; i < count; i++) {
                sum += inputs[i] * weights[i];
            }
            sum += weights[weights.length - 1];

            return 1.0f / (float) (1 + Math.exp(-sum));
        }
    }

    private LetterRecognizer(int numOfInput, int numOfHidden, String[] classes) {
        mPatchSize = (int) Math.sqrt(numOfInput);
        mHiddenLayer = new SigmoidUnit[numOfHidden];
        mClasses = classes;
        mOutputLayer = new SigmoidUnit[classes.length];
    }

    public ArrayList<Prediction> recognize(Gesture gesture) {
        return recognize(gesture, null);
    }

    public ArrayList<Prediction> recognize(Gesture gesture, ArrayList<Prediction> predictions) {
        float[] query = GestureUtilities.spatialSampling(gesture, mPatchSize);
        predictions = classify(query, predictions);
        adjustPrediction(gesture, predictions);
        return predictions;
    }

    private ArrayList<Prediction> classify(float[] vector, ArrayList<Prediction> predictions) {
        if (predictions == null) {
            predictions = new ArrayList<Prediction>();
        } else {
            predictions.clear();
        }

        final float[] intermediateOutput = compute(mHiddenLayer, vector);
        final float[] output = compute(mOutputLayer, intermediateOutput);

        double sum = 0;

        final String[] classes = mClasses;
        final int count = classes.length;

        for (int i = 0; i < count; i++) {
            double score = output[i];
            sum += score;
            predictions.add(new Prediction(classes[i], score));
        }

        for (int i = 0; i < count; i++) {
            predictions.get(i).score /= sum;
        }

        Collections.sort(predictions, mComparator);

        return predictions;
    }

    private float[] compute(SigmoidUnit[] layer, float[] input) {
        final float[] output = new float[layer.length];
        final int count = layer.length;

        for (int i = 0; i < count; i++) {
            output[i] = layer[i].compute(input);
        }

        return output;
    }

    static LetterRecognizer createFromResource(Context context, int resourceID) {
        final Resources resources = context.getResources();
        final InputStream stream = resources.openRawResource(resourceID);
        return createFromStream(stream);
    }

    static LetterRecognizer createFromStream(InputStream stream) {
        DataInputStream in = null;
        LetterRecognizer classifier = null;

        try {
            in = new DataInputStream(new BufferedInputStream(stream,
                    GestureConstants.IO_BUFFER_SIZE));

            final int version = in.readShort();

            switch (version) {
                case 1:
                    classifier = readV1(in);
                    break;
                default:
                    Log.d(LOG_TAG, "Couldn't load handwriting data: version " + version +
                            " not supported");
                    break;
            }

        } catch (IOException e) {
            Log.d(LOG_TAG, "Failed to load handwriting data:", e);
        } finally {
            GestureUtilities.closeStream(in);
        }

        return classifier;
    }

    private static LetterRecognizer readV1(DataInputStream in) throws IOException {

        final int iCount = in.readInt();
        final int hCount = in.readInt();
        final int oCount = in.readInt();

        final String[] classes = new String[oCount];
        for (int i = 0; i < classes.length; i++) {
            classes[i] = in.readUTF();
        }

        final LetterRecognizer classifier = new LetterRecognizer(iCount, hCount, classes);
        final SigmoidUnit[] hiddenLayer = new SigmoidUnit[hCount];
        final SigmoidUnit[] outputLayer = new SigmoidUnit[oCount];

        for (int i = 0; i < hCount; i++) {
            final float[] weights = new float[iCount + 1];
            for (int j = 0; j <= iCount; j++) {
                weights[j] = in.readFloat();
            }
            hiddenLayer[i] = new SigmoidUnit(weights);
        }

        for (int i = 0; i < oCount; i++) {
            final float[] weights = new float[hCount + 1];
            for (int j = 0; j <= hCount; j++) {
                weights[j] = in.readFloat();
            }
            outputLayer[i] = new SigmoidUnit(weights);
        }

        classifier.mHiddenLayer = hiddenLayer;
        classifier.mOutputLayer = outputLayer;

        return classifier;
    }

    /**
     * TODO: Publish this API once we figure out where we should save the personalized
     * gestures, and how to do so across all apps
     *
     * @hide
     */
    public boolean save() {
        if (mGestureStore != null) {
            return mGestureStore.save();
        }
        return false;
    }

    /**
     * TODO: Publish this API once we figure out where we should save the personalized
     * gestures, and how to do so across all apps
     *
     * @hide
     */
    public void setPersonalizationEnabled(boolean enabled) {
        if (enabled) {
            mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME);
            mGestureStore.setSequenceType(GestureStore.SEQUENCE_INVARIANT);
            mGestureStore.load();
        } else {
            mGestureStore = null;
        }
    }

    /**
     * TODO: Publish this API once we figure out where we should save the personalized
     * gestures, and how to do so across all apps
     *
     * @hide
     */
    public void addExample(String letter, Gesture example) {
        if (mGestureStore != null) {
            mGestureStore.addGesture(letter, example);
        }
    }

    private void adjustPrediction(Gesture query, ArrayList<Prediction> predictions) {
        if (mGestureStore != null) {
            final ArrayList<Prediction> results = mGestureStore.recognize(query);
            final HashMap<String, Prediction> topNList = new HashMap<String, Prediction>();

            for (int j = 0; j < ADJUST_RANGE; j++) {
                Prediction prediction = predictions.remove(0);
                topNList.put(prediction.name, prediction);
            }

            final int count = results.size();
            for (int j = count - 1; j >= 0 && !topNList.isEmpty(); j--) {
                final Prediction item = results.get(j);
                final Prediction original = topNList.get(item.name);
                if (original != null) {
                    predictions.add(0, original);
                    topNList.remove(item.name);
                }
            }
        }
    }

    private static class PredictionComparator implements Comparator<Prediction> {
        public int compare(Prediction object1, Prediction object2) {
            double score1 = object1.score;
            double score2 = object2.score;
            if (score1 > score2) {
                return -1;
            } else if (score1 < score2) {
                return 1;
            } else {
                return 0;
            }
        }
    }
}
+0 −65
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 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 android.gesture;

import android.content.Context;
import android.util.Log;
import static android.gesture.GestureConstants.LOG_TAG;
import static android.gesture.LetterRecognizer.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public final class LetterRecognizers {
    public final static int RECOGNIZER_LATIN_LOWERCASE = 0;

    private LetterRecognizers() {
    }

    public static LetterRecognizer fromType(Context context, int type) {
        switch (type) {
            case RECOGNIZER_LATIN_LOWERCASE: {
                return createFromResource(context, com.android.internal.R.raw.latin_lowercase);
            }
        }
        return null;
    }

    public static LetterRecognizer fromResource(Context context, int resourceId) {
        return createFromResource(context, resourceId);
    }

    public static LetterRecognizer fromFile(String path) {
        return fromFile(new File(path));
    }

    public static LetterRecognizer fromFile(File file) {
        try {
            return createFromStream(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            Log.d(LOG_TAG, "Failed to load handwriting data from file " + file, e);
        }
        return null;
    }

    public static LetterRecognizer fromStream(InputStream stream) {
        return createFromStream(stream);
    }
}
+0 −303

File changed.

Preview size limit exceeded, changes collapsed.

core/res/res/raw/latin_lowercase

deleted100644 → 0
−27.8 KiB

File deleted.

+0 −10
Original line number Diff line number Diff line
@@ -1381,16 +1381,6 @@
             will use only the number of items in the adapter and the number of items visible
             on screen to determine the scrollbar's properties. -->
        <attr name="smoothScrollbar" format="boolean" />
        <!-- Defines the type of gesture to enable for the widget. -->
        <attr name="gestures">
            <!-- No gesture -->
            <enum name="none" value="0" />
            <!-- Gestures jump to a specific position in the content. This requires
                  fast scroll to be enabled. -->
            <enum name="jump" value="1" />
            <!-- Gestures filter the content. This requires text filtering to be enabled. -->
            <enum name="filter" value="2" />
        </attr>
    </declare-styleable>
    <declare-styleable name="AbsSpinner">
        <!-- Reference to an array resource that will populate the Spinner.  For static content,
Loading