diff options
author | Luke Shumaker <shumakl@purdue.edu> | 2014-05-04 19:31:11 -0400 |
---|---|---|
committer | Luke Shumaker <shumakl@purdue.edu> | 2014-05-04 19:31:11 -0400 |
commit | 665330a5eda56339a078944dbfde2efbf84d2a03 (patch) | |
tree | 4a6ed8f4f61ce17b834113209c8f777ac8cae0e5 | |
parent | 1a95ab399441cdf3e67d357df43f1ae1c3616ddd (diff) |
Add gesture editor (taken mostly from the SDK gesture editor)
-rw-r--r-- | AndroidManifest.xml | 17 | ||||
-rw-r--r-- | res/layout/create_gesture.xml | 88 | ||||
-rw-r--r-- | res/layout/dialog_rename.xml | 41 | ||||
-rw-r--r-- | res/layout/gestures_item.xml | 31 | ||||
-rw-r--r-- | res/layout/gestures_list.xml | 75 | ||||
-rw-r--r-- | res/values/colors.xml | 4 | ||||
-rw-r--r-- | res/values/dimens.xml | 5 | ||||
-rw-r--r-- | res/values/strings.xml | 44 | ||||
-rw-r--r-- | res/xml/method.xml | 3 | ||||
-rw-r--r-- | src/us/minak/CreateGestureActivity.java | 128 | ||||
-rw-r--r-- | src/us/minak/SettingsActivity.java | 436 |
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…</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; + } + } +} |