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
=== modified file '.classpath'
--- .classpath 2010-01-25 05:01:42 +0000
+++ .classpath 2010-10-03 12:21:44 +0000
@@ -3,5 +3,8 @@
3 <classpathentry kind="src" path="src"/>3 <classpathentry kind="src" path="src"/>
4 <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>4 <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
5 <classpathentry kind="src" path="gen"/>5 <classpathentry kind="src" path="gen"/>
6 <classpathentry kind="lib" path="lib/signpost-core-1.2.1.1.jar"/>
7 <classpathentry kind="lib" path="lib/signpost-commonshttp4-1.2.1.1.jar"/>
6 <classpathentry kind="output" path="bin"/>8 <classpathentry kind="output" path="bin"/>
9 <classpathentry kind="src" path="tests"/>
7</classpath>10</classpath>
811
=== modified file 'AndroidManifest.xml'
--- AndroidManifest.xml 2010-02-18 04:36:08 +0000
+++ AndroidManifest.xml 2010-10-03 12:21:44 +0000
@@ -2,14 +2,16 @@
2<manifest xmlns:android="http://schemas.android.com/apk/res/android"2<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="org.tomdroid"3 package="org.tomdroid"
4 android:versionName="0.3.1" android:versionCode="4">4 android:versionName="0.3.1" android:versionCode="4">
5
6 <uses-sdk android:minSdkVersion="3" />
75
6 <uses-sdk android:minSdkVersion="3"
7 android:targetSdkVersion="4" />
8
9 <supports-screens android:anyDensity="true" />
10
8 <application 11 <application
9 android:icon="@drawable/icon"12 android:icon="@drawable/icon"
10 android:label="@string/app_name"13 android:label="@string/app_name"
11 android:debuggable="false"14 android:theme="@android:style/Theme.Light.NoTitleBar">
12 >
1315
14 <activity android:label="@string/app_name"16 <activity android:label="@string/app_name"
15 android:name=".ui.Tomdroid"17 android:name=".ui.Tomdroid"
@@ -19,6 +21,12 @@
19 <action android:name="android.intent.action.MAIN" />21 <action android:name="android.intent.action.MAIN" />
20 <category android:name="android.intent.category.LAUNCHER" />22 <category android:name="android.intent.category.LAUNCHER" />
21 </intent-filter>23 </intent-filter>
24 <intent-filter>
25 <action android:name="android.intent.action.VIEW" />
26 <category android:name="android.intent.category.DEFAULT" />
27 <category android:name="android.intent.category.BROWSABLE" />
28 <data android:scheme="tomdroid" />
29 </intent-filter>
22 </activity>30 </activity>
23 31
24 <activity android:name=".ui.ViewNote">32 <activity android:name=".ui.ViewNote">
@@ -29,12 +37,19 @@
29 <data android:mimeType="vnd.android.cursor.item/vnd.tomdroid.note" />37 <data android:mimeType="vnd.android.cursor.item/vnd.tomdroid.note" />
30 </intent-filter>38 </intent-filter>
31 </activity>39 </activity>
32
33
34 40
35 <provider android:name="NoteProvider"41 <provider android:name="NoteProvider"
36 android:authorities="org.tomdroid.notes"42 android:authorities="org.tomdroid.notes"
37 />43 />
38</application>
39 <uses-permission android:name="android.permission.INTERNET" />
40</manifest>
41\ No newline at end of file44\ No newline at end of file
45
46 <activity android:name=".ui.PreferencesActivity" android:label="@string/app_name">
47
48 </activity>
49
50 <uses-library android:name="android.test.runner" />
51 </application>
52
53 <uses-permission android:name="android.permission.INTERNET" />
54<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="org.tomdroid"></instrumentation>
55
56</manifest>
4257
=== added file 'data/tomdroid-4.svg'
--- data/tomdroid-4.svg 1970-01-01 00:00:00 +0000
+++ data/tomdroid-4.svg 2010-10-03 12:21:44 +0000
@@ -0,0 +1,612 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:xlink="http://www.w3.org/1999/xlink"
11 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13 width="48"
14 height="48"
15 id="svg2394"
16 sodipodi:version="0.32"
17 inkscape:version="0.47 r22583"
18 version="1.0"
19 sodipodi:docname="tomdroid-4.png"
20 inkscape:output_extension="org.inkscape.output.svg.inkscape"
21 inkscape:export-filename="/data/code/android/web-sync/res/drawable/tomdroid-4.png"
22 inkscape:export-xdpi="90"
23 inkscape:export-ydpi="90">
24 <defs
25 id="defs2396">
26 <inkscape:perspective
27 sodipodi:type="inkscape:persp3d"
28 inkscape:vp_x="0 : 526.18109 : 1"
29 inkscape:vp_y="0 : 1000 : 0"
30 inkscape:vp_z="744.09448 : 526.18109 : 1"
31 inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
32 id="perspective2402" />
33 <linearGradient
34 id="linearGradient6377">
35 <stop
36 id="stop6379"
37 offset="0"
38 style="stop-color:#fff27e;stop-opacity:1;" />
39 <stop
40 id="stop6381"
41 offset="1"
42 style="stop-color:#edd400;stop-opacity:1;" />
43 </linearGradient>
44 <linearGradient
45 id="linearGradient2966">
46 <stop
47 style="stop-color:#ffd1d1;stop-opacity:1;"
48 offset="0"
49 id="stop2968" />
50 <stop
51 id="stop3006"
52 offset="0.5"
53 style="stop-color:#ff1d1d;stop-opacity:1;" />
54 <stop
55 style="stop-color:#6f0000;stop-opacity:1;"
56 offset="1"
57 id="stop2970" />
58 </linearGradient>
59 <linearGradient
60 id="linearGradient2974">
61 <stop
62 style="stop-color:#c1c1c1;stop-opacity:1;"
63 offset="0"
64 id="stop2976" />
65 <stop
66 style="stop-color:#acacac;stop-opacity:1;"
67 offset="1"
68 id="stop2978" />
69 </linearGradient>
70 <linearGradient
71 id="linearGradient2994">
72 <stop
73 style="stop-color:#000000;stop-opacity:1;"
74 offset="0"
75 id="stop2996" />
76 <stop
77 style="stop-color:#c9c9c9;stop-opacity:1;"
78 offset="1"
79 id="stop2998" />
80 </linearGradient>
81 <inkscape:perspective
82 id="perspective2705"
83 inkscape:persp3d-origin="24 : 16 : 1"
84 inkscape:vp_z="48 : 24 : 1"
85 inkscape:vp_y="0 : 1000 : 0"
86 inkscape:vp_x="0 : 24 : 1"
87 sodipodi:type="inkscape:persp3d" />
88 <inkscape:perspective
89 id="perspective3103"
90 inkscape:persp3d-origin="60 : 46.666667 : 1"
91 inkscape:vp_z="120 : 70 : 1"
92 inkscape:vp_y="0 : 1000 : 0"
93 inkscape:vp_x="0 : 70 : 1"
94 sodipodi:type="inkscape:persp3d" />
95 <radialGradient
96 gradientUnits="userSpaceOnUse"
97 gradientTransform="matrix(1,0,0,0.361345,0,22.29694)"
98 r="5.2591065"
99 fy="31.780704"
100 fx="39.907337"
101 cy="31.780704"
102 cx="39.907337"
103 id="radialGradient6423"
104 xlink:href="#linearGradient6417"
105 inkscape:collect="always" />
106 <radialGradient
107 gradientUnits="userSpaceOnUse"
108 gradientTransform="matrix(10.88255,-6.454846e-8,0,11.39737,-433.5968,-381.3811)"
109 r="20.21875"
110 fy="35.90107"
111 fx="43.875"
112 cy="35.90107"
113 cx="43.875"
114 id="radialGradient6413"
115 xlink:href="#linearGradient6407"
116 inkscape:collect="always" />
117 <radialGradient
118 r="21.626934"
119 fy="35.915409"
120 fx="45.150326"
121 cy="35.915409"
122 cx="45.150326"
123 gradientTransform="matrix(1.669712,0,1.702451e-8,1.220484,-30.23773,-11.79928)"
124 gradientUnits="userSpaceOnUse"
125 id="radialGradient6405"
126 xlink:href="#linearGradient6377"
127 inkscape:collect="always" />
128 <linearGradient
129 gradientUnits="userSpaceOnUse"
130 y2="67.031342"
131 x2="26.130388"
132 y1="14.08672"
133 x1="26.213203"
134 id="linearGradient5615"
135 xlink:href="#linearGradient5609"
136 inkscape:collect="always" />
137 <linearGradient
138 id="linearGradient5609"
139 inkscape:collect="always">
140 <stop
141 id="stop5611"
142 offset="0"
143 style="stop-color:white;stop-opacity:1;" />
144 <stop
145 id="stop5613"
146 offset="1"
147 style="stop-color:white;stop-opacity:0;" />
148 </linearGradient>
149 <linearGradient
150 id="linearGradient3342">
151 <stop
152 id="stop3344"
153 offset="0"
154 style="stop-color:#fff27e;stop-opacity:1;" />
155 <stop
156 id="stop3346"
157 offset="1"
158 style="stop-color:#edd400;stop-opacity:1;" />
159 </linearGradient>
160 <linearGradient
161 id="linearGradient6407"
162 inkscape:collect="always">
163 <stop
164 id="stop6409"
165 offset="0"
166 style="stop-color:white;stop-opacity:1;" />
167 <stop
168 id="stop6411"
169 offset="1"
170 style="stop-color:white;stop-opacity:0;" />
171 </linearGradient>
172 <linearGradient
173 id="linearGradient6417"
174 inkscape:collect="always">
175 <stop
176 id="stop6419"
177 offset="0"
178 style="stop-color:black;stop-opacity:1;" />
179 <stop
180 id="stop6421"
181 offset="1"
182 style="stop-color:black;stop-opacity:0;" />
183 </linearGradient>
184 <linearGradient
185 id="linearGradient3326">
186 <stop
187 style="stop-color:#ffd1d1;stop-opacity:1;"
188 offset="0"
189 id="stop3328" />
190 <stop
191 id="stop3330"
192 offset="0.5"
193 style="stop-color:#ff1d1d;stop-opacity:1;" />
194 <stop
195 style="stop-color:#6f0000;stop-opacity:1;"
196 offset="1"
197 id="stop3332" />
198 </linearGradient>
199 <linearGradient
200 id="linearGradient3319">
201 <stop
202 style="stop-color:#c1c1c1;stop-opacity:1;"
203 offset="0"
204 id="stop3321" />
205 <stop
206 style="stop-color:#acacac;stop-opacity:1;"
207 offset="1"
208 id="stop3323" />
209 </linearGradient>
210 <linearGradient
211 inkscape:collect="always"
212 id="linearGradient2984">
213 <stop
214 style="stop-color:#e7e2b8;stop-opacity:1;"
215 offset="0"
216 id="stop2986" />
217 <stop
218 style="stop-color:#e7e2b8;stop-opacity:0;"
219 offset="1"
220 id="stop2988" />
221 </linearGradient>
222 <linearGradient
223 id="linearGradient3308">
224 <stop
225 style="stop-color:#000000;stop-opacity:1;"
226 offset="0"
227 id="stop3310" />
228 <stop
229 style="stop-color:#c9c9c9;stop-opacity:1;"
230 offset="1"
231 id="stop3312" />
232 </linearGradient>
233 <inkscape:perspective
234 id="perspective3305"
235 inkscape:persp3d-origin="24 : 16 : 1"
236 inkscape:vp_z="48 : 24 : 1"
237 inkscape:vp_y="0 : 1000 : 0"
238 inkscape:vp_x="0 : 24 : 1"
239 sodipodi:type="inkscape:persp3d" />
240 <linearGradient
241 inkscape:collect="always"
242 xlink:href="#linearGradient2966"
243 id="linearGradient4272"
244 gradientUnits="userSpaceOnUse"
245 gradientTransform="translate(-5.669292,0)"
246 x1="48.90625"
247 y1="17.376184"
248 x2="50.988335"
249 y2="22.250591" />
250 <linearGradient
251 inkscape:collect="always"
252 xlink:href="#linearGradient2974"
253 id="linearGradient4274"
254 gradientUnits="userSpaceOnUse"
255 gradientTransform="translate(-5.669292,0)"
256 x1="46"
257 y1="19.8125"
258 x2="47.6875"
259 y2="22.625" />
260 <radialGradient
261 inkscape:collect="always"
262 xlink:href="#linearGradient2984"
263 id="radialGradient4276"
264 gradientUnits="userSpaceOnUse"
265 gradientTransform="matrix(2.923565,0,0,2.029717,-61.55532,-27.88417)"
266 cx="29.053354"
267 cy="27.640751"
268 fx="29.053354"
269 fy="27.640751"
270 r="3.2408544" />
271 <linearGradient
272 inkscape:collect="always"
273 xlink:href="#linearGradient2994"
274 id="linearGradient4278"
275 gradientUnits="userSpaceOnUse"
276 gradientTransform="translate(-5.825542,0.125)"
277 x1="25.71875"
278 y1="31.046875"
279 x2="25.514589"
280 y2="30.703125" />
281 <inkscape:perspective
282 id="perspective5044"
283 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
284 inkscape:vp_z="1 : 0.5 : 1"
285 inkscape:vp_y="0 : 1000 : 0"
286 inkscape:vp_x="0 : 0.5 : 1"
287 sodipodi:type="inkscape:persp3d" />
288 <inkscape:perspective
289 id="perspective5252"
290 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
291 inkscape:vp_z="1 : 0.5 : 1"
292 inkscape:vp_y="0 : 1000 : 0"
293 inkscape:vp_x="0 : 0.5 : 1"
294 sodipodi:type="inkscape:persp3d" />
295 <inkscape:perspective
296 id="perspective5278"
297 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
298 inkscape:vp_z="1 : 0.5 : 1"
299 inkscape:vp_y="0 : 1000 : 0"
300 inkscape:vp_x="0 : 0.5 : 1"
301 sodipodi:type="inkscape:persp3d" />
302 <inkscape:perspective
303 id="perspective5380"
304 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
305 inkscape:vp_z="1 : 0.5 : 1"
306 inkscape:vp_y="0 : 1000 : 0"
307 inkscape:vp_x="0 : 0.5 : 1"
308 sodipodi:type="inkscape:persp3d" />
309 <inkscape:perspective
310 id="perspective5404"
311 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
312 inkscape:vp_z="1 : 0.5 : 1"
313 inkscape:vp_y="0 : 1000 : 0"
314 inkscape:vp_x="0 : 0.5 : 1"
315 sodipodi:type="inkscape:persp3d" />
316 <inkscape:perspective
317 id="perspective5433"
318 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
319 inkscape:vp_z="1 : 0.5 : 1"
320 inkscape:vp_y="0 : 1000 : 0"
321 inkscape:vp_x="0 : 0.5 : 1"
322 sodipodi:type="inkscape:persp3d" />
323 <inkscape:perspective
324 id="perspective5459"
325 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
326 inkscape:vp_z="1 : 0.5 : 1"
327 inkscape:vp_y="0 : 1000 : 0"
328 inkscape:vp_x="0 : 0.5 : 1"
329 sodipodi:type="inkscape:persp3d" />
330 <inkscape:perspective
331 id="perspective5509"
332 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
333 inkscape:vp_z="1 : 0.5 : 1"
334 inkscape:vp_y="0 : 1000 : 0"
335 inkscape:vp_x="0 : 0.5 : 1"
336 sodipodi:type="inkscape:persp3d" />
337 <inkscape:perspective
338 id="perspective5564"
339 inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
340 inkscape:vp_z="1 : 0.5 : 1"
341 inkscape:vp_y="0 : 1000 : 0"
342 inkscape:vp_x="0 : 0.5 : 1"
343 sodipodi:type="inkscape:persp3d" />
344 </defs>
345 <sodipodi:namedview
346 id="base"
347 pagecolor="#ffffff"
348 bordercolor="#666666"
349 borderopacity="1.0"
350 inkscape:pageopacity="0.0"
351 inkscape:pageshadow="2"
352 inkscape:zoom="7.9195959"
353 inkscape:cx="25.379657"
354 inkscape:cy="34.644751"
355 inkscape:document-units="px"
356 inkscape:current-layer="layer1"
357 showgrid="false"
358 inkscape:window-width="1280"
359 inkscape:window-height="976"
360 inkscape:window-x="0"
361 inkscape:window-y="25"
362 inkscape:window-maximized="1" />
363 <metadata
364 id="metadata2399">
365 <rdf:RDF>
366 <cc:Work
367 rdf:about="">
368 <dc:format>image/svg+xml</dc:format>
369 <dc:type
370 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
371 <dc:title></dc:title>
372 <dc:date>2009-04-02</dc:date>
373 <dc:creator>
374 <cc:Agent>
375 <dc:title>Olivier Bilodeau</dc:title>
376 </cc:Agent>
377 </dc:creator>
378 <dc:description>A mashup of Tomboy's icon and Android's logo. Both available under free licences.</dc:description>
379 </cc:Work>
380 </rdf:RDF>
381 </metadata>
382 <g
383 inkscape:label="Calque 1"
384 inkscape:groupmode="layer"
385 id="layer1">
386 <g
387 id="g4242"
388 transform="translate(-0.1684169,3.5830001)">
389 <path
390 sodipodi:nodetypes="ccccccccccc"
391 id="rect1975"
392 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"
393 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" />
394 <rect
395 ry="0.67937863"
396 rx="0.67937863"
397 y="35.957905"
398 x="5.1146202"
399 height="7.0714951"
400 width="39.048077"
401 id="rect2851"
402 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" />
403 <path
404 sodipodi:nodetypes="ccccccc"
405 id="path2853"
406 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"
407 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" />
408 <path
409 id="path4730"
410 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"
411 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" />
412 <path
413 sodipodi:nodetypes="cccc"
414 id="path6415"
415 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"
416 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" />
417 <path
418 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"
419 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"
420 id="path2524"
421 sodipodi:nodetypes="cczczcccc" />
422 <path
423 id="path6403"
424 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"
425 style="opacity:0.46629214;fill:none;stroke:url(#radialGradient6413);stroke-width:0.99999964px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
426 <path
427 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"
428 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"
429 id="path6359"
430 sodipodi:nodetypes="ccccccc" />
431 <path
432 sodipodi:nodetypes="ccccccc"
433 id="path6361"
434 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"
435 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" />
436 <path
437 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"
438 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"
439 id="path6363"
440 sodipodi:nodetypes="ccccccc" />
441 </g>
442 <g
443 id="g4166-6"
444 transform="matrix(1.0098472,0,0,0.98902752,-298.47562,-483.65964)"
445 style="fill:#c4a000;fill-opacity:1;stroke:none" />
446 <g
447 id="g3106"
448 inkscape:label="Layer 1"
449 transform="matrix(0.9894224,0,0,0.9894224,86.886011,8.4726515)">
450 <g
451 transform="translate(-299.00515,-503.07627)"
452 id="g3279" />
453 </g>
454 <path
455 style="fill:#d40000;fill-opacity:1;stroke:#ffffff;stroke-width:1.70000005;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
456 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"
457 id="rect5365-8" />
458 <g
459 id="g5488"
460 style="stroke:none"
461 transform="translate(0,0.39995194)">
462 <path
463 id="rect5365"
464 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"
465 style="fill:#97c024;fill-opacity:1;stroke:none" />
466 <rect
467 rx="1.7022525"
468 y="23.661636"
469 x="19.224466"
470 height="9.5017471"
471 width="4.0090432"
472 id="rect5370"
473 style="fill:#97c024;fill-opacity:1;stroke:none" />
474 <rect
475 rx="1.7022525"
476 y="23.645853"
477 x="25.80624"
478 height="9.5017471"
479 width="4.0090432"
480 id="rect5370-3"
481 style="fill:#97c024;fill-opacity:1;stroke:none" />
482 <rect
483 rx="1.9887384"
484 y="11.413537"
485 x="10.922275"
486 height="12.405936"
487 width="3.9774768"
488 id="rect5394"
489 style="fill:#97c024;fill-opacity:1;stroke:none" />
490 <rect
491 rx="1.9887384"
492 y="11.55559"
493 x="33.871677"
494 height="12.405936"
495 width="3.9774768"
496 id="rect5394-2"
497 style="fill:#97c024;fill-opacity:1;stroke:none" />
498 <g
499 transform="translate(-0.20025,0)"
500 id="g5481"
501 style="stroke:none">
502 <path
503 style="fill:#97c024;fill-opacity:1;stroke:none"
504 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"
505 id="path5418" />
506 <path
507 sodipodi:type="arc"
508 style="fill:#ffffff;fill-opacity:1;stroke:none"
509 id="path5449"
510 sodipodi:cx="21.37104"
511 sodipodi:cy="8.3357286"
512 sodipodi:rx="0.78918165"
513 sodipodi:ry="0.77339804"
514 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"
515 transform="matrix(1.0212245,0,0,1.0212245,-1.4154165,-1.4231952)" />
516 <path
517 sodipodi:type="arc"
518 style="fill:#ffffff;fill-opacity:1;stroke:none"
519 id="path5449-6"
520 sodipodi:cx="21.37104"
521 sodipodi:cy="8.3357286"
522 sodipodi:rx="0.78918165"
523 sodipodi:ry="0.77339804"
524 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"
525 transform="matrix(1.0212245,0,0,1.0212245,6.7121839,-1.4238284)" />
526 <rect
527 style="fill:#97c024;fill-opacity:1;stroke:none"
528 id="rect5423"
529 width="1.0101534"
530 height="5.0507627"
531 x="15.824705"
532 y="9.2116003"
533 rx="0.50507671"
534 transform="matrix(0.88965349,-0.45663625,0.45663625,0.88965349,0,0)"
535 ry="0.56354231" />
536 <rect
537 style="fill:#97c024;fill-opacity:1;stroke:none"
538 id="rect5423-6"
539 width="1.0101534"
540 height="5.0507627"
541 x="-27.690969"
542 y="-13.159358"
543 rx="0.50507671"
544 transform="matrix(-0.88965349,-0.45663625,-0.45663625,0.88965349,0,0)"
545 ry="0.56354231" />
546 </g>
547 </g>
548 <g
549 style="display:inline"
550 id="g1574"
551 transform="matrix(0.7113809,-0.1906141,0.1906141,0.7113809,19.866431,-7.0127809)"
552 inkscape:r_cx="true"
553 inkscape:r_cy="true">
554 <path
555 transform="translate(-29.75546,19)"
556 sodipodi:nodetypes="cccccc"
557 id="path2960"
558 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"
559 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"
560 inkscape:r_cx="true"
561 inkscape:r_cy="true" />
562 <path
563 transform="translate(-29.75546,19)"
564 style="fill:url(#linearGradient4272);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
565 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"
566 id="path2964"
567 sodipodi:nodetypes="czcczcc"
568 inkscape:r_cx="true"
569 inkscape:r_cy="true" />
570 <path
571 transform="translate(-29.75546,19)"
572 sodipodi:nodetypes="czcczcc"
573 id="path2962"
574 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"
575 style="fill:url(#linearGradient4274);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
576 inkscape:r_cx="true"
577 inkscape:r_cy="true" />
578 <path
579 transform="translate(-29.75546,19)"
580 sodipodi:nodetypes="cccc"
581 id="path2982"
582 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"
583 style="fill:url(#radialGradient4276);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
584 inkscape:r_cx="true"
585 inkscape:r_cy="true" />
586 <path
587 transform="translate(-29.75546,19)"
588 sodipodi:nodetypes="cccc"
589 id="path2992"
590 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"
591 style="fill:url(#linearGradient4278);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
592 inkscape:r_cx="true"
593 inkscape:r_cy="true" />
594 <path
595 transform="translate(-29.75546,19)"
596 sodipodi:nodetypes="ccccc"
597 id="path3002"
598 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"
599 style="fill:#ffffff;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
600 inkscape:r_cx="true"
601 inkscape:r_cy="true" />
602 <path
603 transform="translate(-29.75546,19)"
604 sodipodi:nodetypes="ccccc"
605 id="path3004"
606 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"
607 style="fill:#000000;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
608 inkscape:r_cx="true"
609 inkscape:r_cy="true" />
610 </g>
611 </g>
612</svg>
0613
=== modified file 'default.properties'
--- default.properties 2010-01-25 05:01:42 +0000
+++ default.properties 2010-10-03 12:21:44 +0000
@@ -10,5 +10,5 @@
10# Indicates whether an apk should be generated for each density.10# Indicates whether an apk should be generated for each density.
11split.density=false11split.density=false
12# Project target.12# Project target.
13target=android-313target=android-4
14apk-configurations=14apk-configurations=
1515
=== added directory 'lib'
=== added file 'lib/signpost-commonshttp4-1.2.1.1.jar'
16Binary 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 differ16Binary 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
=== added file 'lib/signpost-core-1.2.1.1.jar'
17Binary 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 differ17Binary 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
=== added directory 'res/anim'
=== added file 'res/anim/pulse.xml'
--- res/anim/pulse.xml 1970-01-01 00:00:00 +0000
+++ res/anim/pulse.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?>
2
3<alpha xmlns:android="http://schemas.android.com/apk/res/android"
4 android:interpolator="@android:anim/accelerate_interpolator"
5 android:fromAlpha="0.0"
6 android:toAlpha="1.0"
7 android:duration="500"
8 android:repeatMode="reverse"
9 android:repeatCount="-1"
10 />
0\ No newline at end of file11\ No newline at end of file
112
=== added directory 'res/drawable-hdpi'
=== added file 'res/drawable-hdpi/icon_actionbar_dot.png'
2Binary 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 differ13Binary 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
=== added file 'res/drawable-hdpi/icon_actionbar_sync.png'
3Binary 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 differ14Binary 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
=== added file 'res/drawable-hdpi/icon_actionbar_sync_background.png'
4Binary 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 differ15Binary 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
=== added directory 'res/drawable-mdpi'
=== added file 'res/drawable-mdpi/icon_actionbar_dot.png'
5Binary 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 differ16Binary 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
=== added file 'res/drawable-mdpi/icon_actionbar_sync.png'
6Binary 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 differ17Binary 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
=== added file 'res/drawable-mdpi/icon_actionbar_sync_background.png'
7Binary 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 differ18Binary 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
=== modified file 'res/drawable/icon.png'
8Binary files res/drawable/icon.png 2009-04-03 03:19:35 +0000 and res/drawable/icon.png 2010-10-03 12:21:44 +0000 differ19Binary 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
=== added file 'res/drawable/icon_actionbar_dot.png'
9Binary 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 differ20Binary 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
=== added file 'res/drawable/icon_actionbar_sync.png'
10Binary 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 differ21Binary 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
=== added file 'res/drawable/icon_actionbar_sync_background.png'
11Binary 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 differ22Binary 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
=== removed file 'res/drawable/icon_sync.png'
12Binary 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 differ23Binary 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
=== added file 'res/drawable/syncbutton_background.xml'
--- res/drawable/syncbutton_background.xml 1970-01-01 00:00:00 +0000
+++ res/drawable/syncbutton_background.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,16 @@
1<?xml version="1.0" encoding="utf-8"?>
2<selector
3 xmlns:android="http://schemas.android.com/apk/res/android">
4 <item
5 android:state_focused="true"
6 android:state_pressed="false"
7 android:drawable="@drawable/syncbutton_background_focus" />
8 <item
9 android:state_focused="true"
10 android:state_pressed="true"
11 android:drawable="@drawable/syncbutton_background_pressed" />
12 <item
13 android:state_focused="false"
14 android:state_pressed="true"
15 android:drawable="@drawable/syncbutton_background_pressed" />
16</selector>
0\ No newline at end of file17\ No newline at end of file
118
=== added file 'res/drawable/syncbutton_background_focus.xml'
--- res/drawable/syncbutton_background_focus.xml 1970-01-01 00:00:00 +0000
+++ res/drawable/syncbutton_background_focus.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,4 @@
1<?xml version="1.0" encoding="utf-8"?>
2<shape xmlns:android="http://schemas.android.com/apk/res/android">
3 <solid android:color="#ED6400"/>
4</shape>
0\ No newline at end of file5\ No newline at end of file
16
=== added file 'res/drawable/syncbutton_background_pressed.xml'
--- res/drawable/syncbutton_background_pressed.xml 1970-01-01 00:00:00 +0000
+++ res/drawable/syncbutton_background_pressed.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,4 @@
1<?xml version="1.0" encoding="utf-8"?>
2<shape xmlns:android="http://schemas.android.com/apk/res/android">
3 <solid android:color="#EA9F00"/>
4</shape>
0\ No newline at end of file5\ No newline at end of file
16
=== added file 'res/layout/actionbar.xml'
--- res/layout/actionbar.xml 1970-01-01 00:00:00 +0000
+++ res/layout/actionbar.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,84 @@
1<!--
2 Tomdroid
3 Tomboy on Android
4 http://www.launchpad.net/tomdroid
5
6 Copyright 2010 Rodja Trappe <mail@rodja.net>
7
8 This file is part of Tomdroid.
9
10 Tomdroid is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 Tomdroid is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22-->
23<org.tomdroid.ui.Actionbar
24 xmlns:android="http://schemas.android.com/apk/res/android"
25 android:id="@+id/dhistory_row"
26 android:layout_width="fill_parent"
27 android:layout_height="40dip"
28 android:background="#ddd"
29 android:gravity="center_horizontal"
30 android:orientation="horizontal">
31 <ImageView
32 android:id="@+id/action_icon"
33 android:src="@drawable/icon"
34 android:layout_height="wrap_content"
35 android:layout_width="wrap_content"
36 android:scaleType="fitStart"
37 android:padding="4dip"
38 />
39 <TextView
40 android:id="@+id/title"
41 android:text="Tomdroid"
42 android:layout_marginLeft="42dip"
43 android:layout_height="wrap_content"
44 android:layout_width="wrap_content"
45 android:textSize="18dip"
46 android:textStyle="bold"
47 android:textColor="#FF555555"
48 android:singleLine="true"
49 android:ellipsize="marquee"
50 android:fadingEdge="horizontal"
51 android:fadingEdgeLength="5mm"
52 android:paddingTop="10dip"
53 android:paddingRight="30dip"
54 />
55 <ImageView
56 android:id="@+id/sync"
57 android:src="@drawable/icon_actionbar_sync_background"
58 android:background="@drawable/syncbutton_background"
59 android:layout_alignParentRight="true"
60 android:layout_width="wrap_content"
61 android:layout_height="fill_parent"
62 android:scaleType="center"
63 android:clickable="true"
64 android:focusable="true"
65 />
66 <ImageView
67 android:id="@+id/syncIcon"
68 android:src="@drawable/icon_actionbar_sync"
69 android:background="#00000000"
70 android:layout_alignParentRight="true"
71 android:layout_width="wrap_content"
72 android:layout_height="fill_parent"
73 android:scaleType="center"
74 />
75 <ImageView
76 android:id="@+id/sync_dot"
77 android:src="@drawable/icon_actionbar_dot"
78 android:layout_alignParentRight="true"
79 android:layout_width="wrap_content"
80 android:layout_height="fill_parent"
81 android:scaleType="center"
82 android:visibility="invisible"
83 />
84</org.tomdroid.ui.Actionbar>
0\ No newline at end of file85\ No newline at end of file
186
=== modified file 'res/layout/main.xml'
--- res/layout/main.xml 2009-09-29 04:42:34 +0000
+++ res/layout/main.xml 2010-10-03 12:21:44 +0000
@@ -22,17 +22,23 @@
22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
23-->23-->
24<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"24<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
25 android:layout_width="wrap_content"25 android:layout_width="fill_parent"
26 android:layout_height="wrap_content"26 android:layout_height="fill_parent"
27 android:orientation="vertical"
27 >28 >
29
30 <include android:id="@+id/actionbar" layout="@layout/actionbar" />
28 31
29 <ListView android:id="@+id/android:list"32 <ListView android:id="@android:id/android:list"
30 android:layout_width="fill_parent"33 android:layout_width="fill_parent"
31 android:layout_height="fill_parent"34 android:layout_height="fill_parent"
35 android:divider="#00000000"
36 android:dividerHeight="0px"
37 android:cacheColorHint="#ffdddddd"
32 />38 />
33 <TextView android:id="@+id/list_empty"39 <TextView android:id="@+id/list_empty"
34 android:layout_width="wrap_content"40 android:layout_width="wrap_content"
35 android:layout_height="wrap_content"41 android:layout_height="wrap_content"
36 android:text="@string/strListEmptyNoNotes"42 android:text="@string/strListEmptyNoNotes"
37 />43 />
38</LinearLayout>44</LinearLayout>
39\ No newline at end of file45\ No newline at end of file
4046
=== modified file 'res/layout/main_list_item.xml'
--- res/layout/main_list_item.xml 2009-06-23 02:51:17 +0000
+++ res/layout/main_list_item.xml 2010-10-03 12:21:44 +0000
@@ -21,9 +21,28 @@
21 You should have received a copy of the GNU General Public License21 You should have received a copy of the GNU General Public License
22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
23-->23-->
24<TextView android:id="@+id/note_title" xmlns:android="http://schemas.android.com/apk/res/android"24<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
25 android:layout_width="fill_parent"25 android:orientation="vertical"
26 android:layout_height="fill_parent"26 android:layout_width="fill_parent"
27 android:textSize="24dp"27 android:layout_height="wrap_content" >
28 android:padding="10dip"28 <TextView android:id="@+id/note_title" xmlns:android="http://schemas.android.com/apk/res/android"
29 />29 android:layout_width="fill_parent"
30 android:layout_height="fill_parent"
31 android:textStyle="bold"
32 android:textColor="#FF555555"
33 android:textSize="18dp"
34 android:paddingTop="5dip"
35 android:paddingLeft="5dip"
36 android:paddingRight="5dip"
37 />
38 <TextView android:id="@+id/note_date" xmlns:android="http://schemas.android.com/apk/res/android"
39 android:layout_width="fill_parent"
40 android:layout_height="fill_parent"
41 android:textColor="#FF555555"
42 android:textSize="14dp"
43 android:paddingTop="2dip"
44 android:paddingLeft="5dip"
45 android:paddingRight="5dip"
46 android:paddingBottom="5dip"
47 />
48</LinearLayout>
30\ No newline at end of file49\ No newline at end of file
3150
=== modified file 'res/layout/note_view.xml'
--- res/layout/note_view.xml 2009-06-17 11:02:51 +0000
+++ res/layout/note_view.xml 2010-10-03 12:21:44 +0000
@@ -21,22 +21,40 @@
21 You should have received a copy of the GNU General Public License21 You should have received a copy of the GNU General Public License
22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.22 along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
23-->23-->
2424<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
25 android:layout_width="fill_parent"
26 android:layout_height="fill_parent"
27 android:orientation="vertical"
28 >
29<include android:id="@+id/actionbar" layout="@layout/actionbar" />
25<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"30<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
26 android:id="@+id/textScroller"31 android:id="@+id/textScroller"
27 android:layout_width="fill_parent"32 android:layout_width="fill_parent"
28 android:layout_height="fill_parent"33 android:layout_height="fill_parent"
29 >34 android:background="#ffffffff"
3035 >
31 <TextView 36 <LinearLayout
32 xmlns:android="http://schemas.android.com/apk/res/android"37 android:id="@+id/LinearLayout01"
33 android:id="@+id/content"38 android:orientation="vertical"
34 android:layout_width="wrap_content" 39 android:layout_width="fill_parent"
35 android:layout_height="wrap_content"40 android:layout_height="fill_parent">
36 android:singleLine="false"41
37 android:text="@string/strWait"42 <!-- <TextView
38 android:padding="10dip"43 android:id="@+id/title"
39 android:textColor="#ffb8bcb8"44 android:layout_width="fill_parent"
40 />45 android:layout_height="wrap_content"
41 46 android:padding="10dip"
47 android:textStyle="bold" />-->
48
49 <TextView
50 xmlns:android="http://schemas.android.com/apk/res/android"
51 android:id="@+id/content"
52 android:layout_width="wrap_content"
53 android:layout_height="wrap_content"
54 android:singleLine="false"
55 android:text="@string/strWait"
56 android:padding="10dip"
57 android:textColor="#ffb8bcb8" />
58 </LinearLayout>
42</ScrollView>59</ScrollView>
60</LinearLayout>
43\ No newline at end of file61\ No newline at end of file
4462
=== modified file 'res/menu/main.xml'
--- res/menu/main.xml 2010-01-31 22:30:28 +0000
+++ res/menu/main.xml 2010-10-03 12:21:44 +0000
@@ -23,17 +23,15 @@
23-->23-->
24<menu xmlns:android="http://schemas.android.com/apk/res/android">24<menu xmlns:android="http://schemas.android.com/apk/res/android">
2525
26 <item
27 android:icon="@drawable/icon_sync"
28 android:title="@string/menuSyncWithSD"
29 android:id="@+id/menuSyncWithSD"
30 />
31
32
33 <item 26 <item
34 android:icon="@drawable/icon_about"27 android:icon="@drawable/icon_about"
35 android:title="@string/menuAbout"28 android:title="@string/menuAbout"
36 android:id="@+id/menuAbout"29 android:id="@+id/menuAbout"
37 />30 />
3831
32
33 <item
34 android:icon="@android:drawable/ic_menu_preferences"
35 android:title="@string/menuPrefs"
36 android:id="@+id/menuPrefs"/>
39</menu>37</menu>
4038
=== added file 'res/values/arrays.xml'
--- res/values/arrays.xml 1970-01-01 00:00:00 +0000
+++ res/values/arrays.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
3<array name="sortOrderArray">
4 <item>sort_date</item>
5 <item>sort_title</item>
6</array>
7<array name="sortOrderValues">
8 <item>Date Modified</item>
9 <item>Note Title</item>
10</array>
11</resources>
012
=== modified file 'res/values/strings.xml'
--- res/values/strings.xml 2010-01-31 21:54:19 +0000
+++ res/values/strings.xml 2010-10-03 12:21:44 +0000
@@ -33,7 +33,8 @@
33 the tomdroid/ directory on your sdcard then press \"Menu\" and \"Sync with SD Card\".33 the tomdroid/ directory on your sdcard then press \"Menu\" and \"Sync with SD Card\".
34 </string>34 </string>
35 35
36 <string name="menuSyncWithSD">Sync with SD Card</string>36 <string name="menuSync">Sync</string>
37 <string name="menuPrefs">Settings</string>
37 <string name="menuAbout">About</string>38 <string name="menuAbout">About</string>
38 <string name="strWelcome">39 <string name="strWelcome">
39 Welcome to Tomdroid.40 Welcome to Tomdroid.
@@ -59,5 +60,15 @@
5960
60 <!-- note-view.xml -->61 <!-- note-view.xml -->
61 <string name="strWait">Please wait while note loads...</string>62 <string name="strWait">Please wait while note loads...</string>
62 63
64 <string name="prefSync">Synchronization</string>
65
66 <string name="prefSyncService">Service</string>
67 <string name="prefSyncServer">Server</string>
68 <string name="prefAuthenticate">Authenticate</string>
69
70 <string name="prefSyncConnectionFailed">The connection to the server has failed, please check that the address you entered is correct.</string>
71 <string name="prefServerEmpty">The server address changed but the new value is empty</string>
72
73
63</resources>74</resources>
6475
=== added directory 'res/xml'
=== added file 'res/xml/preferences.xml'
--- res/xml/preferences.xml 1970-01-01 00:00:00 +0000
+++ res/xml/preferences.xml 2010-10-03 12:21:44 +0000
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="utf-8"?>
2<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <PreferenceCategory android:title="@string/prefSync">
5
6 <ListPreference android:title="@string/prefSyncService"
7 android:dialogTitle="Choose the sync service to use"
8 android:key="sync_service"
9 android:defaultValue="tomboy-web"/>
10
11 <EditTextPreference android:key="sync_server"
12 android:title="@string/prefSyncServer"
13 android:positiveButtonText="@string/prefAuthenticate"
14 android:shouldDisableView="true"/>
15
16 </PreferenceCategory>
17
18</PreferenceScreen>
0\ No newline at end of file19\ No newline at end of file
120
=== modified file 'src/org/tomdroid/Note.java'
--- src/org/tomdroid/Note.java 2010-02-18 03:58:09 +0000
+++ src/org/tomdroid/Note.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,7 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2008, 2009 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2008, 2009 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 * 8 *
8 * This file is part of Tomdroid.9 * This file is part of Tomdroid.
9 * 10 *
@@ -26,7 +27,10 @@
26import java.util.regex.Matcher;27import java.util.regex.Matcher;
27import java.util.regex.Pattern;28import java.util.regex.Pattern;
2829
30import org.json.JSONObject;
31import org.json.JSONArray;
29import org.tomdroid.util.NoteContentBuilder;32import org.tomdroid.util.NoteContentBuilder;
33import org.tomdroid.util.XmlUtils;
3034
31import android.os.Handler;35import android.os.Handler;
32import android.text.SpannableStringBuilder;36import android.text.SpannableStringBuilder;
@@ -43,6 +47,7 @@
43 public static final String MODIFIED_DATE = "modified_date";47 public static final String MODIFIED_DATE = "modified_date";
44 public static final String URL = "url";48 public static final String URL = "url";
45 public static final String FILE = "file";49 public static final String FILE = "file";
50 public static final String TAGS = "tags";
46 public static final String NOTE_CONTENT = "content";51 public static final String NOTE_CONTENT = "content";
47 52
48 // Logging info53 // Logging info
@@ -52,9 +57,9 @@
52 // TODO this is a weird yellow that was usable for the android emulator, I must confirm this for real usage57 // TODO this is a weird yellow that was usable for the android emulator, I must confirm this for real usage
53 public static final int NOTE_HIGHLIGHT_COLOR = 0xFFFFFF77;58 public static final int NOTE_HIGHLIGHT_COLOR = 0xFFFFFF77;
54 public static final String NOTE_MONOSPACE_TYPEFACE = "monospace";59 public static final String NOTE_MONOSPACE_TYPEFACE = "monospace";
55 public static final float NOTE_SIZE_SMALL_FACTOR = 0.8f;60 public static final float NOTE_SIZE_SMALL_FACTOR = 1.0f;
56 public static final float NOTE_SIZE_LARGE_FACTOR = 1.3f;61 public static final float NOTE_SIZE_LARGE_FACTOR = 1.5f;
57 public static final float NOTE_SIZE_HUGE_FACTOR = 1.6f;62 public static final float NOTE_SIZE_HUGE_FACTOR = 1.8f;
58 63
59 // Members64 // Members
60 private SpannableStringBuilder noteContent;65 private SpannableStringBuilder noteContent;
@@ -62,6 +67,7 @@
62 private String url;67 private String url;
63 private String fileName;68 private String fileName;
64 private String title;69 private String title;
70 private String tags;
65 private Time lastChangeDate;71 private Time lastChangeDate;
66 private int dbId;72 private int dbId;
67 private UUID guid;73 private UUID guid;
@@ -73,8 +79,32 @@
73 ".+" + // matches what we are getting rid of79 ".+" + // matches what we are getting rid of
74 "([-\\+]\\d{2}:\\d{2})"); // matches timezone (-xx:xx or +xx:xx)80 "([-\\+]\\d{2}:\\d{2})"); // matches timezone (-xx:xx or +xx:xx)
75 81
76 public Note() {}82 public Note() {
77 83 tags = new String();
84 }
85
86 public Note(JSONObject json) {
87
88 // These methods return an empty string if the key is not found
89 setTitle(XmlUtils.unescape(json.optString("title")));
90 setGuid(json.optString("guid"));
91 setLastChangeDate(json.optString("last-change-date"));
92 setXmlContent(json.optString("note-content"));
93 JSONArray jtags = json.optJSONArray("tags");
94 String tag;
95 tags = new String();
96 if (jtags != null) {
97 for (int i = 0; i < jtags.length(); i++ ) {
98 tag = jtags.optString(i);
99 tags += tag + ",";
100 }
101 }
102 }
103
104 public String getTags() {
105 return tags;
106 }
107
78 public String getUrl() {108 public String getUrl() {
79 return url;109 return url;
80 }110 }
@@ -138,7 +168,7 @@
138 public void setGuid(String guid) {168 public void setGuid(String guid) {
139 this.guid = UUID.fromString(guid);169 this.guid = UUID.fromString(guid);
140 }170 }
141 171
142 // TODO: should this handler passed around evolve into an observer pattern?172 // TODO: should this handler passed around evolve into an observer pattern?
143 public SpannableStringBuilder getNoteContent(Handler handler) {173 public SpannableStringBuilder getNoteContent(Handler handler) {
144 174
145175
=== modified file 'src/org/tomdroid/NoteManager.java'
--- src/org/tomdroid/NoteManager.java 2010-01-25 05:01:42 +0000
+++ src/org/tomdroid/NoteManager.java 2010-10-03 12:21:44 +0000
@@ -23,12 +23,15 @@
23package org.tomdroid;23package org.tomdroid;
2424
25import org.tomdroid.ui.Tomdroid;25import org.tomdroid.ui.Tomdroid;
26import org.tomdroid.util.NoteListCursorAdapter;
27import org.tomdroid.util.Preferences;
2628
27import android.app.Activity;29import android.app.Activity;
28import android.content.ContentResolver;30import android.content.ContentResolver;
29import android.content.ContentValues;31import android.content.ContentValues;
30import android.database.Cursor;32import android.database.Cursor;
31import android.net.Uri;33import android.net.Uri;
34import android.preference.ListPreference;
32import android.util.Log;35import android.util.Log;
33import android.widget.ListAdapter;36import android.widget.ListAdapter;
34import android.widget.SimpleCursorAdapter;37import android.widget.SimpleCursorAdapter;
@@ -36,8 +39,9 @@
36public class NoteManager {39public class NoteManager {
37 40
38 public static final String[] FULL_PROJECTION = { Note.ID, Note.TITLE, Note.FILE, Note.NOTE_CONTENT, Note.MODIFIED_DATE };41 public static final String[] FULL_PROJECTION = { Note.ID, Note.TITLE, Note.FILE, Note.NOTE_CONTENT, Note.MODIFIED_DATE };
39 public static final String[] LIST_PROJECTION = { Note.ID, Note.TITLE };42 public static final String[] LIST_PROJECTION = { Note.ID, Note.TITLE, Note.MODIFIED_DATE };
40 public static final String[] TITLE_PROJECTION = { Note.TITLE };43 public static final String[] TITLE_PROJECTION = { Note.TITLE };
44 public static final String[] GUID_PROJECTION = { Note.ID, Note.GUID };
41 public static final String[] ID_PROJECTION = { Note.ID };45 public static final String[] ID_PROJECTION = { Note.ID };
42 public static final String[] EMPTY_PROJECTION = {};46 public static final String[] EMPTY_PROJECTION = {};
43 47
@@ -95,6 +99,7 @@
95 // Notice that we store the date in UTC because sqlite doesn't handle RFC3339 timezone information99 // Notice that we store the date in UTC because sqlite doesn't handle RFC3339 timezone information
96 values.put(Note.MODIFIED_DATE, note.getLastChangeDate().format3339(false));100 values.put(Note.MODIFIED_DATE, note.getLastChangeDate().format3339(false));
97 values.put(Note.NOTE_CONTENT, note.getXmlContent());101 values.put(Note.NOTE_CONTENT, note.getXmlContent());
102 values.put(Note.TAGS, note.getTags());
98 103
99 if (managedCursor.getCount() == 0) {104 if (managedCursor.getCount() == 0) {
100 105
@@ -113,18 +118,42 @@
113 }118 }
114 }119 }
115 120
121 public static boolean deleteNote(Activity activity, int id)
122 {
123 Uri uri = Uri.parse(Tomdroid.CONTENT_URI+"/"+id);
124
125 ContentResolver cr = activity.getContentResolver();
126 int result = cr.delete(uri, null, null);
127
128 if(result > 0)
129 return true;
130 else
131 return false;
132 }
133
134 public static Cursor getAllNotes(Activity activity, Boolean includeNotebookTemplates) {
135 // get a cursor representing all notes from the NoteProvider
136 Uri notes = Tomdroid.CONTENT_URI;
137 String where = null;
138 String orderBy;
139 if (!includeNotebookTemplates) {
140 where = Note.TAGS + " NOT LIKE '%" + "system:template" + "%'";
141 }
142 orderBy = Note.MODIFIED_DATE + " DESC";
143 return activity.managedQuery(notes, LIST_PROJECTION, where, null, orderBy);
144 }
145
146
116 public static ListAdapter getListAdapter(Activity activity) {147 public static ListAdapter getListAdapter(Activity activity) {
117 148
118 // get a cursor representing all notes from the NoteProvider149 Cursor notesCursor = getAllNotes(activity, false);
119 Uri notes = Tomdroid.CONTENT_URI;
120 Cursor notesCursor = activity.managedQuery(notes, LIST_PROJECTION, null, null, null);
121 150
122 // set up an adapter binding the TITLE field of the cursor to the list item151 // set up an adapter binding the TITLE field of the cursor to the list item
123 String[] from = new String[] { Note.TITLE };152 String[] from = new String[] { Note.TITLE, Note.MODIFIED_DATE };
124 int[] to = new int[] { R.id.note_title };153 int[] to = new int[] { R.id.note_title, R.id.note_date };
125 return new SimpleCursorAdapter(activity, R.layout.main_list_item, notesCursor, from, to);154 return new NoteListCursorAdapter(activity, R.layout.main_list_item, notesCursor, from, to);
126 }155 }
127 156
128 // gets the titles of the notes present in the db, used in ViewNote.buildLinkifyPattern()157 // gets the titles of the notes present in the db, used in ViewNote.buildLinkifyPattern()
129 public static Cursor getTitles(Activity activity) {158 public static Cursor getTitles(Activity activity) {
130 159
@@ -132,6 +161,13 @@
132 return activity.managedQuery(Tomdroid.CONTENT_URI, TITLE_PROJECTION, null, null, null);161 return activity.managedQuery(Tomdroid.CONTENT_URI, TITLE_PROJECTION, null, null, null);
133 }162 }
134 163
164 // gets the ids of the notes present in the db, used in SyncService.deleteNotes()
165 public static Cursor getGuids(Activity activity) {
166
167 // get a cursor containing the notes guids
168 return activity.managedQuery(Tomdroid.CONTENT_URI, GUID_PROJECTION, null, null, null);
169 }
170
135 public static int getNoteId(Activity activity, String title) {171 public static int getNoteId(Activity activity, String title) {
136 172
137 int id = 0;173 int id = 0;
138174
=== modified file 'src/org/tomdroid/NoteProvider.java'
--- src/org/tomdroid/NoteProvider.java 2010-01-25 05:01:42 +0000
+++ src/org/tomdroid/NoteProvider.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,7 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2009 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2009 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009 Benoit Garret <benoit.garret_launchpad@gadz.org>
7 * 8 *
8 * This file is part of Tomdroid.9 * This file is part of Tomdroid.
9 * 10 *
@@ -68,7 +69,7 @@
68 // -- 69 // --
69 private static final String DATABASE_NAME = "tomdroid-notes.db";70 private static final String DATABASE_NAME = "tomdroid-notes.db";
70 private static final String DB_TABLE_NOTES = "notes";71 private static final String DB_TABLE_NOTES = "notes";
71 private static final int DB_VERSION = 2;72 private static final int DB_VERSION = 3;
72 private static final String DEFAULT_SORT_ORDER = Note.MODIFIED_DATE + " DESC";73 private static final String DEFAULT_SORT_ORDER = Note.MODIFIED_DATE + " DESC";
73 74
74 private static HashMap<String, String> notesProjectionMap;75 private static HashMap<String, String> notesProjectionMap;
@@ -99,7 +100,8 @@
99 + Note.TITLE + " TEXT,"100 + Note.TITLE + " TEXT,"
100 + Note.FILE + " TEXT,"101 + Note.FILE + " TEXT,"
101 + Note.NOTE_CONTENT + " TEXT,"102 + Note.NOTE_CONTENT + " TEXT,"
102 + Note.MODIFIED_DATE + " STRING"103 + Note.MODIFIED_DATE + " STRING,"
104 + Note.TAGS + " STRING"
103 + ");");105 + ");");
104 }106 }
105107
@@ -296,6 +298,7 @@
296 notesProjectionMap.put(Note.TITLE, Note.TITLE);298 notesProjectionMap.put(Note.TITLE, Note.TITLE);
297 notesProjectionMap.put(Note.FILE, Note.FILE);299 notesProjectionMap.put(Note.FILE, Note.FILE);
298 notesProjectionMap.put(Note.NOTE_CONTENT, Note.NOTE_CONTENT);300 notesProjectionMap.put(Note.NOTE_CONTENT, Note.NOTE_CONTENT);
301 notesProjectionMap.put(Note.TAGS, Note.TAGS);
299 notesProjectionMap.put(Note.MODIFIED_DATE, Note.MODIFIED_DATE);302 notesProjectionMap.put(Note.MODIFIED_DATE, Note.MODIFIED_DATE);
300 }303 }
301}304}
302305
=== added directory 'src/org/tomdroid/sync'
=== added file 'src/org/tomdroid/sync/ServiceAuth.java'
--- src/org/tomdroid/sync/ServiceAuth.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/ServiceAuth.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,33 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync;
24
25import android.net.Uri;
26import android.os.Handler;
27
28public interface ServiceAuth {
29
30 public boolean isConfigured();
31 public void getAuthUri(final String server, Handler handler);
32 public void remoteAuthComplete(Uri uri, Handler handler);
33}
034
=== added file 'src/org/tomdroid/sync/SyncManager.java'
--- src/org/tomdroid/sync/SyncManager.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/SyncManager.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,106 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync;
24
25import java.io.FileNotFoundException;
26import java.util.ArrayList;
27
28import org.tomdroid.sync.sd.SdCardSyncService;
29import org.tomdroid.sync.web.SnowySyncService;
30import org.tomdroid.util.Preferences;
31
32import android.app.Activity;
33import android.os.Handler;
34import android.util.Log;
35
36public class SyncManager {
37
38 private static final String TAG = "SyncManager";
39
40 private ArrayList<SyncService> services = new ArrayList<SyncService>();
41
42 public SyncManager() {
43 createServices();
44 }
45
46 public ArrayList<SyncService> getServices() {
47 return services;
48 }
49
50 public SyncService getService(String name) {
51
52 for (int i = 0; i < services.size(); i++) {
53 SyncService service = services.get(i);
54 if (name.equals(service.getName()))
55 return service;
56 }
57
58 return null;
59 }
60
61 public void startSynchronization() {
62
63 SyncService service = getCurrentService();
64 service.startSynchronization();
65 }
66
67 public SyncService getCurrentService() {
68 String serviceName = Preferences.getString(Preferences.Key.SYNC_SERVICE);
69 return getService(serviceName);
70 }
71
72 private static SyncManager instance = null;
73 private static Activity activity;
74 private static Handler handler;
75
76 public static SyncManager getInstance() {
77
78 if (instance == null)
79 instance = new SyncManager();
80
81 return instance;
82 }
83
84 public static void setActivity(Activity a) {
85 activity = a;
86 getInstance().createServices();
87 }
88
89 public static void setHandler(Handler h) {
90 handler = h;
91 getInstance().createServices();
92 }
93
94 private void createServices() {
95 services.clear();
96
97 services.add(new SnowySyncService(activity, handler));
98
99 try {
100 services.add(new SdCardSyncService(activity, handler));
101 } catch (FileNotFoundException e) {
102 // TODO Auto-generated catch block
103 e.printStackTrace();
104 }
105 }
106}
0107
=== added file 'src/org/tomdroid/sync/SyncService.java'
--- src/org/tomdroid/sync/SyncService.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/SyncService.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,196 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
8 * Copyright 2010, Rodja Trappe <mail@rodja.net>
9 *
10 * This file is part of Tomdroid.
11 *
12 * Tomdroid is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * Tomdroid is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
24 */
25package org.tomdroid.sync;
26
27import java.util.ArrayList;
28
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Executors;
31
32import org.tomdroid.Note;
33import org.tomdroid.NoteManager;
34import org.tomdroid.ui.Tomdroid;
35
36import android.app.Activity;
37import android.database.Cursor;
38import android.os.Handler;
39import android.os.Message;
40import android.util.Log;
41import android.widget.Toast;
42
43public abstract class SyncService {
44
45 private static final String TAG = "SyncService";
46
47 private Activity activity;
48 private final ExecutorService pool;
49 private final static int poolSize = 1;
50
51 private Handler handler;
52 private int syncProgress = 100;
53
54 // handler messages
55 public final static int PARSING_COMPLETE = 1;
56 public final static int PARSING_FAILED = 2;
57 public final static int PARSING_NO_NOTES = 3;
58 public final static int NO_INTERNET = 4;
59 public final static int SYNC_PROGRESS = 5;
60
61 public SyncService(Activity activity, Handler handler) {
62
63 this.activity = activity;
64 this.handler = handler;
65 pool = Executors.newFixedThreadPool(poolSize);
66 }
67
68 public void startSynchronization() {
69
70 if (syncProgress != 100){
71 Toast.makeText(activity, "Sync already in prgress", Toast.LENGTH_SHORT).show();
72 return;
73 }
74
75 sync();
76 }
77
78 protected abstract void sync();
79 public abstract boolean needsServer();
80 public abstract boolean needsAuth();
81
82 /**
83 * @return An unique identifier, not visible to the user.
84 */
85
86 public abstract String getName();
87
88 /**
89 * @return An human readable name, used in the preferences to distinguish the different sync services.
90 */
91
92 public abstract String getDescription();
93
94 /**
95 * Execute code in a separate thread.
96 * Use this for blocking and/or cpu intensive operations and thus avoid blocking the UI.
97 *
98 * @param r The Runner subclass to execute
99 */
100
101 protected void execInThread(Runnable r) {
102
103 pool.execute(r);
104 }
105
106 /**
107 * Insert a note in the content provider. The identifier for the notes is the guid.
108 *
109 * @param note The note to insert.
110 */
111
112 protected void insertNote(Note note, boolean syncFinished) {
113
114 NoteManager.putNote(this.activity, note);
115
116 // if last note warn in UI that we are done
117 if (syncFinished) {
118 handler.sendEmptyMessage(PARSING_COMPLETE);
119 }
120 }
121
122 /**
123 * Delete notes in the content provider. The guids passed identify the notes existing
124 * on the remote end (ie. that shouldn't be deleted).
125 *
126 * @param remoteGuids The notes NOT to delete.
127 */
128
129 protected void deleteNotes(ArrayList<String> remoteGuids) {
130
131 Cursor localGuids = NoteManager.getGuids(this.activity);
132
133 // cursor must not be null and must return more than 0 entry
134 if (!(localGuids == null || localGuids.getCount() == 0)) {
135
136 String localGuid;
137
138 localGuids.moveToFirst();
139
140 do {
141 localGuid = localGuids.getString(localGuids.getColumnIndexOrThrow(Note.GUID));
142
143 if(!remoteGuids.contains(localGuid)) {
144 int id = localGuids.getInt(localGuids.getColumnIndexOrThrow(Note.ID));
145 NoteManager.deleteNote(this.activity, id);
146 }
147
148 } while (localGuids.moveToNext());
149
150 } else {
151
152 // TODO send an error to the user
153 if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "Cursor returned null or 0 notes");
154 }
155 }
156
157 /**
158 * Send a message to the main UI.
159 *
160 * @param message The message id to send, the PARSING_* or NO_INTERNET attributes can be used.
161 */
162
163 protected void sendMessage(int message) {
164
165 handler.sendEmptyMessage(message);
166 }
167
168 /**
169 * Update the synchronization progress
170 *
171 * @param progress
172 */
173
174 protected void setSyncProgress(int progress) {
175 synchronized (TAG) {
176 Log.v(TAG, "sync progress: " + progress);
177 Message progressMessage = new Message();
178 progressMessage.what = SYNC_PROGRESS;
179 progressMessage.arg1 = progress;
180 progressMessage.arg2 = syncProgress;
181
182 handler.sendMessage(progressMessage);
183 syncProgress = progress;
184 }
185 }
186
187 protected int getSyncProgress(){
188 synchronized (TAG) {
189 return syncProgress;
190 }
191 }
192
193 public boolean isSyncable() {
194 return getSyncProgress() == 100;
195 }
196}
0197
=== added directory 'src/org/tomdroid/sync/sd'
=== renamed file 'src/org/tomdroid/xml/NoteHandler.java' => 'src/org/tomdroid/sync/sd/NoteHandler.java'
--- src/org/tomdroid/xml/NoteHandler.java 2010-02-16 05:18:09 +0000
+++ src/org/tomdroid/sync/sd/NoteHandler.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,7 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 * 8 *
8 * This file is part of Tomdroid.9 * This file is part of Tomdroid.
9 * 10 *
@@ -20,7 +21,7 @@
20 * You should have received a copy of the GNU General Public License21 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.22 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */23 */
23package org.tomdroid.xml;24package org.tomdroid.sync.sd;
2425
25import org.tomdroid.Note;26import org.tomdroid.Note;
26import org.xml.sax.Attributes;27import org.xml.sax.Attributes;
2728
=== renamed file 'src/org/tomdroid/util/AsyncNoteLoaderAndParser.java' => 'src/org/tomdroid/sync/sd/SdCardSyncService.java'
--- src/org/tomdroid/util/AsyncNoteLoaderAndParser.java 2010-02-16 05:18:09 +0000
+++ src/org/tomdroid/sync/sd/SdCardSyncService.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,8 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
8 * Copyright 2010, Rodja Trappe <mail@rodja.net>
7 * 9 *
8 * This file is part of Tomdroid.10 * This file is part of Tomdroid.
9 * 11 *
@@ -20,17 +22,16 @@
20 * You should have received a copy of the GNU General Public License22 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.23 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */24 */
23package org.tomdroid.util;25package org.tomdroid.sync.sd;
2426
25import java.io.BufferedReader;27import java.io.BufferedReader;
26import java.io.File;28import java.io.File;
27import java.io.FileInputStream;29import java.io.FileInputStream;
30import java.io.FileNotFoundException;
28import java.io.FilenameFilter;31import java.io.FilenameFilter;
29import java.io.IOException;32import java.io.IOException;
30import java.io.InputStreamReader;33import java.io.InputStreamReader;
31import java.io.Reader;34import java.io.Reader;
32import java.util.concurrent.ExecutorService;
33import java.util.concurrent.Executors;
34import java.util.regex.Matcher;35import java.util.regex.Matcher;
35import java.util.regex.Pattern;36import java.util.regex.Pattern;
3637
@@ -39,9 +40,8 @@
39import javax.xml.parsers.SAXParserFactory;40import javax.xml.parsers.SAXParserFactory;
4041
41import org.tomdroid.Note;42import org.tomdroid.Note;
42import org.tomdroid.NoteManager;43import org.tomdroid.sync.SyncService;
43import org.tomdroid.ui.Tomdroid;44import org.tomdroid.ui.Tomdroid;
44import org.tomdroid.xml.NoteHandler;
45import org.xml.sax.InputSource;45import org.xml.sax.InputSource;
46import org.xml.sax.SAXException;46import org.xml.sax.SAXException;
47import org.xml.sax.XMLReader;47import org.xml.sax.XMLReader;
@@ -51,55 +51,75 @@
51import android.util.Log;51import android.util.Log;
52import android.util.TimeFormatException;52import android.util.TimeFormatException;
5353
54public class AsyncNoteLoaderAndParser {54public class SdCardSyncService extends SyncService {
55 55
56 // thread pool info
57 private final ExecutorService pool;
58 private final static int poolSize = 1;
59
60 // members
61 private Activity activity;
62 private File path;56 private File path;
63 private Handler handler;57 private int numberOfFilesToSync = 0;
64
65 // handler messages
66 public final static int PARSING_COMPLETE = 1;
67 public final static int PARSING_FAILED = 2;
68 public final static int PARSING_NO_NOTES = 3;
69 58
70 // regexp for <note-content..>...</note-content>59 // regexp for <note-content..>...</note-content>
71 private static Pattern note_content = Pattern.compile(".*(<note-content.*>.*<\\/note-content>).*", Pattern.CASE_INSENSITIVE+Pattern.DOTALL);60 private static Pattern note_content = Pattern.compile("<note-content.*>(.*)<\\/note-content>", Pattern.CASE_INSENSITIVE+Pattern.DOTALL);
72 61
73 // logging related62 // logging related
74 private final static String TAG = "AsyncNoteLoaderAndParser";63 private final static String TAG = "SdCardSyncService";
75 64
76 public AsyncNoteLoaderAndParser(Activity a, File path) {65 public SdCardSyncService(Activity activity, Handler handler) throws FileNotFoundException {
77 this.activity = a;66 super(activity, handler);
78 this.path = path;67
79 68 path = new File(Tomdroid.NOTES_PATH);
80 pool = Executors.newFixedThreadPool(poolSize);69
81 }70 if (!path.exists())
8271 path.mkdir();
83 public void readAndParseNotes(Handler hndl) {72 }
84 handler = hndl;73
74 @Override
75 public String getDescription() {
76 return "SD Card";
77 }
78
79 @Override
80 public String getName() {
81 return "sdcard";
82 }
83
84 @Override
85 public boolean needsServer() {
86 return false;
87 }
88
89 @Override
90 public boolean needsAuth() {
91 return false;
92 }
93
94 @Override
95 protected void sync() {
96
97 setSyncProgress(0);
98
99 // start loading local notes
100 if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "Loading local notes");
101
85 File[] fileList = path.listFiles(new NotesFilter());102 File[] fileList = path.listFiles(new NotesFilter());
103 numberOfFilesToSync = fileList.length;
86 104
87 // If there are no notes, warn the UI through an empty message105 // If there are no notes, warn the UI through an empty message
88 if (fileList.length == 0) {106 if (fileList == null || fileList.length == 0) {
89 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "There are no notes in "+path);107 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "There are no notes in "+path);
90 handler.sendEmptyMessage(PARSING_NO_NOTES);108 sendMessage(PARSING_NO_NOTES);
109 setSyncProgress(100);
91 return;110 return;
92 }111 }
93 112
94 // every but the last note113 // every but the last note
95 for(int i = 0; i < fileList.length-1; i++) {114 for(int i = 0; i < fileList.length-1; i++) {
115 // TODO better progress reporting from within the workers
96 116
97 // give a filename to a thread and ask to parse it117 // give a filename to a thread and ask to parse it
98 pool.execute(new Worker(fileList[i], false));118 execInThread(new Worker(fileList[i], false));
99 }119 }
100 120
101 // last task, warn it so it'll warn UI when done121 // last task, warn it so it'll warn UI when done
102 pool.execute(new Worker(fileList[fileList.length-1], true));122 execInThread(new Worker(fileList[fileList.length-1], true));
103 }123 }
104 124
105 /**125 /**
@@ -145,7 +165,7 @@
145 165
146 // Get the XMLReader of the SAXParser we created166 // Get the XMLReader of the SAXParser we created
147 XMLReader xr = sp.getXMLReader();167 XMLReader xr = sp.getXMLReader();
148 168
149 // Create a new ContentHandler, send it this note to fill and apply it to the XML-Reader169 // Create a new ContentHandler, send it this note to fill and apply it to the XML-Reader
150 NoteHandler xmlHandler = new NoteHandler(note);170 NoteHandler xmlHandler = new NoteHandler(note);
151 xr.setContentHandler(xmlHandler);171 xr.setContentHandler(xmlHandler);
@@ -157,7 +177,7 @@
157 177
158 if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "parsing note");178 if (Tomdroid.LOGGING_ENABLED) Log.d(TAG, "parsing note");
159 xr.parse(is);179 xr.parse(is);
160 180
161 // TODO wrap and throw a new exception here181 // TODO wrap and throw a new exception here
162 } catch (ParserConfigurationException e) {182 } catch (ParserConfigurationException e) {
163 e.printStackTrace();183 e.printStackTrace();
@@ -168,7 +188,8 @@
168 } catch (TimeFormatException e) {188 } catch (TimeFormatException e) {
169 e.printStackTrace();189 e.printStackTrace();
170 if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Problem parsing the note's date and time");190 if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Problem parsing the note's date and time");
171 handler.sendEmptyMessage(PARSING_FAILED);191 sendMessage(PARSING_FAILED);
192 onWorkDone();
172 return;193 return;
173 }194 }
174195
@@ -202,12 +223,15 @@
202 if (Tomdroid.LOGGING_ENABLED) Log.w(TAG, "Something went wrong trying to read the note");223 if (Tomdroid.LOGGING_ENABLED) Log.w(TAG, "Something went wrong trying to read the note");
203 }224 }
204 225
205 NoteManager.putNote(AsyncNoteLoaderAndParser.this.activity, note);226 insertNote(note, isLast);
206 227 onWorkDone();
207 // if last note warn in UI that we are done228 }
208 if (isLast) {229
209 handler.sendEmptyMessage(PARSING_COMPLETE);230 private void onWorkDone(){
210 }231 if (isLast)
232 setSyncProgress(100);
233 else
234 setSyncProgress((int) (getSyncProgress() + 100.0 / numberOfFilesToSync));
211 }235 }
212 }236 }
213}237}
214238
=== added directory 'src/org/tomdroid/sync/web'
=== added file 'src/org/tomdroid/sync/web/AnonymousConnection.java'
--- src/org/tomdroid/sync/web/AnonymousConnection.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/web/AnonymousConnection.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,62 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync.web;
24
25import java.io.UnsupportedEncodingException;
26import java.net.UnknownHostException;
27
28import org.apache.http.HttpResponse;
29import org.apache.http.client.methods.HttpGet;
30import org.apache.http.client.methods.HttpPut;
31import org.apache.http.entity.StringEntity;
32
33public class AnonymousConnection extends WebConnection {
34
35 @Override
36 public String get(String uri) throws UnknownHostException
37 {
38 // Prepare a request object
39 HttpGet httpGet = new HttpGet(uri);
40 HttpResponse response = execute(httpGet);
41 return parseResponse(response);
42 }
43
44 @Override
45 public String put(String uri, String data) throws UnknownHostException {
46
47 // Prepare a request object
48 HttpPut httpPut = new HttpPut(uri);
49
50 try {
51 // The default http content charset is ISO-8859-1, JSON requires UTF-8
52 httpPut.setEntity(new StringEntity(data, "UTF-8"));
53 } catch (UnsupportedEncodingException e1) {
54 e1.printStackTrace();
55 return null;
56 }
57
58 httpPut.setHeader("Content-Type", "application/json");
59 HttpResponse response = execute(httpPut);
60 return parseResponse(response);
61 }
62}
063
=== added file 'src/org/tomdroid/sync/web/OAuthConnection.java'
--- src/org/tomdroid/sync/web/OAuthConnection.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/web/OAuthConnection.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,278 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync.web;
24
25import java.io.UnsupportedEncodingException;
26import java.net.UnknownHostException;
27
28import oauth.signpost.OAuthConsumer;
29import oauth.signpost.OAuthProvider;
30import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
31import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
32import oauth.signpost.exception.OAuthCommunicationException;
33import oauth.signpost.exception.OAuthExpectationFailedException;
34import oauth.signpost.exception.OAuthMessageSignerException;
35import oauth.signpost.exception.OAuthNotAuthorizedException;
36
37import org.apache.http.HttpRequest;
38import org.apache.http.HttpResponse;
39import org.apache.http.client.methods.HttpGet;
40import org.apache.http.client.methods.HttpPut;
41import org.apache.http.entity.StringEntity;
42import org.json.JSONException;
43import org.json.JSONObject;
44import org.tomdroid.ui.Tomdroid;
45import org.tomdroid.util.Preferences;
46
47import android.net.Uri;
48import android.util.Log;
49
50public class OAuthConnection extends WebConnection {
51
52 private static final String TAG = "OAuthConnection";
53 private static final String CONSUMER_KEY = "anyone";
54 private static final String CONSUMER_SECRET = "anyone";
55
56 private OAuthConsumer consumer = null;
57
58 public String accessToken = "";
59 public String accessTokenSecret = "";
60 public String requestToken = "";
61 public String requestTokenSecret = "";
62 public boolean oauth10a = false;
63 public String authorizeUrl = "";
64 public String requestTokenUrl = "";
65 public String accessTokenUrl = "";
66 public String rootApi = "";
67 public String userApi = "";
68
69 public OAuthConnection() {
70
71 consumer = new CommonsHttpOAuthConsumer(
72 CONSUMER_KEY,
73 CONSUMER_SECRET);
74 }
75
76 public boolean isAuthenticated() {
77
78 if (accessToken.equals("") || accessTokenSecret.equals(""))
79 return false;
80 else
81 return true;
82 }
83
84 private OAuthProvider getProvider() {
85
86 // Use the provider bundled with signpost, the android libs are buggy
87 // See: http://code.google.com/p/oauth-signpost/issues/detail?id=20
88 OAuthProvider provider = new CommonsHttpOAuthProvider(
89 requestTokenUrl,
90 accessTokenUrl,
91 authorizeUrl);
92 provider.setOAuth10a(oauth10a);
93
94 return provider;
95 }
96
97 private void sign(HttpRequest request) {
98
99 if (isAuthenticated())
100 consumer.setTokenWithSecret(accessToken, accessTokenSecret);
101 else
102 return;
103
104 // TODO: figure out if we should throw exceptions
105 try {
106 consumer.sign(request);
107 } catch (OAuthMessageSignerException e1) {
108 e1.printStackTrace();
109 } catch (OAuthExpectationFailedException e1) {
110 e1.printStackTrace();
111 } catch (OAuthCommunicationException e) {
112 // TODO Auto-generated catch block
113 e.printStackTrace();
114 }
115 }
116
117 public Uri getAuthorizationUrl(String server) throws UnknownHostException {
118
119 String url = "";
120
121 // this method shouldn't have been called
122 if (isAuthenticated())
123 return null;
124
125 rootApi = server+"/api/1.0/";
126
127 AnonymousConnection connection = new AnonymousConnection();
128 String response = connection.get(rootApi);
129
130 JSONObject jsonResponse;
131
132 try {
133 jsonResponse = new JSONObject(response);
134
135 accessTokenUrl = jsonResponse.getString("oauth_access_token_url");
136 requestTokenUrl = jsonResponse.getString("oauth_request_token_url");
137 authorizeUrl = jsonResponse.getString("oauth_authorize_url");
138
139 } catch (JSONException e) {
140 e.printStackTrace();
141 return null;
142 }
143
144 OAuthProvider provider = getProvider();
145
146 try {
147 // the argument is the callback used when the remote authorization is complete
148 url = provider.retrieveRequestToken(consumer, "tomdroid://sync");
149
150 requestToken = consumer.getToken();
151 requestTokenSecret = consumer.getTokenSecret();
152 oauth10a = provider.isOAuth10a();
153 accessToken = "";
154 accessTokenSecret = "";
155 saveConfiguration();
156
157 } catch (OAuthMessageSignerException e1) {
158 e1.printStackTrace();
159 return null;
160 } catch (OAuthNotAuthorizedException e1) {
161 e1.printStackTrace();
162 return null;
163 } catch (OAuthExpectationFailedException e1) {
164 e1.printStackTrace();
165 return null;
166 } catch (OAuthCommunicationException e1) {
167 e1.printStackTrace();
168 return null;
169 }
170
171 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Authorization URL : "+url);
172
173 return Uri.parse(url);
174 }
175
176 public boolean getAccess(String verifier) throws UnknownHostException {
177
178 Log.i(TAG, "Verifier: "+verifier);
179
180 // this method shouldn't have been called
181 if (isAuthenticated())
182 return false;
183
184 if (!requestToken.equals("") && !requestTokenSecret.equals("")) {
185 consumer.setTokenWithSecret(requestToken, requestTokenSecret);
186 if(Tomdroid.LOGGING_ENABLED) {
187 Log.d(TAG, "Added request token "+requestTokenSecret+" and request token secret "+requestTokenSecret);
188 }
189 }
190 else
191 return false;
192
193 OAuthProvider provider = getProvider();
194
195 try {
196 provider.retrieveAccessToken(consumer, verifier);
197 } catch (OAuthMessageSignerException e1) {
198 e1.printStackTrace();
199 return false;
200 } catch (OAuthNotAuthorizedException e1) {
201 e1.printStackTrace();
202 return false;
203 } catch (OAuthExpectationFailedException e1) {
204 e1.printStackTrace();
205 return false;
206 } catch (OAuthCommunicationException e1) {
207 e1.printStackTrace();
208 return false;
209 }
210
211 // access has been granted, store the access token
212 accessToken = consumer.getToken();
213 accessTokenSecret = consumer.getTokenSecret();
214 requestToken = "";
215 requestTokenSecret = "";
216
217 try {
218 JSONObject response = new JSONObject(get(rootApi));
219 // append a slash to the url, else the signature will fail
220 userApi = response.getJSONObject("user-ref").getString("api-ref");
221 } catch (JSONException e) {
222 // TODO Auto-generated catch block
223 e.printStackTrace();
224 }
225
226 saveConfiguration();
227
228 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Got access token "+consumer.getToken()+".");
229
230 return true;
231 }
232
233 @Override
234 public String get(String uri) throws java.net.UnknownHostException {
235
236 // Prepare a request object
237 HttpGet httpGet = new HttpGet(uri);
238 sign(httpGet);
239 HttpResponse response = execute(httpGet);
240 return parseResponse(response);
241 }
242
243 @Override
244 public String put(String uri, String data) throws UnknownHostException {
245
246 // Prepare a request object
247 HttpPut httpPut = new HttpPut(uri);
248
249 try {
250 // The default http content charset is ISO-8859-1, JSON requires UTF-8
251 httpPut.setEntity(new StringEntity(data, "UTF-8"));
252 } catch (UnsupportedEncodingException e1) {
253 e1.printStackTrace();
254 return null;
255 }
256
257 httpPut.setHeader("Content-Type", "application/json");
258 sign(httpPut);
259
260 // Do not handle redirects, we need to sign the request again as the old signature will be invalid
261 HttpResponse response = execute(httpPut);
262 return parseResponse(response);
263 }
264
265 private void saveConfiguration() {
266
267 Preferences.putString(Preferences.Key.ACCESS_TOKEN, accessToken);
268 Preferences.putString(Preferences.Key.ACCESS_TOKEN_SECRET, accessTokenSecret);
269 Preferences.putString(Preferences.Key.ACCESS_TOKEN_URL, accessTokenUrl);
270 Preferences.putString(Preferences.Key.REQUEST_TOKEN, requestToken);
271 Preferences.putString(Preferences.Key.REQUEST_TOKEN_SECRET, requestTokenSecret);
272 Preferences.putString(Preferences.Key.REQUEST_TOKEN_URL, requestTokenUrl);
273 Preferences.putBoolean(Preferences.Key.OAUTH_10A, oauth10a);
274 Preferences.putString(Preferences.Key.AUTHORIZE_URL, authorizeUrl);
275 Preferences.putString(Preferences.Key.SYNC_SERVER_ROOT_API, rootApi);
276 Preferences.putString(Preferences.Key.SYNC_SERVER_USER_API, userApi);
277 }
278}
0279
=== added file 'src/org/tomdroid/sync/web/SnowySyncService.java'
--- src/org/tomdroid/sync/web/SnowySyncService.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/web/SnowySyncService.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,232 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync.web;
24
25import java.net.UnknownHostException;
26import java.util.ArrayList;
27
28import org.json.JSONArray;
29import org.json.JSONException;
30import org.json.JSONObject;
31import org.tomdroid.Note;
32import org.tomdroid.sync.ServiceAuth;
33import org.tomdroid.sync.SyncService;
34import org.tomdroid.ui.Tomdroid;
35import org.tomdroid.util.Preferences;
36
37import android.app.Activity;
38import android.net.Uri;
39import android.os.Handler;
40import android.os.Message;
41import android.util.Log;
42
43public class SnowySyncService extends SyncService implements ServiceAuth {
44
45 private static final String TAG = "SnowySyncService";
46
47 public SnowySyncService(Activity activity, Handler handler) {
48 super(activity, handler);
49 }
50
51 @Override
52 public String getDescription() {
53 return "Tomboy Web";
54 }
55
56 @Override
57 public String getName() {
58 return "tomboy-web";
59 }
60
61 public boolean isConfigured() {
62 OAuthConnection auth = getAuthConnection();
63 return auth.isAuthenticated();
64 }
65
66 @Override
67 public boolean needsServer() {
68 return true;
69 }
70
71 @Override
72 public boolean needsAuth() {
73 return true;
74 }
75
76 public void getAuthUri(final String server, final Handler handler) {
77
78 execInThread(new Runnable() {
79
80 public void run() {
81
82 // Reset the authentication credentials
83 OAuthConnection auth = new OAuthConnection();
84 Uri authUri = null;
85
86 try {
87 authUri = auth.getAuthorizationUrl(server);
88
89 } catch (UnknownHostException e) {
90 if (Tomdroid.LOGGING_ENABLED)
91 Log.e(TAG, "Internet connection not available");
92 sendMessage(NO_INTERNET);
93 }
94
95 Message message = new Message();
96 message.obj = authUri;
97 handler.sendMessage(message);
98 }
99
100 });
101 }
102
103 public void remoteAuthComplete(final Uri uri, final Handler handler) {
104
105 execInThread(new Runnable() {
106
107 public void run() {
108
109 try {
110 // TODO: might be intelligent to show something like a progress dialog
111 // else the user might try to sync before the authorization process
112 // is complete
113 OAuthConnection auth = getAuthConnection();
114 boolean result = auth.getAccess(uri.getQueryParameter("oauth_verifier"));
115
116 if (Tomdroid.LOGGING_ENABLED) {
117 if (result) {
118 Log.i(TAG, "The authorization process is complete.");
119 } else
120 Log.e(TAG, "Something went wrong during the authorization process.");
121 }
122 } catch (UnknownHostException e) {
123 if (Tomdroid.LOGGING_ENABLED)
124 Log.e(TAG, "Internet connection not available");
125 sendMessage(NO_INTERNET);
126 }
127
128 // We don't care what we send, just remove the dialog
129 handler.sendEmptyMessage(0);
130 }
131 });
132 }
133
134 @Override
135 public boolean isSyncable(){
136 return super.isSyncable() && isConfigured();
137 }
138
139
140 @Override
141 protected void sync() {
142
143 // start loading snowy notes
144 setSyncProgress(0);
145 if (Tomdroid.LOGGING_ENABLED) Log.v(TAG, "Loading Snowy notes");
146
147 final String userRef = Preferences.getString(Preferences.Key.SYNC_SERVER_USER_API);
148
149 execInThread(new Runnable() {
150
151 public void run() {
152
153 OAuthConnection auth = getAuthConnection();
154
155 try {
156 String rawResponse = auth.get(userRef);
157 setSyncProgress(30);
158 JSONObject response = new JSONObject(rawResponse);
159 String notesUrl = response.getJSONObject("notes-ref").getString("api-ref");
160
161 response = new JSONObject(auth.get(notesUrl));
162
163 long latestSyncRevision = (Long)Preferences.getLong(Preferences.Key.LATEST_SYNC_REVISION);
164 setSyncProgress(35);
165
166 if (response.getLong("latest-sync-revision") < latestSyncRevision) {
167 setSyncProgress(100);
168 return;
169 }
170
171 response = new JSONObject(auth.get(notesUrl + "?include_notes=true"));
172 JSONArray notes = response.getJSONArray("notes");
173 setSyncProgress(60);
174
175 // Delete the notes that are not in the database
176 ArrayList<String> remoteGuids = new ArrayList<String>();
177
178 for (int i = 0; i < notes.length(); i++) {
179 remoteGuids.add(notes.getJSONObject(i).getString("guid"));
180 }
181
182 deleteNotes(remoteGuids);
183 setSyncProgress(70);
184
185 // Insert or update the rest of the notes
186 for (int i = 0; i < notes.length() - 1; i++) {
187
188 JSONObject jsonNote = notes.getJSONObject(i);
189 insertNote(new Note(jsonNote), false);
190 }
191 setSyncProgress(90);
192
193 JSONObject jsonNote = notes.getJSONObject(notes.length() - 1);
194 insertNote(new Note(jsonNote), true);
195
196 Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, response
197 .getLong("latest-sync-revision"));
198 setSyncProgress(100);
199
200 } catch (JSONException e1) {
201 if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Problem parsing the server response", e1);
202 sendMessage(PARSING_FAILED);
203 setSyncProgress(100);
204 return;
205 } catch (java.net.UnknownHostException e) {
206 if (Tomdroid.LOGGING_ENABLED) Log.e(TAG, "Internet connection not available");
207 sendMessage(NO_INTERNET);
208 setSyncProgress(100);
209 return;
210 }
211 }
212 });
213 }
214
215 private OAuthConnection getAuthConnection() {
216
217 OAuthConnection auth = new OAuthConnection();
218
219 auth.accessToken = Preferences.getString(Preferences.Key.ACCESS_TOKEN);
220 auth.accessTokenSecret = Preferences.getString(Preferences.Key.ACCESS_TOKEN_SECRET);
221 auth.requestToken = Preferences.getString(Preferences.Key.REQUEST_TOKEN);
222 auth.requestTokenSecret = Preferences.getString(Preferences.Key.REQUEST_TOKEN_SECRET);
223 auth.oauth10a = Preferences.getBoolean(Preferences.Key.OAUTH_10A);
224 auth.authorizeUrl = Preferences.getString(Preferences.Key.AUTHORIZE_URL);
225 auth.accessTokenUrl = Preferences.getString(Preferences.Key.ACCESS_TOKEN_URL);
226 auth.requestTokenUrl = Preferences.getString(Preferences.Key.REQUEST_TOKEN_URL);
227 auth.rootApi = Preferences.getString(Preferences.Key.SYNC_SERVER_ROOT_API);
228 auth.userApi = Preferences.getString(Preferences.Key.SYNC_SERVER_USER_API);
229
230 return auth;
231 }
232}
0233
=== added file 'src/org/tomdroid/sync/web/WebConnection.java'
--- src/org/tomdroid/sync/web/WebConnection.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/sync/web/WebConnection.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,139 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.sync.web;
24
25import java.io.BufferedReader;
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.InputStreamReader;
29import java.net.UnknownHostException;
30
31import org.apache.http.HttpEntity;
32import org.apache.http.HttpResponse;
33import org.apache.http.client.ClientProtocolException;
34import org.apache.http.client.methods.HttpUriRequest;
35import org.apache.http.impl.client.DefaultHttpClient;
36import org.tomdroid.ui.Tomdroid;
37
38import android.util.Log;
39
40public abstract class WebConnection {
41
42 private static final String TAG = "WebConnection";
43
44 public abstract String get(String uri) throws UnknownHostException;
45 public abstract String put(String uri, String data) throws UnknownHostException;
46
47 private static String convertStreamToString(InputStream is) {
48 /*
49 * To convert the InputStream to String we use the BufferedReader.readLine()
50 * method. We iterate until the BufferedReader return null which means
51 * there's no more data to read. Each line will appended to a StringBuilder
52 * and returned as String.
53 */
54 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
55 StringBuilder sb = new StringBuilder();
56
57 String line = null;
58 try {
59 while ((line = reader.readLine()) != null) {
60 sb.append(line + "\n");
61 }
62 } catch (IOException e) {
63 e.printStackTrace();
64 } finally {
65 try {
66 is.close();
67 } catch (IOException e) {
68 e.printStackTrace();
69 }
70 }
71
72 return sb.toString();
73 }
74
75 protected String parseResponse(HttpResponse response) {
76
77 if (response == null)
78 return "";
79
80 String result = null;
81
82 // Examine the response status
83 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Response status : "+response.getStatusLine().toString());
84
85 // Get hold of the response entity
86 HttpEntity entity = response.getEntity();
87 // If the response does not enclose an entity, there is no need
88 // to worry about connection release
89
90 if (entity != null) {
91
92 try {
93 InputStream instream;
94
95 instream = entity.getContent();
96
97 result = convertStreamToString(instream);
98
99 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG, "Received : "+result);
100
101 // Closing the input stream will trigger connection release
102 instream.close();
103
104 } catch (IllegalStateException e) {
105 // TODO Auto-generated catch block
106 e.printStackTrace();
107 } catch (IOException e) {
108 // TODO Auto-generated catch block
109 e.printStackTrace();
110 }
111 }
112
113 return result;
114 }
115
116 protected HttpResponse execute(HttpUriRequest request) throws UnknownHostException {
117
118 DefaultHttpClient httpclient = new DefaultHttpClient();
119
120 try {
121 // Execute the request
122 HttpResponse response = httpclient.execute(request);
123 return response;
124
125 }catch (UnknownHostException e){
126 throw e;
127 } catch (ClientProtocolException e) {
128 e.printStackTrace();
129 } catch (IOException e) {
130 e.printStackTrace();
131 } catch (IllegalArgumentException e) {
132 e.printStackTrace();
133 } catch (IllegalStateException e) {
134 e.printStackTrace();
135 }
136
137 return null;
138 }
139}
0140
=== added file 'src/org/tomdroid/ui/Actionbar.java'
--- src/org/tomdroid/ui/Actionbar.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/ui/Actionbar.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,69 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2010, Rodja Trappe <mail@rodja.net>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.ui;
24
25import org.tomdroid.R;
26import org.tomdroid.sync.SyncManager;
27
28import android.content.Context;
29import android.util.AttributeSet;
30import android.view.View;
31import android.widget.ImageView;
32import android.widget.RelativeLayout;
33
34public class Actionbar extends RelativeLayout {
35
36 public static final int DEFAULT_ICON_ALPHA = 200;
37
38 public Actionbar(Context context, AttributeSet attrs) {
39 super(context, attrs);
40 }
41
42 public Actionbar(Context context, AttributeSet attrs, int defStyle) {
43 super(context, attrs, defStyle);
44 }
45
46 public Actionbar(Context context) {
47 super(context);
48 }
49
50 @Override
51 public void onFinishInflate(){
52 super.onFinishInflate();
53 setupSyncButton();
54 }
55
56 private void setupSyncButton(){
57
58 final ImageView syncButton = (ImageView) findViewById(R.id.sync);
59 final ImageView syncIcon = (ImageView) findViewById(R.id.syncIcon);
60 syncIcon.getDrawable().setAlpha(Actionbar.DEFAULT_ICON_ALPHA);
61 syncButton.setOnClickListener(new View.OnClickListener() {
62
63 public void onClick(View v) {
64 SyncManager.getInstance().startSynchronization();
65 }
66 });
67 }
68
69}
070
=== added file 'src/org/tomdroid/ui/PreferencesActivity.java'
--- src/org/tomdroid/ui/PreferencesActivity.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/ui/PreferencesActivity.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,217 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.ui;
24
25import java.util.ArrayList;
26
27import org.tomdroid.R;
28import org.tomdroid.sync.ServiceAuth;
29import org.tomdroid.sync.SyncManager;
30import org.tomdroid.sync.SyncService;
31import org.tomdroid.util.Preferences;
32
33import android.app.AlertDialog;
34import android.app.ProgressDialog;
35import android.content.DialogInterface;
36import android.content.Intent;
37import android.content.DialogInterface.OnClickListener;
38import android.net.Uri;
39import android.os.Bundle;
40import android.os.Handler;
41import android.os.Message;
42import android.preference.EditTextPreference;
43import android.preference.ListPreference;
44import android.preference.Preference;
45import android.preference.PreferenceActivity;
46import android.preference.Preference.OnPreferenceChangeListener;
47import android.util.Log;
48import android.widget.Toast;
49
50public class PreferencesActivity extends PreferenceActivity {
51
52 private static final String TAG = "PreferencesActivity";
53
54 // TODO: put the various preferences in fields and figure out what to do on activity suspend/resume
55 private EditTextPreference syncServer = null;
56 private ListPreference syncService = null;
57
58 @Override
59 protected void onCreate(Bundle savedInstanceState) {
60
61 super.onCreate(savedInstanceState);
62 addPreferencesFromResource(R.xml.preferences);
63
64 // Fill the Preferences fields
65 syncServer = (EditTextPreference)findPreference(Preferences.Key.SYNC_SERVER.getName());
66 syncService = (ListPreference)findPreference(Preferences.Key.SYNC_SERVICE.getName());
67
68 // Set the default values if nothing exists
69 this.setDefaults();
70
71 // Fill the services combo list
72 this.fillServices();
73
74 // Enable or disable the server field depending on the selected sync service
75 setServer(syncService.getValue());
76
77 syncService.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
78
79 public boolean onPreferenceChange(Preference preference, Object newValue) {
80
81 setServer((String)newValue);
82 return true;
83 }
84 });
85
86 // Re-authenticate if the sync server changes
87 syncServer.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
88
89 public boolean onPreferenceChange(Preference preference,
90 Object serverUri) {
91
92 if (serverUri == null) {
93 Toast.makeText(PreferencesActivity.this,
94 getString(R.string.prefServerEmpty),
95 Toast.LENGTH_SHORT).show();
96 return false;
97 }
98
99 authenticate((String) serverUri);
100 return true;
101 }
102
103 });
104
105 }
106
107 private void authenticate(String serverUri) {
108
109 // update the value before doing anything
110 Preferences.putString(Preferences.Key.SYNC_SERVER, serverUri);
111
112 SyncService currentService = SyncManager.getInstance().getCurrentService();
113
114 if (!currentService.needsAuth()) {
115 return;
116 }
117
118 // service needs authentication
119 Log.i(TAG, "Creating dialog");
120
121 final ProgressDialog authProgress = ProgressDialog.show(this, "",
122 "Authenticating. Please wait...", true, false);
123
124 Handler handler = new Handler() {
125
126 @Override
127 public void handleMessage(Message msg) {
128
129 boolean wasSuccsessful = false;
130 Uri authorizationUri = (Uri) msg.obj;
131 if (authorizationUri != null) {
132
133 Intent i = new Intent(Intent.ACTION_VIEW, authorizationUri);
134 startActivity(i);
135 wasSuccsessful = true;
136
137 } else {
138 // Auth failed, don't update the value
139 wasSuccsessful = false;
140 }
141
142 if (authProgress != null)
143 authProgress.dismiss();
144
145 if (wasSuccsessful) {
146 resetLocalDatabase();
147 } else {
148 connectionFailed();
149 }
150 }
151 };
152
153 ((ServiceAuth) currentService).getAuthUri(serverUri, handler);
154 }
155
156 private void fillServices()
157 {
158 ArrayList<SyncService> availableServices = SyncManager.getInstance().getServices();
159 CharSequence[] entries = new CharSequence[availableServices.size()];
160 CharSequence[] entryValues = new CharSequence[availableServices.size()];
161
162 for (int i = 0; i < availableServices.size(); i++) {
163 entries[i] = availableServices.get(i).getDescription();
164 entryValues[i] = availableServices.get(i).getName();
165 }
166
167 syncService.setEntries(entries);
168 syncService.setEntryValues(entryValues);
169 }
170
171 private void setDefaults()
172 {
173 String defaultServer = (String)Preferences.Key.SYNC_SERVER.getDefault();
174 syncServer.setDefaultValue(defaultServer);
175 if(syncServer.getText() == null)
176 syncServer.setText(defaultServer);
177
178 String defaultService = (String)Preferences.Key.SYNC_SERVICE.getDefault();
179 syncService.setDefaultValue(defaultService);
180 if(syncService.getValue() == null)
181 syncService.setValue(defaultService);
182
183 }
184
185 private void setServer(String syncServiceKey) {
186
187 SyncService service = SyncManager.getInstance().getService(syncServiceKey);
188
189 if (service == null)
190 return;
191
192 if (!service.needsAuth()){
193 resetLocalDatabase();
194 }
195
196 syncServer.setEnabled(service.needsServer());
197 syncService.setSummary(service.getDescription());
198
199 }
200
201 private void connectionFailed() {
202 new AlertDialog.Builder(this)
203 .setMessage(getString(R.string.prefSyncConnectionFailed))
204 .setNeutralButton(getString(R.string.btnOk), new OnClickListener() {
205 public void onClick(DialogInterface dialog, int which) {
206 dialog.dismiss();
207 }})
208 .show();
209 }
210
211 //TODO use LocalStorage wrapper from two-way-sync branch when it get's merged
212 private void resetLocalDatabase() {
213 getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
214 Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
215 }
216
217}
0218
=== added file 'src/org/tomdroid/ui/SyncMessageHandler.java'
--- src/org/tomdroid/ui/SyncMessageHandler.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/ui/SyncMessageHandler.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,158 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2010, Rodja Trappe <mail@rodja.net>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.ui;
24
25import org.tomdroid.R;
26import org.tomdroid.sync.SyncManager;
27import org.tomdroid.sync.SyncService;
28
29import android.app.Activity;
30import android.app.AlertDialog;
31import android.content.DialogInterface;
32import android.content.DialogInterface.OnClickListener;
33import android.os.Handler;
34import android.os.Message;
35import android.util.Log;
36import android.view.View;
37import android.view.animation.Animation;
38import android.view.animation.AnimationUtils;
39import android.view.animation.RotateAnimation;
40import android.widget.ImageView;
41import android.widget.Toast;
42
43public class SyncMessageHandler extends Handler {
44
45 private static String TAG = "SycnMessageHandler";
46 private Activity activity;
47
48 // State variables
49 private boolean parsingErrorShown = false;
50
51 public SyncMessageHandler(Activity activity) {
52 this.activity = activity;
53 }
54
55 @Override
56 public void handleMessage(Message msg) {
57
58 switch (msg.what) {
59 case SyncService.PARSING_COMPLETE:
60 // TODO put string in a translatable bundle
61 Toast.makeText(
62 activity,
63 "Synchronization with "
64 + SyncManager.getInstance().getCurrentService().getDescription()
65 + " is complete.", Toast.LENGTH_SHORT).show();
66 break;
67
68 case SyncService.PARSING_NO_NOTES:
69 // TODO put string in a translatable bundle
70 Toast.makeText(
71 activity,
72 "No notes found on "
73 + SyncManager.getInstance().getCurrentService().getDescription()
74 + ".", Toast.LENGTH_SHORT).show();
75 break;
76
77 case SyncService.PARSING_FAILED:
78 if (Tomdroid.LOGGING_ENABLED)
79 Log.w(TAG, "handler called with a parsing failed message");
80
81 // if we already shown a parsing error in this pass, we
82 // won't show it again
83 if (!parsingErrorShown) {
84 parsingErrorShown = true;
85
86 // TODO put error string in a translatable resource
87 new AlertDialog.Builder(activity).setMessage(
88 "There was an error trying to parse your note collection. If "
89 + "you are able to replicate the problem, please contact us!")
90 .setTitle("Error").setNeutralButton("Ok", new OnClickListener() {
91 public void onClick(DialogInterface dialog, int which) {
92 dialog.dismiss();
93 }
94 }).show();
95 }
96 break;
97
98 case SyncService.NO_INTERNET:
99 // TODO put string in a translatable bundle
100 Toast.makeText(activity, "You are not connected to the internet.",
101 Toast.LENGTH_SHORT).show();
102 break;
103
104 case SyncService.SYNC_PROGRESS:
105 handleSyncProgress(msg);
106 break;
107
108 default:
109 if (Tomdroid.LOGGING_ENABLED)
110 Log.i(TAG, "handler called with an unknown message");
111 break;
112
113 }
114 }
115
116 private void handleSyncProgress(Message msg) {
117 ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
118
119 RotateAnimation rotation = new RotateAnimation(180 * msg.arg2 / 100f,
120 180 * msg.arg1 / 100f, Animation.RELATIVE_TO_SELF, 0.5f,
121 Animation.RELATIVE_TO_SELF, 0.5f);
122 rotation.setDuration(700);
123 rotation.setFillAfter(true);
124 syncIcon.startAnimation(rotation);
125
126 if (msg.arg1 == 0) {
127 onSynchronizationStarted();
128 } else if (msg.arg1 == 100) {
129 onSynchronizationDone();
130 }
131 }
132
133 private void onSynchronizationDone() {
134 ImageView syncButton = (ImageView) activity.findViewById(R.id.sync);
135 ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
136
137 syncButton.setClickable(true);
138 syncIcon.getDrawable().setAlpha(Actionbar.DEFAULT_ICON_ALPHA);
139
140 View dot = activity.findViewById(R.id.sync_dot);
141 dot.setVisibility(View.INVISIBLE);
142 dot.getAnimation().setRepeatCount(0);
143 }
144
145 private void onSynchronizationStarted() {
146 ImageView syncButton = (ImageView) activity.findViewById(R.id.sync);
147 ImageView syncIcon = (ImageView) activity.findViewById(R.id.syncIcon);
148
149 syncButton.setClickable(false);
150 syncIcon.getDrawable().setAlpha(40);
151
152 Animation pulse = AnimationUtils.loadAnimation(activity, R.anim.pulse);
153 View dot = activity.findViewById(R.id.sync_dot);
154 dot.setVisibility(View.VISIBLE);
155 dot.startAnimation(pulse);
156 }
157
158}
0159
=== modified file 'src/org/tomdroid/ui/Tomdroid.java'
--- src/org/tomdroid/ui/Tomdroid.java 2010-02-16 05:18:09 +0000
+++ src/org/tomdroid/ui/Tomdroid.java 2010-10-03 12:21:44 +0000
@@ -3,7 +3,9 @@
3 * Tomboy on Android3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
8 * Copyright 2010, Rodja Trappe <mail@rodja.net>
7 * 9 *
8 * This file is part of Tomdroid.10 * This file is part of Tomdroid.
9 * 11 *
@@ -22,16 +24,17 @@
22 */24 */
23package org.tomdroid.ui;25package org.tomdroid.ui;
2426
25import java.io.File;
26import java.io.FileNotFoundException;
27
28import org.tomdroid.Note;27import org.tomdroid.Note;
29import org.tomdroid.NoteManager;28import org.tomdroid.NoteManager;
30import org.tomdroid.R;29import org.tomdroid.R;
31import org.tomdroid.util.AsyncNoteLoaderAndParser;30import org.tomdroid.sync.ServiceAuth;
31import org.tomdroid.sync.SyncManager;
32import org.tomdroid.sync.SyncService;
33import org.tomdroid.util.Preferences;
3234
33import android.app.AlertDialog;35import android.app.AlertDialog;
34import android.app.ListActivity;36import android.app.ListActivity;
37import android.app.ProgressDialog;
35import android.content.DialogInterface;38import android.content.DialogInterface;
36import android.content.Intent;39import android.content.Intent;
37import android.content.DialogInterface.OnClickListener;40import android.content.DialogInterface.OnClickListener;
@@ -39,6 +42,7 @@
39import android.database.Cursor;42import android.database.Cursor;
40import android.net.Uri;43import android.net.Uri;
41import android.os.Bundle;44import android.os.Bundle;
45import android.os.Environment;
42import android.os.Handler;46import android.os.Handler;
43import android.os.Message;47import android.os.Message;
44import android.util.Log;48import android.util.Log;
@@ -49,136 +53,127 @@
49import android.widget.ListAdapter;53import android.widget.ListAdapter;
50import android.widget.ListView;54import android.widget.ListView;
51import android.widget.TextView;55import android.widget.TextView;
52import android.widget.Toast;
5356
54public class Tomdroid extends ListActivity {57public class Tomdroid extends ListActivity {
5558
56 // Global definition for Tomdroid59 // Global definition for Tomdroid
57 public static final String AUTHORITY = "org.tomdroid.notes";60 public static final String AUTHORITY = "org.tomdroid.notes";
58 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");61 public static final Uri CONTENT_URI = Uri
59 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.tomdroid.note";62 .parse("content://" + AUTHORITY
60 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.tomdroid.note";63 + "/notes");
61 public static final String PROJECT_HOMEPAGE = "http://www.launchpad.net/tomdroid/";64 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.tomdroid.note";
62 65 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.tomdroid.note";
66 public static final String PROJECT_HOMEPAGE = "http://www.launchpad.net/tomdroid/";
67
63 // config parameters68 // config parameters
64 // TODO hardcoded for now69 // TODO hardcoded for now
65 public static final String NOTES_PATH = "/sdcard/tomdroid/";70 public static final String NOTES_PATH = Environment.getExternalStorageDirectory()
71 + "/tomdroid/";
66 // Logging should be disabled for release builds72 // Logging should be disabled for release builds
67 public static final boolean LOGGING_ENABLED = false;73 public static final boolean LOGGING_ENABLED = false;
74 // Set this to false for release builds, the reason should be obvious
75 public static final boolean CLEAR_PREFERENCES = false;
6876
69 // Logging info77 // Logging info
70 private static final String TAG = "Tomdroid";78 private static final String TAG = "Tomdroid";
71 79
72 // UI to data model glue80 // UI to data model glue
73 private TextView listEmptyView;81 private TextView listEmptyView;
74 private ListAdapter adapter;82 private ListAdapter adapter;
75 83
76 // Bundle keys for saving state84 // UI feedback handler
77 private static final String WARNING_SHOWN = "w";85 private Handler syncMessageHandler = new SyncMessageHandler(this);
78 86
79 // State variables87 /** Called when the activity is created. */
80 private boolean warningShown = false;88 @Override
81 private boolean parsingErrorShown = false;89 public void onCreate(Bundle savedInstanceState) {
8290 super.onCreate(savedInstanceState);
83 91
84 /** Called when the activity is created. */92 setContentView(R.layout.main);
85 @Override93 Preferences.init(this, CLEAR_PREFERENCES);
86 public void onCreate(Bundle savedInstanceState) {94
87 super.onCreate(savedInstanceState);95 // did we already show the warning and got destroyed by android's activity killer?
88 96 if (Preferences.getBoolean(Preferences.Key.FIRST_RUN)) {
89 setContentView(R.layout.main);97
90 98 // Warn that this is a "will eat your babies" release
91 // did we already show the warning and got destroyed by android's activity killer?99 new AlertDialog.Builder(this).setMessage(getString(R.string.strWelcome)).setTitle(
92 if (savedInstanceState == null || !savedInstanceState.getBoolean(WARNING_SHOWN)) {100 "Warning").setNeutralButton("Ok", new OnClickListener() {
93101 public void onClick(DialogInterface dialog, int which) {
94 // Warn that this is a "will eat your babies" release 102 Preferences.putBoolean(Preferences.Key.FIRST_RUN, false);
95 new AlertDialog.Builder(this)103 dialog.dismiss();
96 .setMessage(getString(R.string.strWelcome))104 }
97 .setTitle("Warning")105 }).setIcon(R.drawable.icon).show();
98 .setNeutralButton("Ok", new OnClickListener() {106 }
99 public void onClick(DialogInterface dialog, int which) {107
100 warningShown = true;108 // adapter that binds the ListView UI to the notes in the note manager
101 dialog.dismiss();109 adapter = NoteManager.getListAdapter(this);
102 }})
103 .setIcon(R.drawable.icon)
104 .show();
105 }
106
107 // adapter that binds the ListView UI to the notes in the note manager
108 adapter = NoteManager.getListAdapter(this);
109 setListAdapter(adapter);110 setListAdapter(adapter);
110111
111 // set the view shown when the list is empty112 // set the view shown when the list is empty
112 // TODO default empty-list text is butt-ugly!113 // TODO default empty-list text is butt-ugly!
113 listEmptyView = (TextView)findViewById(R.id.list_empty);114 listEmptyView = (TextView) findViewById(R.id.list_empty);
114 getListView().setEmptyView(listEmptyView);115 getListView().setEmptyView(listEmptyView);
115 }116 }
116117
117 @Override118 @Override
118 public boolean onCreateOptionsMenu(Menu menu) {119 public boolean onCreateOptionsMenu(Menu menu) {
119120
120 // Create the menu based on what is defined in res/menu/main.xml121 // Create the menu based on what is defined in res/menu/main.xml
121 MenuInflater inflater = getMenuInflater();122 MenuInflater inflater = getMenuInflater();
122 inflater.inflate(R.menu.main, menu);123 inflater.inflate(R.menu.main, menu);
123 return true;124 return true;
125
124 }126 }
125127
126 @Override128 @Override
127 public boolean onOptionsItemSelected(MenuItem item) {129 public boolean onOptionsItemSelected(MenuItem item) {
128 switch (item.getItemId()) {130 switch (item.getItemId()) {
129 case R.id.menuSyncWithSD:131 case R.id.menuAbout:
130
131 // start loading local notes
132 if (LOGGING_ENABLED) Log.v(TAG, "Loading local notes");
133 // reset parsing error flag
134 parsingErrorShown = false;
135
136 try {
137 File notesRoot = new File(Tomdroid.NOTES_PATH);
138
139 if (!notesRoot.exists()) {
140 throw new FileNotFoundException("Tomdroid notes folder doesn't exist. It is configured to be at: "+Tomdroid.NOTES_PATH);
141 }
142
143 AsyncNoteLoaderAndParser asyncLoader = new AsyncNoteLoaderAndParser(this, notesRoot);
144 asyncLoader.readAndParseNotes(handler);
145
146 } catch (FileNotFoundException e) {
147 //TODO put strings in an external resource
148 listEmptyView.setText(R.string.strListEmptyNoNotes);
149 new AlertDialog.Builder(this)
150 .setMessage(e.getMessage())
151 .setTitle("Error")
152 .setNeutralButton("Ok", new OnClickListener() {
153 public void onClick(DialogInterface dialog, int which) {
154 dialog.dismiss();
155 }})
156 .show();
157 e.printStackTrace();
158 }
159
160 return true;
161
162 case R.id.menuAbout:
163 showAboutDialog();132 showAboutDialog();
164 return true;133 return true;
165 }134
166 135 case R.id.menuPrefs:
167 return super.onOptionsItemSelected(item);136 startActivity(new Intent(this, PreferencesActivity.class));
137 return true;
138 }
139
140 return super.onOptionsItemSelected(item);
168 }141 }
169142
170 @Override143 public void onResume() {
171 protected void onSaveInstanceState(Bundle outState) {144 super.onResume();
172 super.onSaveInstanceState(outState);145 Intent intent = this.getIntent();
146
147 SyncService currentService = SyncManager.getInstance().getCurrentService();
173 148
174 // saving the state of the warning dialog149 if (currentService.needsAuth() && intent != null) {
175 if (warningShown) {150 Uri uri = intent.getData();
176 outState.putBoolean(WARNING_SHOWN, true);151
152 if (uri != null && uri.getScheme().equals("tomdroid")) {
153 Log.i(TAG, "Got url : " + uri.toString());
154
155 final ProgressDialog dialog = ProgressDialog.show(this, "",
156 "Completing authentication. Please wait...", true, false);
157
158 Handler handler = new Handler() {
159
160 @Override
161 public void handleMessage(Message msg) {
162 dialog.dismiss();
163 }
164
165 };
166
167 ((ServiceAuth) currentService).remoteAuthComplete(uri, handler);
168 }
177 }169 }
170
171 SyncManager.setActivity(this);
172 SyncManager.setHandler(this.syncMessageHandler);
178 }173 }
179174
180 private void showAboutDialog() {175 private void showAboutDialog() {
181 176
182 // grab version info177 // grab version info
183 String ver;178 String ver;
184 try {179 try {
@@ -187,90 +182,38 @@
187 e.printStackTrace();182 e.printStackTrace();
188 ver = "Not found!";183 ver = "Not found!";
189 }184 }
190 185
191 // format the string186 // format the string
192 String aboutDialogFormat = getString(R.string.strAbout);187 String aboutDialogFormat = getString(R.string.strAbout);
193 String aboutDialogStr = String.format(aboutDialogFormat, 188 String aboutDialogStr = String.format(aboutDialogFormat, getString(R.string.app_desc), // App description
194 getString(R.string.app_desc), // App description189 getString(R.string.author), // Author name
195 getString(R.string.author), // Author name190 ver // Version
196 ver // Version
197 );191 );
198 192
199 // build and show the dialog193 // build and show the dialog
200 new AlertDialog.Builder(this)194 new AlertDialog.Builder(this).setMessage(aboutDialogStr).setTitle("About Tomdroid")
201 .setMessage(aboutDialogStr)195 .setIcon(R.drawable.icon).setNegativeButton("Project page", new OnClickListener() {
202 .setTitle("About Tomdroid")196 public void onClick(DialogInterface dialog, int which) {
203 .setIcon(R.drawable.icon)197 startActivity(new Intent(Intent.ACTION_VIEW, Uri
204 .setNegativeButton("Project page", new OnClickListener() {198 .parse(Tomdroid.PROJECT_HOMEPAGE)));
205 public void onClick(DialogInterface dialog, int which) {199 dialog.dismiss();
206 startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Tomdroid.PROJECT_HOMEPAGE)));200 }
207 dialog.dismiss();201 }).setPositiveButton("Ok", new OnClickListener() {
208 }})202 public void onClick(DialogInterface dialog, int which) {
209 .setPositiveButton("Ok", new OnClickListener() {203 dialog.dismiss();
210 public void onClick(DialogInterface dialog, int which) {204 }
211 dialog.dismiss();205 }).show();
212 }})
213 .show();
214 }206 }
215207
216 @Override208 @Override
217 protected void onListItemClick(ListView l, View v, int position, long id) {209 protected void onListItemClick(ListView l, View v, int position, long id) {
218 210
219 Cursor item = (Cursor)adapter.getItem(position);211 Cursor item = (Cursor) adapter.getItem(position);
220 int noteId = item.getInt(item.getColumnIndexOrThrow(Note.ID));212 int noteId = item.getInt(item.getColumnIndexOrThrow(Note.ID));
221 213
222 Uri intentUri = Uri.parse(Tomdroid.CONTENT_URI+"/"+noteId);214 Uri intentUri = Uri.parse(Tomdroid.CONTENT_URI + "/" + noteId);
223 Intent i = new Intent(Intent.ACTION_VIEW, intentUri, this, ViewNote.class);215 Intent i = new Intent(Intent.ACTION_VIEW, intentUri, this, ViewNote.class);
224 startActivity(i);216 startActivity(i);
225 }217 }
226 218
227 private Handler handler = new Handler() {
228
229 @Override
230 public void handleMessage(Message msg) {
231
232 switch(msg.what) {
233 case AsyncNoteLoaderAndParser.PARSING_COMPLETE:
234 // TODO put string in a translatable bundle
235 Toast.makeText(getApplicationContext(),
236 "Synchronization with SD Card is complete.",
237 Toast.LENGTH_SHORT)
238 .show();
239 break;
240
241 case AsyncNoteLoaderAndParser.PARSING_NO_NOTES:
242 // TODO put string in a translatable bundle
243 Toast.makeText(getApplicationContext(),
244 "There are no files in tomdroid/ on the sdcard.",
245 Toast.LENGTH_SHORT)
246 .show();
247 break;
248
249 case AsyncNoteLoaderAndParser.PARSING_FAILED:
250 if (Tomdroid.LOGGING_ENABLED) Log.w(TAG,"handler called with a parsing failed message");
251
252 // if we already shown a parsing error in this pass, we won't show it again
253 if (!parsingErrorShown) {
254 parsingErrorShown = true;
255
256 // TODO put error string in a translatable resource
257 new AlertDialog.Builder(Tomdroid.this)
258 .setMessage("There was an error trying to parse your note collection. If " +
259 "you are able to replicate the problem, please contact us!")
260 .setTitle("Error")
261 .setNeutralButton("Ok", new OnClickListener() {
262 public void onClick(DialogInterface dialog, int which) {
263 dialog.dismiss();
264 }})
265 .show();
266 }
267 break;
268
269 default:
270 if (Tomdroid.LOGGING_ENABLED) Log.i(TAG,"handler called with an unknown message");
271 break;
272
273 }
274 }
275 };
276}219}
277220
=== modified file 'src/org/tomdroid/ui/ViewNote.java'
--- src/org/tomdroid/ui/ViewNote.java 2010-02-06 14:57:07 +0000
+++ src/org/tomdroid/ui/ViewNote.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,7 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 * 8 *
8 * This file is part of Tomdroid.9 * This file is part of Tomdroid.
9 * 10 *
@@ -28,6 +29,7 @@
28import org.tomdroid.Note;29import org.tomdroid.Note;
29import org.tomdroid.NoteManager;30import org.tomdroid.NoteManager;
30import org.tomdroid.R;31import org.tomdroid.R;
32import org.tomdroid.sync.SyncManager;
31import org.tomdroid.util.LinkifyPhone;33import org.tomdroid.util.LinkifyPhone;
32import org.tomdroid.util.NoteContentBuilder;34import org.tomdroid.util.NoteContentBuilder;
3335
@@ -37,6 +39,7 @@
37import android.content.Intent;39import android.content.Intent;
38import android.content.DialogInterface.OnClickListener;40import android.content.DialogInterface.OnClickListener;
39import android.database.Cursor;41import android.database.Cursor;
42import android.graphics.Color;
40import android.net.Uri;43import android.net.Uri;
41import android.os.Bundle;44import android.os.Bundle;
42import android.os.Handler;45import android.os.Handler;
@@ -52,6 +55,7 @@
52public class ViewNote extends Activity {55public class ViewNote extends Activity {
53 56
54 // UI elements57 // UI elements
58 private TextView title;
55 private TextView content;59 private TextView content;
56 60
57 // Model objects61 // Model objects
@@ -61,6 +65,9 @@
61 // Logging info65 // Logging info
62 private static final String TAG = "ViewNote";66 private static final String TAG = "ViewNote";
63 67
68 // UI feedback handler
69 private Handler syncMessageHandler = new SyncMessageHandler(this);
70
64 // TODO extract methods in here71 // TODO extract methods in here
65 @Override72 @Override
66 protected void onCreate(Bundle savedInstanceState) {73 protected void onCreate(Bundle savedInstanceState) {
@@ -68,6 +75,14 @@
68 75
69 setContentView(R.layout.note_view);76 setContentView(R.layout.note_view);
70 content = (TextView) findViewById(R.id.content);77 content = (TextView) findViewById(R.id.content);
78 content.setBackgroundColor(0xffffffff);
79 content.setTextColor(Color.DKGRAY);
80 content.setTextSize(18.0f);
81 title = (TextView) findViewById(R.id.title);
82 title.setBackgroundColor(0xffdddddd);
83 title.setTextColor(Color.DKGRAY);
84 title.setTextSize(18.0f);
85
71 final Intent intent = getIntent();86 final Intent intent = getIntent();
72 Uri uri = intent.getData();87 Uri uri = intent.getData();
73 88
@@ -84,7 +99,9 @@
84 99
85 if(note != null) {100 if(note != null) {
86 101
87 noteContent = note.getNoteContent(handler);102 noteContent = note.getNoteContent(noteContentHandler);
103
104 //Log.i(TAG, "THE NOTE IS: " + note.getXmlContent().toString());
88 105
89 } else {106 } else {
90 107
@@ -120,6 +137,13 @@
120 }137 }
121 }138 }
122 139
140 @Override
141 public void onResume(){
142 super.onResume();
143 SyncManager.setActivity(this);
144 SyncManager.setHandler(this.syncMessageHandler);
145 }
146
123 // TODO add a menu that switches the view to an EditText instead of TextView147 // TODO add a menu that switches the view to an EditText instead of TextView
124 // this will need some other quit mechanism as onKeyDown though.. (but the back key might do it)148 // this will need some other quit mechanism as onKeyDown though.. (but the back key might do it)
125 149
@@ -132,9 +156,9 @@
132 156
133 return true;157 return true;
134 }158 }
135159
136 private void showNote() {160 private void showNote() {
137 setTitle(note.getTitle());161 //setTitle(note.getTitle());
138162
139 // get rid of the title that is doubled in the note's content163 // get rid of the title that is doubled in the note's content
140 // using quote to escape potential regexp chars in pattern164 // using quote to escape potential regexp chars in pattern
@@ -147,6 +171,7 @@
147 171
148 // show the note (spannable makes the TextView able to output styled text)172 // show the note (spannable makes the TextView able to output styled text)
149 content.setText(noteContent, TextView.BufferType.SPANNABLE);173 content.setText(noteContent, TextView.BufferType.SPANNABLE);
174 title.setText((CharSequence) note.getTitle());
150 175
151 // add links to stuff that is understood by Android except phone numbers because it's too aggressive176 // add links to stuff that is understood by Android except phone numbers because it's too aggressive
152 // TODO this is SLOWWWW!!!!177 // TODO this is SLOWWWW!!!!
@@ -165,7 +190,14 @@
165 noteTitleTransformFilter);190 noteTitleTransformFilter);
166 }191 }
167 192
168 private Handler handler = new Handler() {193 public void setTitle(CharSequence title){
194 super.setTitle(title);
195 // temporary setting title of actionbar until we have a better idea
196 TextView titleView = (TextView) findViewById(R.id.title);
197 titleView.setText(title);
198 }
199
200 private Handler noteContentHandler = new Handler() {
169201
170 @Override202 @Override
171 public void handleMessage(Message msg) {203 public void handleMessage(Message msg) {
172204
=== modified file 'src/org/tomdroid/util/NoteContentBuilder.java'
--- src/org/tomdroid/util/NoteContentBuilder.java 2010-01-23 04:43:26 +0000
+++ src/org/tomdroid/util/NoteContentBuilder.java 2010-10-03 12:21:44 +0000
@@ -62,7 +62,8 @@
62 62
63 public NoteContentBuilder setInputSource(String nc) {63 public NoteContentBuilder setInputSource(String nc) {
64 64
65 noteContentIs = new InputSource(new StringReader(nc));65 String noteContent = "<note-content>"+nc+"</note-content>";
66 noteContentIs = new InputSource(new StringReader(noteContent));
66 return this;67 return this;
67 }68 }
68 69
6970
=== added file 'src/org/tomdroid/util/NoteListCursorAdapter.java'
--- src/org/tomdroid/util/NoteListCursorAdapter.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/util/NoteListCursorAdapter.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,113 @@
1package org.tomdroid.util;
2
3import java.text.DateFormat;
4import java.util.Date;
5
6import android.text.format.DateUtils;
7import android.text.format.Time;
8import org.tomdroid.Note;
9import org.tomdroid.R;
10import org.tomdroid.ui.Tomdroid;
11
12import android.content.Context;
13import android.database.Cursor;
14import android.graphics.Color;
15import android.view.LayoutInflater;
16import android.view.View;
17import android.view.ViewGroup;
18import android.widget.Filterable;
19import android.widget.SimpleCursorAdapter;
20import android.widget.TextView;
21
22/* Provides a custom ListView layout for Note List */
23
24public class NoteListCursorAdapter extends SimpleCursorAdapter {
25
26 private Context context;
27
28 private int layout;
29 private int[] colors = new int[] { 0xFFFFFFFF, 0xFFEEEEEE };
30
31 private DateFormat localeDateFormat;
32 private DateFormat localeTimeFormat;
33
34 public NoteListCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to) {
35 super(context, layout, c, from, to);
36 this.context = context;
37 this.layout = layout;
38 localeDateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
39 localeTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
40 }
41
42
43 @Override
44 public View newView(Context context, Cursor cursor, ViewGroup parent) {
45
46 Cursor c = getCursor();
47
48 final LayoutInflater inflater = LayoutInflater.from(context);
49 View v = inflater.inflate(layout, parent, false);
50
51 populateFields(v, c);
52
53 return v;
54 }
55
56 @Override
57 public void bindView(View v, Context context, Cursor c) {
58
59 populateFields(v, c);
60 }
61
62 @Override
63 public View getView(int position, View convertView, ViewGroup parent) {
64 View view = super.getView(position, convertView, parent);
65 int colorPos = position % colors.length;
66 view.setBackgroundColor(colors[colorPos]);
67 //view.setTextColor(Color.DKGRAY);
68 return view;
69 }
70
71 private void populateFields(View v, Cursor c){
72
73 int nameCol = c.getColumnIndex(Note.TITLE);
74 int modifiedCol = c.getColumnIndex(Note.MODIFIED_DATE);
75
76 String title = c.getString(nameCol);
77
78 //Format last modified dates to be similar to desktop Tomboy
79 //TODO this is messy - must be a better way than having 3 separate date types
80 Time lastModified = new Time();
81 lastModified.parse3339(c.getString(modifiedCol));
82 Long lastModifiedMillis = lastModified.toMillis(false);
83 Date lastModifiedDate = new Date(lastModifiedMillis);
84
85 String strModified = "Modified: ";
86 //TODO this is very inefficient
87 if (DateUtils.isToday(lastModifiedMillis)){
88 strModified += "Today, " + localeTimeFormat.format(lastModifiedDate);
89 } else {
90 // Add a day to the last modified date - if the date is now today, it means the note was edited yesterday
91 Time yesterdayTest = lastModified;
92 yesterdayTest.monthDay += 1;
93 if (DateUtils.isToday(yesterdayTest.toMillis(false))){
94 strModified += "Yesterday, " + localeTimeFormat.format(lastModifiedDate);
95 } else {
96 strModified += localeDateFormat.format(lastModifiedDate) + ", " + localeTimeFormat.format(lastModifiedDate);
97 }
98 }
99
100 /**
101 * Next set the name of the entry.
102 */
103 TextView note_title = (TextView) v.findViewById(R.id.note_title);
104 if (note_title != null) {
105 note_title.setText(title);
106 }
107 TextView note_modified = (TextView) v.findViewById(R.id.note_date);
108 if (note_modified != null) {
109 note_modified.setText(strModified);
110 }
111 }
112
113}
0114
=== added file 'src/org/tomdroid/util/Preferences.java'
--- src/org/tomdroid/util/Preferences.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/util/Preferences.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,112 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.util;
24
25import android.content.Context;
26import android.content.SharedPreferences;
27import android.preference.PreferenceManager;
28
29public class Preferences {
30
31 public enum Key {
32 SYNC_SERVICE ("sync_service", "sdcard"),
33 SYNC_SERVER_ROOT_API ("sync_server_root_api", ""),
34 SYNC_SERVER_USER_API ("sync_server_user_api", ""),
35 SYNC_SERVER ("sync_server", "https://one.ubuntu.com/notes"),
36 ACCESS_TOKEN ("access_token", ""),
37 ACCESS_TOKEN_SECRET ("access_token_secret", ""),
38 REQUEST_TOKEN ("request_token", ""),
39 REQUEST_TOKEN_SECRET ("request_token_secret", ""),
40 OAUTH_10A ("oauth_10a", false),
41 AUTHORIZE_URL ("authorize_url", ""),
42 ACCESS_TOKEN_URL ("access_token_url", ""),
43 REQUEST_TOKEN_URL ("request_token_url", ""),
44 LATEST_SYNC_REVISION ("latest_sync_revision", 0L),
45 SORT_ORDER ("sort_order", "sort_date"),
46 FIRST_RUN ("first_run", true);
47
48 private String name = "";
49 private Object defaultValue = "";
50
51 Key(String name, Object defaultValue) {
52 this.name = name;
53 this.defaultValue = defaultValue;
54 }
55
56 public String getName() {
57 return name;
58 }
59
60 public Object getDefault() {
61 return defaultValue;
62 }
63 }
64
65 private static SharedPreferences client = null;
66 private static SharedPreferences.Editor editor = null;
67
68 public static void init(Context context, boolean clean) {
69
70 client = PreferenceManager.getDefaultSharedPreferences(context);
71 editor = client.edit();
72
73 if (clean)
74 editor.clear().commit();
75 }
76
77 public static String getString(Key key) {
78
79 return client.getString(key.getName(), (String) key.getDefault());
80 }
81
82 public static void putString(Key key, String value) {
83
84 if (value == null)
85 editor.putString(key.getName(), (String)key.getDefault());
86 else
87 editor.putString(key.getName(), value);
88 editor.commit();
89 }
90
91 public static long getLong(Key key) {
92
93 return client.getLong(key.getName(), (Long)key.getDefault());
94 }
95
96 public static void putLong(Key key, long value) {
97
98 editor.putLong(key.getName(), value);
99 editor.commit();
100 }
101
102 public static boolean getBoolean(Key key) {
103
104 return client.getBoolean(key.getName(), (Boolean)key.getDefault());
105 }
106
107 public static void putBoolean(Key key, boolean value) {
108
109 editor.putBoolean(key.getName(), value);
110 editor.commit();
111 }
112}
0113
=== added file 'src/org/tomdroid/util/XmlUtils.java'
--- src/org/tomdroid/util/XmlUtils.java 1970-01-01 00:00:00 +0000
+++ src/org/tomdroid/util/XmlUtils.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,58 @@
1/*
2 * Tomdroid
3 * Tomboy on Android
4 * http://www.launchpad.net/tomdroid
5 *
6 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 *
8 * This file is part of Tomdroid.
9 *
10 * Tomdroid is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tomdroid is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tomdroid. If not, see <http://www.gnu.org/licenses/>.
22 */
23package org.tomdroid.util;
24
25public class XmlUtils {
26
27 /**
28 * Useful to replace the characters forbidden in xml by their escaped counterparts
29 * Ex: &amp; -> &amp;amp;
30 *
31 * @param input the string to escape
32 * @return the escaped string
33 */
34 public static String escape(String input) {
35 return input
36 .replace("&", "&amp;")
37 .replace("<", "&lt;")
38 .replace(">", "&gt;")
39 .replace("\"", "&quot;")
40 .replace("\'", "&apos;");
41 }
42
43 /**
44 * Useful to replace the escaped characters their unescaped counterparts
45 * Ex: &amp;amp; -> &amp;
46 *
47 * @param input the string to unescape
48 * @return the unescaped string
49 */
50 public static String unescape(String input) {
51 return input
52 .replace("&amp;", "&")
53 .replace("&lt;", "<")
54 .replace("&gt;", ">")
55 .replace("&quot;", "\"")
56 .replace("&apos;", "\'");
57 }
58}
059
=== modified file 'src/org/tomdroid/xml/NoteContentHandler.java'
--- src/org/tomdroid/xml/NoteContentHandler.java 2010-02-01 05:11:47 +0000
+++ src/org/tomdroid/xml/NoteContentHandler.java 2010-10-03 12:21:44 +0000
@@ -4,6 +4,7 @@
4 * http://www.launchpad.net/tomdroid4 * http://www.launchpad.net/tomdroid
5 * 5 *
6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>6 * Copyright 2008, 2009, 2010 Olivier Bilodeau <olivier@bottomlesspit.org>
7 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
7 * 8 *
8 * This file is part of Tomdroid.9 * This file is part of Tomdroid.
9 * 10 *
1011
=== added directory 'tests'
=== added directory 'tests/org'
=== added directory 'tests/org/tomdroid'
=== added file 'tests/org/tomdroid/NoteManagerTest.java'
--- tests/org/tomdroid/NoteManagerTest.java 1970-01-01 00:00:00 +0000
+++ tests/org/tomdroid/NoteManagerTest.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,74 @@
1package org.tomdroid;
2
3import org.json.JSONObject;
4import org.tomdroid.Note;
5import org.tomdroid.NoteManager;
6import org.tomdroid.ui.Tomdroid;
7import org.tomdroid.util.Preferences;
8
9import android.app.Activity;
10import android.content.Intent;
11import android.database.Cursor;
12import android.test.ActivityUnitTestCase;
13
14public class NoteManagerTest extends ActivityUnitTestCase<Tomdroid> {
15
16 public NoteManagerTest() {
17 super(Tomdroid.class);
18 }
19
20 public void testGetAllNotes() throws Exception {
21 Activity activity = getActivity();
22 putNotes(activity);
23 Cursor cursor;
24 // Get all notes excluding the notebook template ones.
25 cursor = NoteManager.getAllNotes(activity, false);
26 assertEquals(1, cursor.getCount());
27
28 // Get all notes, including notebook templates this time.
29 cursor = NoteManager.getAllNotes(activity, true);
30 assertEquals(2, cursor.getCount());
31 }
32
33 private void putNotes(Activity a) throws Exception {
34 // Add a regular note to the content manager.
35 JSONObject note = new JSONObject(
36 "{'title': 'foo', 'note-content': 'bar', " +
37 "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7704', " +
38 "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
39 "'tags': ['tag1', 'tag2']}");
40 Note n = new Note(note);
41 NoteManager.putNote(a, n);
42
43 // Add a notebook template to the content manager.
44 JSONObject template = new JSONObject(
45 "{'title': 'foo', 'note-content': 'bar', " +
46 "'guid': '992e91a2-2e34-4e2d-bf88-21def49a7712', " +
47 "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
48 "'tags': ['system:template', 'tag2']}");
49 Note t = new Note(template);
50 NoteManager.putNote(a, t);
51 }
52
53 @Override
54 public void setUp() throws Exception {
55 super.setUp();
56 // XXX: For some reason this will raise an
57 // "Unable to add window -- token null is not for an application"
58 // error when you run the test after wiping user data from the emulator.
59 // The error is actually raised when we try to display the AlertDialog that
60 // is shown the first time the user runs tomdroid.
61 startActivity(new Intent(), null, null);
62 // XXX: Soon we'll be able to replace the two lines below with LocalStorage.resetDatabase().
63 getActivity().getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
64 Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
65 }
66
67 @Override
68 public void tearDown() throws Exception {
69 // XXX: Soon we'll be able to replace the two lines below with LocalStorage.resetDatabase().
70 getActivity().getContentResolver().delete(Tomdroid.CONTENT_URI, null, null);
71 Preferences.putLong(Preferences.Key.LATEST_SYNC_REVISION, 0);
72 super.tearDown();
73 }
74}
075
=== added file 'tests/org/tomdroid/NoteTest.java'
--- tests/org/tomdroid/NoteTest.java 1970-01-01 00:00:00 +0000
+++ tests/org/tomdroid/NoteTest.java 2010-10-03 12:21:44 +0000
@@ -0,0 +1,35 @@
1package org.tomdroid;
2
3import junit.framework.Assert;
4import junit.framework.TestCase;
5
6import org.tomdroid.Note;
7import org.json.JSONException;
8import org.json.JSONObject;
9
10
11public class NoteTest extends TestCase {
12
13 public void testConstructorForNoteWithTags() throws JSONException {
14 JSONObject json = new JSONObject(
15 "{'title': 'foo', 'note-content': 'bar', " +
16 "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7705', " +
17 "'last-change-date': '2009-04-19T21:29:23.2197340-07:00', " +
18 "'tags': ['tag1', 'tag2']}");
19 Note n = new Note(json);
20 Assert.assertEquals("foo", n.getTitle());
21 Assert.assertEquals("002e91a2-2e34-4e2d-bf88-21def49a7705", n.getGuid().toString());
22 Assert.assertEquals("bar", n.getXmlContent());
23 Assert.assertEquals("tag1,tag2,", n.getTags());
24 }
25
26 public void testConstructorForNoteWithNoTags() throws JSONException {
27 JSONObject json = new JSONObject(
28 "{'title': 'foo', 'note-content': 'bar', " +
29 "'guid': '002e91a2-2e34-4e2d-bf88-21def49a7705', " +
30 "'last-change-date': '2009-04-19T21:29:23.2197340-07:00'}");
31 Note n = new Note(json);
32 Assert.assertEquals("foo", n.getTitle());
33 Assert.assertEquals("", n.getTags());
34 }
35}

Subscribers

People subscribed via source and target branches