diff options
author | Luke Shumaker <shumakl@purdue.edu> | 2014-05-04 22:13:48 -0400 |
---|---|---|
committer | Luke Shumaker <shumakl@purdue.edu> | 2014-05-04 22:13:48 -0400 |
commit | 8001e3284e069dded37a1aa42b25c201102b9460 (patch) | |
tree | 39d985f62caf6f9d24fa7f9de628668c26009775 /src | |
parent | c4e1aa78bc868d2ee8c8fc01bf7224b98f5524dd (diff) | |
parent | 8662dfb7361c7ba9291a5e2bf9ebd55cb41932ba (diff) |
Merge branch 'lukeshu'
Conflicts:
src/us/minak/OnGestureRecognizedListener.java
Diffstat (limited to 'src')
-rw-r--r-- | src/us/minak/CreateGestureActivity.java | 128 | ||||
-rw-r--r-- | src/us/minak/DrawingSpaceView.java | 60 | ||||
-rw-r--r-- | src/us/minak/IMEService.java | 38 | ||||
-rw-r--r-- | src/us/minak/IMEView.java | 34 | ||||
-rw-r--r-- | src/us/minak/Minak.java | 38 | ||||
-rw-r--r-- | src/us/minak/MinakView.java | 37 | ||||
-rw-r--r-- | src/us/minak/NavigationDrawerFragment.java | 282 | ||||
-rw-r--r-- | src/us/minak/OnCharacterEnteredListener.java | 5 | ||||
-rw-r--r-- | src/us/minak/OnGestureRecognizedListener.java | 23 | ||||
-rw-r--r-- | src/us/minak/SettingsActivity.java | 436 | ||||
-rw-r--r-- | src/us/minak/SketchpadView.java | 42 |
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); - } - } - } -} |