Merge lp:~tomdroid-dev/tomdroid/sync-ui into lp:~tomdroid-maintainers/tomdroid/main

Proposed by Rodja
Status: Merged
Approved by: Olivier Bilodeau
Approved revision: 241
Merged at revision: 186
Proposed branch: lp:~tomdroid-dev/tomdroid/sync-ui
Merge into: lp:~tomdroid-maintainers/tomdroid/main
Diff against target: 4072 lines (+3072/-290)
40 files modified
.classpath (+3/-0)
AndroidManifest.xml (+24/-9)
data/tomdroid-4.svg (+612/-0)
default.properties (+1/-1)
res/anim/pulse.xml (+10/-0)
res/drawable/syncbutton_background.xml (+16/-0)
res/drawable/syncbutton_background_focus.xml (+4/-0)
res/drawable/syncbutton_background_pressed.xml (+4/-0)
res/layout/actionbar.xml (+84/-0)
res/layout/main.xml (+10/-4)
res/layout/main_list_item.xml (+25/-6)
res/layout/note_view.xml (+32/-14)
res/menu/main.xml (+6/-8)
res/values/arrays.xml (+11/-0)
res/values/strings.xml (+13/-2)
res/xml/preferences.xml (+18/-0)
src/org/tomdroid/Note.java (+36/-6)
src/org/tomdroid/NoteManager.java (+45/-9)
src/org/tomdroid/NoteProvider.java (+5/-2)
src/org/tomdroid/sync/ServiceAuth.java (+33/-0)
src/org/tomdroid/sync/SyncManager.java (+106/-0)
src/org/tomdroid/sync/SyncService.java (+196/-0)
src/org/tomdroid/sync/sd/NoteHandler.java (+2/-1)
src/org/tomdroid/sync/sd/SdCardSyncService.java (+68/-44)
src/org/tomdroid/sync/web/AnonymousConnection.java (+62/-0)
src/org/tomdroid/sync/web/OAuthConnection.java (+278/-0)
src/org/tomdroid/sync/web/SnowySyncService.java (+232/-0)
src/org/tomdroid/sync/web/WebConnection.java (+139/-0)
src/org/tomdroid/ui/Actionbar.java (+69/-0)
src/org/tomdroid/ui/PreferencesActivity.java (+217/-0)
src/org/tomdroid/ui/SyncMessageHandler.java (+158/-0)
src/org/tomdroid/ui/Tomdroid.java (+122/-179)
src/org/tomdroid/ui/ViewNote.java (+36/-4)
src/org/tomdroid/util/NoteContentBuilder.java (+2/-1)
src/org/tomdroid/util/NoteListCursorAdapter.java (+113/-0)
src/org/tomdroid/util/Preferences.java (+112/-0)
src/org/tomdroid/util/XmlUtils.java (+58/-0)
src/org/tomdroid/xml/NoteContentHandler.java (+1/-0)
tests/org/tomdroid/NoteManagerTest.java (+74/-0)
tests/org/tomdroid/NoteTest.java (+35/-0)
To merge this branch: bzr merge lp:~tomdroid-dev/tomdroid/sync-ui
Reviewer Review Type Date Requested Status
Olivier Bilodeau Approve
Review via email: mp+33434@code.launchpad.net

Commit message

Note synchronization support (SD Card and Tomboy Web)

Description of the change

The changes made in the sync-ui branch are of great value to the end users and do not seem to have any obvious bugs. Time to do a 0.4 release!

Not only does the new code introduce a visual progress feedback while syncing, but is also using an Actionbar as lately proposed by the Android developers from Google.

To post a comment you must log in.
Revision history for this message
Olivier Bilodeau (plaxx) wrote :

First, I only glanced quickly at your work but I'm very excited to see your work!

Second, sorry if anything here was already mentionned elsewhere but I haven't replied..

Third, all of this is a matter of discussion. Given the right arguments I will change my mind.

1) order of things
I'm a bit confused with the merge hiearchy here..

Is this based on web-sync meaning we need to merge web-sync first or does this integrates web-sync so merging this will result in a web-sync + sync-ui integration?

2) commons-codec
What is commons-codec used for? Was there no built-in alternative already available? I'm trying to avoid adding a 64k jar into the tomdroid package here.

3) signpost
Not sure what you did with signpost.. haven't looked but can't you use a released jar (with a version we can track bugs against, etc.) instead of having a git checkout of their stuff? Did you need to modify their upstream code? If you can't proceed with released code, can you get rid of the git metadata (signpost/.git/) and identify in a README.tomdroid what revision/repo is this code from and what custom changes you needed done. Also, I would like the eclipse metadata removed.

4) file headers
Some new files are missing the usual project header. Be careful to assign copyright to the right person who created the work. If you are willing to give me the copyright feel free to do so. In any case you will be attributed for your work elsewhere.

Start looking at these please. Once dealt with I'll checkout the branch and play with it.

Thanks!

Revision history for this message
Benoit Garret (benoit.garret) wrote :

It's also my fault the merge hasn't been done before, I haven't followed through with Rodja, mainly due to a lack of time on my part.

1) No new commits have been done on the web-sync branch, so the sync-ui supersedes it. This merge request is the right one.

2) commons-codec is needed by signpost. I pulled everything because it was easier, but I believe only a small set of its functionality is needed. We could rip out the parts that interest us and cut down a lot on the size.

3) When I began working with signpost, a few bugs prevented it from working with snowy and ubuntu one, that's why I kept a patched copy. It's just a matter of looking if there's been a release with the fixes I submitted upstream.

4) As far as I know, only Rodja and I worked on this. I suggest we both go through the files and put the notices on the files we know we created.

lp:~tomdroid-dev/tomdroid/sync-ui updated
234. By Rodja

Modified copyright headers in files I've worked

235. By Benoit Garret

Add copyright and license headers in files I've worked on

236. By Benoit Garret

Removed commons-codec and signpost git, using the jar as bugs with Android have been fixed

Revision history for this message
Benoit Garret (benoit.garret) wrote :

Every point you've (Olivier) mentioned should be solved now. I originally included commons-codec because the version shipped with Android was buggy and messed with the Authorization headers, but signpost got a custom implementation since then.

You're now welcome to play with the branch ;-), I'm really looking forward to the day this is merged.

Revision history for this message
Olivier Bilodeau (plaxx) wrote :

Thanks for addressing my concerns quickly. I'll be trying it on my phone in the next couple of day.

Revision history for this message
Olivier Bilodeau (plaxx) wrote :

Hi guys,

I played with the branch for a couple of minutes tonight. Here are my comments:

5) I upgraded and my notes were all still there. However any attempts to open a note resulted in a parsing error. I'm guessing that something changed about the database. If not db then appending note-content tags or not? I would recommend dropping the database on upgrade. It's easy, notes should be on sdcard because sync never existed and nothing should be only in the db since you can't create content yet (oh and we are not 1.0).

6) I'm also unsure about the sync button.. It was not obvious to me at first that I needed to hit that to sync. It's a bit small and the animation for SD card notes was only a dot blinking in the middle. Is that normal? Nothing turning? But this is minor and could be reworked later.

7) Lastly, I'm unsure about the black text on white background for the note list (I haven't tested notes yet since they crash). It is quite the opposite as most android apps (especially the natives one), well.. at least, on my phone (normal android UI). I might revert these changes unless convinced otherwise (or provide an option?).

By the way, I don't want to sound like I'm dumping all this burden on you guys, I'll look into fixing stuff myself but I don't have time now.

I'll be testing more soon. The sync goodness! I can't wait!

review: Needs Fixing
Revision history for this message
Rodja (trappe) wrote :

Hi Oliver,

Thank you for looking at this branch.

5) I'm not aware of any db schema changes. I guess the issue you experienced happens if you change from sd-card notes to web sync... but I've no time to investigate this further right now. Benoit, could you help?

6a) I have decided to follow Google's blog article about the Twitter App: http://android-developers.blogspot.com/2010/05/twitter-for-android-closer-look-at.html ? They recommend to use the Action bar for common and often used global actions. See http://code.google.com/intl/de-DE/events/io/2010/sessions/android-ui-design-patterns.html at minute 12:40 for the whole Action bar talking, and note at 14:35: "What are the actions the user should not have to press menu to get at". See also the discussion at https://bugs.launchpad.net/tomdroid/+bug/549643

6b) SD card syncing is very fast so the turning animation is rather a quick jump from 0% to 100%.

7) All newer Google Apps like the Gallery, Android Market, Clock, Google Talk, Google Goggles, ... and the thrid party Apps where Google was involved like Amazon MP3 and the offical Twitter App use a light theme. I think this is a trend Tomdroid should follow.

Revision history for this message
Olivier Bilodeau (plaxx) wrote :

Quick note to say that I asked about the color changes to the tomdroid-dev mailing list.

lp:~tomdroid-dev/tomdroid/sync-ui updated
237. By Rodja

droping local database when switching between syc methods

238. By Rodja

Fixed switching back to sdcard sync method.

239. By Rodja

Merged Guilherme Salgado's changes to excludes notebook templates.

Revision history for this message
Rodja (trappe) wrote :

Hi Oliver,

5) I've now implemented a simple drop of the local database when switching sync methods. Should fix your issue.

I'm not aware of any other issues or ToDo's which may prevent merging. Please tell me if anything else must be done.

lp:~tomdroid-dev/tomdroid/sync-ui updated
240. By Rodja

Merged with Matt Stevenson's freshen-ui branch which introduces last change dates in the note list and inserts the tomdroid logo in the actionbar.

241. By Rodja

Made sure the tags member is initalized.

Revision history for this message
Olivier Bilodeau (plaxx) wrote :

Ok, thanks, I'll re-test this week and let you know.

For color and style, based on the feedback, we will move forward with what
you have done.

On Sun, Sep 26, 2010 at 8:16 AM, Rodja <email address hidden> wrote:

> Hi Oliver,
>
> 5) I've now implemented a simple drop of the local database when switching
> sync methods. Should fix your issue.
>
> I'm not aware of any other issues or ToDo's which may prevent merging.
> Please tell me if anything else must be done.
> --
> https://code.launchpad.net/~tomdroid-dev/tomdroid/sync-ui/+merge/33434<https://code.launchpad.net/%7Etomdroid-dev/tomdroid/sync-ui/+merge/33434>
> You are reviewing the proposed merge of lp:~tomdroid-dev/tomdroid/sync-ui
> into lp:tomdroid.
>

--
Olivier Bilodeau <email address hidden>

Revision history for this message
Olivier Bilodeau (plaxx) wrote :

Performed some tests:
- 1.6 emulator SD Card sync - ok
- 1.6 emulator with QA note set (public.img) - ok
- 2.1 emulator SD Card sync - ok
- 2.1 emulator with QA note set (public.img) - ok
- 1.6 ADP1 SD Card sync - ok
- 1.6 ADP1 with personal note set - ok

Tomorrow or saturday I'll be entering release mode. Now, I'm heading for bug triage and building the NEWS file, actually I will need your help for that as I'm afraid some noteworthy changes and contributors will be lost in all the various branches..

Contributors I have for now: Benoit Garret, Rodja Trappe, Matt Stevenson, Guilherme Salgado

NEWS: I'll start something in lp:tomdroid and you guys send me a patch if I'm missing something new or proper attribution.

