blob: f916842521455ee6d6c4128aac001f2d9924f400 [file] [log] [blame]
/*
* 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 com.android.gesture.builder;
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 GestureBuilderActivity 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;
}
@SuppressWarnings({"UnusedDeclaration"})
public void reloadGestures(View v) {
loadGestures();
}
@SuppressWarnings({"UnusedDeclaration"})
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;
}
}
}