summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/us/minak/CreateGestureActivity.java128
-rw-r--r--src/us/minak/SettingsActivity.java436
2 files changed, 564 insertions, 0 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/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;
+ }
+ }
+}