Anyone tested the recently launched tomboy-online service? I know I will but only after releasing 0.4.0.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.classpath'
2--- .classpath 2010-01-25 05:01:42 +0000
3+++ .classpath 2010-10-03 12:21:44 +0000
4@@ -3,5 +3,8 @@
5 <classpathentry kind="src" path="src"/>
6 <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
7 <classpathentry kind="src" path="gen"/>
8+ <classpathentry kind="lib" path="lib/signpost-core-1.2.1.1.jar"/>
9+ <classpathentry kind="lib" path="lib/signpost-commonshttp4-1.2.1.1.jar"/>
10 <classpathentry kind="output" path="bin"/>
11+ <classpathentry kind="src" path="tests"/>
12 </classpath>
13
14=== modified file 'AndroidManifest.xml'
15--- AndroidManifest.xml 2010-02-18 04:36:08 +0000
16+++ AndroidManifest.xml 2010-10-03 12:21:44 +0000
17@@ -2,14 +2,16 @@
18 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
19 package="org.tomdroid"
20 android:versionName="0.3.1" android:versionCode="4">
21-
22- <uses-sdk android:minSdkVersion="3" />
23
24+ <uses-sdk android:minSdkVersion="3"
25+ android:targetSdkVersion="4" />
26+
27+ <supports-screens android:anyDensity="true" />
28+
29 <application
30 android:icon="@drawable/icon"
31 android:label="@string/app_name"
32- android:debuggable="false"
33- >
34+ android:theme="@android:style/Theme.Light.NoTitleBar">
35
36 <activity android:label="@string/app_name"
37 android:name=".ui.Tomdroid"
38@@ -19,6 +21,12 @@
39 <action android:name="android.intent.action.MAIN" />
40 <category android:name="android.intent.category.LAUNCHER" />
41 </intent-filter>
42+ <intent-filter>
43+ <action android:name="android.intent.action.VIEW" />
44+ <category android:name="android.intent.category.DEFAULT" />
45+ <category android:name="android.intent.category.BROWSABLE" />
46+ <data android:scheme="tomdroid" />
47+ </intent-filter>
48 </activity>
49
50 <activity android:name=".ui.ViewNote">
51@@ -29,12 +37,19 @@
52 <data android:mimeType="vnd.android.cursor.item/vnd.tomdroid.note" />
53 </intent-filter>
54 </activity>
55-
56-
57
58 <provider android:name="NoteProvider"
59 android:authorities="org.tomdroid.notes"
60 />
61-</application>
62- <uses-permission android:name="android.permission.INTERNET" />
63-</manifest>
64\ No newline at end of file
65+
66+ <activity android:name=".ui.PreferencesActivity" android:label="@string/app_name">
67+
68+ </activity>
69+
70+ <uses-library android:name="android.test.runner" />
71+ </application>
72+
73+ <uses-permission android:name="android.permission.INTERNET" />
74+<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="org.tomdroid"></instrumentation>
75+
76+</manifest>
77
78=== added file 'data/tomdroid-4.svg'
79--- data/tomdroid-4.svg 1970-01-01 00:00:00 +0000
80+++ data/tomdroid-4.svg 2010-10-03 12:21:44 +0000
81@@ -0,0 +1,612 @@
82+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
83+<!-- Created with Inkscape (http://www.inkscape.org/) -->
84+
85+<svg
86+ xmlns:dc="http://purl.org/dc/elements/1.1/"
87+ xmlns:cc="http://creativecommons.org/ns#"
88+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
89+ xmlns:svg="http://www.w3.org/2000/svg"
90+ xmlns="http://www.w3.org/2000/svg"
91+ xmlns:xlink="http://www.w3.org/1999/xlink"
92+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
93+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
94+ width="48"
95+ height="48"
96+ id="svg2394"
97+ sodipodi:version="0.32"
98+ inkscape:version="0.47 r22583"
99+ version="1.0"
100+ sodipodi:docname="tomdroid-4.png"
101+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
102+ inkscape:export-filename="/data/code/android/web-sync/res/drawable/tomdroid-4.png"
103+ inkscape:export-xdpi="90"
104+ inkscape:export-ydpi="90">
105+ <defs
106+ id="defs2396">
107+ <inkscape:perspective
108+ sodipodi:type="inkscape:persp3d"
109+ inkscape:vp_x="0 : 526.18109 : 1"
110+ inkscape:vp_y="0 : 1000 : 0"
111+ inkscape:vp_z="744.09448 : 526.18109 : 1"
112+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
113+ id="perspective2402" />
114+ <linearGradient
115+ id="linearGradient6377">
116+ <stop
117+ id="stop6379"
118+ offset="0"
119+ style="stop-color:#fff27e;stop-opacity:1;" />
120+ <stop
121+ id="stop6381"
122+ offset="1"
123+ style="stop-color:#edd400;stop-opacity:1;" />
124+ </linearGradient>
125+ <linearGradient
126+ id="linearGradient2966">
127+ <stop
128+ style="stop-color:#ffd1d1;stop-opacity:1;"
129+ offset="0"
130+ id="stop2968" />
131+ <stop
132+ id="stop3006"
133+ offset="0.5"
134+ style="stop-color:#ff1d1d;stop-opacity:1;" />
135+ <stop
136+ style="stop-color:#6f0000;stop-opacity:1;"
137+ offset="1"
138+ id="stop2970" />
139+ </linearGradient>
140+ <linearGradient
141+ id="linearGradient2974">
142+ <stop
143+ style="stop-color:#c1c1c1;stop-opacity:1;"
144+ offset="0"
145+ id="stop2976" />
146+ <stop
147+ style="stop-color:#acacac;stop-opacity:1;"
148+ offset="1"
149+ id="stop2978" />
150+ </linearGradient>
151+ <linearGradient
152+ id="linearGradient2994">
153+ <stop
154+ style="stop-color:#000000;stop-opacity:1;"
155+ offset="0"
156+ id="stop2996" />
157+ <stop
158+ style="stop-color:#c9c9c9;stop-opacity:1;"
159+ offset="1"
160+ id="stop2998" />
161+ </linearGradient>
162+ <inkscape:perspective
163+ id="perspective2705"
164+ inkscape:persp3d-origin="24 : 16 : 1"
165+ inkscape:vp_z="48 : 24 : 1"
166+ inkscape:vp_y="0 : 1000 : 0"
167+ inkscape:vp_x="0 : 24 : 1"
168+ sodipodi:type="inkscape:persp3d" />
169+ <inkscape:perspective
170+ id="perspective3103"
171+ inkscape:persp3d-origin="60 : 46.666667 : 1"
172+ inkscape:vp_z="120 : 70 : 1"
173+ inkscape:vp_y="0 : 1000 : 0"
174+ inkscape:vp_x="0 : 70 : 1"
175+ sodipodi:type="inkscape:persp3d" />
176+ <radialGradient
177+ gradientUnits="userSpaceOnUse"
178+ gradientTransform="matrix(1,0,0,0.361345,0,22.29694)"
179+ r="5.2591065"
180+ fy="31.780704"
181+ fx="39.907337"
182+ cy="31.780704"
183+ cx="39.907337"
184+ id="radialGradient6423"
185+ xlink:href="#linearGradient6417"
186+ inkscape:collect="always" />
187+ <radialGradient
188+ gradientUnits="userSpaceOnUse"
189+ gradientTransform="matrix(10.88255,-6.454846e-8,0,11.39737,-433.5968,-381.3811)"
190+ r="20.21875"
191+ fy="35.90107"
192+ fx="43.875"
193+ cy="35.90107"
194+ cx="43.875"
195+ id="radialGradient6413"
196+ xlink:href="#linearGradient6407"
197+ inkscape:collect="always" />
198+ <radialGradient
199+ r="21.626934"
200+ fy="35.915409"
201+ fx="45.150326"
202+ cy="35.915409"
203+ cx="45.150326"
204+ gradientTransform="matrix(1.669712,0,1.702451e-8,1.220484,-30.23773,-11.79928)"
205+ gradientUnits="userSpaceOnUse"
206+ id="radialGradient6405"
207+ xlink:href="#linearGradient6377"
208+ inkscape:collect="always" />
209+ <linearGradient
210+ gradientUnits="userSpaceOnUse"
211+ y2="67.031342"
212+ x2="26.130388"
213+ y1="14.08672"
214+ x1="26.213203"
215+ id="linearGradient5615"
216+ xlink:href="#linearGradient5609"
217+ inkscape:collect="always" />
218+ <linearGradient
219+ id="linearGradient5609"
220+ inkscape:collect="always">
221+ <stop
222+ id="stop5611"
223+ offset="0"
224+ style="stop-color:white;stop-opacity:1;" />
225+ <stop
226+ id="stop5613"
227+ offset="1"
228+ style="stop-color:white;stop-opacity:0;" />
229+ </linearGradient>
230+ <linearGradient
231+ id="linearGradient3342">
232+ <stop
233+ id="stop3344"
234+ offset="0"
235+ style="stop-color:#fff27e;stop-opacity:1;" />
236+ <stop
237+ id="stop3346"
238+ offset="1"
239+ style="stop-color:#edd400;stop-opacity:1;" />
240+ </linearGradient>
241+ <linearGradient
242+ id="linearGradient6407"
243+ inkscape:collect="always">
244+ <stop
245+ id="stop6409"
246+ offset="0"
247+ style="stop-color:white;stop-opacity:1;" />
248+ <stop
249+ id="stop6411"
250+ offset="1"
251+ style="stop-color:white;stop-opacity:0;" />
252+ </linearGradient>
253+ <linearGradient
254+ id="linearGradient6417"
255+ inkscape:collect="always">
256+ <stop
257+ id="stop6419"
258+ offset="0"
259+ style="stop-color:black;stop-opacity:1;" />
260+ <stop
261+ id="stop6421"
262+ offset="1"
263+ style="stop-color:black;stop-opacity:0;" />
264+ </linearGradient>
265+ <linearGradient
266+ id="linearGradient3326">
267+ <stop
268+ style="stop-color:#ffd1d1;stop-opacity:1;"
269+ offset="0"
270+ id="stop3328" />
271+ <stop
272+ id="stop3330"
273+ offset="0.5"
274+ style="stop-color:#ff1d1d;stop-opacity:1;" />
275+ <stop
276+ style="stop-color:#6f0000;stop-opacity:1;"
277+ offset="1"
278+ id="stop3332" />
279+ </linearGradient>
280+ <linearGradient
281+ id="linearGradient3319">
282+ <stop
283+ style="stop-color:#c1c1c1;stop-opacity:1;"
284+ offset="0"
285+ id="stop3321" />
286+ <stop
287+ style="stop-color:#acacac;stop-opacity:1;"
288+ offset="1"
289+ id="stop3323" />
290+ </linearGradient>
291+ <linearGradient
292+ inkscape:collect="always"
293+ id="linearGradient2984">
294+ <stop
295+ style="stop-color:#e7e2b8;stop-opacity:1;"
296+ offset="0"
297+ id="stop2986" />
298+ <stop
299+ style="stop-color:#e7e2b8;stop-opacity:0;"
300+ offset="1"
301+ id="stop2988" />
302+ </linearGradient>
303+ <linearGradient
304+ id="linearGradient3308">
305+ <stop
306+ style="stop-color:#000000;stop-opacity:1;"
307+ offset="0"
308+ id="stop3310" />
309+ <stop
310+ style="stop-color:#c9c9c9;stop-opacity:1;"
311+ offset="1"
312+ id="stop3312" />
313+ </linearGradient>
314+ <inkscape:perspective
315+ id="perspective3305"
316+ inkscape:persp3d-origin="24 : 16 : 1"
317+ inkscape:vp_z="48 : 24 : 1"
318+ inkscape:vp_y="0 : 1000 : 0"
319+ inkscape:vp_x="0 : 24 : 1"
320+ sodipodi:type="inkscape:persp3d" />
321+ <linearGradient
322+ inkscape:collect="always"
323+ xlink:href="#linearGradient2966"
324+ id="linearGradient4272"
325+ gradientUnits="userSpaceOnUse"
326+ gradientTransform="translate(-5.669292,0)"
327+ x1="48.90625"
328+ y1="17.376184"
329+ x2="50.988335"
330+ y2="22.250591" />
331+ <linearGradient
332+ inkscape:collect="always"
333+ xlink:href="#linearGradient2974"
334+ id="linearGradient4274"
335+ gradientUnits="userSpaceOnUse"
336+ gradientTransform="translate(-5.669292,0)"
337+ x1="46"
338+ y1="19.8125"
339+ x2="47.6875"
340+ y2="22.625" />
341+ <radialGradient
342+ inkscape:collect="always"
343+ xlink:href="#linearGradient2984"
344+ id="radialGradient4276"
345+ gradientUnits="userSpaceOnUse"
346+ gradientTransform="matrix(2.923565,0,0,2.029717,-61.55532,-27.88417)"
347+ cx="29.053354"
348+ cy="27.640751"
349+ fx="29.053354"
350+ fy="27.640751"
351+ r="3.2408544" />
352+ <linearGradient
353+ inkscape:collect="always"
354+ xlink:href="#linearGradient2994"
355+ id="linearGradient4278"
356+ gradientUnits="userSpaceOnUse"
357+ gradientTransform="translate(-5.825542,0.125)"
358+ x1="25.71875"
359+ y1="31.046875"
360+ x2="25.514589"
361+ y2="30.703125" />
362+ <inkscape:perspective
363+ id="perspective5044"
364+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
365+ inkscape:vp_z="1 : 0.5 : 1"
366+ inkscape:vp_y="0 : 1000 : 0"
367+ inkscape:vp_x="0 : 0.5 : 1"
368+ sodipodi:type="inkscape:persp3d" />
369+ <inkscape:perspective
370+ id="perspective5252"
371+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
372+ inkscape:vp_z="1 : 0.5 : 1"
373+ inkscape:vp_y="0 : 1000 : 0"
374+ inkscape:vp_x="0 : 0.5 : 1"
375+ sodipodi:type="inkscape:persp3d" />
376+ <inkscape:perspective
377+ id="perspective5278"
378+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
379+ inkscape:vp_z="1 : 0.5 : 1"
380+ inkscape:vp_y="0 : 1000 : 0"
381+ inkscape:vp_x="0 : 0.5 : 1"
382+ sodipodi:type="inkscape:persp3d" />
383+ <inkscape:perspective
384+ id="perspective5380"
385+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
386+ inkscape:vp_z="1 : 0.5 : 1"
387+ inkscape:vp_y="0 : 1000 : 0"
388+ inkscape:vp_x="0 : 0.5 : 1"
389+ sodipodi:type="inkscape:persp3d" />
390+ <inkscape:perspective
391+ id="perspective5404"
392+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
393+ inkscape:vp_z="1 : 0.5 : 1"
394+ inkscape:vp_y="0 : 1000 : 0"
395+ inkscape:vp_x="0 : 0.5 : 1"
396+ sodipodi:type="inkscape:persp3d" />
397+ <inkscape:perspective
398+ id="perspective5433"
399+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
400+ inkscape:vp_z="1 : 0.5 : 1"
401+ inkscape:vp_y="0 : 1000 : 0"
402+ inkscape:vp_x="0 : 0.5 : 1"
403+ sodipodi:type="inkscape:persp3d" />
404+ <inkscape:perspective
405+ id="perspective5459"
406+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
407+ inkscape:vp_z="1 : 0.5 : 1"
408+ inkscape:vp_y="0 : 1000 : 0"
409+ inkscape:vp_x="0 : 0.5 : 1"
410+ sodipodi:type="inkscape:persp3d" />
411+ <inkscape:perspective
412+ id="perspective5509"
413+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
414+ inkscape:vp_z="1 : 0.5 : 1"
415+ inkscape:vp_y="0 : 1000 : 0"
416+ inkscape:vp_x="0 : 0.5 : 1"
417+ sodipodi:type="inkscape:persp3d" />
418+ <inkscape:perspective
419+ id="perspective5564"
420+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
421+ inkscape:vp_z="1 : 0.5 : 1"
422+ inkscape:vp_y="0 : 1000 : 0"
423+ inkscape:vp_x="0 : 0.5 : 1"
424+ sodipodi:type="inkscape:persp3d" />
425+ </defs>
426+ <sodipodi:namedview
427+ id="base"
428+ pagecolor="#ffffff"
429+ bordercolor="#666666"
430+ borderopacity="1.0"
431+ inkscape:pageopacity="0.0"
432+ inkscape:pageshadow="2"
433+ inkscape:zoom="7.9195959"
434+ inkscape:cx="25.379657"
435+ inkscape:cy="34.644751"
436+ inkscape:document-units="px"
437+ inkscape:current-layer="layer1"
438+ showgrid="false"
439+ inkscape:window-width="1280"
440+ inkscape:window-height="976"
441+ inkscape:window-x="0"
442+ inkscape:window-y="25"
443+ inkscape:window-maximized="1" />
444+ <metadata
445+ id="metadata2399">
446+ <rdf:RDF>
447+ <cc:Work
448+ rdf:about="">
449+ <dc:format>image/svg+xml</dc:format>
450+ <dc:type
451+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
452+ <dc:title></dc:title>
453+ <dc:date>2009-04-02</dc:date>
454+ <dc:creator>
455+ <cc:Agent>
456+ <dc:title>Olivier Bilodeau</dc:title>
457+ </cc:Agent>
458+ </dc:creator>
459+ <dc:description>A mashup of Tomboy's icon and Android's logo. Both available under free licences.</dc:description>
460+ </cc:Work>
461+ </rdf:RDF>
462+ </metadata>
463+ <g
464+ inkscape:label="Calque 1"
465+ inkscape:groupmode="layer"
466+ id="layer1">
467+ <g
468+ id="g4242"
469+ transform="translate(-0.1684169,3.5830001)">
470+ <path
471+ sodipodi:nodetypes="ccccccccccc"
472+ id="rect1975"
473+ d="m 10.301452,14.596007 28.649253,0.353553 c 0.762577,0 1.24391,0.576646 1.376493,1.193837 0,0 4.40133,19.815144 4.40133,19.815144 0,0 0.01246,6.347622 0.01246,6.347622 0,0.661386 -0.613915,1.193837 -1.376492,1.193837 l -37.4768304,0 C 5.1250868,43.5 4.5111713,42.967549 4.5111713,42.306163 L 4.4999999,36.139247 8.9249601,15.789844 c 0.3093592,-0.661386 0.6139156,-1.193837 1.3764919,-1.193837 z"
474+ style="fill:#edd400;fill-opacity:1;fill-rule:evenodd;stroke:#c4a000;stroke-width:0.99999982;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
475+ <rect
476+ ry="0.67937863"
477+ rx="0.67937863"
478+ y="35.957905"
479+ x="5.1146202"
480+ height="7.0714951"
481+ width="39.048077"
482+ id="rect2851"
483+ style="opacity:0.37078654;fill:#f57900;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
484+ <path
485+ sodipodi:nodetypes="ccccccc"
486+ id="path2853"
487+ d="m 5.0643333,36.53243 c 0,0 0.1508618,-0.53033 0.704022,-0.574524 l 37.5646027,0 c 0.754309,0 0.804596,0.751301 0.804596,0.751301 0,0 0.0236,-1.619573 -1.283871,-1.619573 l -36.4118845,0 c -1.0057457,0.08839 -1.3774652,0.779883 -1.3774652,1.442796 z"
488+ style="opacity:0.16292138;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
489+ <path
490+ id="path4730"
491+ d="m 10.3125,15.59375 c -0.151316,0 -0.09946,-0.02674 -0.15625,0.03125 -0.05088,0.05195 -0.155811,0.275085 -0.28125,0.53125 -0.014579,0.02977 -0.016184,0.03029 -0.03125,0.0625 L 5.5,36.125 l 0,0.0625 0,6.125 C 5.5,42.35657 5.572368,42.5 5.875,42.5 l 37.5,0 c 0.302632,0 0.375,-0.143429 0.375,-0.1875 0,0 -0.03035,-6.068147 -0.03125,-6.25 -2.51e-4,-0.0011 4.27e-4,-0.09183 0,-0.09375 C 43.625157,35.547912 39.34375,16.375 39.34375,16.375 39.2872,16.111751 39.174175,15.9375 38.9375,15.9375 l -28.625,-0.34375 z"
492+ style="opacity:0.4831461;fill:none;stroke:url(#linearGradient5615);stroke-width:0.99999982;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
493+ <path
494+ sodipodi:nodetypes="cccc"
495+ id="path6415"
496+ d="M 44.194174,35.681088 C 43.814854,34.425759 43.31029,31.880389 43.31029,31.880389 l -8.927222,3.181981 c 5.745243,0 8.573669,-0.265165 9.811106,0.618718 z"
497+ style="opacity:0.46629214;fill:url(#radialGradient6423);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
498+ <path
499+ style="fill:url(#radialGradient6405);fill-opacity:1;fill-rule:evenodd;stroke:#c4a000;stroke-width:0.99999964px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
500+ d="m 8.7832195,16.426565 -4.13483,18.029279 c 0,0 21.4716075,0.125001 29.2755775,0.125001 7.980569,0 11.155067,-2.96842 11.155067,-2.96842 0,0 -1.006743,-0.453488 -2.325109,-4.966721 0,0 -2.403785,-10.500389 -2.403785,-10.500389 -0.463441,-1.186401 -0.856206,-1.663669 -1.75639,-1.65625 l -27.78125,0 c -1.7452746,0.03209 -1.7364187,0.835552 -2.0292805,1.9375 z"
501+ id="path2524"
502+ sodipodi:nodetypes="cczczcccc" />
503+ <path
504+ id="path6403"
505+ d="m 10.8125,15.5 c -0.640289,0.01823 -0.708297,0.09979 -0.75,0.15625 C 10.01913,15.71497 9.9124269,16.076338 9.75,16.6875 L 5.90625,33.46875 c 1.6944357,0.0098 20.570644,0.125 28.03125,0.125 3.866286,0 6.517278,-0.714302 8.1875,-1.40625 1.054074,-0.436687 1.113325,-0.577029 1.4375,-0.8125 -0.490628,-0.789905 -1.105041,-2.122597 -1.78125,-4.4375 -6.51e-4,-0.02083 -6.51e-4,-0.04167 0,-0.0625 0,0 -2.305929,-10.042349 -2.375,-10.34375 C 39.192263,15.983448 39.024353,15.680487 38.9375,15.59375 38.85065,15.50701 38.86595,15.49776 38.59375,15.5 l -27.75,0 -0.03125,0 z"
506+ style="opacity:0.46629214;fill:none;stroke:url(#radialGradient6413);stroke-width:0.99999964px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
507+ <path
508+ style="opacity:0.26404497;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
509+ d="m 5.0643333,38.53243 c 0,0 0.1508618,-0.53033 0.704022,-0.574524 l 37.5646027,0 c 0.754309,0 0.804596,0.751301 0.804596,0.751301 0,0 0.0236,-1.619573 -1.283871,-1.619573 l -36.4118845,0 c -1.0057457,0.08839 -1.3774652,0.779883 -1.3774652,1.442796 z"
510+ id="path6359"
511+ sodipodi:nodetypes="ccccccc" />
512+ <path
513+ sodipodi:nodetypes="ccccccc"
514+ id="path6361"
515+ d="m 5.0643333,40.53243 c 0,0 0.1508618,-0.53033 0.704022,-0.574524 l 37.5646027,0 c 0.754309,0 0.804596,0.751301 0.804596,0.751301 0,0 0.0236,-1.619573 -1.283871,-1.619573 l -36.4118845,0 c -1.0057457,0.08839 -1.3774652,0.779883 -1.3774652,1.442796 z"
516+ style="opacity:0.26404497;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
517+ <path
518+ style="opacity:0.26404497;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
519+ d="m 5.0643333,42.53243 c 0,0 0.1508618,-0.53033 0.704022,-0.574524 l 37.5646027,0 c 0.754309,0 0.804596,0.751301 0.804596,0.751301 0,0 0.0236,-1.619573 -1.283871,-1.619573 l -36.4118845,0 c -1.0057457,0.08839 -1.3774652,0.779883 -1.3774652,1.442796 z"
520+ id="path6363"
521+ sodipodi:nodetypes="ccccccc" />
522+ </g>
523+ <g
524+ id="g4166-6"
525+ transform="matrix(1.0098472,0,0,0.98902752,-298.47562,-483.65964)"
526+ style="fill:#c4a000;fill-opacity:1;stroke:none" />
527+ <g
528+ id="g3106"
529+ inkscape:label="Layer 1"
530+ transform="matrix(0.9894224,0,0,0.9894224,86.886011,8.4726515)">
531+ <g
532+ transform="translate(-299.00515,-503.07627)"
533+ id="g3279" />
534+ </g>
535+ <path
536+ style="fill:#d40000;fill-opacity:1;stroke:#ffffff;stroke-width:1.70000005;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
537+ d="m 29.78125,1.0249519 c -0.200346,-0.00693 -0.393078,0.104186 -0.5,0.3125001 l -1.375,2.65625 c -1.068903,-0.3679226 -2.237706,-0.5625 -3.46875,-0.5625 -1.366399,0 -2.652232,0.2394169 -3.8125,0.6875 l -1.40625,-2.75 C 19.076187,1.0909499 18.780186,0.99092944 18.53125,1.1187019 18.282314,1.2464745 18.169937,1.5597 18.3125,1.837452 l 1.40625,2.6875 c -2.563621,1.3037284 -4.28125,3.6451702 -4.28125,6.3125 0,0.224276 0.03887,0.43704 0.0625,0.65625 l 17.875,0 c 0.02363,-0.21921 0.0625,-0.431974 0.0625,-0.65625 0,-2.7740229 -1.857873,-5.1997008 -4.59375,-6.46875 l 1.3125,-2.5625 c 0.142563,-0.277752 0.06144,-0.6222276 -0.1875,-0.7500001 -0.06223,-0.031943 -0.120718,-0.028941 -0.1875,-0.03125 z m -16.875,10.7500001 c -1.101761,0 -2,0.898239 -2,2 l 0,8.40625 c 0,1.101761 0.898239,2 2,2 1.101761,0 1.96875,-0.898239 1.96875,-2 l 0,-8.40625 c 0,-1.101761 -0.866989,-2 -1.96875,-2 z m 22.9375,0.15625 c -1.101761,0 -2,0.866989 -2,1.96875 l 0,8.4375 c 0,1.101761 0.898239,2 2,2 1.101761,0 2,-0.898239 2,-2 l 0,-8.4375 c 0,-1.101761 -0.898239,-1.96875 -2,-1.96875 z m -20.21875,0.1875 0,1.15625 0,0.34375 0,1.4375 0,6.25 0,3.75 c 0,1.19785 0.9584,2.15625 2.15625,2.15625 l 1.4375,0 0,4.625 c 0,0.943048 0.744452,1.6875 1.6875,1.6875 l 0.59375,0 c 0.943048,0 1.71875,-0.744452 1.71875,-1.6875 l 0,-4.625 2.5625,0 0,4.59375 c 0,0.943048 0.775702,1.71875 1.71875,1.71875 l 0.59375,0 c 0.943048,0 1.71875,-0.775702 1.71875,-1.71875 l 0,-4.59375 1.1875,0 c 1.19785,0 2.1875,-0.9584 2.1875,-2.15625 l 0,-3.75 0,-6.25 0,-1.78125 0,-1.15625 -17.5625,0 z"
538+ id="rect5365-8" />
539+ <g
540+ id="g5488"
541+ style="stroke:none"
542+ transform="translate(0,0.39995194)">
543+ <path
544+ id="rect5365"
545+ d="m 15.65625,11.75 0,1.15625 0,0.34375 0,1.4375 0,6.25 0,3.75 c 0,1.19785 0.9584,2.15625 2.15625,2.15625 l 13.21875,0 c 1.19785,0 2.1875,-0.9584 2.1875,-2.15625 l 0,-3.75 0,-6.25 0,-1.78125 0,-1.15625 -17.5625,0 z"
546+ style="fill:#97c024;fill-opacity:1;stroke:none" />
547+ <rect
548+ rx="1.7022525"
549+ y="23.661636"
550+ x="19.224466"
551+ height="9.5017471"
552+ width="4.0090432"
553+ id="rect5370"
554+ style="fill:#97c024;fill-opacity:1;stroke:none" />
555+ <rect
556+ rx="1.7022525"
557+ y="23.645853"
558+ x="25.80624"
559+ height="9.5017471"
560+ width="4.0090432"
561+ id="rect5370-3"
562+ style="fill:#97c024;fill-opacity:1;stroke:none" />
563+ <rect
564+ rx="1.9887384"
565+ y="11.413537"
566+ x="10.922275"
567+ height="12.405936"
568+ width="3.9774768"
569+ id="rect5394"
570+ style="fill:#97c024;fill-opacity:1;stroke:none" />
571+ <rect
572+ rx="1.9887384"
573+ y="11.55559"
574+ x="33.871677"
575+ height="12.405936"
576+ width="3.9774768"
577+ id="rect5394-2"
578+ style="fill:#97c024;fill-opacity:1;stroke:none" />
579+ <g
580+ transform="translate(-0.20025,0)"
581+ id="g5481"
582+ style="stroke:none">
583+ <path
584+ style="fill:#97c024;fill-opacity:1;stroke:none"
585+ d="m 24.65625,3.0625 c -4.968725,0 -9,3.3092317 -9,7.40625 0,0.224276 0.03887,0.43704 0.0625,0.65625 l 17.875,0 c 0.02363,-0.21921 0.0625,-0.431974 0.0625,-0.65625 0,-4.0970183 -4.031275,-7.40625 -9,-7.40625 z"
586+ id="path5418" />
587+ <path
588+ sodipodi:type="arc"
589+ style="fill:#ffffff;fill-opacity:1;stroke:none"
590+ id="path5449"
591+ sodipodi:cx="21.37104"
592+ sodipodi:cy="8.3357286"
593+ sodipodi:rx="0.78918165"
594+ sodipodi:ry="0.77339804"
595+ d="m 22.160222,8.3357286 c 0,0.427136 -0.353329,0.7733981 -0.789182,0.7733981 -0.435853,0 -0.789181,-0.3462621 -0.789181,-0.7733981 0,-0.4271359 0.353328,-0.773398 0.789181,-0.773398 0.435853,0 0.789182,0.3462621 0.789182,0.773398 z"
596+ transform="matrix(1.0212245,0,0,1.0212245,-1.4154165,-1.4231952)" />
597+ <path
598+ sodipodi:type="arc"
599+ style="fill:#ffffff;fill-opacity:1;stroke:none"
600+ id="path5449-6"
601+ sodipodi:cx="21.37104"
602+ sodipodi:cy="8.3357286"
603+ sodipodi:rx="0.78918165"
604+ sodipodi:ry="0.77339804"
605+ d="m 22.160222,8.3357286 c 0,0.427136 -0.353329,0.7733981 -0.789182,0.7733981 -0.435853,0 -0.789181,-0.3462621 -0.789181,-0.7733981 0,-0.4271359 0.353328,-0.773398 0.789181,-0.773398 0.435853,0 0.789182,0.3462621 0.789182,0.773398 z"
606+ transform="matrix(1.0212245,0,0,1.0212245,6.7121839,-1.4238284)" />
607+ <rect
608+ style="fill:#97c024;fill-opacity:1;stroke:none"
609+ id="rect5423"
610+ width="1.0101534"
611+ height="5.0507627"
612+ x="15.824705"
613+ y="9.2116003"
614+ rx="0.50507671"
615+ transform="matrix(0.88965349,-0.45663625,0.45663625,0.88965349,0,0)"
616+ ry="0.56354231" />
617+ <rect
618+ style="fill:#97c024;fill-opacity:1;stroke:none"
619+ id="rect5423-6"
620+ width="1.0101534"
621+ height="5.0507627"
622+ x="-27.690969"
623+ y="-13.159358"
624+ rx="0.50507671"
625+ transform="matrix(-0.88965349,-0.45663625,-0.45663625,0.88965349,0,0)"
626+ ry="0.56354231" />
627+ </g>
628+ </g>
629+ <g
630+ style="display:inline"
631+ id="g1574"
632+ transform="matrix(0.7113809,-0.1906141,0.1906141,0.7113809,19.866431,-7.0127809)"
633+ inkscape:r_cx="true"
634+ inkscape:r_cy="true">
635+ <path
636+ transform="translate(-29.75546,19)"
637+ sodipodi:nodetypes="cccccc"
638+ id="path2960"
639+ d="m 17.34116,32.5 5.625,-5.625 20.093749,-9.75 c 3.25,-1.25 5.1875,3.375 2.3125,5 L 25.34116,31.5 l -8,1 z"
640+ style="fill:#cb9022;fill-opacity:1;fill-rule:evenodd;stroke:#5c410c;stroke-width:0.93443578;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
641+ inkscape:r_cx="true"
642+ inkscape:r_cy="true" />
643+ <path
644+ transform="translate(-29.75546,19)"
645+ style="fill:url(#linearGradient4272);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
646+ d="m 38.330708,20 c 0,0 1.4375,0.09375 2,1.34375 0.579493,1.287761 0,2.65625 0,2.65625 l 5.03125,-2.46875 c 0,0 1.452032,-0.881367 0.65625,-2.84375 -0.784912,-1.935577 -2.6875,-1.15625 -2.6875,-1.15625 l -5,2.46875 z"
647+ id="path2964"
648+ sodipodi:nodetypes="czcczcc"
649+ inkscape:r_cx="true"
650+ inkscape:r_cy="true" />
651+ <path
652+ transform="translate(-29.75546,19)"
653+ sodipodi:nodetypes="czcczcc"
654+ id="path2962"
655+ d="m 38.330708,20 c 0,0 1.4375,0.09375 2,1.34375 0.579493,1.287761 0,2.65625 0,2.65625 l 2,-1 c 0,0 0.827032,-1.318867 0.21875,-2.6875 C 41.924458,18.90625 40.330708,19 40.330708,19 l -2,1 z"
656+ style="fill:url(#linearGradient4274);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
657+ inkscape:r_cx="true"
658+ inkscape:r_cy="true" />
659+ <path
660+ transform="translate(-29.75546,19)"
661+ sodipodi:nodetypes="cccc"
662+ id="path2982"
663+ d="m 18.768208,31.78125 4.5,-4.5 c 1.5,0.8125 2.28125,2.15625 1.875,3.71875 l -6.375,0.78125 z"
664+ style="fill:url(#radialGradient4276);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
665+ inkscape:r_cx="true"
666+ inkscape:r_cy="true" />
667+ <path
668+ transform="translate(-29.75546,19)"
669+ sodipodi:nodetypes="cccc"
670+ id="path2992"
671+ d="m 20.111958,30.375 -1.625,1.59375 2.34375,-0.3125 c 0.21875,-0.71875 -0.1875,-1.0625 -0.71875,-1.28125 z"
672+ style="fill:url(#linearGradient4278);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
673+ inkscape:r_cx="true"
674+ inkscape:r_cy="true" />
675+ <path
676+ transform="translate(-29.75546,19)"
677+ sodipodi:nodetypes="ccccc"
678+ id="path3002"
679+ d="m 23.268208,27.25 1.5625,1.25 15.38734,-7.31867 C 39.773616,20.325286 38.976281,20.096733 38.314669,20.019068 L 23.268208,27.25 z"
680+ style="fill:#ffffff;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
681+ inkscape:r_cx="true"
682+ inkscape:r_cy="true" />
683+ <path
684+ transform="translate(-29.75546,19)"
685+ sodipodi:nodetypes="ccccc"
686+ id="path3004"
687+ d="m 25.143208,31.0625 0.1875,-0.75 15.23109,-7.1296 c 0,0 -0.11016,0.613627 -0.215879,0.74935 L 25.143208,31.0625 z"
688+ style="fill:#000000;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
689+ inkscape:r_cx="true"
690+ inkscape:r_cy="true" />
691+ </g>
692+ </g>
693+</svg>
694
695=== modified file 'default.properties'
696--- default.properties 2010-01-25 05:01:42 +0000
697+++ default.properties 2010-10-03 12:21:44 +0000
698@@ -10,5 +10,5 @@
699 # Indicates whether an apk should be generated for each density.
700 split.density=false
701 # Project target.
702-target=android-3
703+target=android-4
704 apk-configurations=
705
706=== added directory 'lib'
707=== added file 'lib/signpost-commonshttp4-1.2.1.1.jar'
708Binary files lib/signpost-commonshttp4-1.2.1.1.jar 1970-01-01 00:00:00 +0000 and lib/signpost-commonshttp4-1.2.1.1.jar 2010-10-03 12:21:44 +0000 differ
709=== added file 'lib/signpost-core-1.2.1.1.jar'
710Binary files lib/signpost-core-1.2.1.1.jar 1970-01-01 00:00:00 +0000 and lib/signpost-core-1.2.1.1.jar 2010-10-03 12:21:44 +0000 differ
711=== added directory 'res/anim'
712=== added file 'res/anim/pulse.xml'
713--- res/anim/pulse.xml 1970-01-01 00:00:00 +0000
714+++ res/anim/pulse.xml 2010-10-03 12:21:44 +0000
715@@ -0,0 +1,10 @@
716+<?xml version="1.0" encoding="utf-8"?>
717+
718+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
719+ android:interpolator="@android:anim/accelerate_interpolator"
720+ android:fromAlpha="0.0"
721+ android:toAlpha="1.0"
722+ android:duration="500"
723+ android:repeatMode="reverse"
724+ android:repeatCount="-1"
725+ />
726\ No newline at end of file
727
728=== added directory 'res/drawable-hdpi'
729=== added file 'res/drawable-hdpi/icon_actionbar_dot.png'
730Binary files res/drawable-hdpi/icon_actionbar_dot.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/icon_actionbar_dot.png 2010-10-03 12:21:44 +0000 differ
731=== added file 'res/drawable-hdpi/icon_actionbar_sync.png'
732Binary files res/drawable-hdpi/icon_actionbar_sync.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/icon_actionbar_sync.png 2010-10-03 12:21:44 +0000 differ
733=== added file 'res/drawable-hdpi/icon_actionbar_sync_background.png'
734Binary files res/drawable-hdpi/icon_actionbar_sync_background.png 1970-01-01 00:00:00 +0000 and res/drawable-hdpi/icon_actionbar_sync_background.png 2010-10-03 12:21:44 +0000 differ
735=== added directory 'res/drawable-mdpi'
736=== added file 'res/drawable-mdpi/icon_actionbar_dot.png'
737Binary files res/drawable-mdpi/icon_actionbar_dot.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/icon_actionbar_dot.png 2010-10-03 12:21:44 +0000 differ
738=== added file 'res/drawable-mdpi/icon_actionbar_sync.png'
739Binary files res/drawable-mdpi/icon_actionbar_sync.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/icon_actionbar_sync.png 2010-10-03 12:21:44 +0000 differ
740=== added file 'res/drawable-mdpi/icon_actionbar_sync_background.png'
741Binary files res/drawable-mdpi/icon_actionbar_sync_background.png 1970-01-01 00:00:00 +0000 and res/drawable-mdpi/icon_actionbar_sync_background.png 2010-10-03 12:21:44 +0000 differ
742=== modified file 'res/drawable/icon.png'
743Binary files res/drawable/icon.png 2009-04-03 03:19:35 +0000 and res/drawable/icon.png 2010-10-03 12:21:44 +0000 differ
744=== added file 'res/drawable/icon_actionbar_dot.png'
745Binary files res/drawable/icon_actionbar_dot.png 1970-01-01 00:00:00 +0000 and res/drawable/icon_actionbar_dot.png 2010-10-03 12:21:44 +0000 differ
746=== added file 'res/drawable/icon_actionbar_sync.png'
747Binary files res/drawable/icon_actionbar_sync.png 1970-01-01 00:00:00 +0000 and res/drawable/icon_actionbar_sync.png 2010-10-03 12:21:44 +0000 differ
748=== added file 'res/drawable/icon_actionbar_sync_background.png'
749Binary files res/drawable/icon_actionbar_sync_background.png 1970-01-01 00:00:00 +0000 and res/drawable/icon_actionbar_sync_background.png 2010-10-03 12:21:44 +0000 differ
750=== removed file 'res/drawable/icon_sync.png'
751Binary files res/drawable/icon_sync.png 2009-10-03 15:29:03 +0000 and res/drawable/icon_sync.png 1970-01-01 00:00:00 +0000 differ
752=== added file 'res/drawable/syncbutton_background.xml'
753--- res/drawable/syncbutton_background.xml 1970-01-01 00:00:00 +0000
754+++ res/drawable/syncbutton_background.xml 2010-10-03 12:21:44 +0000
755@@ -0,0 +1,16 @@
756+<?xml version="1.0" encoding="utf-8"?>
757+<selector
758+ xmlns:android="http://schemas.android.com/apk/res/android">
759+ <item
760+ android:state_focused="true"
761+ android:state_pressed="false"
762+ android:drawable="@drawable/syncbutton_background_focus" />
763+ <item
764+ android:state_focused="true"
765+ android:state_pressed="true"
766+ android:drawable="@drawable/syncbutton_background_pressed" />
767+ <item
768+ android:state_focused="false"
769+ android:state_pressed="true"
770+ android:drawable="@drawable/syncbutton_background_pressed" />
771+</selector>
772\ No newline at end of file
773
774=== added file 'res/drawable/syncbutton_background_focus.xml'
775--- res/drawable/syncbutton_background_focus.xml 1970-01-01 00:00:00 +0000
776+++ res/drawable/syncbutton_background_focus.xml 2010-10-03 12:21:44 +0000
777@@ -0,0 +1,4 @@
778+<?xml version="1.0" encoding="utf-8"?>
779+<shape xmlns:android="http://schemas.android.com/apk/res/android">
780+ <solid android:color="#ED6400"/>
781+</shape>
782\ No newline at end of file
783
784=== added file 'res/drawable/syncbutton_background_pressed.xml'
785--- res/drawable/syncbutton_background_pressed.xml 1970-01-01 00:00:00 +0000
786+++ res/drawable/syncbutton_background_pressed.xml 2010-10-03 12:21:44 +0000
787@@ -0,0 +1,4 @@
788+<?xml version="1.0" encoding="utf-8"?>
789+<shape xmlns:android="http://schemas.android.com/apk/res/android">
790+ <solid android:color="#EA9F00"/>
791+</shape>
792\ No newline at end of file
793
794=== added file 'res/layout/actionbar.xml'
795--- res/layout/actionbar.xml 1970-01-01 00:00:00 +0000
796+++ res/layout/actionbar.xml 2010-10-03 12:21:44 +0000
797@@ -0,0 +1,84 @@
798+<!--
799+ Tomdroid
800+ Tomboy on Android
801+ http://www.launchpad.net/tomdroid
802+
803+ Copyright 2010 Rodja Trappe <mail@rodja.net>
804+
805+ This file is part of Tomdroid.
806+
807+ Tomdroid is free software: you can redistribute it and/or modify
808+ it under the terms of the GNU General Public License as published by
809+ the Free Software Foundation, either version 3 of the License, or
810+ (at your option) any later version.
811+
812+ Tomdroid is distributed in the hope that it will be useful,
813+ but WITHOUT ANY WARRANTY; without even the implied warranty of
814+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
815+ GNU General Public License for more details.
816+
817+ You should have received a copy of the GNU General Public License
818+ along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
819+-->
820+<org.tomdroid.ui.Actionbar
821+ xmlns:android="http://schemas.android.com/apk/res/android"
822+ android:id="@+id/dhistory_row"
823+ android:layout_width="fill_parent"
824+ android:layout_height="40dip"
825+ android:background="#ddd"
826+ android:gravity="center_horizontal"
827+ android:orientation="horizontal">
828+ <ImageView
829+ android:id="@+id/action_icon"
830+ android:src="@drawable/icon"
831+ android:layout_height="wrap_content"
832+ android:layout_width="wrap_content"
833+ android:scaleType="fitStart"
834+ android:padding="4dip"
835+ />
836+ <TextView
837+ android:id="@+id/title"
838+ android:text="Tomdroid"
839+ android:layout_marginLeft="42dip"
840+ android:layout_height="wrap_content"
841+ android:layout_width="wrap_content"
842+ android:textSize="18dip"
843+ android:textStyle="bold"
844+ android:textColor="#FF555555"
845+ android:singleLine="true"
846+ android:ellipsize="marquee"
847+ android:fadingEdge="horizontal"
848+ android:fadingEdgeLength="5mm"
849+ android:paddingTop="10dip"
850+ android:paddingRight="30dip"
851+ />
852+ <ImageView
853+ android:id="@+id/sync"
854+ android:src="@drawable/icon_actionbar_sync_background"
855+ android:background="@drawable/syncbutton_background"
856+ android:layout_alignParentRight="true"
857+ android:layout_width="wrap_content"
858+ android:layout_height="fill_parent"
859+ android:scaleType="center"
860+ android:clickable="true"
861+ android:focusable="true"
862+ />
863+ <ImageView
864+ android:id="@+id/syncIcon"
865+ android:src="@drawable/icon_actionbar_sync"
866+ android:background="#00000000"
867+ android:layout_alignParentRight="true"
868+ android:layout_width="wrap_content"
869+ android:layout_height="fill_parent"
870+ android:scaleType="center"
871+ />
872+ <ImageView
873+ android:id="@+id/sync_dot"
874+ android:src="@drawable/icon_actionbar_dot"
875+ android:layout_alignParentRight="true"
876+ android:layout_width="wrap_content"
877+ android:layout_height="fill_parent"
878+ android:scaleType="center"
879+ android:visibility="invisible"
880+ />
881+</org.tomdroid.ui.Actionbar>
882\ No newline at end of file
883
884=== modified file 'res/layout/main.xml'
885--- res/layout/main.xml 2009-09-29 04:42:34 +0000
886+++ res/layout/main.xml 2010-10-03 12:21:44 +0000
887@@ -22,17 +22,23 @@
888 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
889 -->
890 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
891- android:layout_width="wrap_content"
892- android:layout_height="wrap_content"
893+ android:layout_width="fill_parent"
894+ android:layout_height="fill_parent"
895+ android:orientation="vertical"
896 >
897+
898+ <include android:id="@+id/actionbar" layout="@layout/actionbar" />
899
900- <ListView android:id="@+id/android:list"
901+ <ListView android:id="@android:id/android:list"
902 android:layout_width="fill_parent"
903 android:layout_height="fill_parent"
904+ android:divider="#00000000"
905+ android:dividerHeight="0px"
906+ android:cacheColorHint="#ffdddddd"
907 />
908 <TextView android:id="@+id/list_empty"
909 android:layout_width="wrap_content"
910 android:layout_height="wrap_content"
911 android:text="@string/strListEmptyNoNotes"
912 />
913-</LinearLayout>
914+</LinearLayout>
915\ No newline at end of file
916
917=== modified file 'res/layout/main_list_item.xml'
918--- res/layout/main_list_item.xml 2009-06-23 02:51:17 +0000
919+++ res/layout/main_list_item.xml 2010-10-03 12:21:44 +0000
920@@ -21,9 +21,28 @@
921 You should have received a copy of the GNU General Public License
922 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
923 -->
924-<TextView android:id="@+id/note_title" xmlns:android="http://schemas.android.com/apk/res/android"
925- android:layout_width="fill_parent"
926- android:layout_height="fill_parent"
927- android:textSize="24dp"
928- android:padding="10dip"
929- />
930+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
931+ android:orientation="vertical"
932+ android:layout_width="fill_parent"
933+ android:layout_height="wrap_content" >
934+ <TextView android:id="@+id/note_title" xmlns:android="http://schemas.android.com/apk/res/android"
935+ android:layout_width="fill_parent"
936+ android:layout_height="fill_parent"
937+ android:textStyle="bold"
938+ android:textColor="#FF555555"
939+ android:textSize="18dp"
940+ android:paddingTop="5dip"
941+ android:paddingLeft="5dip"
942+ android:paddingRight="5dip"
943+ />
944+ <TextView android:id="@+id/note_date" xmlns:android="http://schemas.android.com/apk/res/android"
945+ android:layout_width="fill_parent"
946+ android:layout_height="fill_parent"
947+ android:textColor="#FF555555"
948+ android:textSize="14dp"
949+ android:paddingTop="2dip"
950+ android:paddingLeft="5dip"
951+ android:paddingRight="5dip"
952+ android:paddingBottom="5dip"
953+ />
954+</LinearLayout>
955\ No newline at end of file
956
957=== modified file 'res/layout/note_view.xml'
958--- res/layout/note_view.xml 2009-06-17 11:02:51 +0000
959+++ res/layout/note_view.xml 2010-10-03 12:21:44 +0000
960@@ -21,22 +21,40 @@
961 You should have received a copy of the GNU General Public License
962 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
963 -->
964-
965+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
966+ android:layout_width="fill_parent"
967+ android:layout_height="fill_parent"
968+ android:orientation="vertical"
969+ >
970+<include android:id="@+id/actionbar" layout="@layout/actionbar" />
971 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
972 android:id="@+id/textScroller"
973 android:layout_width="fill_parent"
974 android:layout_height="fill_parent"
975- >
976-
977- <TextView
978- xmlns:android="http://schemas.android.com/apk/res/android"
979- android:id="@+id/content"
980- android:layout_width="wrap_content"
981- android:layout_height="wrap_content"
982- android:singleLine="false"
983- android:text="@string/strWait"
984- android:padding="10dip"
985- android:textColor="#ffb8bcb8"
986- />
987-
988+ android:background="#ffffffff"
989+ >
990+ <LinearLayout
991+ android:id="@+id/LinearLayout01"
992+ android:orientation="vertical"
993+ android:layout_width="fill_parent"
994+ android:layout_height="fill_parent">
995+
996+ <!-- <TextView
997+ android:id="@+id/title"
998+ android:layout_width="fill_parent"
999+ android:layout_height="wrap_content"
1000+ android:padding="10dip"
1001+ android:textStyle="bold" />-->
1002+
1003+ <TextView
1004+ xmlns:android="http://schemas.android.com/apk/res/android"
1005+ android:id="@+id/content"
1006+ android:layout_width="wrap_content"
1007+ android:layout_height="wrap_content"
1008+ android:singleLine="false"
1009+ android:text="@string/strWait"
1010+ android:padding="10dip"
1011+ android:textColor="#ffb8bcb8" />
1012+ </LinearLayout>
1013 </ScrollView>
1014+</LinearLayout>
1015\ No newline at end of file
1016
1017=== modified file 'res/menu/main.xml'
1018--- res/menu/main.xml 2010-01-31 22:30:28 +0000
1019+++ res/menu/main.xml 2010-10-03 12:21:44 +0000
1020@@ -23,17 +23,15 @@
1021 -->
1022 <menu xmlns:android="http://schemas.android.com/apk/res/android">
1023
1024- <item
1025- android:icon="@drawable/icon_sync"
1026- android:title="@string/menuSyncWithSD"
1027- android:id="@+id/menuSyncWithSD"
1028- />
1029-
1030-
1031 <item
1032 android:icon="@drawable/icon_about"
1033 android:title="@string/menuAbout"
1034 android:id="@+id/menuAbout"
1035 />
1036-
1037+
1038+
1039+ <item
1040+ android:icon="@android:drawable/ic_menu_preferences"
1041+ android:title="@string/menuPrefs"
1042+ android:id="@+id/menuPrefs"/>
1043 </menu>
1044
1045=== added file 'res/values/arrays.xml'
1046--- res/values/arrays.xml 1970-01-01 00:00:00 +0000
1047+++ res/values/arrays.xml 2010-10-03 12:21:44 +0000
1048@@ -0,0 +1,11 @@
1049+<?xml version="1.0" encoding="utf-8"?>
1050+<resources>
1051+<array name="sortOrderArray">
1052+ <item>sort_date</item>
1053+ <item>sort_title</item>
1054+</array>
1055+<array name="sortOrderValues">
1056+ <item>Date Modified</item>
1057+ <item>Note Title</item>
1058+</array>
1059+</resources>
1060
1061=== modified file 'res/values/strings.xml'
1062--- res/values/strings.xml 2010-01-31 21:54:19 +0000
1063+++ res/values/strings.xml 2010-10-03 12:21:44 +0000
1064@@ -33,7 +33,8 @@
1065 the tomdroid/ directory on your sdcard then press \"Menu\" and \"Sync with SD Card\".
1066 </string>
1067
1068- <string name="menuSyncWithSD">Sync with SD Card</string>
1069+ <string name="menuSync">Sync</string>
1070+ <string name="menuPrefs">Settings</string>
1071 <string name="menuAbout">About</string>
1072 <string name="strWelcome">
1073 Welcome to Tomdroid.
1074@@ -59,5 +60,15 @@
1075
1076 <!-- note-view.xml -->
1077 <string name="strWait">Please wait while note loads...</string>
1078-
1079+
1080+ <string name="prefSync">Synchronization</string>
1081+
1082+ <string name="prefSyncService">Service</string>
1083+ <string name="prefSyncServer">Server</string>
1084+ <string name="prefAuthenticate">Authenticate</string>
1085+
1086+ <string name="prefSyncConnectionFailed">The connection to the server has failed, please check that the address you entered is correct.</string>
1087+ <string name="prefServerEmpty">The server address changed but the new value is empty</string>
1088+
1089+
1090 </resources>
1091
1092=== added directory 'res/xml'
1093=== added file 'res/xml/preferences.xml'
1094--- res/xml/preferences.xml 1970-01-01 00:00:00 +0000
1095+++ res/xml/preferences.xml 2010-10-03 12:21:44 +0000
1096@@ -0,0 +1,18 @@
1097+<?xml version="1.0" encoding="utf-8"?>
1098+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
1099+
1100+ <PreferenceCategory android:title="@string/prefSync">
1101+
1102+ <ListPreference android:title="@string/prefSyncService"
1103+ android:dialogTitle="Choose the sync service to use"
1104+ android:key="sync_service"
1105+ android:defaultValue="tomboy-web"/>
1106+
1107+ <EditTextPreference android:key="sync_server"
1108+ android:title="@string/prefSyncServer"
1109+ android:positiveButtonText="@string/prefAuthenticate"
1110+ android:shouldDisableView="true"/>
1111+
1112+ </PreferenceCategory>
1113+
1114+</PreferenceScreen>
1115\ No newline at end of file
1116
1117=== modified file 'src/org/tomdroid/Note.java'
1118--- src/org/tomdroid/Note.java 2010-02-18 03:58:09 +0000
1119+++ src/org/tomdroid/Note.java 2010-10-03 12:21:44 +0000
1120@@ -4,6 +4,7 @@
1121 * http://www.launchpad.net/tomdroid
1122 *
1123 * Copyright 2008, 2009 Olivier Bilodeau <olivier@bottomlesspit.org>
1124+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1125 *
1126 * This file is part of Tomdroid.
1127 *
1128@@ -26,7 +27,10 @@
1129 import java.util.regex.Matcher;
1130 import java.util.regex.Pattern;
1131
1132+import org.json.JSONObject;
1133+import org.json.JSONArray;
1134 import org.tomdroid.util.NoteContentBuilder;
1135+import org.tomdroid.util.XmlUtils;
1136
1137 import android.os.Handler;
1138 import android.text.SpannableStringBuilder;
1139@@ -43,6 +47,7 @@
1140 public static final String MODIFIED_DATE = "modified_date";
1141 public static final String URL = "url";
1142 public static final String FILE = "file";
1143+ public static final String TAGS = "tags";
1144 public static final String NOTE_CONTENT = "content";
1145
1146 // Logging info
1147@@ -52,9 +57,9 @@
1148 // TODO this is a weird yellow that was usable for the android emulator, I must confirm this for real usage
1149 public static final int NOTE_HIGHLIGHT_COLOR = 0xFFFFFF77;
1150 public static final String NOTE_MONOSPACE_TYPEFACE = "monospace";
1151- public static final float NOTE_SIZE_SMALL_FACTOR = 0.8f;
1152- public static final float NOTE_SIZE_LARGE_FACTOR = 1.3f;
1153- public static final float NOTE_SIZE_HUGE_FACTOR = 1.6f;
1154+ public static final float NOTE_SIZE_SMALL_FACTOR = 1.0f;
1155+ public static final float NOTE_SIZE_LARGE_FACTOR = 1.5f;
1156+ public static final float NOTE_SIZE_HUGE_FACTOR = 1.8f;
1157
1158 // Members
1159 private SpannableStringBuilder noteContent;
1160@@ -62,6 +67,7 @@
1161 private String url;
1162 private String fileName;
1163 private String title;
1164+ private String tags;
1165 private Time lastChangeDate;
1166 private int dbId;
1167 private UUID guid;
1168@@ -73,8 +79,32 @@
1169 ".+" + // matches what we are getting rid of
1170 "([-\\+]\\d{2}:\\d{2})"); // matches timezone (-xx:xx or +xx:xx)
1171
1172- public Note() {}
1173-
1174+ public Note() {
1175+ tags = new String();
1176+ }
1177+
1178+ public Note(JSONObject json) {
1179+
1180+ // These methods return an empty string if the key is not found
1181+ setTitle(XmlUtils.unescape(json.optString("title")));
1182+ setGuid(json.optString("guid"));
1183+ setLastChangeDate(json.optString("last-change-date"));
1184+ setXmlContent(json.optString("note-content"));
1185+ JSONArray jtags = json.optJSONArray("tags");
1186+ String tag;
1187+ tags = new String();
1188+ if (jtags != null) {
1189+ for (int i = 0; i < jtags.length(); i++ ) {
1190+ tag = jtags.optString(i);
1191+ tags += tag + ",";
1192+ }
1193+ }
1194+ }
1195+
1196+ public String getTags() {
1197+ return tags;
1198+ }
1199+
1200 public String getUrl() {
1201 return url;
1202 }
1203@@ -138,7 +168,7 @@
1204 public void setGuid(String guid) {
1205 this.guid = UUID.fromString(guid);
1206 }
1207-
1208+
1209 // TODO: should this handler passed around evolve into an observer pattern?
1210 public SpannableStringBuilder getNoteContent(Handler handler) {
1211
1212
1213=== modified file 'src/org/tomdroid/NoteManager.java'
1214--- src/org/tomdroid/NoteManager.java 2010-01-25 05:01:42 +0000
1215+++ src/org/tomdroid/NoteManager.java 2010-10-03 12:21:44 +0000
1216@@ -23,12 +23,15 @@
1217 package org.tomdroid;
1218
1219 import org.tomdroid.ui.Tomdroid;
1220+import org.tomdroid.util.NoteListCursorAdapter;
1221+import org.tomdroid.util.Preferences;
1222
1223 import android.app.Activity;
1224 import android.content.ContentResolver;
1225 import android.content.ContentValues;
1226 import android.database.Cursor;
1227 import android.net.Uri;
1228+import android.preference.ListPreference;
1229 import android.util.Log;
1230 import android.widget.ListAdapter;
1231 import android.widget.SimpleCursorAdapter;
1232@@ -36,8 +39,9 @@
1233 public class NoteManager {
1234
1235 public static final String[] FULL_PROJECTION = { Note.ID, Note.TITLE, Note.FILE, Note.NOTE_CONTENT, Note.MODIFIED_DATE };
1236- public static final String[] LIST_PROJECTION = { Note.ID, Note.TITLE };
1237+ public static final String[] LIST_PROJECTION = { Note.ID, Note.TITLE, Note.MODIFIED_DATE };
1238 public static final String[] TITLE_PROJECTION = { Note.TITLE };
1239+ public static final String[] GUID_PROJECTION = { Note.ID, Note.GUID };
1240 public static final String[] ID_PROJECTION = { Note.ID };
1241 public static final String[] EMPTY_PROJECTION = {};
1242
1243@@ -95,6 +99,7 @@
1244 // Notice that we store the date in UTC because sqlite doesn't handle RFC3339 timezone information
1245 values.put(Note.MODIFIED_DATE, note.getLastChangeDate().format3339(false));
1246 values.put(Note.NOTE_CONTENT, note.getXmlContent());
1247+ values.put(Note.TAGS, note.getTags());
1248
1249 if (managedCursor.getCount() == 0) {
1250
1251@@ -113,18 +118,42 @@
1252 }
1253 }
1254
1255+ public static boolean deleteNote(Activity activity, int id)
1256+ {
1257+ Uri uri = Uri.parse(Tomdroid.CONTENT_URI+"/"+id);
1258+
1259+ ContentResolver cr = activity.getContentResolver();
1260+ int result = cr.delete(uri, null, null);
1261+
1262+ if(result > 0)
1263+ return true;
1264+ else
1265+ return false;
1266+ }
1267+
1268+ public static Cursor getAllNotes(Activity activity, Boolean includeNotebookTemplates) {
1269+ // get a cursor representing all notes from the NoteProvider
1270+ Uri notes = Tomdroid.CONTENT_URI;
1271+ String where = null;
1272+ String orderBy;
1273+ if (!includeNotebookTemplates) {
1274+ where = Note.TAGS + " NOT LIKE '%" + "system:template" + "%'";
1275+ }
1276+ orderBy = Note.MODIFIED_DATE + " DESC";
1277+ return activity.managedQuery(notes, LIST_PROJECTION, where, null, orderBy);
1278+ }
1279+
1280+
1281 public static ListAdapter getListAdapter(Activity activity) {
1282-
1283- // get a cursor representing all notes from the NoteProvider
1284- Uri notes = Tomdroid.CONTENT_URI;
1285- Cursor notesCursor = activity.managedQuery(notes, LIST_PROJECTION, null, null, null);
1286+
1287+ Cursor notesCursor = getAllNotes(activity, false);
1288
1289 // set up an adapter binding the TITLE field of the cursor to the list item
1290- String[] from = new String[] { Note.TITLE };
1291- int[] to = new int[] { R.id.note_title };
1292- return new SimpleCursorAdapter(activity, R.layout.main_list_item, notesCursor, from, to);
1293+ String[] from = new String[] { Note.TITLE, Note.MODIFIED_DATE };
1294+ int[] to = new int[] { R.id.note_title, R.id.note_date };
1295+ return new NoteListCursorAdapter(activity, R.layout.main_list_item, notesCursor, from, to);
1296 }
1297-
1298+
1299 // gets the titles of the notes present in the db, used in ViewNote.buildLinkifyPattern()
1300 public static Cursor getTitles(Activity activity) {
1301
1302@@ -132,6 +161,13 @@
1303 return activity.managedQuery(Tomdroid.CONTENT_URI, TITLE_PROJECTION, null, null, null);
1304 }
1305
1306+ // gets the ids of the notes present in the db, used in SyncService.deleteNotes()
1307+ public static Cursor getGuids(Activity activity) {
1308+
1309+ // get a cursor containing the notes guids
1310+ return activity.managedQuery(Tomdroid.CONTENT_URI, GUID_PROJECTION, null, null, null);
1311+ }
1312+
1313 public static int getNoteId(Activity activity, String title) {
1314
1315 int id = 0;
1316
1317=== modified file 'src/org/tomdroid/NoteProvider.java'
1318--- src/org/tomdroid/NoteProvider.java 2010-01-25 05:01:42 +0000
1319+++ src/org/tomdroid/NoteProvider.java 2010-10-03 12:21:44 +0000
1320@@ -4,6 +4,7 @@
1321 * http://www.launchpad.net/tomdroid
1322 *
1323 * Copyright 2009 Olivier Bilodeau <olivier@bottomlesspit.org>
1324+ * Copyright 2009 Benoit Garret <benoit.garret_launchpad@gadz.org>
1325 *
1326 * This file is part of Tomdroid.
1327 *
1328@@ -68,7 +69,7 @@
1329 // --
1330 private static final String DATABASE_NAME = "tomdroid-notes.db";
1331 private static final String DB_TABLE_NOTES = "notes";
1332- private static final int DB_VERSION = 2;
1333+ private static final int DB_VERSION = 3;
1334 private static final String DEFAULT_SORT_ORDER = Note.MODIFIED_DATE + " DESC";
1335
1336 private static HashMap<String, String> notesProjectionMap;
1337@@ -99,7 +100,8 @@
1338 + Note.TITLE + " TEXT,"
1339 + Note.FILE + " TEXT,"
1340 + Note.NOTE_CONTENT + " TEXT,"
1341- + Note.MODIFIED_DATE + " STRING"
1342+ + Note.MODIFIED_DATE + " STRING,"
1343+ + Note.TAGS + " STRING"
1344 + ");");
1345 }
1346
1347@@ -296,6 +298,7 @@
1348 notesProjectionMap.put(Note.TITLE, Note.TITLE);
1349 notesProjectionMap.put(Note.FILE, Note.FILE);
1350 notesProjectionMap.put(Note.NOTE_CONTENT, Note.NOTE_CONTENT);
1351+ notesProjectionMap.put(Note.TAGS, Note.TAGS);
1352 notesProjectionMap.put(Note.MODIFIED_DATE, Note.MODIFIED_DATE);
1353 }
1354 }
1355
1356=== added directory 'src/org/tomdroid/sync'
1357=== added file 'src/org/tomdroid/sync/ServiceAuth.java'
1358--- src/org/tomdroid/sync/ServiceAuth.java 1970-01-01 00:00:00 +0000
1359+++ src/org/tomdroid/sync/ServiceAuth.java 2010-10-03 12:21:44 +0000
1360@@ -0,0 +1,33 @@
1361+/*
1362+ * Tomdroid
1363+ * Tomboy on Android
1364+ * http://www.launchpad.net/tomdroid
1365+ *
1366+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1367+ *
1368+ * This file is part of Tomdroid.
1369+ *
1370+ * Tomdroid is free software: you can redistribute it and/or modify
1371+ * it under the terms of the GNU General Public License as published by
1372+ * the Free Software Foundation, either version 3 of the License, or
1373+ * (at your option) any later version.
1374+ *
1375+ * Tomdroid is distributed in the hope that it will be useful,
1376+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1377+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1378+ * GNU General Public License for more details.
1379+ *
1380+ * You should have received a copy of the GNU General Public License
1381+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1382+ */
1383+package org.tomdroid.sync;
1384+
1385+import android.net.Uri;
1386+import android.os.Handler;
1387+
1388+public interface ServiceAuth {
1389+
1390+ public boolean isConfigured();
1391+ public void getAuthUri(final String server, Handler handler);
1392+ public void remoteAuthComplete(Uri uri, Handler handler);
1393+}
1394
1395=== added file 'src/org/tomdroid/sync/SyncManager.java'
1396--- src/org/tomdroid/sync/SyncManager.java 1970-01-01 00:00:00 +0000
1397+++ src/org/tomdroid/sync/SyncManager.java 2010-10-03 12:21:44 +0000
1398@@ -0,0 +1,106 @@
1399+/*
1400+ * Tomdroid
1401+ * Tomboy on Android
1402+ * http://www.launchpad.net/tomdroid
1403+ *
1404+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1405+ *
1406+ * This file is part of Tomdroid.
1407+ *
1408+ * Tomdroid is free software: you can redistribute it and/or modify
1409+ * it under the terms of the GNU General Public License as published by
1410+ * the Free Software Foundation, either version 3 of the License, or
1411+ * (at your option) any later version.
1412+ *
1413+ * Tomdroid is distributed in the hope that it will be useful,
1414+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1415+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1416+ * GNU General Public License for more details.
1417+ *
1418+ * You should have received a copy of the GNU General Public License
1419+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1420+ */
1421+package org.tomdroid.sync;
1422+
1423+import java.io.FileNotFoundException;
1424+import java.util.ArrayList;
1425+
1426+import org.tomdroid.sync.sd.SdCardSyncService;
1427+import org.tomdroid.sync.web.SnowySyncService;
1428+import org.tomdroid.util.Preferences;
1429+
1430+import android.app.Activity;
1431+import android.os.Handler;
1432+import android.util.Log;
1433+
1434+public class SyncManager {
1435+
1436+ private static final String TAG = "SyncManager";
1437+
1438+ private ArrayList<SyncService> services = new ArrayList<SyncService>();
1439+
1440+ public SyncManager() {
1441+ createServices();
1442+ }
1443+
1444+ public ArrayList<SyncService> getServices() {
1445+ return services;
1446+ }
1447+
1448+ public SyncService getService(String name) {
1449+
1450+ for (int i = 0; i < services.size(); i++) {
1451+ SyncService service = services.get(i);
1452+ if (name.equals(service.getName()))
1453+ return service;
1454+ }
1455+
1456+ return null;
1457+ }
1458+
1459+ public void startSynchronization() {
1460+
1461+ SyncService service = getCurrentService();
1462+ service.startSynchronization();
1463+ }
1464+
1465+ public SyncService getCurrentService() {
1466+ String serviceName = Preferences.getString(Preferences.Key.SYNC_SERVICE);
1467+ return getService(serviceName);
1468+ }
1469+
1470+ private static SyncManager instance = null;
1471+ private static Activity activity;
1472+ private static Handler handler;
1473+
1474+ public static SyncManager getInstance() {
1475+
1476+ if (instance == null)
1477+ instance = new SyncManager();
1478+
1479+ return instance;
1480+ }
1481+
1482+ public static void setActivity(Activity a) {
1483+ activity = a;
1484+ getInstance().createServices();
1485+ }
1486+
1487+ public static void setHandler(Handler h) {
1488+ handler = h;
1489+ getInstance().createServices();
1490+ }
1491+
1492+ private void createServices() {
1493+ services.clear();
1494+
1495+ services.add(new SnowySyncService(activity, handler));
1496+
1497+ try {
1498+ services.add(new SdCardSyncService(activity, handler));
1499+ } catch (FileNotFoundException e) {
1500+ // TODO Auto-generated catch block
1501+ e.printStackTrace();
1502+ }
1503+ }
1504+}
1505
1506=== added file 'src/org/tomdroid/sync/SyncService.java'
1507--- src/org/tomdroid/sync/SyncService.java 1970-01-01 00:00:00 +0000
1508+++ src/org/tomdroid/sync/SyncService.java 2010-10-03 12:21:44 +0000
1509@@ -0,0 +1,196 @@
1510+/*
1511+ * Tomdroid
1512+ * Tomboy on Android
1513+ * http://www.launchpad.net/tomdroid
1514+ *
1515+ * Copyright 2009, Olivier Bilodeau <olivier@bottomlesspit.org>
1516+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1517+ * Copyright 2010, Rodja Trappe <mail@rodja.net>
1518+ *
1519+ * This file is part of Tomdroid.
1520+ *
1521+ * Tomdroid is free software: you can redistribute it and/or modify
1522+ * it under the terms of the GNU General Public License as published by
1523+ * the Free Software Foundation, either version 3 of the License, or
1524+ * (at your option) any later version.
1525+ *
1526+ * Tomdroid is distributed in the hope that it will be useful,
1527+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1528+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1529+ * GNU General Public License for more details.
1530+ *
1531+ * You should have received a copy of the GNU General Public License
1532+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1533+ */
1534+package org.tomdroid.sync;
1535+
1536+import java.util.ArrayList;
1537+
1538+import java.util.concurrent.ExecutorService;
1539+import java.util.concurrent.Executors;
1540+
1541+import org.tomdroid.Note;
1542+import org.tomdroid.NoteManager;
1543+import org.tomdroid.ui.Tomdroid;
1544+
1545+import android.app.Activity;
1546+import android.database.Cursor;
1547+import android.os.Handler;
1548+import android.os.Message;
1549+import android.util.Log;
1550+import android.widget.Toast;
1551+
1552+public abstract class SyncService {
1553+
1554+ private static final String TAG = "SyncService";
1555+
1556+ private Activity activity;
1557+ private final ExecutorService pool;
1558+ private final static int poolSize = 1;
1559+
1560+ private Handler handler;
1561+ private int syncProgress = 100;
1562+
1563+ // handler messages
1564+ public final static int PARSING_COMPLETE = 1;
1565+ public final static int PARSING_FAILED = 2;
1566+ public final static int PARSING_NO_NOTES = 3;
1567+ public final static int NO_INTERNET = 4;
1568+ public final static int SYNC_PROGRESS = 5;
1569+
1570+ public SyncService(Activity activity, Handler handler) {
1571+
1572+ this.activity = activity;
1573+ this.handler = handler;
1574+ pool = Executors.newFixedThreadPool(poolSize);
1575+ }
1576+
1577+ public void startSynchronization() {
1578+
1579+ if (syncProgress != 100){
1580+ Toast.makeText(activity, "Sync already in prgress", Toast.LENGTH_SHORT).show();
1581+ return;
1582+ }
1583+
1584+ sync();
1585+ }
1586+
1587+ protected abstract void sync();
1588+ public abstract boolean needsServer();
1589+ public abstract boolean needsAuth();
1590+
1591+ /**
1592+ * @return An unique identifier, not visible to the user.
1593+ */
1594+
1595+ public abstract String getName();
1596+
1597+ /**
1598+ * @return An human readable name, used in the preferences to distinguish the different sync services.
1599+ */
1600+
1601+ public abstract String getDescription();
1602+
1603+ /**
1604+ * Execute code in a separate thread.
1605+ * Use this for blocking and/or cpu intensive operations and thus avoid blocking the UI.
1606+ *
1607+ * @param r The Runner subclass to execute
1608+ */
1609+
1610+ protected void execInThread(Runnable r) {
1611+
1612+ pool.execute(r);
1613+ }
1614+
1615+ /**
1616+ * Insert a note in the content provider. The identifier for the notes is the guid.
1617+ *
1618+ * @param note The note to insert.
1619+ */
1620+
1621+ protected void insertNote(Note note, boolean syncFinished) {
1622+
1623+ NoteManager.putNote(this.activity, note);
1624+
1625+ // if last note warn in UI that we are done
1626+ if (syncFinished) {
1627+ handler.sendEmptyMessage(PARSING_COMPLETE);
1628+ }
1629+ }
1630+
1631+ /**
1632+ * Delete notes in the content provider. The guids passed identify the notes existing
1633+ * on the remote end (ie. that shouldn't be deleted).
1634+ *
1635+ * @param remoteGuids The notes NOT to delete.
1636+ */
1637+
1638+ protected void deleteNotes(ArrayList<String> remoteGuids) {
1639+
1640+ Cursor localGuids = NoteManager.getGuids(this.activity);
1641+
1642+ // cursor must not be null and must return more than 0 entry
1643+ if (!(localGuids == null || localGuids.getCount() == 0)) {
1644+
1645+ String localGuid;
1646+
1647+ localGuids.moveToFirst();
1648+
1649+ do {
1650+ localGuid = localGuids.getString(localGuids.getColumnIndexOrThrow(Note.GUID));
1651+
1652+ if(!remoteGuids.contains(localGuid)) {
1653+ int id = localGuids.getInt(localGuids.getColumnIndexOrThrow(Note.ID));
1654+ NoteManager.deleteNote(this.activity, id);
1655+ }
1656+
1657+ } while (localGuids.moveToNext());
1658+
1659+ } else {
1660+
1661+ // TODO send an error to the user
1662+ if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "Cursor returned null or 0 notes");
1663+ }
1664+ }
1665+
1666+ /**
1667+ * Send a message to the main UI.
1668+ *
1669+ * @param message The message id to send, the PARSING_* or NO_INTERNET attributes can be used.
1670+ */
1671+
1672+ protected void sendMessage(int message) {
1673+
1674+ handler.sendEmptyMessage(message);
1675+ }
1676+
1677+ /**
1678+ * Update the synchronization progress
1679+ *
1680+ * @param progress
1681+ */
1682+
1683+ protected void setSyncProgress(int progress) {
1684+ synchronized (TAG) {
1685+ Log.v(TAG, "sync progress: " + progress);
1686+ Message progressMessage = new Message();
1687+ progressMessage.what = SYNC_PROGRESS;
1688+ progressMessage.arg1 = progress;
1689+ progressMessage.arg2 = syncProgress;
1690+
1691+ handler.sendMessage(progressMessage);
1692+ syncProgress = progress;
1693+ }
1694+ }
1695+
1696+ protected int getSyncProgress(){
1697+ synchronized (TAG) {
1698+ return syncProgress;
1699+ }
1700+ }
1701+
1702+ public boolean isSyncable() {
1703+ return getSyncProgress() == 100;
1704+ }
1705+}
1706
1707=== added directory 'src/org/tomdroid/sync/sd'
1708=== renamed file 'src/org/tomdroid/xml/NoteHandler.java' => 'src/org/tomdroid/sync/sd/NoteHandler.java'
1709--- src/org/tomdroid/xml/NoteHandler.java 2010-02-16 05:18:09 +0000
1710+++ src/org/tomdroid/sync/sd/NoteHandler.java 2010-10-03 12:21:44 +0000
1711@@ -4,6 +4,7 @@
1712 * http://www.launchpad.net/tomdroid
1713 *
1714 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
1715+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1716 *
1717 * This file is part of Tomdroid.
1718 *
1719@@ -20,7 +21,7 @@
1720 * You should have received a copy of the GNU General Public License
1721 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1722 */
1723-package org.tomdroid.xml;
1724+package org.tomdroid.sync.sd;
1725
1726 import org.tomdroid.Note;
1727 import org.xml.sax.Attributes;
1728
1729=== renamed file 'src/org/tomdroid/util/AsyncNoteLoaderAndParser.java' => 'src/org/tomdroid/sync/sd/SdCardSyncService.java'
1730--- src/org/tomdroid/util/AsyncNoteLoaderAndParser.java 2010-02-16 05:18:09 +0000
1731+++ src/org/tomdroid/sync/sd/SdCardSyncService.java 2010-10-03 12:21:44 +0000
1732@@ -4,6 +4,8 @@
1733 * http://www.launchpad.net/tomdroid
1734 *
1735 * Copyright 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
1736+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1737+ * Copyright 2010, Rodja Trappe <mail@rodja.net>
1738 *
1739 * This file is part of Tomdroid.
1740 *
1741@@ -20,17 +22,16 @@
1742 * You should have received a copy of the GNU General Public License
1743 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1744 */
1745-package org.tomdroid.util;
1746+package org.tomdroid.sync.sd;
1747
1748 import java.io.BufferedReader;
1749 import java.io.File;
1750 import java.io.FileInputStream;
1751+import java.io.FileNotFoundException;
1752 import java.io.FilenameFilter;
1753 import java.io.IOException;
1754 import java.io.InputStreamReader;
1755 import java.io.Reader;
1756-import java.util.concurrent.ExecutorService;
1757-import java.util.concurrent.Executors;
1758 import java.util.regex.Matcher;
1759 import java.util.regex.Pattern;
1760
1761@@ -39,9 +40,8 @@
1762 import javax.xml.parsers.SAXParserFactory;
1763
1764 import org.tomdroid.Note;
1765-import org.tomdroid.NoteManager;
1766+import org.tomdroid.sync.SyncService;
1767 import org.tomdroid.ui.Tomdroid;
1768-import org.tomdroid.xml.NoteHandler;
1769 import org.xml.sax.InputSource;
1770 import org.xml.sax.SAXException;
1771 import org.xml.sax.XMLReader;
1772@@ -51,55 +51,75 @@
1773 import android.util.Log;
1774 import android.util.TimeFormatException;
1775
1776-public class AsyncNoteLoaderAndParser {
1777-
1778- // thread pool info
1779- private final ExecutorService pool;
1780- private final static int poolSize = 1;
1781-
1782- // members
1783- private Activity activity;
1784+public class SdCardSyncService extends SyncService {
1785+
1786 private File path;
1787- private Handler handler;
1788-
1789- // handler messages
1790- public final static int PARSING_COMPLETE = 1;
1791- public final static int PARSING_FAILED = 2;
1792- public final static int PARSING_NO_NOTES = 3;
1793+ private int numberOfFilesToSync = 0;
1794
1795 // regexp for <note-content..>...</note-content>
1796- private static Pattern note_content = Pattern.compile(".*(<note-content.*>.*<\\/note-content>).*", Pattern.CASE_INSENSITIVE+Pattern.DOTALL);
1797+ private static Pattern note_content = Pattern.compile("<note-content.*>(.*)<\\/note-content>", Pattern.CASE_INSENSITIVE+Pattern.DOTALL);
1798
1799 // logging related
1800- private final static String TAG = "AsyncNoteLoaderAndParser";
1801-
1802- public AsyncNoteLoaderAndParser(Activity a, File path) {
1803- this.activity = a;
1804- this.path = path;
1805-
1806- pool = Executors.newFixedThreadPool(poolSize);
1807- }
1808-
1809- public void readAndParseNotes(Handler hndl) {
1810- handler = hndl;
1811+ private final static String TAG = "SdCardSyncService";
1812+
1813+ public SdCardSyncService(Activity activity, Handler handler) throws FileNotFoundException {
1814+ super(activity, handler);
1815+
1816+ path = new File(Tomdroid.NOTES_PATH);
1817+
1818+ if (!path.exists())
1819+ path.mkdir();
1820+ }
1821+
1822+ @Override
1823+ public String getDescription() {
1824+ return "SD Card";
1825+ }
1826+
1827+ @Override
1828+ public String getName() {
1829+ return "sdcard";
1830+ }
1831+
1832+ @Override
1833+ public boolean needsServer() {
1834+ return false;
1835+ }
1836+
1837+ @Override
1838+ public boolean needsAuth() {
1839+ return false;
1840+ }
1841+
1842+ @Override
1843+ protected void sync() {
1844+
1845+ setSyncProgress(0);
1846+
1847+ // start loading local notes
1848+ if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "Loading local notes");
1849+
1850 File[] fileList = path.listFiles(new NotesFilter());
1851+ numberOfFilesToSync = fileList.length;
1852
1853 // If there are no notes, warn the UI through an empty message
1854- if (fileList.length == 0) {
1855+ if (fileList == null || fileList.length == 0) {
1856 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "There are no notes in "+path);
1857- handler.sendEmptyMessage(PARSING_NO_NOTES);
1858+ sendMessage(PARSING_NO_NOTES);
1859+ setSyncProgress(100);
1860 return;
1861 }
1862
1863 // every but the last note
1864 for(int i = 0; i < fileList.length-1; i++) {
1865+ // TODO better progress reporting from within the workers
1866
1867 // give a filename to a thread and ask to parse it
1868- pool.execute(new Worker(fileList[i], false));
1869+ execInThread(new Worker(fileList[i], false));
1870 }
1871
1872 // last task, warn it so it'll warn UI when done
1873- pool.execute(new Worker(fileList[fileList.length-1], true));
1874+ execInThread(new Worker(fileList[fileList.length-1], true));
1875 }
1876
1877 /**
1878@@ -145,7 +165,7 @@
1879
1880 // Get the XMLReader of the SAXParser we created
1881 XMLReader xr = sp.getXMLReader();
1882-
1883+
1884 // Create a new ContentHandler, send it this note to fill and apply it to the XML-Reader
1885 NoteHandler xmlHandler = new NoteHandler(note);
1886 xr.setContentHandler(xmlHandler);
1887@@ -157,7 +177,7 @@
1888
1889 if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "parsing note");
1890 xr.parse(is);
1891-
1892+
1893 // TODO wrap and throw a new exception here
1894 } catch (ParserConfigurationException e) {
1895 e.printStackTrace();
1896@@ -168,7 +188,8 @@
1897 } catch (TimeFormatException e) {
1898 e.printStackTrace();
1899 if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Problem parsing the note's date and time");
1900- handler.sendEmptyMessage(PARSING_FAILED);
1901+ sendMessage(PARSING_FAILED);
1902+ onWorkDone();
1903 return;
1904 }
1905
1906@@ -202,12 +223,15 @@
1907 if (Tomdroid.LOGGING_ENABLED) Log.w(TAG, "Something went wrong trying to read the note");
1908 }
1909
1910- NoteManager.putNote(AsyncNoteLoaderAndParser.this.activity, note);
1911-
1912- // if last note warn in UI that we are done
1913- if (isLast) {
1914- handler.sendEmptyMessage(PARSING_COMPLETE);
1915- }
1916+ insertNote(note, isLast);
1917+ onWorkDone();
1918+ }
1919+
1920+ private void onWorkDone(){
1921+ if (isLast)
1922+ setSyncProgress(100);
1923+ else
1924+ setSyncProgress((int) (getSyncProgress() + 100.0 / numberOfFilesToSync));
1925 }
1926 }
1927 }
1928
1929=== added directory 'src/org/tomdroid/sync/web'
1930=== added file 'src/org/tomdroid/sync/web/AnonymousConnection.java'
1931--- src/org/tomdroid/sync/web/AnonymousConnection.java 1970-01-01 00:00:00 +0000
1932+++ src/org/tomdroid/sync/web/AnonymousConnection.java 2010-10-03 12:21:44 +0000
1933@@ -0,0 +1,62 @@
1934+/*
1935+ * Tomdroid
1936+ * Tomboy on Android
1937+ * http://www.launchpad.net/tomdroid
1938+ *
1939+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
1940+ *
1941+ * This file is part of Tomdroid.
1942+ *
1943+ * Tomdroid is free software: you can redistribute it and/or modify
1944+ * it under the terms of the GNU General Public License as published by
1945+ * the Free Software Foundation, either version 3 of the License, or
1946+ * (at your option) any later version.
1947+ *
1948+ * Tomdroid is distributed in the hope that it will be useful,
1949+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1950+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1951+ * GNU General Public License for more details.
1952+ *
1953+ * You should have received a copy of the GNU General Public License
1954+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
1955+ */
1956+package org.tomdroid.sync.web;
1957+
1958+import java.io.UnsupportedEncodingException;
1959+import java.net.UnknownHostException;
1960+
1961+import org.apache.http.HttpResponse;
1962+import org.apache.http.client.methods.HttpGet;
1963+import org.apache.http.client.methods.HttpPut;
1964+import org.apache.http.entity.StringEntity;
1965+
1966+public class AnonymousConnection extends WebConnection {
1967+
1968+ @Override
1969+ public String get(String uri) throws UnknownHostException
1970+ {
1971+ // Prepare a request object
1972+ HttpGet httpGet = new HttpGet(uri);
1973+ HttpResponse response = execute(httpGet);
1974+ return parseResponse(response);
1975+ }
1976+
1977+ @Override
1978+ public String put(String uri, String data) throws UnknownHostException {
1979+
1980+ // Prepare a request object
1981+ HttpPut httpPut = new HttpPut(uri);
1982+
1983+ try {
1984+ // The default http content charset is ISO-8859-1, JSON requires UTF-8
1985+ httpPut.setEntity(new StringEntity(data, "UTF-8"));
1986+ } catch (UnsupportedEncodingException e1) {
1987+ e1.printStackTrace();
1988+ return null;
1989+ }
1990+
1991+ httpPut.setHeader("Content-Type", "application/json");
1992+ HttpResponse response = execute(httpPut);
1993+ return parseResponse(response);
1994+ }
1995+}
1996
1997=== added file 'src/org/tomdroid/sync/web/OAuthConnection.java'
1998--- src/org/tomdroid/sync/web/OAuthConnection.java 1970-01-01 00:00:00 +0000
1999+++ src/org/tomdroid/sync/web/OAuthConnection.java 2010-10-03 12:21:44 +0000
2000@@ -0,0 +1,278 @@
2001+/*
2002+ * Tomdroid
2003+ * Tomboy on Android
2004+ * http://www.launchpad.net/tomdroid
2005+ *
2006+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
2007+ *
2008+ * This file is part of Tomdroid.
2009+ *
2010+ * Tomdroid is free software: you can redistribute it and/or modify
2011+ * it under the terms of the GNU General Public License as published by
2012+ * the Free Software Foundation, either version 3 of the License, or
2013+ * (at your option) any later version.
2014+ *
2015+ * Tomdroid is distributed in the hope that it will be useful,
2016+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2017+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2018+ * GNU General Public License for more details.
2019+ *
2020+ * You should have received a copy of the GNU General Public License
2021+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2022+ */
2023+package org.tomdroid.sync.web;
2024+
2025+import java.io.UnsupportedEncodingException;
2026+import java.net.UnknownHostException;
2027+
2028+import oauth.signpost.OAuthConsumer;
2029+import oauth.signpost.OAuthProvider;
2030+import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
2031+import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
2032+import oauth.signpost.exception.OAuthCommunicationException;
2033+import oauth.signpost.exception.OAuthExpectationFailedException;
2034+import oauth.signpost.exception.OAuthMessageSignerException;
2035+import oauth.signpost.exception.OAuthNotAuthorizedException;
2036+
2037+import org.apache.http.HttpRequest;
2038+import org.apache.http.HttpResponse;
2039+import org.apache.http.client.methods.HttpGet;
2040+import org.apache.http.client.methods.HttpPut;
2041+import org.apache.http.entity.StringEntity;
2042+import org.json.JSONException;
2043+import org.json.JSONObject;
2044+import org.tomdroid.ui.Tomdroid;
2045+import org.tomdroid.util.Preferences;
2046+
2047+import android.net.Uri;
2048+import android.util.Log;
2049+
2050+public class OAuthConnection extends WebConnection {
2051+
2052+ private static final String TAG = "OAuthConnection";
2053+ private static final String CONSUMER_KEY = "anyone";
2054+ private static final String CONSUMER_SECRET = "anyone";
2055+
2056+ private OAuthConsumer consumer = null;
2057+
2058+ public String accessToken = "";
2059+ public String accessTokenSecret = "";
2060+ public String requestToken = "";
2061+ public String requestTokenSecret = "";
2062+ public boolean oauth10a = false;
2063+ public String authorizeUrl = "";
2064+ public String requestTokenUrl = "";
2065+ public String accessTokenUrl = "";
2066+ public String rootApi = "";
2067+ public String userApi = "";
2068+
2069+ public OAuthConnection() {
2070+
2071+ consumer = new CommonsHttpOAuthConsumer(
2072+ CONSUMER_KEY,
2073+ CONSUMER_SECRET);
2074+ }
2075+
2076+ public boolean isAuthenticated() {
2077+
2078+ if (accessToken.equals("") || accessTokenSecret.equals(""))
2079+ return false;
2080+ else
2081+ return true;
2082+ }
2083+
2084+ private OAuthProvider getProvider() {
2085+
2086+ // Use the provider bundled with signpost, the android libs are buggy
2087+ // See: http://code.google.com/p/oauth-signpost/issues/detail?id=20
2088+ OAuthProvider provider = new CommonsHttpOAuthProvider(
2089+ requestTokenUrl,
2090+ accessTokenUrl,
2091+ authorizeUrl);
2092+ provider.setOAuth10a(oauth10a);
2093+
2094+ return provider;
2095+ }
2096+
2097+ private void sign(HttpRequest request) {
2098+
2099+ if (isAuthenticated())
2100+ consumer.setTokenWithSecret(accessToken, accessTokenSecret);
2101+ else
2102+ return;
2103+
2104+ // TODO: figure out if we should throw exceptions
2105+ try {
2106+ consumer.sign(request);
2107+ } catch (OAuthMessageSignerException e1) {
2108+ e1.printStackTrace();
2109+ } catch (OAuthExpectationFailedException e1) {
2110+ e1.printStackTrace();
2111+ } catch (OAuthCommunicationException e) {
2112+ // TODO Auto-generated catch block
2113+ e.printStackTrace();
2114+ }
2115+ }
2116+
2117+ public Uri getAuthorizationUrl(String server) throws UnknownHostException {
2118+
2119+ String url = "";
2120+
2121+ // this method shouldn't have been called
2122+ if (isAuthenticated())
2123+ return null;
2124+
2125+ rootApi = server+"/api/1.0/";
2126+
2127+ AnonymousConnection connection = new AnonymousConnection();
2128+ String response = connection.get(rootApi);
2129+
2130+ JSONObject jsonResponse;
2131+
2132+ try {
2133+ jsonResponse = new JSONObject(response);
2134+
2135+ accessTokenUrl = jsonResponse.getString("oauth_access_token_url");
2136+ requestTokenUrl = jsonResponse.getString("oauth_request_token_url");
2137+ authorizeUrl = jsonResponse.getString("oauth_authorize_url");
2138+
2139+ } catch (JSONException e) {
2140+ e.printStackTrace();
2141+ return null;
2142+ }
2143+
2144+ OAuthProvider provider = getProvider();
2145+
2146+ try {
2147+ // the argument is the callback used when the remote authorization is complete
2148+ url = provider.retrieveRequestToken(consumer, "tomdroid://sync");
2149+
2150+ requestToken = consumer.getToken();
2151+ requestTokenSecret = consumer.getTokenSecret();
2152+ oauth10a = provider.isOAuth10a();
2153+ accessToken = "";
2154+ accessTokenSecret = "";
2155+ saveConfiguration();
2156+
2157+ } catch (OAuthMessageSignerException e1) {
2158+ e1.printStackTrace();
2159+ return null;
2160+ } catch (OAuthNotAuthorizedException e1) {
2161+ e1.printStackTrace();
2162+ return null;
2163+ } catch (OAuthExpectationFailedException e1) {
2164+ e1.printStackTrace();
2165+ return null;
2166+ } catch (OAuthCommunicationException e1) {
2167+ e1.printStackTrace();
2168+ return null;
2169+ }
2170+
2171+ if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Authorization URL : "+url);
2172+
2173+ return Uri.parse(url);
2174+ }
2175+
2176+ public boolean getAccess(String verifier) throws UnknownHostException {
2177+
2178+ Log.i(TAG, "Verifier: "+verifier);
2179+
2180+ // this method shouldn't have been called
2181+ if (isAuthenticated())
2182+ return false;
2183+
2184+ if (!requestToken.equals("") && !requestTokenSecret.equals("")) {
2185+ consumer.setTokenWithSecret(requestToken, requestTokenSecret);
2186+ if(Tomdroid.LOGGING_ENABLED) {
2187+ Log.d(TAG, "Added request token "+requestTokenSecret+" and request token secret "+requestTokenSecret);
2188+ }
2189+ }
2190+ else
2191+ return false;
2192+
2193+ OAuthProvider provider = getProvider();
2194+
2195+ try {
2196+ provider.retrieveAccessToken(consumer, verifier);
2197+ } catch (OAuthMessageSignerException e1) {
2198+ e1.printStackTrace();
2199+ return false;
2200+ } catch (OAuthNotAuthorizedException e1) {
2201+ e1.printStackTrace();
2202+ return false;
2203+ } catch (OAuthExpectationFailedException e1) {
2204+ e1.printStackTrace();
2205+ return false;
2206+ } catch (OAuthCommunicationException e1) {
2207+ e1.printStackTrace();
2208+ return false;
2209+ }
2210+
2211+ // access has been granted, store the access token
2212+ accessToken = consumer.getToken();
2213+ accessTokenSecret = consumer.getTokenSecret();
2214+ requestToken = "";
2215+ requestTokenSecret = "";
2216+
2217+ try {
2218+ JSONObject response = new JSONObject(get(rootApi));
2219+ // append a slash to the url, else the signature will fail
2220+ userApi = response.getJSONObject("user-ref").getString("api-ref");
2221+ } catch (JSONException e) {
2222+ // TODO Auto-generated catch block
2223+ e.printStackTrace();
2224+ }
2225+
2226+ saveConfiguration();
2227+
2228+ if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Got access token "+consumer.getToken()+".");
2229+
2230+ return true;
2231+ }
2232+
2233+ @Override
2234+ public String get(String uri) throws java.net.UnknownHostException {
2235+
2236+ // Prepare a request object
2237+ HttpGet httpGet = new HttpGet(uri);
2238+ sign(httpGet);
2239+ HttpResponse response = execute(httpGet);
2240+ return parseResponse(response);
2241+ }
2242+
2243+ @Override
2244+ public String put(String uri, String data) throws UnknownHostException {
2245+
2246+ // Prepare a request object
2247+ HttpPut httpPut = new HttpPut(uri);
2248+
2249+ try {
2250+ // The default http content charset is ISO-8859-1, JSON requires UTF-8
2251+ httpPut.setEntity(new StringEntity(data, "UTF-8"));
2252+ } catch (UnsupportedEncodingException e1) {
2253+ e1.printStackTrace();
2254+ return null;
2255+ }
2256+
2257+ httpPut.setHeader("Content-Type", "application/json");
2258+ sign(httpPut);
2259+
2260+ // Do not handle redirects, we need to sign the request again as the old signature will be invalid
2261+ HttpResponse response = execute(httpPut);
2262+ return parseResponse(response);
2263+ }
2264+
2265+ private void saveConfiguration() {
2266+
2267+ Preferences.putString(Preferences.Key.ACCESS_TOKEN, accessToken);
2268+ Preferences.putString(Preferences.Key.ACCESS_TOKEN_SECRET, accessTokenSecret);
2269+ Preferences.putString(Preferences.Key.ACCESS_TOKEN_URL, accessTokenUrl);
2270+ Preferences.putString(Preferences.Key.REQUEST_TOKEN, requestToken);
2271+ Preferences.putString(Preferences.Key.REQUEST_TOKEN_SECRET, requestTokenSecret);
2272+ Preferences.putString(Preferences.Key.REQUEST_TOKEN_URL, requestTokenUrl);
2273+ Preferences.putBoolean(Preferences.Key.OAUTH_10A, oauth10a);
2274+ Preferences.putString(Preferences.Key.AUTHORIZE_URL, authorizeUrl);
2275+ Preferences.putString(Preferences.Key.SYNC_SERVER_ROOT_API, rootApi);
2276+ Preferences.putString(Preferences.Key.SYNC_SERVER_USER_API, userApi);
2277+ }
2278+}
2279
2280=== added file 'src/org/tomdroid/sync/web/SnowySyncService.java'
2281--- src/org/tomdroid/sync/web/SnowySyncService.java 1970-01-01 00:00:00 +0000
2282+++ src/org/tomdroid/sync/web/SnowySyncService.java 2010-10-03 12:21:44 +0000
2283@@ -0,0 +1,232 @@
2284+/*
2285+ * Tomdroid
2286+ * Tomboy on Android
2287+ * http://www.launchpad.net/tomdroid
2288+ *
2289+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
2290+ *
2291+ * This file is part of Tomdroid.
2292+ *
2293+ * Tomdroid is free software: you can redistribute it and/or modify
2294+ * it under the terms of the GNU General Public License as published by
2295+ * the Free Software Foundation, either version 3 of the License, or
2296+ * (at your option) any later version.
2297+ *
2298+ * Tomdroid is distributed in the hope that it will be useful,
2299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2301+ * GNU General Public License for more details.
2302+ *
2303+ * You should have received a copy of the GNU General Public License
2304+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2305+ */
2306+package org.tomdroid.sync.web;
2307+
2308+import java.net.UnknownHostException;
2309+import java.util.ArrayList;
2310+
2311+import org.json.JSONArray;
2312+import org.json.JSONException;
2313+import org.json.JSONObject;
2314+import org.tomdroid.Note;
2315+import org.tomdroid.sync.ServiceAuth;
2316+import org.tomdroid.sync.SyncService;
2317+import org.tomdroid.ui.Tomdroid;
2318+import org.tomdroid.util.Preferences;
2319+
2320+import android.app.Activity;
2321+import android.net.Uri;
2322+import android.os.Handler;
2323+import android.os.Message;
2324+import android.util.Log;
2325+
2326+public class SnowySyncService extends SyncService implements ServiceAuth {
2327+
2328+ private static final String TAG = "SnowySyncService";
2329+
2330+ public SnowySyncService(Activity activity, Handler handler) {
2331+ super(activity, handler);
2332+ }
2333+
2334+ @Override
2335+ public String getDescription() {
2336+ return "Tomboy Web";
2337+ }
2338+
2339+ @Override
2340+ public String getName() {
2341+ return "tomboy-web";
2342+ }
2343+
2344+ public boolean isConfigured() {
2345+ OAuthConnection auth = getAuthConnection();
2346+ return auth.isAuthenticated();
2347+ }
2348+
2349+ @Override
2350+ public boolean needsServer() {
2351+ return true;
2352+ }
2353+
2354+ @Override
2355+ public boolean needsAuth() {
2356+ return true;
2357+ }
2358+
2359+ public void getAuthUri(final String server, final Handler handler) {
2360+
2361+ execInThread(new Runnable() {
2362+
2363+ public void run() {
2364+
2365+ // Reset the authentication credentials
2366+ OAuthConnection auth = new OAuthConnection();
2367+ Uri authUri = null;
2368+
2369+ try {
2370+ authUri = auth.getAuthorizationUrl(server);
2371+
2372+ } catch (UnknownHostException e) {
2373+ if (Tomdroid.LOGGING_ENABLED)
2374+ Log.e(TAG, "Internet connection not available");
2375+ sendMessage(NO_INTERNET);
2376+ }
2377+
2378+ Message message = new Message();
2379+ message.obj = authUri;
2380+ handler.sendMessage(message);
2381+ }
2382+
2383+ });
2384+ }
2385+
2386+ public void remoteAuthComplete(final Uri uri, final Handler handler) {
2387+
2388+ execInThread(new Runnable() {
2389+
2390+ public void run() {
2391+
2392+ try {
2393+ // TODO: might be intelligent to show something like a progress dialog
2394+ // else the user might try to sync before the authorization process
2395+ // is complete
2396+ OAuthConnection auth = getAuthConnection();
2397+ boolean result = auth.getAccess(uri.getQueryParameter("oauth_verifier"));
2398+
2399+ if (Tomdroid.LOGGING_ENABLED) {
2400+ if (result) {
2401+ Log.i(TAG, "The authorization process is complete.");
2402+ } else
2403+ Log.e(TAG, "Something went wrong during the authorization process.");
2404+ }
2405+ } catch (UnknownHostException e) {
2406+ if (Tomdroid.LOGGING_ENABLED)
2407+ Log.e(TAG, "Internet connection not available");
2408+ sendMessage(NO_INTERNET);
2409+ }
2410+
2411+ // We don't care what we send, just remove the dialog
2412+ handler.sendEmptyMessage(0);
2413+ }
2414+ });
2415+ }
2416+
2417+ @Override
2418+ public boolean isSyncable(){
2419+ return super.isSyncable() && isConfigured();
2420+ }
2421+
2422+
2423+ @Override
2424+ protected void sync() {
2425+
2426+ // start loading snowy notes
2427+ setSyncProgress(0);
2428+ if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "Loading Snowy notes");
2429+
2430+ final String userRef = Preferences.getString(Preferences.Key.SYNC_SERVER_USER_API);
2431+
2432+ execInThread(new Runnable() {
2433+
2434+ public void run() {
2435+
2436+ OAuthConnection auth = getAuthConnection();
2437+
2438+ try {
2439+ String rawResponse = auth.get(userRef);
2440+ setSyncProgress(30);
2441+ JSONObject response = new JSONObject(rawResponse);
2442+ String notesUrl = response.getJSONObject("notes-ref").getString("api-ref");
2443+
2444+ response = new JSONObject(auth.get(notesUrl));
2445+
2446+ long latestSyncRevision = (Long)Preferences.getLong(Preferences.Key.LATEST_SYNC_REVISION);
2447+ setSyncProgress(35);
2448+
2449+ if (response.getLong("latest-sync-revision") < latestSyncRevision) {
2450+ setSyncProgress(100);
2451+ return;
2452+ }
2453+
2454+ response = new JSONObject(auth.get(notesUrl + "?include_notes=true"));
2455+ JSONArray notes = response.getJSONArray("notes");
2456+ setSyncProgress(60);
2457+
2458+ // Delete the notes that are not in the database
2459+ ArrayList<String> remoteGuids = new ArrayList<String>();
2460+
2461+ for (int i = 0; i < notes.length(); i++) {
2462+ remoteGuids.add(notes.getJSONObject(i).getString("guid"));
2463+ }
2464+
2465+ deleteNotes(remoteGuids);
2466+ setSyncProgress(70);
2467+
2468+ // Insert or update the rest of the notes
2469+ for (int i = 0; i < notes.length() - 1; i++) {
2470+
2471+ JSONObject jsonNote = notes.getJSONObject(i);
2472+ insertNote(new Note(jsonNote), false);
2473+ }
2474+ setSyncProgress(90);
2475+
2476+ JSONObject jsonNote = notes.getJSONObject(notes.length() - 1);
2477+ insertNote(new Note(jsonNote), true);
2478+
2479+ Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, response
2480+ .getLong("latest-sync-revision"));
2481+ setSyncProgress(100);
2482+
2483+ } catch (JSONException e1) {
2484+ if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Problem parsing the server response", e1);
2485+ sendMessage(PARSING_FAILED);
2486+ setSyncProgress(100);
2487+ return;
2488+ } catch (java.net.UnknownHostException e) {
2489+ if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Internet connection not available");
2490+ sendMessage(NO_INTERNET);
2491+ setSyncProgress(100);
2492+ return;
2493+ }
2494+ }
2495+ });
2496+ }
2497+
2498+ private OAuthConnection getAuthConnection() {
2499+
2500+ OAuthConnection auth = new OAuthConnection();
2501+
2502+ auth.accessToken = Preferences.getString(Preferences.Key.ACCESS_TOKEN);
2503+ auth.accessTokenSecret = Preferences.getString(Preferences.Key.ACCESS_TOKEN_SECRET);
2504+ auth.requestToken = Preferences.getString(Preferences.Key.REQUEST_TOKEN);
2505+ auth.requestTokenSecret = Preferences.getString(Preferences.Key.REQUEST_TOKEN_SECRET);
2506+ auth.oauth10a = Preferences.getBoolean(Preferences.Key.OAUTH_10A);
2507+ auth.authorizeUrl = Preferences.getString(Preferences.Key.AUTHORIZE_URL);
2508+ auth.accessTokenUrl = Preferences.getString(Preferences.Key.ACCESS_TOKEN_URL);
2509+ auth.requestTokenUrl = Preferences.getString(Preferences.Key.REQUEST_TOKEN_URL);
2510+ auth.rootApi = Preferences.getString(Preferences.Key.SYNC_SERVER_ROOT_API);
2511+ auth.userApi = Preferences.getString(Preferences.Key.SYNC_SERVER_USER_API);
2512+
2513+ return auth;
2514+ }
2515+}
2516
2517=== added file 'src/org/tomdroid/sync/web/WebConnection.java'
2518--- src/org/tomdroid/sync/web/WebConnection.java 1970-01-01 00:00:00 +0000
2519+++ src/org/tomdroid/sync/web/WebConnection.java 2010-10-03 12:21:44 +0000
2520@@ -0,0 +1,139 @@
2521+/*
2522+ * Tomdroid
2523+ * Tomboy on Android
2524+ * http://www.launchpad.net/tomdroid
2525+ *
2526+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
2527+ *
2528+ * This file is part of Tomdroid.
2529+ *
2530+ * Tomdroid is free software: you can redistribute it and/or modify
2531+ * it under the terms of the GNU General Public License as published by
2532+ * the Free Software Foundation, either version 3 of the License, or
2533+ * (at your option) any later version.
2534+ *
2535+ * Tomdroid is distributed in the hope that it will be useful,
2536+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2537+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2538+ * GNU General Public License for more details.
2539+ *
2540+ * You should have received a copy of the GNU General Public License
2541+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2542+ */
2543+package org.tomdroid.sync.web;
2544+
2545+import java.io.BufferedReader;
2546+import java.io.IOException;
2547+import java.io.InputStream;
2548+import java.io.InputStreamReader;
2549+import java.net.UnknownHostException;
2550+
2551+import org.apache.http.HttpEntity;
2552+import org.apache.http.HttpResponse;
2553+import org.apache.http.client.ClientProtocolException;
2554+import org.apache.http.client.methods.HttpUriRequest;
2555+import org.apache.http.impl.client.DefaultHttpClient;
2556+import org.tomdroid.ui.Tomdroid;
2557+
2558+import android.util.Log;
2559+
2560+public abstract class WebConnection {
2561+
2562+ private static final String TAG = "WebConnection";
2563+
2564+ public abstract String get(String uri) throws UnknownHostException;
2565+ public abstract String put(String uri, String data) throws UnknownHostException;
2566+
2567+ private static String convertStreamToString(InputStream is) {
2568+ /*
2569+ * To convert the InputStream to String we use the BufferedReader.readLine()
2570+ * method. We iterate until the BufferedReader return null which means
2571+ * there's no more data to read. Each line will appended to a StringBuilder
2572+ * and returned as String.
2573+ */
2574+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
2575+ StringBuilder sb = new StringBuilder();
2576+
2577+ String line = null;
2578+ try {
2579+ while ((line = reader.readLine()) != null) {
2580+ sb.append(line + "\n");
2581+ }
2582+ } catch (IOException e) {
2583+ e.printStackTrace();
2584+ } finally {
2585+ try {
2586+ is.close();
2587+ } catch (IOException e) {
2588+ e.printStackTrace();
2589+ }
2590+ }
2591+
2592+ return sb.toString();
2593+ }
2594+
2595+ protected String parseResponse(HttpResponse response) {
2596+
2597+ if (response == null)
2598+ return "";
2599+
2600+ String result = null;
2601+
2602+ // Examine the response status
2603+ if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Response status : "+response.getStatusLine().toString());
2604+
2605+ // Get hold of the response entity
2606+ HttpEntity entity = response.getEntity();
2607+ // If the response does not enclose an entity, there is no need
2608+ // to worry about connection release
2609+
2610+ if (entity != null) {
2611+
2612+ try {
2613+ InputStream instream;
2614+
2615+ instream = entity.getContent();
2616+
2617+ result = convertStreamToString(instream);
2618+
2619+ if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Received : "+result);
2620+
2621+ // Closing the input stream will trigger connection release
2622+ instream.close();
2623+
2624+ } catch (IllegalStateException e) {
2625+ // TODO Auto-generated catch block
2626+ e.printStackTrace();
2627+ } catch (IOException e) {
2628+ // TODO Auto-generated catch block
2629+ e.printStackTrace();
2630+ }
2631+ }
2632+
2633+ return result;
2634+ }
2635+
2636+ protected HttpResponse execute(HttpUriRequest request) throws UnknownHostException {
2637+
2638+ DefaultHttpClient httpclient = new DefaultHttpClient();
2639+
2640+ try {
2641+ // Execute the request
2642+ HttpResponse response = httpclient.execute(request);
2643+ return response;
2644+
2645+ }catch (UnknownHostException e){
2646+ throw e;
2647+ } catch (ClientProtocolException e) {
2648+ e.printStackTrace();
2649+ } catch (IOException e) {
2650+ e.printStackTrace();
2651+ } catch (IllegalArgumentException e) {
2652+ e.printStackTrace();
2653+ } catch (IllegalStateException e) {
2654+ e.printStackTrace();
2655+ }
2656+
2657+ return null;
2658+ }
2659+}
2660
2661=== added file 'src/org/tomdroid/ui/Actionbar.java'
2662--- src/org/tomdroid/ui/Actionbar.java 1970-01-01 00:00:00 +0000
2663+++ src/org/tomdroid/ui/Actionbar.java 2010-10-03 12:21:44 +0000
2664@@ -0,0 +1,69 @@
2665+/*
2666+ * Tomdroid
2667+ * Tomboy on Android
2668+ * http://www.launchpad.net/tomdroid
2669+ *
2670+ * Copyright 2010, Rodja Trappe <mail@rodja.net>
2671+ *
2672+ * This file is part of Tomdroid.
2673+ *
2674+ * Tomdroid is free software: you can redistribute it and/or modify
2675+ * it under the terms of the GNU General Public License as published by
2676+ * the Free Software Foundation, either version 3 of the License, or
2677+ * (at your option) any later version.
2678+ *
2679+ * Tomdroid is distributed in the hope that it will be useful,
2680+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2681+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2682+ * GNU General Public License for more details.
2683+ *
2684+ * You should have received a copy of the GNU General Public License
2685+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2686+ */
2687+package org.tomdroid.ui;
2688+
2689+import org.tomdroid.R;
2690+import org.tomdroid.sync.SyncManager;
2691+
2692+import android.content.Context;
2693+import android.util.AttributeSet;
2694+import android.view.View;
2695+import android.widget.ImageView;
2696+import android.widget.RelativeLayout;
2697+
2698+public class Actionbar extends RelativeLayout {
2699+
2700+ public static final int DEFAULT_ICON_ALPHA = 200;
2701+
2702+ public Actionbar(Context context, AttributeSet attrs) {
2703+ super(context, attrs);
2704+ }
2705+
2706+ public Actionbar(Context context, AttributeSet attrs, int defStyle) {
2707+ super(context, attrs, defStyle);
2708+ }
2709+
2710+ public Actionbar(Context context) {
2711+ super(context);
2712+ }
2713+
2714+ @Override
2715+ public void onFinishInflate(){
2716+ super.onFinishInflate();
2717+ setupSyncButton();
2718+ }
2719+
2720+ private void setupSyncButton(){
2721+
2722+ final ImageView syncButton = (ImageView) findViewById(R.id.sync);
2723+ final ImageView syncIcon = (ImageView) findViewById(R.id.syncIcon);
2724+ syncIcon.getDrawable().setAlpha(Actionbar.DEFAULT_ICON_ALPHA);
2725+ syncButton.setOnClickListener(new View.OnClickListener() {
2726+
2727+ public void onClick(View v) {
2728+ SyncManager.getInstance().startSynchronization();
2729+ }
2730+ });
2731+ }
2732+
2733+}
2734
2735=== added file 'src/org/tomdroid/ui/PreferencesActivity.java'
2736--- src/org/tomdroid/ui/PreferencesActivity.java 1970-01-01 00:00:00 +0000
2737+++ src/org/tomdroid/ui/PreferencesActivity.java 2010-10-03 12:21:44 +0000
2738@@ -0,0 +1,217 @@
2739+/*
2740+ * Tomdroid
2741+ * Tomboy on Android
2742+ * http://www.launchpad.net/tomdroid
2743+ *
2744+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
2745+ *
2746+ * This file is part of Tomdroid.
2747+ *
2748+ * Tomdroid is free software: you can redistribute it and/or modify
2749+ * it under the terms of the GNU General Public License as published by
2750+ * the Free Software Foundation, either version 3 of the License, or
2751+ * (at your option) any later version.
2752+ *
2753+ * Tomdroid is distributed in the hope that it will be useful,
2754+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2755+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2756+ * GNU General Public License for more details.
2757+ *
2758+ * You should have received a copy of the GNU General Public License
2759+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2760+ */
2761+package org.tomdroid.ui;
2762+
2763+import java.util.ArrayList;
2764+
2765+import org.tomdroid.R;
2766+import org.tomdroid.sync.ServiceAuth;
2767+import org.tomdroid.sync.SyncManager;
2768+import org.tomdroid.sync.SyncService;
2769+import org.tomdroid.util.Preferences;
2770+
2771+import android.app.AlertDialog;
2772+import android.app.ProgressDialog;
2773+import android.content.DialogInterface;
2774+import android.content.Intent;
2775+import android.content.DialogInterface.OnClickListener;
2776+import android.net.Uri;
2777+import android.os.Bundle;
2778+import android.os.Handler;
2779+import android.os.Message;
2780+import android.preference.EditTextPreference;
2781+import android.preference.ListPreference;
2782+import android.preference.Preference;
2783+import android.preference.PreferenceActivity;
2784+import android.preference.Preference.OnPreferenceChangeListener;
2785+import android.util.Log;
2786+import android.widget.Toast;
2787+
2788+public class PreferencesActivity extends PreferenceActivity {
2789+
2790+ private static final String TAG = "PreferencesActivity";
2791+
2792+ // TODO: put the various preferences in fields and figure out what to do on activity suspend/resume
2793+ private EditTextPreference syncServer = null;
2794+ private ListPreference syncService = null;
2795+
2796+ @Override
2797+ protected void onCreate(Bundle savedInstanceState) {
2798+
2799+ super.onCreate(savedInstanceState);
2800+ addPreferencesFromResource(R.xml.preferences);
2801+
2802+ // Fill the Preferences fields
2803+ syncServer = (EditTextPreference)findPreference(Preferences.Key.SYNC_SERVER.getName());
2804+ syncService = (ListPreference)findPreference(Preferences.Key.SYNC_SERVICE.getName());
2805+
2806+ // Set the default values if nothing exists
2807+ this.setDefaults();
2808+
2809+ // Fill the services combo list
2810+ this.fillServices();
2811+
2812+ // Enable or disable the server field depending on the selected sync service
2813+ setServer(syncService.getValue());
2814+
2815+ syncService.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
2816+
2817+ public boolean onPreferenceChange(Preference preference, Object newValue) {
2818+
2819+ setServer((String)newValue);
2820+ return true;
2821+ }
2822+ });
2823+
2824+ // Re-authenticate if the sync server changes
2825+ syncServer.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
2826+
2827+ public boolean onPreferenceChange(Preference preference,
2828+ Object serverUri) {
2829+
2830+ if (serverUri == null) {
2831+ Toast.makeText(PreferencesActivity.this,
2832+ getString(R.string.prefServerEmpty),
2833+ Toast.LENGTH_SHORT).show();
2834+ return false;
2835+ }
2836+
2837+ authenticate((String) serverUri);
2838+ return true;
2839+ }
2840+
2841+ });
2842+
2843+ }
2844+
2845+ private void authenticate(String serverUri) {
2846+
2847+ // update the value before doing anything
2848+ Preferences.putString(Preferences.Key.SYNC_SERVER, serverUri);
2849+
2850+ SyncService currentService = SyncManager.getInstance().getCurrentService();
2851+
2852+ if (!currentService.needsAuth()) {
2853+ return;
2854+ }
2855+
2856+ // service needs authentication
2857+ Log.i(TAG, "Creating dialog");
2858+
2859+ final ProgressDialog authProgress = ProgressDialog.show(this, "",
2860+ "Authenticating. Please wait...", true, false);
2861+
2862+ Handler handler = new Handler() {
2863+
2864+ @Override
2865+ public void handleMessage(Message msg) {
2866+
2867+ boolean wasSuccsessful = false;
2868+ Uri authorizationUri = (Uri) msg.obj;
2869+ if (authorizationUri != null) {
2870+
2871+ Intent i = new Intent(Intent.ACTION_VIEW, authorizationUri);
2872+ startActivity(i);
2873+ wasSuccsessful = true;
2874+
2875+ } else {
2876+ // Auth failed, don't update the value
2877+ wasSuccsessful = false;
2878+ }
2879+
2880+ if (authProgress != null)
2881+ authProgress.dismiss();
2882+
2883+ if (wasSuccsessful) {
2884+ resetLocalDatabase();
2885+ } else {
2886+ connectionFailed();
2887+ }
2888+ }
2889+ };
2890+
2891+ ((ServiceAuth) currentService).getAuthUri(serverUri, handler);
2892+ }
2893+
2894+ private void fillServices()
2895+ {
2896+ ArrayList<SyncService> availableServices = SyncManager.getInstance().getServices();
2897+ CharSequence[] entries = new CharSequence[availableServices.size()];
2898+ CharSequence[] entryValues = new CharSequence[availableServices.size()];
2899+
2900+ for (int i = 0; i < availableServices.size(); i++) {
2901+ entries[i] = availableServices.get(i).getDescription();
2902+ entryValues[i] = availableServices.get(i).getName();
2903+ }
2904+
2905+ syncService.setEntries(entries);
2906+ syncService.setEntryValues(entryValues);
2907+ }
2908+
2909+ private void setDefaults()
2910+ {
2911+ String defaultServer = (String)Preferences.Key.SYNC_SERVER.getDefault();
2912+ syncServer.setDefaultValue(defaultServer);
2913+ if(syncServer.getText() == null)
2914+ syncServer.setText(defaultServer);
2915+
2916+ String defaultService = (String)Preferences.Key.SYNC_SERVICE.getDefault();
2917+ syncService.setDefaultValue(defaultService);
2918+ if(syncService.getValue() == null)
2919+ syncService.setValue(defaultService);
2920+
2921+ }
2922+
2923+ private void setServer(String syncServiceKey) {
2924+
2925+ SyncService service = SyncManager.getInstance().getService(syncServiceKey);
2926+
2927+ if (service == null)
2928+ return;
2929+
2930+ if (!service.needsAuth()){
2931+ resetLocalDatabase();
2932+ }
2933+
2934+ syncServer.setEnabled(service.needsServer());
2935+ syncService.setSummary(service.getDescription());
2936+
2937+ }
2938+
2939+ private void connectionFailed() {
2940+ new AlertDialog.Builder(this)
2941+ .setMessage(getString(R.string.prefSyncConnectionFailed))
2942+ .setNeutralButton(getString(R.string.btnOk), new OnClickListener() {
2943+ public void onClick(DialogInterface dialog, int which) {
2944+ dialog.dismiss();
2945+ }})
2946+ .show();
2947+ }
2948+
2949+ //TODO use LocalStorage wrapper from two-way-sync branch when it get's merged
2950+ private void resetLocalDatabase() {
2951+ getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
2952+ Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
2953+ }
2954+
2955+}
2956
2957=== added file 'src/org/tomdroid/ui/SyncMessageHandler.java'
2958--- src/org/tomdroid/ui/SyncMessageHandler.java 1970-01-01 00:00:00 +0000
2959+++ src/org/tomdroid/ui/SyncMessageHandler.java 2010-10-03 12:21:44 +0000
2960@@ -0,0 +1,158 @@
2961+/*
2962+ * Tomdroid
2963+ * Tomboy on Android
2964+ * http://www.launchpad.net/tomdroid
2965+ *
2966+ * Copyright 2010, Rodja Trappe <mail@rodja.net>
2967+ *
2968+ * This file is part of Tomdroid.
2969+ *
2970+ * Tomdroid is free software: you can redistribute it and/or modify
2971+ * it under the terms of the GNU General Public License as published by
2972+ * the Free Software Foundation, either version 3 of the License, or
2973+ * (at your option) any later version.
2974+ *
2975+ * Tomdroid is distributed in the hope that it will be useful,
2976+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2977+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2978+ * GNU General Public License for more details.
2979+ *
2980+ * You should have received a copy of the GNU General Public License
2981+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
2982+ */
2983+package org.tomdroid.ui;
2984+
2985+import org.tomdroid.R;
2986+import org.tomdroid.sync.SyncManager;
2987+import org.tomdroid.sync.SyncService;
2988+
2989+import android.app.Activity;
2990+import android.app.AlertDialog;
2991+import android.content.DialogInterface;
2992+import android.content.DialogInterface.OnClickListener;
2993+import android.os.Handler;
2994+import android.os.Message;
2995+import android.util.Log;
2996+import android.view.View;
2997+import android.view.animation.Animation;
2998+import android.view.animation.AnimationUtils;
2999+import android.view.animation.RotateAnimation;
3000+import android.widget.ImageView;
3001+import android.widget.Toast;
3002+
3003+public class SyncMessageHandler extends Handler {
3004+
3005+ private static String TAG = "SycnMessageHandler";
3006+ private Activity activity;
3007+
3008+ // State variables
3009+ private boolean parsingErrorShown = false;
3010+
3011+ public SyncMessageHandler(Activity activity) {
3012+ this.activity = activity;
3013+ }
3014+
3015+ @Override
3016+ public void handleMessage(Message msg) {
3017+
3018+ switch (msg.what) {
3019+ case SyncService.PARSING_COMPLETE:
3020+ // TODO put string in a translatable bundle
3021+ Toast.makeText(
3022+ activity,
3023+ "Synchronization with "
3024+ + SyncManager.getInstance().getCurrentService().getDescription()
3025+ + " is complete.", Toast.LENGTH_SHORT).show();
3026+ break;
3027+
3028+ case SyncService.PARSING_NO_NOTES:
3029+ // TODO put string in a translatable bundle
3030+ Toast.makeText(
3031+ activity,
3032+ "No notes found on "
3033+ + SyncManager.getInstance().getCurrentService().getDescription()
3034+ + ".", Toast.LENGTH_SHORT).show();
3035+ break;
3036+
3037+ case SyncService.PARSING_FAILED:
3038+ if (Tomdroid.LOGGING_ENABLED)
3039+ Log.w(TAG, "handler called with a parsing failed message");
3040+
3041+ // if we already shown a parsing error in this pass, we
3042+ // won't show it again
3043+ if (!parsingErrorShown) {
3044+ parsingErrorShown = true;
3045+
3046+ // TODO put error string in a translatable resource
3047+ new AlertDialog.Builder(activity).setMessage(
3048+ "There was an error trying to parse your note collection. If "
3049+ + "you are able to replicate the problem, please contact us!")
3050+ .setTitle("Error").setNeutralButton("Ok", new OnClickListener() {
3051+ public void onClick(DialogInterface dialog, int which) {
3052+ dialog.dismiss();
3053+ }
3054+ }).show();
3055+ }
3056+ break;
3057+
3058+ case SyncService.NO_INTERNET:
3059+ // TODO put string in a translatable bundle
3060+ Toast.makeText(activity, "You are not connected to the internet.",
3061+ Toast.LENGTH_SHORT).show();
3062+ break;
3063+
3064+ case SyncService.SYNC_PROGRESS:
3065+ handleSyncProgress(msg);
3066+ break;
3067+
3068+ default:
3069+ if (Tomdroid.LOGGING_ENABLED)
3070+ Log.i(TAG, "handler called with an unknown message");
3071+ break;
3072+
3073+ }
3074+ }
3075+
3076+ private void handleSyncProgress(Message msg) {
3077+ ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
3078+
3079+ RotateAnimation rotation = new RotateAnimation(180 * msg.arg2 / 100f,
3080+ 180 * msg.arg1 / 100f, Animation.RELATIVE_TO_SELF, 0.5f,
3081+ Animation.RELATIVE_TO_SELF, 0.5f);
3082+ rotation.setDuration(700);
3083+ rotation.setFillAfter(true);
3084+ syncIcon.startAnimation(rotation);
3085+
3086+ if (msg.arg1 == 0) {
3087+ onSynchronizationStarted();
3088+ } else if (msg.arg1 == 100) {
3089+ onSynchronizationDone();
3090+ }
3091+ }
3092+
3093+ private void onSynchronizationDone() {
3094+ ImageView syncButton = (ImageView) activity.findViewById(R.id.sync);
3095+ ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
3096+
3097+ syncButton.setClickable(true);
3098+ syncIcon.getDrawable().setAlpha(Actionbar.DEFAULT_ICON_ALPHA);
3099+
3100+ View dot = activity.findViewById(R.id.sync_dot);
3101+ dot.setVisibility(View.INVISIBLE);
3102+ dot.getAnimation().setRepeatCount(0);
3103+ }
3104+
3105+ private void onSynchronizationStarted() {
3106+ ImageView syncButton = (ImageView) activity.findViewById(R.id.sync);
3107+ ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
3108+
3109+ syncButton.setClickable(false);
3110+ syncIcon.getDrawable().setAlpha(40);
3111+
3112+ Animation pulse = AnimationUtils.loadAnimation(activity, R.anim.pulse);
3113+ View dot = activity.findViewById(R.id.sync_dot);
3114+ dot.setVisibility(View.VISIBLE);
3115+ dot.startAnimation(pulse);
3116+ }
3117+
3118+}
3119
3120=== modified file 'src/org/tomdroid/ui/Tomdroid.java'
3121--- src/org/tomdroid/ui/Tomdroid.java 2010-02-16 05:18:09 +0000
3122+++ src/org/tomdroid/ui/Tomdroid.java 2010-10-03 12:21:44 +0000
3123@@ -3,7 +3,9 @@
3124 * Tomboy on Android
3125 * http://www.launchpad.net/tomdroid
3126 *
3127- * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
3128+ * Copyright 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
3129+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
3130+ * Copyright 2010, Rodja Trappe <mail@rodja.net>
3131 *
3132 * This file is part of Tomdroid.
3133 *
3134@@ -22,16 +24,17 @@
3135 */
3136 package org.tomdroid.ui;
3137
3138-import java.io.File;
3139-import java.io.FileNotFoundException;
3140-
3141 import org.tomdroid.Note;
3142 import org.tomdroid.NoteManager;
3143 import org.tomdroid.R;
3144-import org.tomdroid.util.AsyncNoteLoaderAndParser;
3145+import org.tomdroid.sync.ServiceAuth;
3146+import org.tomdroid.sync.SyncManager;
3147+import org.tomdroid.sync.SyncService;
3148+import org.tomdroid.util.Preferences;
3149
3150 import android.app.AlertDialog;
3151 import android.app.ListActivity;
3152+import android.app.ProgressDialog;
3153 import android.content.DialogInterface;
3154 import android.content.Intent;
3155 import android.content.DialogInterface.OnClickListener;
3156@@ -39,6 +42,7 @@
3157 import android.database.Cursor;
3158 import android.net.Uri;
3159 import android.os.Bundle;
3160+import android.os.Environment;
3161 import android.os.Handler;
3162 import android.os.Message;
3163 import android.util.Log;
3164@@ -49,136 +53,127 @@
3165 import android.widget.ListAdapter;
3166 import android.widget.ListView;
3167 import android.widget.TextView;
3168-import android.widget.Toast;
3169
3170 public class Tomdroid extends ListActivity {
3171
3172 // Global definition for Tomdroid
3173- public static final String AUTHORITY = "org.tomdroid.notes";
3174- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
3175- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.tomdroid.note";
3176- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.tomdroid.note";
3177- public static final String PROJECT_HOMEPAGE = "http://www.launchpad.net/tomdroid/";
3178-
3179+ public static final String AUTHORITY = "org.tomdroid.notes";
3180+ public static final Uri CONTENT_URI = Uri
3181+ .parse("content://" + AUTHORITY
3182+ + "/notes");
3183+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.tomdroid.note";
3184+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.tomdroid.note";
3185+ public static final String PROJECT_HOMEPAGE = "http://www.launchpad.net/tomdroid/";
3186+
3187 // config parameters
3188 // TODO hardcoded for now
3189- public static final String NOTES_PATH = "/sdcard/tomdroid/";
3190+ public static final String NOTES_PATH = Environment.getExternalStorageDirectory()
3191+ + "/tomdroid/";
3192 // Logging should be disabled for release builds
3193- public static final boolean LOGGING_ENABLED = false;
3194+ public static final boolean LOGGING_ENABLED = false;
3195+ // Set this to false for release builds, the reason should be obvious
3196+ public static final boolean CLEAR_PREFERENCES = false;
3197
3198 // Logging info
3199- private static final String TAG = "Tomdroid";
3200-
3201+ private static final String TAG = "Tomdroid";
3202+
3203 // UI to data model glue
3204- private TextView listEmptyView;
3205- private ListAdapter adapter;
3206-
3207- // Bundle keys for saving state
3208- private static final String WARNING_SHOWN = "w";
3209-
3210- // State variables
3211- private boolean warningShown = false;
3212- private boolean parsingErrorShown = false;
3213-
3214-
3215- /** Called when the activity is created. */
3216- @Override
3217- public void onCreate(Bundle savedInstanceState) {
3218- super.onCreate(savedInstanceState);
3219-
3220- setContentView(R.layout.main);
3221-
3222- // did we already show the warning and got destroyed by android's activity killer?
3223- if (savedInstanceState == null || !savedInstanceState.getBoolean(WARNING_SHOWN)) {
3224-
3225- // Warn that this is a "will eat your babies" release
3226- new AlertDialog.Builder(this)
3227- .setMessage(getString(R.string.strWelcome))
3228- .setTitle("Warning")
3229- .setNeutralButton("Ok", new OnClickListener() {
3230- public void onClick(DialogInterface dialog, int which) {
3231- warningShown = true;
3232- dialog.dismiss();
3233- }})
3234- .setIcon(R.drawable.icon)
3235- .show();
3236- }
3237-
3238- // adapter that binds the ListView UI to the notes in the note manager
3239- adapter = NoteManager.getListAdapter(this);
3240+ private TextView listEmptyView;
3241+ private ListAdapter adapter;
3242+
3243+ // UI feedback handler
3244+ private Handler syncMessageHandler = new SyncMessageHandler(this);
3245+
3246+ /** Called when the activity is created. */
3247+ @Override
3248+ public void onCreate(Bundle savedInstanceState) {
3249+ super.onCreate(savedInstanceState);
3250+
3251+ setContentView(R.layout.main);
3252+ Preferences.init(this, CLEAR_PREFERENCES);
3253+
3254+ // did we already show the warning and got destroyed by android's activity killer?
3255+ if (Preferences.getBoolean(Preferences.Key.FIRST_RUN)) {
3256+
3257+ // Warn that this is a "will eat your babies" release
3258+ new AlertDialog.Builder(this).setMessage(getString(R.string.strWelcome)).setTitle(
3259+ "Warning").setNeutralButton("Ok", new OnClickListener() {
3260+ public void onClick(DialogInterface dialog, int which) {
3261+ Preferences.putBoolean(Preferences.Key.FIRST_RUN, false);
3262+ dialog.dismiss();
3263+ }
3264+ }).setIcon(R.drawable.icon).show();
3265+ }
3266+
3267+ // adapter that binds the ListView UI to the notes in the note manager
3268+ adapter = NoteManager.getListAdapter(this);
3269 setListAdapter(adapter);
3270
3271- // set the view shown when the list is empty
3272+ // set the view shown when the list is empty
3273 // TODO default empty-list text is butt-ugly!
3274- listEmptyView = (TextView)findViewById(R.id.list_empty);
3275- getListView().setEmptyView(listEmptyView);
3276- }
3277+ listEmptyView = (TextView) findViewById(R.id.list_empty);
3278+ getListView().setEmptyView(listEmptyView);
3279+ }
3280
3281 @Override
3282 public boolean onCreateOptionsMenu(Menu menu) {
3283
3284 // Create the menu based on what is defined in res/menu/main.xml
3285- MenuInflater inflater = getMenuInflater();
3286- inflater.inflate(R.menu.main, menu);
3287- return true;
3288+ MenuInflater inflater = getMenuInflater();
3289+ inflater.inflate(R.menu.main, menu);
3290+ return true;
3291+
3292 }
3293
3294 @Override
3295 public boolean onOptionsItemSelected(MenuItem item) {
3296- switch (item.getItemId()) {
3297- case R.id.menuSyncWithSD:
3298-
3299- // start loading local notes
3300- if (LOGGING_ENABLED) Log.v(TAG, "Loading local notes");
3301- // reset parsing error flag
3302- parsingErrorShown = false;
3303-
3304- try {
3305- File notesRoot = new File(Tomdroid.NOTES_PATH);
3306-
3307- if (!notesRoot.exists()) {
3308- throw new FileNotFoundException("Tomdroid notes folder doesn't exist. It is configured to be at: "+Tomdroid.NOTES_PATH);
3309- }
3310-
3311- AsyncNoteLoaderAndParser asyncLoader = new AsyncNoteLoaderAndParser(this, notesRoot);
3312- asyncLoader.readAndParseNotes(handler);
3313-
3314- } catch (FileNotFoundException e) {
3315- //TODO put strings in an external resource
3316- listEmptyView.setText(R.string.strListEmptyNoNotes);
3317- new AlertDialog.Builder(this)
3318- .setMessage(e.getMessage())
3319- .setTitle("Error")
3320- .setNeutralButton("Ok", new OnClickListener() {
3321- public void onClick(DialogInterface dialog, int which) {
3322- dialog.dismiss();
3323- }})
3324- .show();
3325- e.printStackTrace();
3326- }
3327-
3328- return true;
3329-
3330- case R.id.menuAbout:
3331+ switch (item.getItemId()) {
3332+ case R.id.menuAbout:
3333 showAboutDialog();
3334- return true;
3335- }
3336-
3337- return super.onOptionsItemSelected(item);
3338+ return true;
3339+
3340+ case R.id.menuPrefs:
3341+ startActivity(new Intent(this, PreferencesActivity.class));
3342+ return true;
3343+ }
3344+
3345+ return super.onOptionsItemSelected(item);
3346 }
3347
3348- @Override
3349- protected void onSaveInstanceState(Bundle outState) {
3350- super.onSaveInstanceState(outState);
3351+ public void onResume() {
3352+ super.onResume();
3353+ Intent intent = this.getIntent();
3354+
3355+ SyncService currentService = SyncManager.getInstance().getCurrentService();
3356
3357- // saving the state of the warning dialog
3358- if (warningShown) {
3359- outState.putBoolean(WARNING_SHOWN, true);
3360+ if (currentService.needsAuth() && intent != null) {
3361+ Uri uri = intent.getData();
3362+
3363+ if (uri != null && uri.getScheme().equals("tomdroid")) {
3364+ Log.i(TAG, "Got url : " + uri.toString());
3365+
3366+ final ProgressDialog dialog = ProgressDialog.show(this, "",
3367+ "Completing authentication. Please wait...", true, false);
3368+
3369+ Handler handler = new Handler() {
3370+
3371+ @Override
3372+ public void handleMessage(Message msg) {
3373+ dialog.dismiss();
3374+ }
3375+
3376+ };
3377+
3378+ ((ServiceAuth) currentService).remoteAuthComplete(uri, handler);
3379+ }
3380 }
3381+
3382+ SyncManager.setActivity(this);
3383+ SyncManager.setHandler(this.syncMessageHandler);
3384 }
3385
3386 private void showAboutDialog() {
3387-
3388+
3389 // grab version info
3390 String ver;
3391 try {
3392@@ -187,90 +182,38 @@
3393 e.printStackTrace();
3394 ver = "Not found!";
3395 }
3396-
3397+
3398 // format the string
3399 String aboutDialogFormat = getString(R.string.strAbout);
3400- String aboutDialogStr = String.format(aboutDialogFormat,
3401- getString(R.string.app_desc), // App description
3402- getString(R.string.author), // Author name
3403- ver // Version
3404+ String aboutDialogStr = String.format(aboutDialogFormat, getString(R.string.app_desc), // App description
3405+ getString(R.string.author), // Author name
3406+ ver // Version
3407 );
3408-
3409+
3410 // build and show the dialog
3411- new AlertDialog.Builder(this)
3412- .setMessage(aboutDialogStr)
3413- .setTitle("About Tomdroid")
3414- .setIcon(R.drawable.icon)
3415- .setNegativeButton("Project page", new OnClickListener() {
3416- public void onClick(DialogInterface dialog, int which) {
3417- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Tomdroid.PROJECT_HOMEPAGE)));
3418- dialog.dismiss();
3419- }})
3420- .setPositiveButton("Ok", new OnClickListener() {
3421- public void onClick(DialogInterface dialog, int which) {
3422- dialog.dismiss();
3423- }})
3424- .show();
3425+ new AlertDialog.Builder(this).setMessage(aboutDialogStr).setTitle("About Tomdroid")
3426+ .setIcon(R.drawable.icon).setNegativeButton("Project page", new OnClickListener() {
3427+ public void onClick(DialogInterface dialog, int which) {
3428+ startActivity(new Intent(Intent.ACTION_VIEW, Uri
3429+ .parse(Tomdroid.PROJECT_HOMEPAGE)));
3430+ dialog.dismiss();
3431+ }
3432+ }).setPositiveButton("Ok", new OnClickListener() {
3433+ public void onClick(DialogInterface dialog, int which) {
3434+ dialog.dismiss();
3435+ }
3436+ }).show();
3437 }
3438
3439 @Override
3440 protected void onListItemClick(ListView l, View v, int position, long id) {
3441-
3442- Cursor item = (Cursor)adapter.getItem(position);
3443+
3444+ Cursor item = (Cursor) adapter.getItem(position);
3445 int noteId = item.getInt(item.getColumnIndexOrThrow(Note.ID));
3446-
3447- Uri intentUri = Uri.parse(Tomdroid.CONTENT_URI+"/"+noteId);
3448+
3449+ Uri intentUri = Uri.parse(Tomdroid.CONTENT_URI + "/" + noteId);
3450 Intent i = new Intent(Intent.ACTION_VIEW, intentUri, this, ViewNote.class);
3451 startActivity(i);
3452 }
3453-
3454- private Handler handler = new Handler() {
3455-
3456- @Override
3457- public void handleMessage(Message msg) {
3458-
3459- switch(msg.what) {
3460- case AsyncNoteLoaderAndParser.PARSING_COMPLETE:
3461- // TODO put string in a translatable bundle
3462- Toast.makeText(getApplicationContext(),
3463- "Synchronization with SD Card is complete.",
3464- Toast.LENGTH_SHORT)
3465- .show();
3466- break;
3467-
3468- case AsyncNoteLoaderAndParser.PARSING_NO_NOTES:
3469- // TODO put string in a translatable bundle
3470- Toast.makeText(getApplicationContext(),
3471- "There are no files in tomdroid/ on the sdcard.",
3472- Toast.LENGTH_SHORT)
3473- .show();
3474- break;
3475-
3476- case AsyncNoteLoaderAndParser.PARSING_FAILED:
3477- if (Tomdroid.LOGGING_ENABLED) Log.w(TAG,"handler called with a parsing failed message");
3478-
3479- // if we already shown a parsing error in this pass, we won't show it again
3480- if (!parsingErrorShown) {
3481- parsingErrorShown = true;
3482-
3483- // TODO put error string in a translatable resource
3484- new AlertDialog.Builder(Tomdroid.this)
3485- .setMessage("There was an error trying to parse your note collection. If " +
3486- "you are able to replicate the problem, please contact us!")
3487- .setTitle("Error")
3488- .setNeutralButton("Ok", new OnClickListener() {
3489- public void onClick(DialogInterface dialog, int which) {
3490- dialog.dismiss();
3491- }})
3492- .show();
3493- }
3494- break;
3495-
3496- default:
3497- if (Tomdroid.LOGGING_ENABLED) Log.i(TAG,"handler called with an unknown message");
3498- break;
3499-
3500- }
3501- }
3502- };
3503+
3504 }
3505
3506=== modified file 'src/org/tomdroid/ui/ViewNote.java'
3507--- src/org/tomdroid/ui/ViewNote.java 2010-02-06 14:57:07 +0000
3508+++ src/org/tomdroid/ui/ViewNote.java 2010-10-03 12:21:44 +0000
3509@@ -4,6 +4,7 @@
3510 * http://www.launchpad.net/tomdroid
3511 *
3512 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
3513+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
3514 *
3515 * This file is part of Tomdroid.
3516 *
3517@@ -28,6 +29,7 @@
3518 import org.tomdroid.Note;
3519 import org.tomdroid.NoteManager;
3520 import org.tomdroid.R;
3521+import org.tomdroid.sync.SyncManager;
3522 import org.tomdroid.util.LinkifyPhone;
3523 import org.tomdroid.util.NoteContentBuilder;
3524
3525@@ -37,6 +39,7 @@
3526 import android.content.Intent;
3527 import android.content.DialogInterface.OnClickListener;
3528 import android.database.Cursor;
3529+import android.graphics.Color;
3530 import android.net.Uri;
3531 import android.os.Bundle;
3532 import android.os.Handler;
3533@@ -52,6 +55,7 @@
3534 public class ViewNote extends Activity {
3535
3536 // UI elements
3537+ private TextView title;
3538 private TextView content;
3539
3540 // Model objects
3541@@ -61,6 +65,9 @@
3542 // Logging info
3543 private static final String TAG = "ViewNote";
3544
3545+ // UI feedback handler
3546+ private Handler syncMessageHandler = new SyncMessageHandler(this);
3547+
3548 // TODO extract methods in here
3549 @Override
3550 protected void onCreate(Bundle savedInstanceState) {
3551@@ -68,6 +75,14 @@
3552
3553 setContentView(R.layout.note_view);
3554 content = (TextView) findViewById(R.id.content);
3555+ content.setBackgroundColor(0xffffffff);
3556+ content.setTextColor(Color.DKGRAY);
3557+ content.setTextSize(18.0f);
3558+ title = (TextView) findViewById(R.id.title);
3559+ title.setBackgroundColor(0xffdddddd);
3560+ title.setTextColor(Color.DKGRAY);
3561+ title.setTextSize(18.0f);
3562+
3563 final Intent intent = getIntent();
3564 Uri uri = intent.getData();
3565
3566@@ -84,7 +99,9 @@
3567
3568 if(note != null) {
3569
3570- noteContent = note.getNoteContent(handler);
3571+ noteContent = note.getNoteContent(noteContentHandler);
3572+
3573+ //Log.i(TAG, "THE NOTE IS: " + note.getXmlContent().toString());
3574
3575 } else {
3576
3577@@ -120,6 +137,13 @@
3578 }
3579 }
3580
3581+ @Override
3582+ public void onResume(){
3583+ super.onResume();
3584+ SyncManager.setActivity(this);
3585+ SyncManager.setHandler(this.syncMessageHandler);
3586+ }
3587+
3588 // TODO add a menu that switches the view to an EditText instead of TextView
3589 // this will need some other quit mechanism as onKeyDown though.. (but the back key might do it)
3590
3591@@ -132,9 +156,9 @@
3592
3593 return true;
3594 }
3595-
3596+
3597 private void showNote() {
3598- setTitle(note.getTitle());
3599+ //setTitle(note.getTitle());
3600
3601 // get rid of the title that is doubled in the note's content
3602 // using quote to escape potential regexp chars in pattern
3603@@ -147,6 +171,7 @@
3604
3605 // show the note (spannable makes the TextView able to output styled text)
3606 content.setText(noteContent, TextView.BufferType.SPANNABLE);
3607+ title.setText((CharSequence) note.getTitle());
3608
3609 // add links to stuff that is understood by Android except phone numbers because it's too aggressive
3610 // TODO this is SLOWWWW!!!!
3611@@ -165,7 +190,14 @@
3612 noteTitleTransformFilter);
3613 }
3614
3615- private Handler handler = new Handler() {
3616+ public void setTitle(CharSequence title){
3617+ super.setTitle(title);
3618+ // temporary setting title of actionbar until we have a better idea
3619+ TextView titleView = (TextView) findViewById(R.id.title);
3620+ titleView.setText(title);
3621+ }
3622+
3623+ private Handler noteContentHandler = new Handler() {
3624
3625 @Override
3626 public void handleMessage(Message msg) {
3627
3628=== modified file 'src/org/tomdroid/util/NoteContentBuilder.java'
3629--- src/org/tomdroid/util/NoteContentBuilder.java 2010-01-23 04:43:26 +0000
3630+++ src/org/tomdroid/util/NoteContentBuilder.java 2010-10-03 12:21:44 +0000
3631@@ -62,7 +62,8 @@
3632
3633 public NoteContentBuilder setInputSource(String nc) {
3634
3635- noteContentIs = new InputSource(new StringReader(nc));
3636+ String noteContent = "<note-content>"+nc+"</note-content>";
3637+ noteContentIs = new InputSource(new StringReader(noteContent));
3638 return this;
3639 }
3640
3641
3642=== added file 'src/org/tomdroid/util/NoteListCursorAdapter.java'
3643--- src/org/tomdroid/util/NoteListCursorAdapter.java 1970-01-01 00:00:00 +0000
3644+++ src/org/tomdroid/util/NoteListCursorAdapter.java 2010-10-03 12:21:44 +0000
3645@@ -0,0 +1,113 @@
3646+package org.tomdroid.util;
3647+
3648+import java.text.DateFormat;
3649+import java.util.Date;
3650+
3651+import android.text.format.DateUtils;
3652+import android.text.format.Time;
3653+import org.tomdroid.Note;
3654+import org.tomdroid.R;
3655+import org.tomdroid.ui.Tomdroid;
3656+
3657+import android.content.Context;
3658+import android.database.Cursor;
3659+import android.graphics.Color;
3660+import android.view.LayoutInflater;
3661+import android.view.View;
3662+import android.view.ViewGroup;
3663+import android.widget.Filterable;
3664+import android.widget.SimpleCursorAdapter;
3665+import android.widget.TextView;
3666+
3667+/* Provides a custom ListView layout for Note List */
3668+
3669+public class NoteListCursorAdapter extends SimpleCursorAdapter {
3670+
3671+ private Context context;
3672+
3673+ private int layout;
3674+ private int[] colors = new int[] { 0xFFFFFFFF, 0xFFEEEEEE };
3675+
3676+ private DateFormat localeDateFormat;
3677+ private DateFormat localeTimeFormat;
3678+
3679+ public NoteListCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to) {
3680+ super(context, layout, c, from, to);
3681+ this.context = context;
3682+ this.layout = layout;
3683+ localeDateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
3684+ localeTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
3685+ }
3686+
3687+
3688+ @Override
3689+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
3690+
3691+ Cursor c = getCursor();
3692+
3693+ final LayoutInflater inflater = LayoutInflater.from(context);
3694+ View v = inflater.inflate(layout, parent, false);
3695+
3696+ populateFields(v, c);
3697+
3698+ return v;
3699+ }
3700+
3701+ @Override
3702+ public void bindView(View v, Context context, Cursor c) {
3703+
3704+ populateFields(v, c);
3705+ }
3706+
3707+ @Override
3708+ public View getView(int position, View convertView, ViewGroup parent) {
3709+ View view = super.getView(position, convertView, parent);
3710+ int colorPos = position % colors.length;
3711+ view.setBackgroundColor(colors[colorPos]);
3712+ //view.setTextColor(Color.DKGRAY);
3713+ return view;
3714+ }
3715+
3716+ private void populateFields(View v, Cursor c){
3717+
3718+ int nameCol = c.getColumnIndex(Note.TITLE);
3719+ int modifiedCol = c.getColumnIndex(Note.MODIFIED_DATE);
3720+
3721+ String title = c.getString(nameCol);
3722+
3723+ //Format last modified dates to be similar to desktop Tomboy
3724+ //TODO this is messy - must be a better way than having 3 separate date types
3725+ Time lastModified = new Time();
3726+ lastModified.parse3339(c.getString(modifiedCol));
3727+ Long lastModifiedMillis = lastModified.toMillis(false);
3728+ Date lastModifiedDate = new Date(lastModifiedMillis);
3729+
3730+ String strModified = "Modified: ";
3731+ //TODO this is very inefficient
3732+ if (DateUtils.isToday(lastModifiedMillis)){
3733+ strModified += "Today, " + localeTimeFormat.format(lastModifiedDate);
3734+ } else {
3735+ // Add a day to the last modified date - if the date is now today, it means the note was edited yesterday
3736+ Time yesterdayTest = lastModified;
3737+ yesterdayTest.monthDay += 1;
3738+ if (DateUtils.isToday(yesterdayTest.toMillis(false))){
3739+ strModified += "Yesterday, " + localeTimeFormat.format(lastModifiedDate);
3740+ } else {
3741+ strModified += localeDateFormat.format(lastModifiedDate) + ", " + localeTimeFormat.format(lastModifiedDate);
3742+ }
3743+ }
3744+
3745+ /**
3746+ * Next set the name of the entry.
3747+ */
3748+ TextView note_title = (TextView) v.findViewById(R.id.note_title);
3749+ if (note_title != null) {
3750+ note_title.setText(title);
3751+ }
3752+ TextView note_modified = (TextView) v.findViewById(R.id.note_date);
3753+ if (note_modified != null) {
3754+ note_modified.setText(strModified);
3755+ }
3756+ }
3757+
3758+}
3759
3760=== added file 'src/org/tomdroid/util/Preferences.java'
3761--- src/org/tomdroid/util/Preferences.java 1970-01-01 00:00:00 +0000
3762+++ src/org/tomdroid/util/Preferences.java 2010-10-03 12:21:44 +0000
3763@@ -0,0 +1,112 @@
3764+/*
3765+ * Tomdroid
3766+ * Tomboy on Android
3767+ * http://www.launchpad.net/tomdroid
3768+ *
3769+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
3770+ *
3771+ * This file is part of Tomdroid.
3772+ *
3773+ * Tomdroid is free software: you can redistribute it and/or modify
3774+ * it under the terms of the GNU General Public License as published by
3775+ * the Free Software Foundation, either version 3 of the License, or
3776+ * (at your option) any later version.
3777+ *
3778+ * Tomdroid is distributed in the hope that it will be useful,
3779+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3780+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3781+ * GNU General Public License for more details.
3782+ *
3783+ * You should have received a copy of the GNU General Public License
3784+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
3785+ */
3786+package org.tomdroid.util;
3787+
3788+import android.content.Context;
3789+import android.content.SharedPreferences;
3790+import android.preference.PreferenceManager;
3791+
3792+public class Preferences {
3793+
3794+ public enum Key {
3795+ SYNC_SERVICE ("sync_service", "sdcard"),
3796+ SYNC_SERVER_ROOT_API ("sync_server_root_api", ""),
3797+ SYNC_SERVER_USER_API ("sync_server_user_api", ""),
3798+ SYNC_SERVER ("sync_server", "https://one.ubuntu.com/notes"),
3799+ ACCESS_TOKEN ("access_token", ""),
3800+ ACCESS_TOKEN_SECRET ("access_token_secret", ""),
3801+ REQUEST_TOKEN ("request_token", ""),
3802+ REQUEST_TOKEN_SECRET ("request_token_secret", ""),
3803+ OAUTH_10A ("oauth_10a", false),
3804+ AUTHORIZE_URL ("authorize_url", ""),
3805+ ACCESS_TOKEN_URL ("access_token_url", ""),
3806+ REQUEST_TOKEN_URL ("request_token_url", ""),
3807+ LATEST_SYNC_REVISION ("latest_sync_revision", 0L),
3808+ SORT_ORDER ("sort_order", "sort_date"),
3809+ FIRST_RUN ("first_run", true);
3810+
3811+ private String name = "";
3812+ private Object defaultValue = "";
3813+
3814+ Key(String name, Object defaultValue) {
3815+ this.name = name;
3816+ this.defaultValue = defaultValue;
3817+ }
3818+
3819+ public String getName() {
3820+ return name;
3821+ }
3822+
3823+ public Object getDefault() {
3824+ return defaultValue;
3825+ }
3826+ }
3827+
3828+ private static SharedPreferences client = null;
3829+ private static SharedPreferences.Editor editor = null;
3830+
3831+ public static void init(Context context, boolean clean) {
3832+
3833+ client = PreferenceManager.getDefaultSharedPreferences(context);
3834+ editor = client.edit();
3835+
3836+ if (clean)
3837+ editor.clear().commit();
3838+ }
3839+
3840+ public static String getString(Key key) {
3841+
3842+ return client.getString(key.getName(), (String) key.getDefault());
3843+ }
3844+
3845+ public static void putString(Key key, String value) {
3846+
3847+ if (value == null)
3848+ editor.putString(key.getName(), (String)key.getDefault());
3849+ else
3850+ editor.putString(key.getName(), value);
3851+ editor.commit();
3852+ }
3853+
3854+ public static long getLong(Key key) {
3855+
3856+ return client.getLong(key.getName(), (Long)key.getDefault());
3857+ }
3858+
3859+ public static void putLong(Key key, long value) {
3860+
3861+ editor.putLong(key.getName(), value);
3862+ editor.commit();
3863+ }
3864+
3865+ public static boolean getBoolean(Key key) {
3866+
3867+ return client.getBoolean(key.getName(), (Boolean)key.getDefault());
3868+ }
3869+
3870+ public static void putBoolean(Key key, boolean value) {
3871+
3872+ editor.putBoolean(key.getName(), value);
3873+ editor.commit();
3874+ }
3875+}
3876
3877=== added file 'src/org/tomdroid/util/XmlUtils.java'
3878--- src/org/tomdroid/util/XmlUtils.java 1970-01-01 00:00:00 +0000
3879+++ src/org/tomdroid/util/XmlUtils.java 2010-10-03 12:21:44 +0000
3880@@ -0,0 +1,58 @@
3881+/*
3882+ * Tomdroid
3883+ * Tomboy on Android
3884+ * http://www.launchpad.net/tomdroid
3885+ *
3886+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
3887+ *
3888+ * This file is part of Tomdroid.
3889+ *
3890+ * Tomdroid is free software: you can redistribute it and/or modify
3891+ * it under the terms of the GNU General Public License as published by
3892+ * the Free Software Foundation, either version 3 of the License, or
3893+ * (at your option) any later version.
3894+ *
3895+ * Tomdroid is distributed in the hope that it will be useful,
3896+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3897+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3898+ * GNU General Public License for more details.
3899+ *
3900+ * You should have received a copy of the GNU General Public License
3901+ * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
3902+ */
3903+package org.tomdroid.util;
3904+
3905+public class XmlUtils {
3906+
3907+ /**
3908+ * Useful to replace the characters forbidden in xml by their escaped counterparts
3909+ * Ex: &amp; -> &amp;amp;
3910+ *
3911+ * @param input the string to escape
3912+ * @return the escaped string
3913+ */
3914+ public static String escape(String input) {
3915+ return input
3916+ .replace("&", "&amp;")
3917+ .replace("<", "&lt;")
3918+ .replace(">", "&gt;")
3919+ .replace("\"", "&quot;")
3920+ .replace("\'", "&apos;");
3921+ }
3922+
3923+ /**
3924+ * Useful to replace the escaped characters their unescaped counterparts
3925+ * Ex: &amp;amp; -> &amp;
3926+ *
3927+ * @param input the string to unescape
3928+ * @return the unescaped string
3929+ */
3930+ public static String unescape(String input) {
3931+ return input
3932+ .replace("&amp;", "&")
3933+ .replace("&lt;", "<")
3934+ .replace("&gt;", ">")
3935+ .replace("&quot;", "\"")
3936+ .replace("&apos;", "\'");
3937+ }
3938+}
3939
3940=== modified file 'src/org/tomdroid/xml/NoteContentHandler.java'
3941--- src/org/tomdroid/xml/NoteContentHandler.java 2010-02-01 05:11:47 +0000
3942+++ src/org/tomdroid/xml/NoteContentHandler.java 2010-10-03 12:21:44 +0000
3943@@ -4,6 +4,7 @@
3944 * http://www.launchpad.net/tomdroid
3945 *
3946 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
3947+ * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
3948 *
3949 * This file is part of Tomdroid.
3950 *
3951
3952=== added directory 'tests'
3953=== added directory 'tests/org'
3954=== added directory 'tests/org/tomdroid'
3955=== added file 'tests/org/tomdroid/NoteManagerTest.java'
3956--- tests/org/tomdroid/NoteManagerTest.java 1970-01-01 00:00:00 +0000
3957+++ tests/org/tomdroid/NoteManagerTest.java 2010-10-03 12:21:44 +0000
3958@@ -0,0 +1,74 @@
3959+package org.tomdroid;
3960+
3961+import org.json.JSONObject;
3962+import org.tomdroid.Note;
3963+import org.tomdroid.NoteManager;
3964+import org.tomdroid.ui.Tomdroid;
3965+import org.tomdroid.util.Preferences;
3966+
3967+import android.app.Activity;
3968+import android.content.Intent;
3969+import android.database.Cursor;
3970+import android.test.ActivityUnitTestCase;
3971+
3972+public class NoteManagerTest extends ActivityUnitTestCase<Tomdroid> {
3973+
3974+ public NoteManagerTest() {
3975+ super(Tomdroid.class);
3976+ }
3977+
3978+ public void testGetAllNotes() throws Exception {
3979+ Activity activity = getActivity();
3980+ putNotes(activity);
3981+ Cursor cursor;
3982+ // Get all notes excluding the notebook template ones.
3983+ cursor = NoteManager.getAllNotes(activity, false);
3984+ assertEquals(1, cursor.getCount());
3985+
3986+ // Get all notes, including notebook templates this time.
3987+ cursor = NoteManager.getAllNotes(activity, true);
3988+ assertEquals(2, cursor.getCount());
3989+ }
3990+
3991+ private void putNotes(Activity a) throws Exception {
3992+ // Add a regular note to the content manager.
3993+ JSONObject note = new JSONObject(
3994+ "{'title': 'foo', 'note-content': 'bar', " +
3995+ "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7704', " +
3996+ "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
3997+ "'tags': ['tag1', 'tag2']}");
3998+ Note n = new Note(note);
3999+ NoteManager.putNote(a, n);
4000+
4001+ // Add a notebook template to the content manager.
4002+ JSONObject template = new JSONObject(
4003+ "{'title': 'foo', 'note-content': 'bar', " +
4004+ "'guid': '992e91a2-2e34-4e2d-bf88-21def49a7712', " +
4005+ "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
4006+ "'tags': ['system:template', 'tag2']}");
4007+ Note t = new Note(template);
4008+ NoteManager.putNote(a, t);
4009+ }
4010+
4011+ @Override
4012+ public void setUp() throws Exception {
4013+ super.setUp();
4014+ // XXX: For some reason this will raise an
4015+ // "Unable to add window -- token null is not for an application"
4016+ // error when you run the test after wiping user data from the emulator.
4017+ // The error is actually raised when we try to display the AlertDialog that
4018+ // is shown the first time the user runs tomdroid.
4019+ startActivity(new Intent(), null, null);
4020+ // XXX: Soon we'll be able to replace the two lines below with LocalStorage.resetDatabase().
4021+ getActivity().getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
4022+ Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
4023+ }
4024+
4025+ @Override
4026+ public void tearDown() throws Exception {
4027+ // XXX: Soon we'll be able to replace the two lines below with LocalStorage.resetDatabase().
4028+ getActivity().getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
4029+ Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
4030+ super.tearDown();
4031+ }
4032+}
4033
4034=== added file 'tests/org/tomdroid/NoteTest.java'
4035--- tests/org/tomdroid/NoteTest.java 1970-01-01 00:00:00 +0000
4036+++ tests/org/tomdroid/NoteTest.java 2010-10-03 12:21:44 +0000
4037@@ -0,0 +1,35 @@
4038+package org.tomdroid;
4039+
4040+import junit.framework.Assert;
4041+import junit.framework.TestCase;
4042+
4043+import org.tomdroid.Note;
4044+import org.json.JSONException;
4045+import org.json.JSONObject;
4046+
4047+
4048+public class NoteTest extends TestCase {
4049+
4050+ public void testConstructorForNoteWithTags() throws JSONException {
4051+ JSONObject json = new JSONObject(
4052+ "{'title': 'foo', 'note-content': 'bar', " +
4053+ "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7705', " +
4054+ "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
4055+ "'tags': ['tag1', 'tag2']}");
4056+ Note n = new Note(json);
4057+ Assert.assertEquals("foo", n.getTitle());
4058+ Assert.assertEquals("002e91a2-2e34-4e2d-bf88-21def49a7705", n.getGuid().toString());
4059+ Assert.assertEquals("bar", n.getXmlContent());
4060+ Assert.assertEquals("tag1,tag2,", n.getTags());
4061+ }
4062+
4063+ public void testConstructorForNoteWithNoTags() throws JSONException {
4064+ JSONObject json = new JSONObject(
4065+ "{'title': 'foo', 'note-content': 'bar', " +
4066+ "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7705', " +
4067+ "'last-change-date': '2009-04-19T21:29:23.2197340-07:00'}");
4068+ Note n = new Note(json);
4069+ Assert.assertEquals("foo", n.getTitle());
4070+ Assert.assertEquals("", n.getTags());
4071+ }
4072+}

Subscribers

People subscribed via source and target branches