summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuke Shumaker <shumakl@purdue.edu>2014-05-04 22:13:48 -0400
committerLuke Shumaker <shumakl@purdue.edu>2014-05-04 22:13:48 -0400
commit8001e3284e069dded37a1aa42b25c201102b9460 (patch)
tree39d985f62caf6f9d24fa7f9de628668c26009775 /src
parentc4e1aa78bc868d2ee8c8fc01bf7224b98f5524dd (diff)
parent8662dfb7361c7ba9291a5e2bf9ebd55cb41932ba (diff)
Merge branch 'lukeshu'
Conflicts: src/us/minak/OnGestureRecognizedListener.java
Diffstat (limited to 'src')
-rw-r--r--src/us/minak/CreateGestureActivity.java128
-rw-r--r--src/us/minak/DrawingSpaceView.java60
-rw-r--r--src/us/minak/IMEService.java38
-rw-r--r--src/us/minak/IMEView.java34
-rw-r--r--src/us/minak/Minak.java38
-rw-r--r--src/us/minak/MinakView.java37
-rw-r--r--src/us/minak/NavigationDrawerFragment.java282
-rw-r--r--src/us/minak/OnCharacterEnteredListener.java5
-rw-r--r--src/us/minak/OnGestureRecognizedListener.java23
-rw-r--r--src/us/minak/SettingsActivity.java436
-rw-r--r--src/us/minak/SketchpadView.java42
11 files changed, 718 insertions, 405 deletions
diff --git a/src/us/minak/CreateGestureActivity.java b/src/us/minak/CreateGestureActivity.java
new file mode 100644
index 0000000..6844b36
--- /dev/null
+++ b/src/us/minak/CreateGestureActivity.java
@@ -0,0 +1,128 @@
+/*
+ * 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 us.minak;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.view.MotionEvent;
+import android.gesture.GestureOverlayView;
+import android.gesture.Gesture;
+import android.gesture.GestureLibrary;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+
+public class CreateGestureActivity extends Activity {
+ private static final float LENGTH_THRESHOLD = 120.0f;
+
+ private Gesture mGesture;
+ private View mDoneButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.create_gesture);
+
+ mDoneButton = findViewById(R.id.done);
+
+ GestureOverlayView overlay = (GestureOverlayView) findViewById(R.id.gestures_overlay);
+ overlay.addOnGestureListener(new GesturesProcessor());
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ if (mGesture != null) {
+ outState.putParcelable("gesture", mGesture);
+ }
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ mGesture = savedInstanceState.getParcelable("gesture");
+ if (mGesture != null) {
+ final GestureOverlayView overlay =
+ (GestureOverlayView) findViewById(R.id.gestures_overlay);
+ overlay.post(new Runnable() {
+ public void run() {
+ overlay.setGesture(mGesture);
+ }
+ });
+
+ mDoneButton.setEnabled(true);
+ }
+ }
+
+ public void addGesture(View v) {
+ if (mGesture != null) {
+ final TextView input = (TextView) findViewById(R.id.gesture_name);
+ final CharSequence name = input.getText();
+ if (name.length() == 0) {
+ input.setError(getString(R.string.error_missing_name));
+ return;
+ }
+
+ final GestureLibrary store = SettingsActivity.getStore();
+ store.addGesture(name.toString(), mGesture);
+ store.save();
+
+ setResult(RESULT_OK);
+
+ final String path = new File(Environment.getExternalStorageDirectory(),
+ "gestures").getAbsolutePath();
+ Toast.makeText(this, getString(R.string.save_success, path), Toast.LENGTH_LONG).show();
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+
+ }
+
+ public void cancelGesture(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ private class GesturesProcessor implements GestureOverlayView.OnGestureListener {
+ public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
+ mDoneButton.setEnabled(false);
+ mGesture = null;
+ }
+
+ public void onGesture(GestureOverlayView overlay, MotionEvent event) {
+ }
+
+ public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
+ mGesture = overlay.getGesture();
+ if (mGesture.getLength() < LENGTH_THRESHOLD) {
+ overlay.clear(false);
+ }
+ mDoneButton.setEnabled(true);
+ }
+
+ public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
+ }
+ }
+}
diff --git a/src/us/minak/DrawingSpaceView.java b/src/us/minak/DrawingSpaceView.java
new file mode 100644
index 0000000..7f9c3fa
--- /dev/null
+++ b/src/us/minak/DrawingSpaceView.java
@@ -0,0 +1,60 @@
+/*
+ ********************************************************************************
+ * Copyright (c) 2012 Samsung Electronics, Inc.
+ * All rights reserved.
+ *
+ * This software is a confidential and proprietary information of Samsung
+ * Electronics, Inc. ("Confidential Information"). You shall not disclose such
+ * Confidential Information and shall use it only in accordance with the terms
+ * of the license agreement you entered into with Samsung Electronics.
+ ********************************************************************************
+ */
+
+package us.minak;
+
+import java.util.List;
+
+import android.content.Context;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.gesture.GestureLibrary;
+import android.gesture.GestureOverlayView;
+import android.gesture.GestureOverlayView.OnGesturePerformedListener;
+import android.gesture.Prediction;
+import android.util.AttributeSet;
+
+/**
+ * Represent a space where drawing gestures are performed.
+ */
+public class DrawingSpaceView extends GestureOverlayView implements OnGesturePerformedListener {
+ private static final double SCORE_TRESHOLD = 3.0;
+ private final GestureLibrary mGestureLibrary;
+ private OnGestureRecognizedListener mOnGestureRecognizedListener;
+
+ public DrawingSpaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.gestures);
+ mGestureLibrary.load();
+ addOnGesturePerformedListener(this);
+ }
+
+ public void setOnGestureRecognizedListener(OnGestureRecognizedListener onGestureRecognizedListener) {
+ mOnGestureRecognizedListener = onGestureRecognizedListener;
+ }
+
+ @Override
+ public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
+ final List<Prediction> predictions = mGestureLibrary.recognize(gesture);
+ Prediction bestPrediction = null;
+ if (!predictions.isEmpty()) {
+ bestPrediction = predictions.get(0);
+ }
+ if (mOnGestureRecognizedListener != null && bestPrediction != null) {
+ if (bestPrediction.score > SCORE_TRESHOLD) {
+ mOnGestureRecognizedListener.gestureRecognized(bestPrediction.name);
+ } else {
+ clear(false);
+ }
+ }
+ }
+}
diff --git a/src/us/minak/IMEService.java b/src/us/minak/IMEService.java
new file mode 100644
index 0000000..6064be9
--- /dev/null
+++ b/src/us/minak/IMEService.java
@@ -0,0 +1,38 @@
+package us.minak;
+
+import android.inputmethodservice.InputMethodService;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+
+public class IMEService extends InputMethodService {
+ private IMEView imeView;
+ /**
+ * Loads the configuration.
+ */
+ @Override
+ public void onInitializeInterface() {
+ // TODO
+ }
+
+ @Override
+ public View onCreateInputView() {
+ final IMEView view = (IMEView) getLayoutInflater().inflate(R.layout.ime, null);
+
+ view.setOnCharacterEnteredListener(new OnCharacterEnteredListener() {
+ @Override
+ public void characterEntered(String character) {
+ getCurrentInputConnection().commitText(character, 1);
+ }
+ });
+
+ this.imeView = view;
+ return view;
+ }
+
+ /**
+ * Called to inform the input method that text input has started in an editor.
+ */
+ public void onStartInput(EditorInfo info, boolean restarting) {
+ // TODO: get characters from this.imeView, and pass them to getCurrentInputConnection().commitText(..., 1);
+ }
+}
diff --git a/src/us/minak/IMEView.java b/src/us/minak/IMEView.java
new file mode 100644
index 0000000..d341ae5
--- /dev/null
+++ b/src/us/minak/IMEView.java
@@ -0,0 +1,34 @@
+package us.minak;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+public class IMEView extends RelativeLayout{
+ private final Context mContext;
+ private OnCharacterEnteredListener mOnCharacterEnteredListener;
+ public IMEView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ DrawingSpaceView drawingSpaceView = (DrawingSpaceView) findViewById(R.id.drawing_space);
+ drawingSpaceView.setOnGestureRecognizedListener(new OnGestureRecognizedListener() {
+ @Override
+ public void gestureRecognized(String character) {
+ enterCharacter(character);
+ }
+
+ });
+ }
+
+ public void setOnCharacterEnteredListener(OnCharacterEnteredListener onCharacterEnteredListener) {
+ mOnCharacterEnteredListener = onCharacterEnteredListener;
+ }
+
+ private void enterCharacter(String character) {
+ mOnCharacterEnteredListener.characterEntered(character);
+ }
+}
diff --git a/src/us/minak/Minak.java b/src/us/minak/Minak.java
deleted file mode 100644
index 30f7989..0000000
--- a/src/us/minak/Minak.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package us.minak;
-
-import android.inputmethodservice.InputMethodService;
-import java.util.Queue;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-
-
-public class Minak extends InputMethodService {
- private MinakView minakView;
-
- @Override
- public void onStartInput(EditorInfo attribute, boolean restart) {
- if (minview != null) {
- final Queue<Character> symbolsQueue = minview.getSymbolsQueue();
- while (!symbolsQueue.isEmpty()) {
- final Character character = symbolsQueue.poll();
- getCurrentInputConnection().commitText(String.valueOf(character), 1);
- }
- }
- }
-
- @Override
- public View onCreateInputView() {
- final MinakView mv = getLayoutInflater().inflate(R.layout.minak, null);
-
- // TODO set Listeners here
- mv.setOnCharacterEnteredListener(new OnCharacterEnteredListener()) {
- @Override
- public void characterEntered(String character) {
- getCurrentInputConnection().commitText(character, 1);
- }
- }
-
- minakView = mv;
- return mv;
- }
-}
diff --git a/src/us/minak/MinakView.java b/src/us/minak/MinakView.java
deleted file mode 100644
index e6ec2e0..0000000
--- a/src/us/minak/MinakView.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package us.minak;
-
-import android.content.Context;
-import android.widget.RelativeLayout;
-import android.util.AttributeSet;
-import android.support.v4.content.LocalBroadcastManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
-import java.util.*;
-
-public class MinakView extends RelativeLayout {
- private final Context mContext;
- private final Queue<Character> minakSymbolsQueue = new LinkedList<Character>();
-
- public MinakView(Context context, AttributeSet attr) {
- super(context, attr);
- mContext = context;
- LocalBroadcastManager.getInstance(mContext).registerReceiver(minakBroadcastReceiver,
- new IntentFilter(SketchpadActivity.INTENT_ACTION));
- }
-
- public Queue<Character> getSymbolsQueue() {
- return minakSymbolsQueue;
- }
-
- private final BroadcastReceiver minakBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (SketchpadActivity.INTENT_ACTION.equals(intent.getAction())) {
- minakSymbolsQueue.add(intent.getCharExtra(SketchpadActivity.INTENT_EXTRA_NAME, '?'));
- }
- }
- };
-
-} \ No newline at end of file
diff --git a/src/us/minak/NavigationDrawerFragment.java b/src/us/minak/NavigationDrawerFragment.java
deleted file mode 100644
index 6e704c8..0000000
--- a/src/us/minak/NavigationDrawerFragment.java
+++ /dev/null
@@ -1,282 +0,0 @@
-package us.minak;
-
-;
-import android.app.Activity;
-import android.app.ActionBar;
-import android.app.Fragment;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.Toast;
-
-/**
- * Fragment used for managing interactions for and presentation of a navigation drawer.
- * See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction">
- * design guidelines</a> for a complete explanation of the behaviors implemented here.
- */
-public class NavigationDrawerFragment extends Fragment {
-
- /**
- * Remember the position of the selected item.
- */
- private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
-
- /**
- * Per the design guidelines, you should show the drawer on launch until the user manually
- * expands it. This shared preference tracks this.
- */
- private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
-
- /**
- * A pointer to the current callbacks instance (the Activity).
- */
- private NavigationDrawerCallbacks mCallbacks;
-
- /**
- * Helper component that ties the action bar to the navigation drawer.
- */
- private ActionBarDrawerToggle mDrawerToggle;
-
- private DrawerLayout mDrawerLayout;
- private ListView mDrawerListView;
- private View mFragmentContainerView;
-
- private int mCurrentSelectedPosition = 0;
- private boolean mFromSavedInstanceState;
- private boolean mUserLearnedDrawer;
-
- public NavigationDrawerFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Read in the flag indicating whether or not the user has demonstrated awareness of the
- // drawer. See PREF_USER_LEARNED_DRAWER for details.
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
- mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
-
- if (savedInstanceState != null) {
- mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
- mFromSavedInstanceState = true;
- }
-
- // Select either the default item (0) or the last selected item.
- selectItem(mCurrentSelectedPosition);
- }
-
- @Override
- public void onActivityCreated (Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- // Indicate that this fragment would like to influence the set of actions in the action bar.
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mDrawerListView = (ListView) inflater.inflate(
- R.layout.fragment_navigation_drawer, container, false);
- mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- selectItem(position);
- }
- });
- mDrawerListView.setAdapter(new ArrayAdapter<String>(
- getActionBar().getThemedContext(),
- android.R.layout.simple_list_item_activated_1,
- android.R.id.text1,
- new String[]{
- getString(R.string.title_section1),
- getString(R.string.title_section2),
- getString(R.string.title_section3),
- }));
- mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
- return mDrawerListView;
- }
-
- public boolean isDrawerOpen() {
- return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
- }
-
- /**
- * Users of this fragment must call this method to set up the navigation drawer interactions.
- *
- * @param fragmentId The android:id of this fragment in its activity's layout.
- * @param drawerLayout The DrawerLayout containing this fragment's UI.
- */
- public void setUp(int fragmentId, DrawerLayout drawerLayout) {
- mFragmentContainerView = getActivity().findViewById(fragmentId);
- mDrawerLayout = drawerLayout;
-
- // set a custom shadow that overlays the main content when the drawer opens
- mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
- // set up the drawer's list view with items and click listener
-
- ActionBar actionBar = getActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
-
- // ActionBarDrawerToggle ties together the the proper interactions
- // between the navigation drawer and the action bar app icon.
- mDrawerToggle = new ActionBarDrawerToggle(
- getActivity(), /* host Activity */
- mDrawerLayout, /* DrawerLayout object */
- R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
- R.string.navigation_drawer_open, /* "open drawer" description for accessibility */
- R.string.navigation_drawer_close /* "close drawer" description for accessibility */
- ) {
- @Override
- public void onDrawerClosed(View drawerView) {
- super.onDrawerClosed(drawerView);
- if (!isAdded()) {
- return;
- }
-
- getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
- }
-
- @Override
- public void onDrawerOpened(View drawerView) {
- super.onDrawerOpened(drawerView);
- if (!isAdded()) {
- return;
- }
-
- if (!mUserLearnedDrawer) {
- // The user manually opened the drawer; store this flag to prevent auto-showing
- // the navigation drawer automatically in the future.
- mUserLearnedDrawer = true;
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(getActivity());
- sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
- }
-
- getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
- }
- };
-
- // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer,
- // per the navigation drawer design guidelines.
- if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
- mDrawerLayout.openDrawer(mFragmentContainerView);
- }
-
- // Defer code dependent on restoration of previous instance state.
- mDrawerLayout.post(new Runnable() {
- @Override
- public void run() {
- mDrawerToggle.syncState();
- }
- });
-
- mDrawerLayout.setDrawerListener(mDrawerToggle);
- }
-
- private void selectItem(int position) {
- mCurrentSelectedPosition = position;
- if (mDrawerListView != null) {
- mDrawerListView.setItemChecked(position, true);
- }
- if (mDrawerLayout != null) {
- mDrawerLayout.closeDrawer(mFragmentContainerView);
- }
- if (mCallbacks != null) {
- mCallbacks.onNavigationDrawerItemSelected(position);
- }
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mCallbacks = (NavigationDrawerCallbacks) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
- }
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- mCallbacks = null;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- // Forward the new configuration the drawer toggle component.
- mDrawerToggle.onConfigurationChanged(newConfig);
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- // If the drawer is open, show the global app actions in the action bar. See also
- // showGlobalContextActionBar, which controls the top-left area of the action bar.
- if (mDrawerLayout != null && isDrawerOpen()) {
- inflater.inflate(R.menu.global, menu);
- showGlobalContextActionBar();
- }
- super.onCreateOptionsMenu(menu, inflater);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
-
- if (item.getItemId() == R.id.action_example) {
- Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show();
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- /**
- * Per the navigation drawer design guidelines, updates the action bar to show the global app
- * 'context', rather than just what's in the current screen.
- */
- private void showGlobalContextActionBar() {
- ActionBar actionBar = getActionBar();
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- actionBar.setTitle(R.string.app_name);
- }
-
- private ActionBar getActionBar() {
- return getActivity().getActionBar();
- }
-
- /**
- * Callbacks interface that all activities using this fragment must implement.
- */
- public static interface NavigationDrawerCallbacks {
- /**
- * Called when an item in the navigation drawer is selected.
- */
- void onNavigationDrawerItemSelected(int position);
- }
-}
diff --git a/src/us/minak/OnCharacterEnteredListener.java b/src/us/minak/OnCharacterEnteredListener.java
deleted file mode 100644
index a6c7f56..0000000
--- a/src/us/minak/OnCharacterEnteredListener.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package us.minak;
-
-public interface OnCharacterEnteredListener {
- void CharacterEntered(String character);
-} \ No newline at end of file
diff --git a/src/us/minak/OnGestureRecognizedListener.java b/src/us/minak/OnGestureRecognizedListener.java
index 5c3638f..a30e8b8 100644
--- a/src/us/minak/OnGestureRecognizedListener.java
+++ b/src/us/minak/OnGestureRecognizedListener.java
@@ -1,5 +1,26 @@
+/*
+ ********************************************************************************
+ * Copyright (c) 2012 Samsung Electronics, Inc.
+ * All rights reserved.
+ *
+ * This software is a confidential and proprietary information of Samsung
+ * Electronics, Inc. ("Confidential Information"). You shall not disclose such
+ * Confidential Information and shall use it only in accordance with the terms
+ * of the license agreement you entered into with Samsung Electronics.
+ ********************************************************************************
+ */
+
package us.minak;
+/**
+ * A simple interface for handling recognizing a gesture.
+ */
public interface OnGestureRecognizedListener {
+ /**
+ * Invoked when a gesture is recognized.
+ *
+ * @param character
+ * The character represented by the gesture.
+ */
void gestureRecognized(String character);
-} \ No newline at end of file
+}
diff --git a/src/us/minak/SettingsActivity.java b/src/us/minak/SettingsActivity.java
new file mode 100644
index 0000000..b366826
--- /dev/null
+++ b/src/us/minak/SettingsActivity.java
@@ -0,0 +1,436 @@
+/*
+ * 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 us.minak;
+
+import android.app.Dialog;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.view.View;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.gesture.GestureLibrary;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.AdapterView;
+import android.widget.Toast;
+import android.widget.ArrayAdapter;
+import android.content.DialogInterface;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
+
+import java.util.Map;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Comparator;
+import java.util.Set;
+import java.io.File;
+
+public class SettingsActivity extends ListActivity {
+ private static final int STATUS_SUCCESS = 0;
+ private static final int STATUS_CANCELLED = 1;
+ private static final int STATUS_NO_STORAGE = 2;
+ private static final int STATUS_NOT_LOADED = 3;
+
+ private static final int MENU_ID_RENAME = 1;
+ private static final int MENU_ID_REMOVE = 2;
+
+ private static final int DIALOG_RENAME_GESTURE = 1;
+
+ private static final int REQUEST_NEW_GESTURE = 1;
+
+ // Type: long (id)
+ private static final String GESTURES_INFO_ID = "gestures.info_id";
+
+ private final File mStoreFile = new File(Environment.getExternalStorageDirectory(), "gestures");
+
+ private final Comparator<NamedGesture> mSorter = new Comparator<NamedGesture>() {
+ public int compare(NamedGesture object1, NamedGesture object2) {
+ return object1.name.compareTo(object2.name);
+ }
+ };
+
+ private static GestureLibrary sStore;
+
+ private GesturesAdapter mAdapter;
+ private GesturesLoadTask mTask;
+ private TextView mEmpty;
+
+ private Dialog mRenameDialog;
+ private EditText mInput;
+ private NamedGesture mCurrentRenameGesture;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.gestures_list);
+
+ mAdapter = new GesturesAdapter(this);
+ setListAdapter(mAdapter);
+
+ if (sStore == null) {
+ sStore = GestureLibraries.fromFile(mStoreFile);
+ }
+ mEmpty = (TextView) findViewById(android.R.id.empty);
+ loadGestures();
+
+ registerForContextMenu(getListView());
+ }
+
+ static GestureLibrary getStore() {
+ return sStore;
+ }
+
+ public void reloadGestures(View v) {
+ loadGestures();
+ }
+
+ public void addGesture(View v) {
+ Intent intent = new Intent(this, CreateGestureActivity.class);
+ startActivityForResult(intent, REQUEST_NEW_GESTURE);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode == RESULT_OK) {
+ switch (requestCode) {
+ case REQUEST_NEW_GESTURE:
+ loadGestures();
+ break;
+ }
+ }
+ }
+
+ private void loadGestures() {
+ if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
+ mTask.cancel(true);
+ }
+ mTask = (GesturesLoadTask) new GesturesLoadTask().execute();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (mTask != null && mTask.getStatus() != GesturesLoadTask.Status.FINISHED) {
+ mTask.cancel(true);
+ mTask = null;
+ }
+
+ cleanupRenameDialog();
+ }
+
+ private void checkForEmpty() {
+ if (mAdapter.getCount() == 0) {
+ mEmpty.setText(R.string.gestures_empty);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ if (mCurrentRenameGesture != null) {
+ outState.putLong(GESTURES_INFO_ID, mCurrentRenameGesture.gesture.getID());
+ }
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+
+ long id = state.getLong(GESTURES_INFO_ID, -1);
+ if (id != -1) {
+ final Set<String> entries = sStore.getGestureEntries();
+out: for (String name : entries) {
+ for (Gesture gesture : sStore.getGestures(name)) {
+ if (gesture.getID() == id) {
+ mCurrentRenameGesture = new NamedGesture();
+ mCurrentRenameGesture.name = name;
+ mCurrentRenameGesture.gesture = gesture;
+ break out;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ menu.setHeaderTitle(((TextView) info.targetView).getText());
+
+ menu.add(0, MENU_ID_RENAME, 0, R.string.gestures_rename);
+ menu.add(0, MENU_ID_REMOVE, 0, R.string.gestures_delete);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ final AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)
+ item.getMenuInfo();
+ final NamedGesture gesture = (NamedGesture) menuInfo.targetView.getTag();
+
+ switch (item.getItemId()) {
+ case MENU_ID_RENAME:
+ renameGesture(gesture);
+ return true;
+ case MENU_ID_REMOVE:
+ deleteGesture(gesture);
+ return true;
+ }
+
+ return super.onContextItemSelected(item);
+ }
+
+ private void renameGesture(NamedGesture gesture) {
+ mCurrentRenameGesture = gesture;
+ showDialog(DIALOG_RENAME_GESTURE);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id == DIALOG_RENAME_GESTURE) {
+ return createRenameDialog();
+ }
+ return super.onCreateDialog(id);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog) {
+ super.onPrepareDialog(id, dialog);
+ if (id == DIALOG_RENAME_GESTURE) {
+ mInput.setText(mCurrentRenameGesture.name);
+ }
+ }
+
+ private Dialog createRenameDialog() {
+ final View layout = View.inflate(this, R.layout.dialog_rename, null);
+ mInput = (EditText) layout.findViewById(R.id.name);
+ ((TextView) layout.findViewById(R.id.label)).setText(R.string.gestures_rename_label);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(0);
+ builder.setTitle(getString(R.string.gestures_rename_title));
+ builder.setCancelable(true);
+ builder.setOnCancelListener(new Dialog.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ cleanupRenameDialog();
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel_action),
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ cleanupRenameDialog();
+ }
+ }
+ );
+ builder.setPositiveButton(getString(R.string.rename_action),
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ changeGestureName();
+ }
+ }
+ );
+ builder.setView(layout);
+ return builder.create();
+ }
+
+ private void changeGestureName() {
+ final String name = mInput.getText().toString();
+ if (!TextUtils.isEmpty(name)) {
+ final NamedGesture renameGesture = mCurrentRenameGesture;
+ final GesturesAdapter adapter = mAdapter;
+ final int count = adapter.getCount();
+
+ // Simple linear search, there should not be enough items to warrant
+ // a more sophisticated search
+ for (int i = 0; i < count; i++) {
+ final NamedGesture gesture = adapter.getItem(i);
+ if (gesture.gesture.getID() == renameGesture.gesture.getID()) {
+ sStore.removeGesture(gesture.name, gesture.gesture);
+ gesture.name = mInput.getText().toString();
+ sStore.addGesture(gesture.name, gesture.gesture);
+ break;
+ }
+ }
+
+ adapter.notifyDataSetChanged();
+ }
+ mCurrentRenameGesture = null;
+ }
+
+ private void cleanupRenameDialog() {
+ if (mRenameDialog != null) {
+ mRenameDialog.dismiss();
+ mRenameDialog = null;
+ }
+ mCurrentRenameGesture = null;
+ }
+
+ private void deleteGesture(NamedGesture gesture) {
+ sStore.removeGesture(gesture.name, gesture.gesture);
+ sStore.save();
+
+ final GesturesAdapter adapter = mAdapter;
+ adapter.setNotifyOnChange(false);
+ adapter.remove(gesture);
+ adapter.sort(mSorter);
+ checkForEmpty();
+ adapter.notifyDataSetChanged();
+
+ Toast.makeText(this, R.string.gestures_delete_success, Toast.LENGTH_SHORT).show();
+ }
+
+ private class GesturesLoadTask extends AsyncTask<Void, NamedGesture, Integer> {
+ private int mThumbnailSize;
+ private int mThumbnailInset;
+ private int mPathColor;
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+
+ final Resources resources = getResources();
+ mPathColor = resources.getColor(R.color.gesture_color);
+ mThumbnailInset = (int) resources.getDimension(R.dimen.gesture_thumbnail_inset);
+ mThumbnailSize = (int) resources.getDimension(R.dimen.gesture_thumbnail_size);
+
+ findViewById(R.id.addButton).setEnabled(false);
+ findViewById(R.id.reloadButton).setEnabled(false);
+
+ mAdapter.setNotifyOnChange(false);
+ mAdapter.clear();
+ }
+
+ @Override
+ protected Integer doInBackground(Void... params) {
+ if (isCancelled()) return STATUS_CANCELLED;
+ if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ return STATUS_NO_STORAGE;
+ }
+
+ final GestureLibrary store = sStore;
+
+ if (store.load()) {
+ for (String name : store.getGestureEntries()) {
+ if (isCancelled()) break;
+
+ for (Gesture gesture : store.getGestures(name)) {
+ final Bitmap bitmap = gesture.toBitmap(mThumbnailSize, mThumbnailSize,
+ mThumbnailInset, mPathColor);
+ final NamedGesture namedGesture = new NamedGesture();
+ namedGesture.gesture = gesture;
+ namedGesture.name = name;
+
+ mAdapter.addBitmap(namedGesture.gesture.getID(), bitmap);
+ publishProgress(namedGesture);
+ }
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_NOT_LOADED;
+ }
+
+ @Override
+ protected void onProgressUpdate(NamedGesture... values) {
+ super.onProgressUpdate(values);
+
+ final GesturesAdapter adapter = mAdapter;
+ adapter.setNotifyOnChange(false);
+
+ for (NamedGesture gesture : values) {
+ adapter.add(gesture);
+ }
+
+ adapter.sort(mSorter);
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ super.onPostExecute(result);
+
+ if (result == STATUS_NO_STORAGE) {
+ getListView().setVisibility(View.GONE);
+ mEmpty.setVisibility(View.VISIBLE);
+ mEmpty.setText(getString(R.string.gestures_error_loading,
+ mStoreFile.getAbsolutePath()));
+ } else {
+ findViewById(R.id.addButton).setEnabled(true);
+ findViewById(R.id.reloadButton).setEnabled(true);
+ checkForEmpty();
+ }
+ }
+ }
+
+ static class NamedGesture {
+ String name;
+ Gesture gesture;
+ }
+
+ private class GesturesAdapter extends ArrayAdapter<NamedGesture> {
+ private final LayoutInflater mInflater;
+ private final Map<Long, Drawable> mThumbnails = Collections.synchronizedMap(
+ new HashMap<Long, Drawable>());
+
+ public GesturesAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ void addBitmap(Long id, Bitmap bitmap) {
+ mThumbnails.put(id, new BitmapDrawable(bitmap));
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.gestures_item, parent, false);
+ }
+
+ final NamedGesture gesture = getItem(position);
+ final TextView label = (TextView) convertView;
+
+ label.setTag(gesture);
+ label.setText(gesture.name);
+ label.setCompoundDrawablesWithIntrinsicBounds(mThumbnails.get(gesture.gesture.getID()),
+ null, null, null);
+
+ return convertView;
+ }
+ }
+}
diff --git a/src/us/minak/SketchpadView.java b/src/us/minak/SketchpadView.java
deleted file mode 100644
index bbfd471..0000000
--- a/src/us/minak/SketchpadView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-import java.util.List;
-
-import android.gesture.Gesture;
-import android.gesture.GestureLibraries;
-import android.gesture.GestureLibrary;
-import android.gesture.GestureOverlayView;
-import android.gesture.GestureOverlayView.OnGesturePerformedListener;
-import android.gesture.Prediction;
-import android.util.AttributeSet;
-
-public class SketchpadView extends GestureOverlayView implements OnGesturePerformedListener{
- private static final double score_threshold = 3.0;
- private final GestureLibrary gestureLib;
- private onGestureRecognizedListener gestureRecognizer;
-
- public SketchpadView (Context context, AttributeSet attrs){
- super(context, attrs);
- mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.gestures);
- mGestureLibrary.load();
- addOnGesturePerformedListener(this);
- }
-
- public void setOnGestureRecognizedListener(OnGestureRecognizedListener onGestureRecognizedListener) {
- gestureRecognizer = onGestureRecognizedListener;
- }
-
- @Override
- public void onGesturePerformed(GestureOverlayView view, Gesture gesture){
- List<Prediction> predictions = gestureLib.recognize(gesture);
- Prediction bestPrediction = null;
- if(!predictions.isEmpty()){
- bestPrediction = predictions.get(0);
- }
- if(gestureRecognizer != null && bestPrediction != null){
- if(bestPrediction.score > score_threshold){
- gestureRecognizer.gestureRecognized(bestPrediction.name);
- }else{
- clear(false);
- }
- }
- }
-}