summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <shumakl@purdue.edu>2014-05-04 19:31:11 -0400
committerLuke Shumaker <shumakl@purdue.edu>2014-05-04 19:31:11 -0400
commit665330a5eda56339a078944dbfde2efbf84d2a03 (patch)
tree4a6ed8f4f61ce17b834113209c8f777ac8cae0e5
parent1a95ab399441cdf3e67d357df43f1ae1c3616ddd (diff)
Add gesture editor (taken mostly from the SDK gesture editor)
-rw-r--r--AndroidManifest.xml17
-rw-r--r--res/layout/create_gesture.xml88
-rw-r--r--res/layout/dialog_rename.xml41
-rw-r--r--res/layout/gestures_item.xml31
-rw-r--r--res/layout/gestures_list.xml75
-rw-r--r--res/values/colors.xml4
-rw-r--r--res/values/dimens.xml5
-rw-r--r--res/values/strings.xml44
-rw-r--r--res/xml/method.xml3
-rw-r--r--src/us/minak/CreateGestureActivity.java128
-rw-r--r--src/us/minak/SettingsActivity.java436
11 files changed, 864 insertions, 8 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b061ef0..07457fd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -12,9 +12,9 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
-
+
<!-- The main input service -->
- <service android:name="us.minak.IMEService"
+ <service android:name="IMEService"
android:label="@string/minak_ime_label"
android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
@@ -22,16 +22,19 @@
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
-
+
<!-- The settings menu activity -->
- <!--
- <activity android:name="us.minak.SettingsActivity"
- android:label="@string/minak_settings_label">
+ <activity android:name=".SettingsActivity" android:label="@string/minak_settings_label">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
- -->
+
+ <!-- The individual gesture editor -->
+ <activity
+ android:name="CreateGestureActivity"
+ android:label="@string/label_create_gesture" />
+
</application>
</manifest>
diff --git a/res/layout/create_gesture.xml b/res/layout/create_gesture.xml
new file mode 100644
index 0000000..89e64c6
--- /dev/null
+++ b/res/layout/create_gesture.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+
+ android:text="@string/prompt_gesture_name"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/gesture_name"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+
+ android:maxLength="40"
+ android:singleLine="true" />
+
+ </LinearLayout>
+
+ <android.gesture.GestureOverlayView
+ android:id="@+id/gestures_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+
+ android:gestureStrokeType="multiple" />
+
+ <LinearLayout
+ style="@android:style/ButtonBar"
+
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/done"
+
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+
+ android:enabled="false"
+
+ android:onClick="addGesture"
+ android:text="@string/button_done" />
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+
+ android:onClick="cancelGesture"
+ android:text="@string/button_discard" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/dialog_rename.xml b/res/layout/dialog_rename.xml
new file mode 100644
index 0000000..49df806
--- /dev/null
+++ b/res/layout/dialog_rename.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="20dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/gestures_rename_label"
+ android:gravity="left"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/name"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:scrollHorizontally="true"
+ android:autoText="false"
+ android:capitalize="none"
+ android:gravity="fill_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/res/layout/gestures_item.xml b/res/layout/gestures_item.xml
new file mode 100644
index 0000000..36c93b8
--- /dev/null
+++ b/res/layout/gestures_item.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+
+ android:drawablePadding="12dip"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
diff --git a/res/layout/gestures_list.xml b/res/layout/gestures_list.xml
new file mode 100644
index 0000000..d0b5e3d
--- /dev/null
+++ b/res/layout/gestures_list.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@android:id/list"
+
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0" />
+
+ <TextView
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+
+ android:gravity="center"
+
+ android:text="@string/gestures_loading"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <LinearLayout
+ style="@android:style/ButtonBar"
+
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/addButton"
+ android:onClick="addGesture"
+
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+
+ android:enabled="false"
+ android:text="@string/button_add" />
+
+ <Button
+ android:id="@+id/reloadButton"
+ android:onClick="reloadGestures"
+
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+
+ android:enabled="false"
+ android:text="@string/button_reload" />
+
+ </LinearLayout>
+
+</LinearLayout>
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..c180987
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="gesture_color">#FFFFFF00</color>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..1a6cbca
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="gesture_thumbnail_inset">8dip</dimen>
+ <dimen name="gesture_thumbnail_size">64dip</dimen>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2485e35..e6474ea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4,4 +4,48 @@
<string name="minak_ime_label">Minak IME Label</string>
<string name="minak_settings_label">Minak Settings Label</string>
+ <string name="application_name">Gestures Builder</string>
+ <!-- Title, name of the activity used to create a gesture -->
+ <string name="label_create_gesture">Create a gesture</string>
+
+ <!-- Buttons -->
+ <!-- Label, button used to add a gesture -->
+ <string name="button_add">Add gesture</string>
+ <!-- Label, button used to reload all gestures -->
+ <string name="button_reload">Reload</string>
+ <!-- Label, button used to cancel the operation of adding a gesture -->
+ <string name="button_discard">Discard</string>
+ <!-- Label, button used to save a gesture newly created -->
+ <string name="button_done">Done</string>
+
+ <!-- Gestures -->
+ <!-- Label, prompt asking the user to enter the name of the gesture -->
+ <string name="prompt_gesture_name">Name</string>
+ <!-- Error message, informs the user he needs to enter a name before saving a gesture -->
+ <string name="error_missing_name">You must enter a name</string>
+ <!-- success message, tells the user where the gesture was saved -->
+ <string name="save_success">Gesture saved in %s</string>
+ <!-- Buttons in Rename gesture dialog box -->
+ <string name="rename_action">OK</string>
+ <!-- Buttons in Rename gesture dialog box -->
+ <string name="cancel_action">Cancel</string>
+ <!-- Message displayed when the user opens the gestures settings screen -->
+ <string name="gestures_loading">Loading gestures&#8230;</string>
+ <!-- Message displayed when the user has no gestures -->
+ <string name="gestures_empty">No gestures</string>
+ <!-- Title of the screen used to view/manage gestures -->
+ <string name="gestures_activity">Gestures</string>
+ <!-- Noun, menu item used to rename a gesture -->
+ <string name="gestures_rename">Rename</string>
+ <!-- Noun, menu item used to remove a gesture -->
+ <string name="gestures_delete">Delete</string>
+ <!-- Message displayed when a gesture is successfully deleted -->
+ <string name="gestures_delete_success">Gesture deleted</string>
+ <!-- Title of dialog box -->
+ <string name="gestures_rename_title">Rename gesture</string>
+ <!-- Label of gesture name field in Rename gesture dialog box -->
+ <string name="gestures_rename_label">Gesture name</string>
+ <!-- Message, displayed when the sdcard cannot be found, 1st parameter is the name of the file that stores the gestures. CHAR LIMIT=80 -->
+ <string name="gestures_error_loading">Could not load %s. Make sure you have storage available.</string>
+
</resources>
diff --git a/res/xml/method.xml b/res/xml/method.xml
index b8d98ff..adf59b5 100644
--- a/res/xml/method.xml
+++ b/res/xml/method.xml
@@ -1,2 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
-<input-method xmlns:android="http://schemas.android.com/apk/res/android" /> \ No newline at end of file
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="us.minak.SettingsActivity" />
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/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;
+ }
+ }
+}