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 | } |