| 1 | /* |
| 2 | * Tomdroid |
| 3 | * Tomboy on Android |
| 4 | * http://www.launchpad.net/tomdroid |
| 5 | * |
| 6 | * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org> |
| 7 | * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org> |
| 8 | * |
| 9 | * This file is part of Tomdroid. |
| 10 | * |
| 11 | * Tomdroid is free software: you can redistribute it and/or modify |
| 12 | * it under the terms of the GNU General Public License as published by |
| 13 | * the Free Software Foundation, either version 3 of the License, or |
| 14 | * (at your option) any later version. |
| 15 | * |
| 16 | * Tomdroid is distributed in the hope that it will be useful, |
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | * GNU General Public License for more details. |
| 20 | * |
| 21 | * You should have received a copy of the GNU General Public License |
| 22 | * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>. |
| 23 | */ |
| 24 | package org.tomdroid.ui; |
| 25 | |
| 26 | import java.util.regex.Matcher; |
| 27 | import java.util.regex.Pattern; |
| 28 | |
| 29 | import org.tomdroid.Note; |
| 30 | import org.tomdroid.NoteManager; |
| 31 | import org.tomdroid.R; |
| 32 | import org.tomdroid.sync.SyncManager; |
| 33 | import org.tomdroid.util.LinkifyPhone; |
| 34 | import org.tomdroid.util.NoteContentBuilder; |
| 35 | import org.tomdroid.util.Send; |
| 36 | |
| 37 | import android.app.Activity; |
| 38 | import android.app.AlertDialog; |
| 39 | import android.content.DialogInterface; |
| 40 | import android.content.Intent; |
| 41 | import android.content.DialogInterface.OnClickListener; |
| 42 | import android.database.Cursor; |
| 43 | import android.graphics.Color; |
| 44 | import android.net.Uri; |
| 45 | import android.os.Bundle; |
| 46 | import android.os.Handler; |
| 47 | import android.os.Message; |
| 48 | import android.text.SpannableStringBuilder; |
| 49 | import android.text.util.Linkify; |
| 50 | import android.text.util.Linkify.TransformFilter; |
| 51 | import android.util.Log; |
| 52 | import android.view.Menu; |
| 53 | import android.view.MenuInflater; |
| 54 | import android.view.MenuItem; |
| 55 | import android.widget.TextView; |
| 56 | |
| 57 | // TODO this class is starting to smell |
| 58 | public class ViewNote extends Activity { |
| 59 | |
| 60 | // UI elements |
| 61 | private TextView title; |
| 62 | private TextView content; |
| 63 | |
| 64 | // Model objects |
| 65 | private Note note; |
| 66 | private SpannableStringBuilder noteContent; |
| 67 | |
| 68 | // Logging info |
| 69 | private static final String TAG = "ViewNote"; |
| 70 | |
| 71 | // UI feedback handler |
| 72 | private Handler syncMessageHandler = new SyncMessageHandler(this); |
| 73 | |
| 74 | // TODO extract methods in here |
| 75 | @Override |
| 76 | protected void onCreate(Bundle savedInstanceState) { |
| 77 | super.onCreate(savedInstanceState); |
| 78 | |
| 79 | setContentView(R.layout.note_view); |
| 80 | content = (TextView) findViewById(R.id.content); |
| 81 | content.setBackgroundColor(0xffffffff); |
| 82 | content.setTextColor(Color.DKGRAY); |
| 83 | content.setTextSize(18.0f); |
| 84 | title = (TextView) findViewById(R.id.title); |
| 85 | title.setTextColor(Color.DKGRAY); |
| 86 | title.setTextSize(18.0f); |
| 87 | |
| 88 | final Intent intent = getIntent(); |
| 89 | Uri uri = intent.getData(); |
| 90 | |
| 91 | if (uri != null) { |
| 92 | |
| 93 | // We were triggered by an Intent URI |
| 94 | if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "ViewNote started: Intent-filter triggered."); |
| 95 | |
| 96 | // TODO validate the good action? |
| 97 | // intent.getAction() |
| 98 | |
| 99 | // TODO verify that getNote is doing the proper validation |
| 100 | note = NoteManager.getNote(this, uri); |
| 101 | |
| 102 | if(note != null) { |
| 103 | |
| 104 | noteContent = note.getNoteContent(noteContentHandler); |
| 105 | |
| 106 | //Log.i(TAG, "THE NOTE IS: " + note.getXmlContent().toString()); |
| 107 | |
| 108 | } else { |
| 109 | |
| 110 | if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "The note "+uri+" doesn't exist"); |
| 111 | |
| 112 | // TODO put error string in a translatable resource |
| 113 | new AlertDialog.Builder(this) |
| 114 | .setMessage("The requested note could not be found. If you see this error " + |
| 115 | "and you are able to replicate it, please file a bug!") |
| 116 | .setTitle("Error") |
| 117 | .setNeutralButton("Ok", new OnClickListener() { |
| 118 | public void onClick(DialogInterface dialog, int which) { |
| 119 | dialog.dismiss(); |
| 120 | finish(); |
| 121 | }}) |
| 122 | .show(); |
| 123 | } |
| 124 | } else { |
| 125 | |
| 126 | if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "The Intent's data was null."); |
| 127 | |
| 128 | // TODO put error string in a translatable resource |
| 129 | new AlertDialog.Builder(this) |
| 130 | .setMessage("The requested note could not be found. If you see this error " + |
| 131 | " and you are able to replicate it, please file a bug!") |
| 132 | .setTitle(getString(R.string.error)) |
| 133 | .setNeutralButton(getString(R.string.btnOk), new OnClickListener() { |
| 134 | public void onClick(DialogInterface dialog, int which) { |
| 135 | dialog.dismiss(); |
| 136 | finish(); |
| 137 | }}) |
| 138 | .show(); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | @Override |
| 143 | public void onResume(){ |
| 144 | super.onResume(); |
| 145 | SyncManager.setActivity(this); |
| 146 | SyncManager.setHandler(this.syncMessageHandler); |
| 147 | } |
| 148 | |
| 149 | @Override |
| 150 | public boolean onCreateOptionsMenu(Menu menu) { |
| 151 | |
| 152 | // Create the menu based on what is defined in res/menu/noteview.xml |
| 153 | MenuInflater inflater = getMenuInflater(); |
| 154 | inflater.inflate(R.menu.view_note, menu); |
| 155 | return true; |
| 156 | |
| 157 | } |
| 158 | |
| 159 | @Override |
| 160 | public boolean onOptionsItemSelected(MenuItem item) { |
| 161 | switch (item.getItemId()) { |
| 162 | case R.id.view_note_send: |
| 163 | (new Send(this, note)).send(); |
| 164 | return true; |
| 165 | } |
| 166 | return super.onOptionsItemSelected(item); |
| 167 | } |
| 168 | |
| 169 | private void showNote() { |
| 170 | |
| 171 | // show the note (spannable makes the TextView able to output styled text) |
| 172 | content.setText(noteContent, TextView.BufferType.SPANNABLE); |
| 173 | title.setText((CharSequence) note.getTitle()); |
| 174 | |
| 175 | // add links to stuff that is understood by Android except phone numbers because it's too aggressive |
| 176 | // TODO this is SLOWWWW!!!! |
| 177 | Linkify.addLinks(content, Linkify.EMAIL_ADDRESSES|Linkify.WEB_URLS|Linkify.MAP_ADDRESSES); |
| 178 | |
| 179 | // Custom phone number linkifier (fixes lp:512204) |
| 180 | Linkify.addLinks(content, LinkifyPhone.PHONE_PATTERN, "tel:", LinkifyPhone.sPhoneNumberMatchFilter, Linkify.sPhoneNumberTransformFilter); |
| 181 | |
| 182 | // This will create a link every time a note title is found in the text. |
| 183 | // The pattern contains a very dumb (title1)|(title2) escaped correctly |
| 184 | // Then we transform the url from the note name to the note id to avoid characters that mess up with the URI (ex: ?) |
| 185 | Linkify.addLinks(content, |
| 186 | buildNoteLinkifyPattern(), |
| 187 | Tomdroid.CONTENT_URI+"/", |
| 188 | null, |
| 189 | noteTitleTransformFilter); |
| 190 | } |
| 191 | |
| 192 | private Handler noteContentHandler = new Handler() { |
| 193 | |
| 194 | @Override |
| 195 | public void handleMessage(Message msg) { |
| 196 | |
| 197 | //parsed ok - show |
| 198 | if(msg.what == NoteContentBuilder.PARSE_OK) { |
| 199 | showNote(); |
| 200 | |
| 201 | //parsed not ok - error |
| 202 | } else if(msg.what == NoteContentBuilder.PARSE_ERROR) { |
| 203 | |
| 204 | // TODO put this String in a translatable resource |
| 205 | new AlertDialog.Builder(ViewNote.this) |
| 206 | .setMessage("The requested note could not be parsed. If you see this error " + |
| 207 | " and you are able to replicate it, please file a bug!") |
| 208 | .setTitle("Error") |
| 209 | .setNeutralButton("Ok", new OnClickListener() { |
| 210 | public void onClick(DialogInterface dialog, int which) { |
| 211 | dialog.dismiss(); |
| 212 | finish(); |
| 213 | }}) |
| 214 | .show(); |
| 215 | } |
| 216 | } |
| 217 | }; |
| 218 | |
| 219 | /** |
| 220 | * Builds a regular expression pattern that will match any of the note title currently in the collection. |
| 221 | * Useful for the Linkify to create the links to the notes. |
| 222 | * @return regexp pattern |
| 223 | */ |
| 224 | private Pattern buildNoteLinkifyPattern() { |
| 225 | |
| 226 | StringBuilder sb = new StringBuilder(); |
| 227 | Cursor cursor = NoteManager.getTitles(this); |
| 228 | |
| 229 | // cursor must not be null and must return more than 0 entry |
| 230 | if (!(cursor == null || cursor.getCount() == 0)) { |
| 231 | |
| 232 | String title; |
| 233 | |
| 234 | cursor.moveToFirst(); |
| 235 | |
| 236 | do { |
| 237 | title = cursor.getString(cursor.getColumnIndexOrThrow(Note.TITLE)); |
| 238 | |
| 239 | // Pattern.quote() here make sure that special characters in the note's title are properly escaped |
| 240 | sb.append("("+Pattern.quote(title)+")|"); |
| 241 | |
| 242 | } while (cursor.moveToNext()); |
| 243 | |
| 244 | // get rid of the last | that is not needed (I know, its ugly.. better idea?) |
| 245 | String pt = sb.substring(0, sb.length()-1); |
| 246 | |
| 247 | // return a compiled match pattern |
| 248 | return Pattern.compile(pt); |
| 249 | |
| 250 | } else { |
| 251 | |
| 252 | // TODO send an error to the user |
| 253 | if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "Cursor returned null or 0 notes"); |
| 254 | } |
| 255 | |
| 256 | return null; |
| 257 | } |
| 258 | |
| 259 | // custom transform filter that takes the note's title part of the URI and translate it into the note id |
| 260 | // this was done to avoid problems with invalid characters in URI (ex: ? is the query separator but could be in a note title) |
| 261 | private TransformFilter noteTitleTransformFilter = new TransformFilter() { |
| 262 | |
| 263 | public String transformUrl(Matcher m, String str) { |
| 264 | |
| 265 | int id = NoteManager.getNoteId(ViewNote.this, str); |
| 266 | |
| 267 | // return something like content://org.tomdroid.notes/notes/3 |
| 268 | return Tomdroid.CONTENT_URI.toString()+"/"+id; |
| 269 | } |
| 270 | }; |
| 271 | } |