Delete all rmilk sources

pull/14/head
Sam Bosley 13 years ago
parent 3622ae6bc1
commit cae8c4eb35

@ -5,7 +5,6 @@
<classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/actfm/ProjectDetailExposer.java|com/todoroo/astrid/actfm/ProjectListActivity.java|com/todoroo/astrid/actfm/ShowProjectExposer.java|com/todoroo/astrid/actfm/TaskFields.java|com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="rmilk-src"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="lib" path="libs/commons-codec-1.3.jar"/>

@ -648,49 +648,6 @@
</activity>
<service android:name="com.todoroo.astrid.reminders.ReminderSchedulingService"/>
<!-- rmilk -->
<receiver android:name="org.weloveastrid.rmilk.MilkFilterExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_FILTERS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="org.weloveastrid.rmilk.MilkDetailExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_DETAILS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity android:name="org.weloveastrid.rmilk.MilkLoginActivity" />
<activity android:name="org.weloveastrid.rmilk.MilkPreferences"
android:theme="@android:style/Theme"
android:icon="@drawable/icon"
android:label="@string/rmilk_MPr_header">
<meta-data android:name="category"
android:resource="@string/SyP_label" />
<intent-filter>
<action android:name="com.todoroo.astrid.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name="org.weloveastrid.rmilk.MilkBackgroundService"/>
<receiver android:name="org.weloveastrid.rmilk.MilkStartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="com.todoroo.astrid.STARTUP" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="org.weloveastrid.rmilk.MilkSyncActionExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_SYNC_ACTIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- other task actions -->
<receiver android:name="com.todoroo.astrid.core.LinkActionExposer">
<intent-filter>

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rmilk_MLA_label"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:baselineAligned="false">
<Button android:id="@+id/done"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/DLG_done"
/>
<Button android:id="@+id/cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/cancel"
/>
</LinearLayout>
<WebView android:id="@+id/browser"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

@ -714,17 +714,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Configuració Remember the Milk</string>
<string name="rmilk_TLA_repeat">RTM Tasca Repetitiva</string>
<string name="rmilk_TLA_sync">Necessita sincronitzar-se amb RTM</string>
<string name="rmilk_FEx_list">Llistes</string>
<string name="rmilk_FEx_list_title">RTM Llista \'%s\'</string>
<string name="rmilk_MEA_list_label">RTM Llista:</string>
<string name="rmilk_MEA_repeat_label">RTM Estat de les Repeticions:</string>
<string name="rmilk_MEA_repeat_hint">p.e. cada setmana, després de 14 dies</string>
<string name="rmilk_MLA_label">Si us plau, inicia sessió i autoritza Astrid:</string>
<string name="rmilk_MLA_error">Ho sento, hi ha hagut un error verificant les credencials. Si us plau, torni-ho a intentar. \n\n Missatge d\'error: %s</string>
<string name="rmilk_ioerror">Error de connectivitat! Verifiqui la seva connexió, o potser els servidor de RTM (status.rememberthemilk.com), per possibles solucions.</string>
<string name="TEA_tags_label">Llistes</string>
<string name="TEA_tag_hint">Llista nova</string>
<string name="tag_new_list">Llista nova</string>

@ -804,17 +804,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Nastavení Remember the Milk</string>
<string name="rmilk_TLA_repeat">RTM Opakující se úkol</string>
<string name="rmilk_TLA_sync">Je nutná synchronizace s RTM</string>
<string name="rmilk_FEx_list">Seznamy</string>
<string name="rmilk_FEx_list_title">RTM seznam \'%s\'</string>
<string name="rmilk_MEA_list_label">RTM seznam:</string>
<string name="rmilk_MEA_repeat_label">RTM Opakovací status:</string>
<string name="rmilk_MEA_repeat_hint">to je každý týden, po 14 dnech</string>
<string name="rmilk_MLA_label">Prosím přihlaš se a autorizuj Astrid:</string>
<string name="rmilk_MLA_error">Omlouvám se, nastala chyba při ověřování tvého přihlášení. Prosím, zkus to znovu. \n\n Chybová hláška: %s</string>
<string name="rmilk_ioerror">Chyba připojení! Zkontroluj Tvé Internetové připojení nebo možná RTM servery (status.rememberthemilk.com), pro možná řešení.</string>
<string name="TEA_tags_label">Seznamy</string>
<string name="TEA_tags_none">Žádný</string>
<string name="tag_FEx_header">Seznamy</string>

@ -580,7 +580,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk-opsætning</string>
<string name="tag_settings_title">Settings:</string>
<string name="welcome_title_1">Velkommen til Astrid!</string>
<string name="welcome_body_5">and much more!</string>

@ -978,21 +978,6 @@
<item>Ich bin stolz auf Sie!</item>
<item>Es gefällt mir, wenn Sie produktiv sind!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk Einstellungen</string>
<string name="rmilk_TLA_repeat">RTM Wiederholende Aufgabe</string>
<string name="rmilk_TLA_sync">Synchronisierung mit RTM benötigt</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Listen</string>
<string name="rmilk_FEx_list_title">RTM Liste \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">RTM Liste:</string>
<string name="rmilk_MEA_repeat_label">RTM Wiederholungsstatus</string>
<string name="rmilk_MEA_repeat_hint">z.B. jede Woche, nach 14 Tagen</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Bitte einloggen und Astrid autorisieren</string>
<string name="rmilk_MLA_error">Entschuldigung, beim Einloggen ist ein Fehler aufgetreten. Bitte versuche es erneut. \n\n Fehlermeldung: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">Verbindungsfehler! Bitte überprüfe deine Internetverbindung oder die RTM Server (status.rememberthemilk.com) zur Lösung des Problems.</string>
<string name="subtasks_help_title">Sortieren und einrücken in Astrid</string>
<string name="subtasks_help_1">Drücken und halten, um Aufgabe zu verschieben</string>
<string name="subtasks_help_2">Sortieren ändern durch Ziehen nach oben oder unten</string>

@ -1017,21 +1017,6 @@
<item>¡Estoy muy orgulloso de usted!</item>
<item>¡Me encanta cuando usted es productivo!</item>
</string-array>
<string name="rmilk_EOE_button">Ajustes de Remember the Milk</string>
<string name="rmilk_TLA_repeat">RTM Tarea Repetitiva</string>
<string name="rmilk_TLA_sync">Se necesita sincronizar con RTM</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Listas</string>
<string name="rmilk_FEx_list_title">RTM Lista \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">RTM Lista:</string>
<string name="rmilk_MEA_repeat_label">RTM Repita Estado:</string>
<string name="rmilk_MEA_repeat_hint">Es decir, cada semana, después de 14 días</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Por favor, inicie sesión y autorice a Astrid:</string>
<string name="rmilk_MLA_error">Lo siento. Se produjo un error al verificar sus credenciales. Por favor, intente de nuevo. \n\nMensaje de error: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">Error de conexión! Compruebe su conexión a Internet, o quizá los servidores de RTM (status.rememberthemilk.com), para posibles soluciones.</string>
<string name="subtasks_help_title">Organice y Aplique Sangrías en Astrid</string>
<string name="subtasks_help_1">Mantenga presionado para mover una tarea</string>
<string name="subtasks_help_2">Arrastre verticalmente para reacomodar</string>

@ -990,21 +990,6 @@
<item>Je suis si fière de toi !</item>
<item>J\'adore quand vous êtes productif !</item>
</string-array>
<string name="rmilk_EOE_button">Paramètres Remember the Milk</string>
<string name="rmilk_TLA_repeat">Répétition de tâche RTM</string>
<string name="rmilk_TLA_sync">Requiert une synchronisation avec RTM</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Listes</string>
<string name="rmilk_FEx_list_title">Liste RTM \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">Liste RTM :</string>
<string name="rmilk_MEA_repeat_label">Statut de répétition RTM :</string>
<string name="rmilk_MEA_repeat_hint">ex. : chaque semaine, après 14 jours</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Veuillez vous connecter et autoriser Astrid :</string>
<string name="rmilk_MLA_error">Désolé, une erreur est survenue lors de la vérification de votre identifiant. Veuillez réessayer. \n\n Message d\'erreur : %s</string>
<string name="rmilk_notification_title">Astrid : Remember the Milk</string>
<string name="rmilk_ioerror">Erreur de connexion ! Vérifiez votre connexion Internet ou les serveur RTM (status.rememberthemilk.com) pour de possibles solutions.</string>
<string name="subtasks_help_title">Trier et indenter dans Astrid</string>
<string name="subtasks_help_1">Garder appuyer pour déplacer une tâche</string>
<string name="subtasks_help_2">Déplacer verticalement pour réorganiser</string>

@ -755,21 +755,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Ricorda le impostazioni di Milk</string>
<string name="rmilk_TLA_repeat">Ripetizione Attività RTM</string>
<string name="rmilk_TLA_sync">Necessita la sincronizzazione con RTM</string>
<string name="rmilk_FEx_header">Ricorda Milk</string>
<string name="rmilk_FEx_list">Liste</string>
<string name="rmilk_FEx_list_title">LIsta RTM \'%s\'</string>
<string name="rmilk_MEA_title">Ricorda Milk</string>
<string name="rmilk_MEA_list_label">Lista RTM:</string>
<string name="rmilk_MEA_repeat_label">Stato di ripetizione RTM:</string>
<string name="rmilk_MEA_repeat_hint">in altre parole ogni settimana, dopo 14 giorni</string>
<string name="rmilk_MPr_header">Ricorda Milk</string>
<string name="rmilk_MLA_label">Per favore esegui l\'accesso e autorizza Astrid:</string>
<string name="rmilk_MLA_error">Spiacenti,si è verificato un errore durante la verifica di accesso. Prova di nuovo. \n\n Messaggio di Errore: %s</string>
<string name="rmilk_notification_title">Astrid: Ricorda il Milk</string>
<string name="rmilk_ioerror">Errore di connessione! Verificare la connessione Internet, o magari i server RTM (status.rememberthemilk.com), per le possibili soluzioni.</string>
<string name="TEA_tags_label">Liste</string>
<string name="TEA_tag_hint">Nuovo elenco</string>
<string name="tag_new_list">Nuovo elenco</string>

@ -929,21 +929,6 @@
<item>אני כל כך גֵּאָה בְּךָ!</item>
<item>אני אוהבת שאתה פורה!</item>
</string-array>
<string name="rmilk_EOE_button">הגדרות Remember the Milk</string>
<string name="rmilk_TLA_repeat">משימה חוזרת של RTM</string>
<string name="rmilk_TLA_sync">נדרש סינכרון עם ּRTM</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">רשימות</string>
<string name="rmilk_FEx_list_title">רשימת RTM \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">רשימה של RTM:</string>
<string name="rmilk_MEA_repeat_label">סטטוס חזרה של RTM:</string>
<string name="rmilk_MEA_repeat_hint">למשל כל שבוע, או אחרי 14 יום</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">אנא התחבר ואשר את אסטריד:</string>
<string name="rmilk_MLA_error">מצטערת, אירעה שגיאה בהתחברות למערכת.\n\nהודעת השגיאה הייתה: %s</string>
<string name="rmilk_notification_title">אסטריד: Remember the Milk</string>
<string name="rmilk_ioerror">שגיאת התחברות! בדוק את החיבור שלך לאינטרנט ואת השרתים של RTM (status.rememberthemilk.com) כדי לנסות לפתור את הבעיה.</string>
<string name="subtasks_help_title">מיין והזח באסטריד</string>
<string name="subtasks_help_1">גע והחזק כדי להזיז משימה</string>
<string name="subtasks_help_2">גרור אנכית כדי לארגן מחדש</string>

@ -627,11 +627,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk の設定</string>
<string name="rmilk_FEx_list">リスト</string>
<string name="rmilk_FEx_list_title">RTM のリスト: %s</string>
<string name="rmilk_MLA_label">ログインしてAstridに権限を与えてください</string>
<string name="rmilk_ioerror">接続に失敗しました。インターネット接続を確認してください。RTM のサーバ運用情報 (status.rememberthemilk.com) も確かめてみてください。</string>
<string name="TEA_tags_label">リスト</string>
<string name="tag_FEx_header">リスト</string>
<string name="TEA_no_tags_modified">変更されませんでした</string>

@ -1017,21 +1017,6 @@
<item>네가 자랑스럽다!</item>
<item>네가 생산적일 때가 좋더라!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk 설정</string>
<string name="rmilk_TLA_repeat">RTM 반복 일정</string>
<string name="rmilk_TLA_sync">RTM 과 싱크가 필요</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">목록</string>
<string name="rmilk_FEx_list_title">RTM 목록 \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">RTM 목록:</string>
<string name="rmilk_MEA_repeat_label">RTM 반복 설정:</string>
<string name="rmilk_MEA_repeat_hint">예: 매주, 14일 후</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">로그인 후 Astrid 를 인증하세요:</string>
<string name="rmilk_MLA_error">로그인 정보 확인에 실패하였습니다. 다시 시도하세요. \n\n 에러 메시지: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">연결 오류! 인터넷 연결이나 RTM 서버 (status.rememberthemilk.com) 를 확인하세요.</string>
<string name="subtasks_help_title">Astrid에서 정렬하고 들여쓰기</string>
<string name="subtasks_help_1">일정을 옮기려면 길게 두드립니다</string>
<string name="subtasks_help_2">재배열하려면 수직으로 드래그합니다</string>

@ -564,17 +564,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk innstillinger</string>
<string name="rmilk_TLA_repeat">RTM gjentagende oppgave</string>
<string name="rmilk_TLA_sync">Trenger synkronisering med RTM</string>
<string name="rmilk_FEx_list">Lister</string>
<string name="rmilk_FEx_list_title">RTM Liste \'%s\'</string>
<string name="rmilk_MEA_list_label">RTM Liste:</string>
<string name="rmilk_MEA_repeat_label">RTM Gjentakelsesstatus</string>
<string name="rmilk_MEA_repeat_hint">f.eks. hver uke, etter 14 dager</string>
<string name="rmilk_MLA_label">Vennligst logg inn og autoriser Astrid</string>
<string name="rmilk_MLA_error">Beklager, kunne ikke verifisere innloggingen. Vennligst prøv igjen. \n\n Feilmelding: %s</string>
<string name="rmilk_ioerror">Tilkoblings feil! Sjekk internettforbindelsen din, evt. RTM serverene (status.rememberthemilk.com), for mulig feilløsning.</string>
<string name="TEA_tags_label">Lister</string>
<string name="tag_FEx_header">Lister</string>
<string name="TEA_no_tags_modified">Ingen endringer gjort</string>

@ -925,21 +925,6 @@
<item>Ik ben zo trots op je!</item>
<item>Ik hou ervan wanneer je productief bent!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk instellingen</string>
<string name="rmilk_TLA_repeat">RTM herhalende taak</string>
<string name="rmilk_TLA_sync">Synchroniseren met RTM</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Lijsten</string>
<string name="rmilk_FEx_list_title">RTM-lijst \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">RTM-lijst:</string>
<string name="rmilk_MEA_repeat_label">RTM herhaal status:</string>
<string name="rmilk_MEA_repeat_hint">bijv. wekelijks, over 14 dagen</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Aanmelden en Astrid autoriseren:</string>
<string name="rmilk_MLA_error">Fout bij het aanmelden. Probeer het nogmaals. \n\n Foutmelding: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">Verbindingsfout! Controleer de internetverbinding, of de RTM servers (status.rememberthemilk.com), voor mogelijke oplossingen.</string>
<string name="subtasks_help_title">Sorteren en inspringen in Astrid</string>
<string name="subtasks_help_1">Druk en sleep om een taak te verplaatsen</string>
<string name="subtasks_help_2">Sleep verticaal om te herschikken</string>

@ -917,21 +917,6 @@
<item>Jestem z Ciebie taka dumna!</item>
<item>Uwielbiam, gdy jesteś produktywny/a!</item>
</string-array>
<string name="rmilk_EOE_button">Zapamiętaj ustawienia aplikacji \"Remember the Milk\"</string>
<string name="rmilk_TLA_repeat">Powtarzanie zadań RTM</string>
<string name="rmilk_TLA_sync">Potrzebna synchronizacja z RTM</string>
<string name="rmilk_FEx_header">Pamiętaj o mleku</string>
<string name="rmilk_FEx_list">Listy</string>
<string name="rmilk_FEx_list_title">Lista RTM \'%s\'</string>
<string name="rmilk_MEA_title">Pamiętaj o mleku</string>
<string name="rmilk_MEA_list_label">Lista RTM:</string>
<string name="rmilk_MEA_repeat_label">Status powtarzania RTM:</string>
<string name="rmilk_MEA_repeat_hint">np. co tydzień, po 14 dniach</string>
<string name="rmilk_MPr_header">Pamiętaj o mleku</string>
<string name="rmilk_MLA_label">Zaloguj i autoryzuj Astrid:</string>
<string name="rmilk_MLA_error">Wybacz, wystąpił błąd podczas weryfikacji Twojego loginu. Spróbuj ponownie. \n\n Treść błędu: %s</string>
<string name="rmilk_notification_title">Astrid: Remeber the Milk</string>
<string name="rmilk_ioerror">Błąd połączenia! Sprawdź swoje połączenie z Internetem lub odwiedź serwer RTM (status.rememberthemilk.com) w celu możliwego rozwiązania problemu.</string>
<string name="TEA_tags_label">Listy</string>
<string name="TEA_tags_none">Brak</string>
<string name="TEA_tag_hint">Nowa lista</string>

@ -996,21 +996,6 @@
<item>Estou tão orgulhoso de você!</item>
<item>Eu adoro quando você é produtivo(a)!</item>
</string-array>
<string name="rmilk_EOE_button">Configurações do Remember the Milk</string>
<string name="rmilk_TLA_repeat">Tarefa recorrente do RTM</string>
<string name="rmilk_TLA_sync">Precisa ser sincronizado com RTM</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Listas</string>
<string name="rmilk_FEx_list_title">Lista RTM \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">Lista RTM:</string>
<string name="rmilk_MEA_repeat_label">Status de recorrência do RTM:</string>
<string name="rmilk_MEA_repeat_hint">ex. semanalmente, depois de 14 dias</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Por favor, conecte-se e autorize o Astrid:</string>
<string name="rmilk_MLA_error">Desculpe, houve um erro identificando seu login. Por favor tente novamente. \n\n Mensagem de Erro: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">Erro de conexão! Verifique sua conexão com a Internet, ou talvez os servidores do RTM (status.rememberthemilk.com), para possíveis soluções.</string>
<string name="subtasks_help_title">Ordenar e Recuar no Astrid</string>
<string name="subtasks_help_1">Toque e segure para mover a tarefa</string>
<string name="subtasks_help_2">Arraste verticalmente para reordenar</string>

@ -490,7 +490,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_FEx_list">Listas</string>
<string name="TEA_tags_label">Listas</string>
<string name="tag_FEx_header">Listas</string>
<string name="tag_settings_title">Settings:</string>

@ -1011,21 +1011,6 @@
<item>Я так горжусь Вами!</item>
<item>Я люблю когда Вы плодотворны!</item>
</string-array>
<string name="rmilk_EOE_button">Запомнить настройки Milk</string>
<string name="rmilk_TLA_repeat">Повторяющаяся задача RTM</string>
<string name="rmilk_TLA_sync">Необходима синхронизация с RTM</string>
<string name="rmilk_FEx_header">Помни про молоко</string>
<string name="rmilk_FEx_list">Списки</string>
<string name="rmilk_FEx_list_title">Список RTM \'%s\'</string>
<string name="rmilk_MEA_title">Помни про молоко</string>
<string name="rmilk_MEA_list_label">Список RTM:</string>
<string name="rmilk_MEA_repeat_label">Состояние повтора RTM</string>
<string name="rmilk_MEA_repeat_hint">например, каждую неделю, спустя 14 дней</string>
<string name="rmilk_MPr_header">Помни про молоко</string>
<string name="rmilk_MLA_label">Пожалуйста, войдите и авторизуйте Astrid:</string>
<string name="rmilk_MLA_error">Извините, при авторизации возникла ошибка. Пожалуйста, попробуйте ещё раз. \n\n Сообщение об ошибке: %s</string>
<string name="rmilk_notification_title">Astrid: Помни про молоко</string>
<string name="rmilk_ioerror">Ошибка соединения! Проверьте соединение с интернетом и, возможно, сервером RTM (status.rememberthemilk.com) для возможного решения.</string>
<string name="subtasks_help_title">Сортировка и отступ в Astrid</string>
<string name="subtasks_help_1">Нажмите и удерживайте, чтобы переместить задачу</string>
<string name="subtasks_help_2">Перетащите вертикально, чтобы перестроить</string>

@ -847,21 +847,6 @@
<item>I\'m so proud of you!</item>
<item>I love when you\'re productive!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk inställningar</string>
<string name="rmilk_TLA_repeat">RTM upprepande uppgift</string>
<string name="rmilk_TLA_sync">Behöver synkronisering med RTM</string>
<string name="rmilk_FEx_header">Kom ihåg mjölk</string>
<string name="rmilk_FEx_list">Listor</string>
<string name="rmilk_FEx_list_title">RTM Lista \'%s\'</string>
<string name="rmilk_MEA_title">Kom ihåg mjölk</string>
<string name="rmilk_MEA_list_label">RTM Lista:</string>
<string name="rmilk_MEA_repeat_label">RTM Upprepningsstatus:</string>
<string name="rmilk_MEA_repeat_hint">t.ex. varje vecka, efter 14 dagar</string>
<string name="rmilk_MPr_header">Kom ihåg mjölk</string>
<string name="rmilk_MLA_label">Var god logga in och auktorisera Astrid</string>
<string name="rmilk_MLA_error">Förlåt, din inloggning kunde inte bekräftas. Var god försök igen. \n\n Felbesked: %s</string>
<string name="rmilk_notification_title">Astrid: Kom ihåg mjölk</string>
<string name="rmilk_ioerror">Tillkopplingsfel! Kolla din internetförbindelse, eller kanske RTM servrarna (status.rememberthemilk.com), för möjliga lösningar.</string>
<string name="subtasks_help_title">Sortera och gör indrag i Astrid</string>
<string name="subtasks_help_1">Rör vid och håll kvar fingret för att flytta en uppgift</string>
<string name="subtasks_help_2">Dra upp eller ner för att ändra ordningen</string>

@ -990,21 +990,6 @@
<item>Seninle gurur duyuyorum!</item>
<item>Verimli olduğun zaman seni çok seviyorum!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk Ayarları</string>
<string name="rmilk_TLA_repeat">RTM Yinelenen Görev</string>
<string name="rmilk_TLA_sync">RTM ile eşleştirme gerekli</string>
<string name="rmilk_FEx_header">Remember the Milk</string>
<string name="rmilk_FEx_list">Listeler</string>
<string name="rmilk_FEx_list_title">RTM Listesi \'%s\'</string>
<string name="rmilk_MEA_title">Remember the Milk</string>
<string name="rmilk_MEA_list_label">RTM Listesi:</string>
<string name="rmilk_MEA_repeat_label">RTM Yineleme Durumu:</string>
<string name="rmilk_MEA_repeat_hint">örn: her hafta, 14 gün sonra</string>
<string name="rmilk_MPr_header">Remember the Milk</string>
<string name="rmilk_MLA_label">Lütfen giriş yap ve Astrid\'i yetkilendir:</string>
<string name="rmilk_MLA_error">Üzgünüz, kullanıcı adınızı doğrulamada hata oluştu. Lütfen tekrar deneyin. \n\n Hata Mesajı: %s</string>
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<string name="rmilk_ioerror">Bağlantı Hatası! İnternet bağlantınızı kontrol edin ya da muhtemel çözümler için RTM sunucularını kontrol edin (status.rememberthemilk.com)</string>
<string name="subtasks_help_title">Astrid\'te Sınıflandırma ve Giritili Yazma</string>
<string name="subtasks_help_1">Bir görevi taşımak için dokunup sürükleyin</string>
<string name="subtasks_help_2">Yeniden sıralamak için dikine sürükle</string>

@ -976,21 +976,6 @@
<item>我真为您骄傲啊!</item>
<item>每次您能做那么多事情的时候,我都爱死您了!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk 设定</string>
<string name="rmilk_TLA_repeat">RTM重复任务</string>
<string name="rmilk_TLA_sync">需要与 RTM 同步</string>
<string name="rmilk_FEx_header">记得登录the Milk。</string>
<string name="rmilk_FEx_list">列表</string>
<string name="rmilk_FEx_list_title">RTM 列表 \'%s\'</string>
<string name="rmilk_MEA_title">记得登录the Milk。</string>
<string name="rmilk_MEA_list_label">RTM 列表:</string>
<string name="rmilk_MEA_repeat_label">RTM 重复状态:</string>
<string name="rmilk_MEA_repeat_hint">例如每星期14天后</string>
<string name="rmilk_MPr_header">记得登录the Milk。</string>
<string name="rmilk_MLA_label">请登陆并授权 Astrid</string>
<string name="rmilk_MLA_error">抱歉,登陆错误。请再试一次。\n\n 错误信息:%s</string>
<string name="rmilk_notification_title">同步到网站 \"别忘记牛奶\"</string>
<string name="rmilk_ioerror">连接错误!请检查网络链接或 RTM 服务器(status.rememberthemilk.com)。</string>
<string name="subtasks_help_title">在 Astrid 中排序并缩进</string>
<string name="subtasks_help_1">轻点并按住一项任务来移动它</string>
<string name="subtasks_help_2">上下拖动以重新整理</string>

@ -979,21 +979,6 @@
<item>我真為您驕傲啊!</item>
<item>每次您能做那麼多事情的時候,我都愛死您了!</item>
</string-array>
<string name="rmilk_EOE_button">Remember the Milk 設定</string>
<string name="rmilk_TLA_repeat">RTM重複任務工作</string>
<string name="rmilk_TLA_sync">需要與 RTM 同步</string>
<string name="rmilk_FEx_header">記得登錄the Milk。</string>
<string name="rmilk_FEx_list">列表</string>
<string name="rmilk_FEx_list_title">RTM 列表 \'%s\'</string>
<string name="rmilk_MEA_title">記得登錄the Milk。</string>
<string name="rmilk_MEA_list_label">RTM 列表:</string>
<string name="rmilk_MEA_repeat_label">RTM 重複狀態:</string>
<string name="rmilk_MEA_repeat_hint">例如每星期14天后</string>
<string name="rmilk_MPr_header">記得登錄the Milk。</string>
<string name="rmilk_MLA_label">請登陸並授權 Astrid</string>
<string name="rmilk_MLA_error">抱歉,登陸錯誤。請再試一次。\n\n 錯誤信息:%s</string>
<string name="rmilk_notification_title">同步到網站 \"別忘記牛奶\"</string>
<string name="rmilk_ioerror">連接錯誤請檢查網絡鏈接或RTM 服務器(status.rememberthemilk.com)。</string>
<string name="subtasks_help_title">在 Astrid 中排序並縮進</string>
<string name="subtasks_help_1">輕點並按住一項任務工作來移動它</string>
<string name="subtasks_help_2">垂直拖動以重新整理</string>

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains preference keys and preference list values.
These should not be translated
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="sync_SPr_interval_values">
<!-- sync_SPr_interval_values: interval in seconds for sync entries (do not edit) -->
<item>0</item>
<item>900</item>
<item>1800</item>
<item>3600</item>
<item>10800</item>
<item>21600</item>
<item>43200</item>
<item>86400</item>
<item>259200</item>
<item>604800</item>
</string-array>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_status_key">sync_status</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_bgwifi_key">sync_bgwifi</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_sync_key">sync_sync</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_forget_key">sync_forget</string>
<!-- ============================================================ MILK == -->
<!-- Preference Key (do not translate) -->
<string name="rmilk_MPr_interval_key">sync_freq</string>
</resources>

@ -403,11 +403,6 @@
<!-- ===========================================================ACTFM == -->
<string name="actfm_https_key">actfmHttps</string>
<!-- ============================================================ MILK == -->
<!-- Preference Key (do not translate) -->
<string name="rmilk_MPr_interval_key">sync_freq</string>
<!-- =========================================================== GTASKS == -->

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ====================== Plugin Boilerplate ========================= -->
<!-- label for RMilk button in Task Edit Activity -->
<string name="rmilk_EOE_button">Remember the Milk Settings</string>
<!-- task detail showing RTM repeat information -->
<string name="rmilk_TLA_repeat">RTM Repeating Task</string>
<!-- task detail showing item needs to be synchronized -->
<string name="rmilk_TLA_sync">Needs synchronization with RTM</string>
<!-- filters header: RTM -->
<string name="rmilk_FEx_header">Remember the Milk</string>
<!-- filter category for RTM lists -->
<string name="rmilk_FEx_list">Lists</string>
<!-- RTM list filter title (%s => list) -->
<string name="rmilk_FEx_list_title">RTM List \'%s\'</string>
<!-- ======================= MilkEditActivity ========================== -->
<!-- RTM edit activity Title -->
<string name="rmilk_MEA_title">Remember the Milk</string>
<!-- RTM edit List Edit Label -->
<string name="rmilk_MEA_list_label">RTM List:</string>
<!-- RTM edit Repeat Label -->
<string name="rmilk_MEA_repeat_label">RTM Repeat Status:</string>
<!-- RTM edit Repeat Hint -->
<string name="rmilk_MEA_repeat_hint">i.e. every week, after 14 days</string>
<!-- ======================== MilkPreferences ========================== -->
<!-- Milk Preferences Title -->
<string name="rmilk_MPr_header">Remember the Milk</string>
<!-- ======================= MilkLoginActivity ========================= -->
<!-- RTM Login Instructions -->
<string name="rmilk_MLA_label">Please Log In and Authorize Astrid:</string>
<!-- Login Error Dialog (%s => message) -->
<string name="rmilk_MLA_error">
Sorry, there was an error verifying your login. Please try again.
\n\n
Error Message: %s
</string>
<!-- ======================== Synchronization ========================== -->
<!-- title for notification tray when synchronizing -->
<string name="rmilk_notification_title">Astrid: Remember the Milk</string>
<!-- Error msg when io exception with rmilk -->
<string name="rmilk_ioerror">Connection Error! Check your Internet connection,
or maybe RTM servers (status.rememberthemilk.com), for possible solutions.</string>
</resources>

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/sync_SPr_group_status">
<com.todoroo.astrid.ui.MultilinePreference
android:layout="@layout/status_preference"
android:key="@string/sync_SPr_status_key"
android:textSize="24sp"
android:gravity="center"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/sync_SPr_group_options">
<com.todoroo.astrid.ui.MultilineListPreference
android:key="@string/rmilk_MPr_interval_key"
android:entries="@array/sync_SPr_interval_entries"
android:entryValues="@array/sync_SPr_interval_values"
android:title="@string/sync_SPr_interval_title" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/sync_SPr_group_actions">
<com.todoroo.astrid.ui.MultilinePreference
android:key="@string/sync_SPr_sync_key"
android:title="@string/sync_SPr_sync" />
<com.todoroo.astrid.ui.MultilinePreference
android:key="@string/sync_SPr_forget_key"
android:title="@string/sync_SPr_forget"
android:summary="@string/sync_SPr_forget_description" />
</PreferenceCategory>
</PreferenceScreen>

@ -1,135 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import org.weloveastrid.rmilk.sync.MilkSyncProvider;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities;
/**
* SynchronizationService is the service that performs Astrid's background
* synchronization with online task managers. Starting this service
* schedules a repeating alarm which handles the synchronization
*
* @author Tim Su
*
*/
public class MilkBackgroundService extends Service {
/** Minimum time before an auto-sync */
private static final long AUTO_SYNC_MIN_OFFSET = 5*60*1000L;
/** alarm identifier */
public static final String SYNC_ACTION = "sync"; //$NON-NLS-1$
// --- BroadcastReceiver abstract methods
/** start the synchronization service. sits in the background */
@Override
public void onStart(Intent intent, int startId) {
try {
if(intent != null && SYNC_ACTION.equals(intent.getAction()))
startSynchronization(this);
} catch (Exception e) {
MilkUtilities.INSTANCE.setLastError(e.toString(), "");
}
}
/** Start the actual synchronization */
private void startSynchronization(Context context) {
if(context == null || context.getResources() == null)
return;
ContextManager.setContext(context);
if(MilkUtilities.INSTANCE.isOngoing())
return;
new MilkSyncProvider().synchronize(context);
}
// --- alarm management
/**
* Schedules repeating alarm for auto-synchronization
*/
public static void scheduleService() {
Context context = ContextManager.getContext();
int syncFrequencySeconds = MilkUtilities.INSTANCE.getSyncAutoSyncFrequency();
if(syncFrequencySeconds <= 0) {
unscheduleService(context);
return;
}
// figure out synchronization frequency
long interval = 1000L * syncFrequencySeconds;
long offset = computeNextSyncOffset(interval);
// give a little padding
offset = Math.max(offset, AUTO_SYNC_MIN_OFFSET);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
Log.i("Astrid", "Autosync set for " + offset / 1000 //$NON-NLS-1$ //$NON-NLS-2$
+ " seconds repeating every " + syncFrequencySeconds); //$NON-NLS-1$
// cancel all existing
am.cancel(pendingIntent);
// schedule new
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + offset,
interval, pendingIntent);
}
/**
* Removes repeating alarm for auto-synchronization
*/
private static void unscheduleService(Context context) {
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent);
}
/** Create the alarm intent */
private static Intent createAlarmIntent(Context context) {
Intent intent = new Intent(context, MilkBackgroundService.class);
intent.setAction(SYNC_ACTION);
return intent;
}
// --- utility methods
private static long computeNextSyncOffset(long interval) {
// figure out last synchronize time
long lastSyncDate = MilkUtilities.INSTANCE.getLastSyncDate();
// if user never synchronized, give them a full offset period before bg sync
if(lastSyncDate != 0)
return Math.max(0, lastSyncDate + interval - DateUtilities.now());
else
return interval;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

@ -1,58 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import org.weloveastrid.rmilk.data.MilkListService;
import org.weloveastrid.rmilk.data.MilkMetadataService;
import com.todoroo.andlib.service.AbstractDependencyInjector;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService.ErrorReporter;
/**
* RTM Dependency Injection for service classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkDependencyInjector extends AbstractDependencyInjector {
/**
* variable to prevent multiple copies of this injector to be loaded
*/
private static MilkDependencyInjector instance = null;
/**
* Initialize list of injectables. Special care must used when
* instantiating classes that themselves depend on dependency injection
* (i.e. {@link ErrorReporter}.
*/
@Override
@SuppressWarnings("nls")
protected void addInjectables() {
injectables.put("milkMetadataService", MilkMetadataService.class);
injectables.put("milkListService", MilkListService.class);
}
/**
* Install this dependency injector
*/
public static void initialize() {
if(instance != null)
return;
synchronized(MilkDependencyInjector.class) {
if(instance == null)
instance = new MilkDependencyInjector();
DependencyInjectionService.getInstance().addInjector(instance);
}
}
MilkDependencyInjector() {
// prevent instantiation
super();
}
}

@ -1,92 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import org.weloveastrid.rmilk.data.MilkListService;
import org.weloveastrid.rmilk.data.MilkMetadataService;
import org.weloveastrid.rmilk.data.MilkTaskFields;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Metadata;
/**
* Exposes Task Details for Remember the Milk:
* - RTM list
* - RTM repeat information
* - RTM notes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkDetailExposer extends BroadcastReceiver {
public static final String DETAIL_SEPARATOR = " | "; //$NON-NLS-1$
@Autowired private MilkMetadataService milkMetadataService;
@Autowired private MilkListService milkListService;
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
MilkDependencyInjector.initialize();
DependencyInjectionService.getInstance().inject(this);
// if we aren't logged in, don't expose features
if(!MilkUtilities.INSTANCE.isLoggedIn())
return;
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1)
return;
String taskDetail = getTaskDetails(context, taskId);
if(taskDetail == null)
return;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, MilkUtilities.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
public String getTaskDetails(Context context, long id) {
Metadata metadata = milkMetadataService.getTaskMetadata(id);
if(metadata == null)
return null;
StringBuilder builder = new StringBuilder();
long listId = metadata.getValue(MilkTaskFields.LIST_ID);
String listName = milkListService.getListName(listId);
// RTM list is out of date. don't display RTM stuff
if(listName == null)
return null;
if(listId > 0 && !"Inbox".equals(listName)) { //$NON-NLS-1$
builder.append("<img src='silk_folder'/> ").append(listName).append(DETAIL_SEPARATOR); //$NON-NLS-1$
}
int repeat = metadata.getValue(MilkTaskFields.REPEATING);
if(repeat != 0) {
builder.append(context.getString(R.string.rmilk_TLA_repeat)).append(DETAIL_SEPARATOR);
}
if(builder.length() == 0)
return null;
String result = builder.toString();
return result.substring(0, result.length() - DETAIL_SEPARATOR.length());
}
}

@ -1,121 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import org.weloveastrid.rmilk.data.MilkListFields;
import org.weloveastrid.rmilk.data.MilkListService;
import org.weloveastrid.rmilk.data.MilkTaskFields;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.AstridFilterExposer;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
/**
* Exposes filters based on RTM lists
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkFilterExposer extends BroadcastReceiver implements AstridFilterExposer {
@Autowired private MilkListService milkListService;
static {
MilkDependencyInjector.initialize();
}
private Filter filterFromList(Context context, StoreObject list) {
String listName = list.getValue(MilkListFields.NAME);
String title = context.getString(R.string.rmilk_FEx_list_title,
listName);
ContentValues values = new ContentValues();
values.put(Metadata.KEY.name, MilkTaskFields.METADATA_KEY);
values.put(MilkTaskFields.LIST_ID.name, list.getValue(MilkListFields.REMOTE_ID));
values.put(MilkTaskFields.TASK_SERIES_ID.name, 0);
values.put(MilkTaskFields.TASK_ID.name, 0);
values.put(MilkTaskFields.REPEATING.name, 0);
Filter filter = new Filter(listName, title, new QueryTemplate().join(
Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK))).where(Criterion.and(
MetadataCriteria.withKey(MilkTaskFields.METADATA_KEY),
TaskCriteria.activeAndVisible(),
MilkTaskFields.LIST_ID.eq(list.getValue(MilkListFields.REMOTE_ID)))),
values);
return filter;
}
@Override
public void onReceive(Context context, Intent intent) {
Resources r = context.getResources();
ContextManager.setContext(context);
FilterListItem[] list = prepareFilters(r);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, MilkUtilities.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
private FilterListItem[] prepareFilters(Resources r) {
// if we aren't logged in, don't expose features
if(!MilkUtilities.INSTANCE.isLoggedIn())
return null;
DependencyInjectionService.getInstance().inject(this);
StoreObject[] lists = milkListService.getLists();
// If user does not have any tags, don't show this section at all
if(lists.length == 0)
return null;
Filter[] listFilters = new Filter[lists.length];
for(int i = 0; i < lists.length; i++)
listFilters[i] = filterFromList(ContextManager.getContext(), lists[i]);
FilterListHeader rtmHeader = new FilterListHeader(ContextManager.getContext().getString(R.string.rmilk_FEx_header));
FilterCategory rtmLists = new FilterCategory(ContextManager.getContext().getString(R.string.rmilk_FEx_list),
listFilters);
// transmit filter list
FilterListItem[] list = new FilterListItem[2];
list[0] = rtmHeader;
list[1] = rtmLists;
return list;
}
@Override
public FilterListItem[] getFilters() {
if (ContextManager.getContext() == null || ContextManager.getContext().getResources() == null)
return null;
Resources r = ContextManager.getContext().getResources();
return prepareFilters(r);
}
}

@ -1,119 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import com.timsu.astrid.R;
import com.todoroo.andlib.utility.DialogUtilities;
/**
* This activity displays a <code>WebView</code> that allows users to log in to the
* synchronization provider requested. A callback method determines whether
* their login was successful and therefore whether to dismiss the dialog.
*
* @author timsu
*
*/
public class MilkLoginActivity extends Activity {
// --- bundle arguments
/**
* URL to display
*/
public static final String URL_TOKEN = "u"; //$NON-NLS-1$
// --- callback
/** Callback interface */
public interface SyncLoginCallback {
/**
* Verifies whether the user's login attempt was successful. Will be
* called off of the UI thread, use the handler to post messages.
*
* @return error string, or null if sync was successful
*/
public String verifyLogin(Handler handler);
}
protected static SyncLoginCallback callback = null;
/** Sets callback method */
public static void setCallback(SyncLoginCallback newCallback) {
callback = newCallback;
}
// --- ui initialization
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rmilk_login_activity);
setTitle(R.string.rmilk_MPr_header);
final String urlParam = getIntent().getStringExtra(URL_TOKEN);
final WebView webView = (WebView)findViewById(R.id.browser);
Button done = (Button)findViewById(R.id.done);
Button cancel = (Button)findViewById(R.id.cancel);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setSavePassword(false);
webView.getSettings().setSupportZoom(true);
webView.loadUrl(urlParam);
done.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
final Handler handler = new Handler();
if(callback == null) {
finish();
return;
}
new Thread(new Runnable() {
public void run() {
final String result = callback.verifyLogin(handler);
if(result == null) {
finish();
} else {
// display the error, re-load url
handler.post(new Runnable() {
public void run() {
DialogUtilities.okDialog(MilkLoginActivity.this,
result, null);
webView.loadUrl(urlParam);
}
});
}
}
}).start();
}
});
cancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
}
}

@ -1,44 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import org.weloveastrid.rmilk.sync.MilkSyncProvider;
import com.timsu.astrid.R;
import com.todoroo.astrid.sync.SyncProviderPreferences;
import com.todoroo.astrid.sync.SyncProviderUtilities;
/**
* Displays synchronization preferences and an action panel so users can
* initiate actions from the menu.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkPreferences extends SyncProviderPreferences {
@Override
public int getPreferenceResource() {
return R.xml.preferences_rmilk;
}
@Override
public void startSync() {
new MilkSyncProvider().synchronize(this);
finish();
}
@Override
public void logOut() {
new MilkSyncProvider().signOut(this);
}
@Override
public SyncProviderUtilities getUtilities() {
return MilkUtilities.INSTANCE;
}
}

@ -1,25 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.service.ContextManager;
public class MilkStartupReceiver extends BroadcastReceiver {
@Override
/** Called when device is restarted */
public void onReceive(final Context context, Intent intent) {
ContextManager.setContext(context);
MilkBackgroundService.scheduleService();
MilkUtilities.INSTANCE.stopOngoing();
}
}

@ -1,44 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.SyncAction;
/**
* Exposes sync action
*
*/
public class MilkSyncActionExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
// if we aren't logged in, don't expose sync action
if(!MilkUtilities.INSTANCE.isLoggedIn())
return;
Intent syncIntent = new Intent(MilkBackgroundService.SYNC_ACTION, null,
context, MilkBackgroundService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT);
SyncAction syncAction = new SyncAction(context.getString(R.string.rmilk_MPr_header),
pendingIntent);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, MilkUtilities.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, syncAction);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
}

@ -1,74 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.sync.SyncProviderUtilities;
/**
* Constants and preferences for RTM plugin
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class MilkUtilities extends SyncProviderUtilities {
// --- constants
/** add-on identifier */
public static final String IDENTIFIER = "rmilk";
public static final MilkUtilities INSTANCE = new MilkUtilities();
private static final String EXPORTED_PREFS_CHECKED = IDENTIFIER + "-prefscheck";
// --- utilities boilerplate
private MilkUtilities() {
// if no token is set, see if astrid has exported one
if(getToken() == null && !Preferences.getBoolean(EXPORTED_PREFS_CHECKED, false)) {
try {
Context astridContext = ContextManager.getContext().createPackageContext("com.timsu.astrid", 0);
SharedPreferences sharedPreferences = astridContext.getSharedPreferences("rtm", 0);
Editor editor = getPrefs().edit();
if(sharedPreferences != null) {
String token = sharedPreferences.getString("rmilk_token", null);
long lastSyncDate = sharedPreferences.getLong("rmilk_last_sync", 0);
editor.putString(getIdentifier() + PREF_TOKEN, token);
editor.putLong(getIdentifier() + PREF_LAST_SYNC, lastSyncDate);
}
editor.putBoolean(EXPORTED_PREFS_CHECKED, true);
editor.commit();
} catch (Exception e) {
// too bad
}
}
}
; @Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public int getSyncIntervalKey() {
return R.string.rmilk_MPr_interval_key;
}
@Override
public String getLoggedInUserName() {
return "";
}
}

@ -1,74 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
/**
* Encapsulates information about an application that is a client of RememberTheMilk. Includes information required by RTM to connect: the API key and
* the shared secret.
*
* @author Will Ross Jun 22, 2007
*/
public class ApplicationInfo
{
private final String apiKey;
private final String sharedSecret;
private final String name;
private final String authToken;
public ApplicationInfo(String apiKey, String sharedSecret, String name)
{
this(apiKey, sharedSecret, name, null);
}
public ApplicationInfo(String apiKey, String sharedSecret, String name,
String authToken)
{
super();
this.apiKey = apiKey;
this.sharedSecret = sharedSecret;
this.name = name;
this.authToken = authToken;
}
public String getApiKey()
{
return apiKey;
}
public String getSharedSecret()
{
return sharedSecret;
}
public String getName()
{
return name;
}
public String getAuthToken()
{
return authToken;
}
}

@ -1,300 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import android.util.Log;
/**
* Handles the details of invoking a method on the RTM REST API.
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class Invoker {
private static final String SERVICE_UNAVAILABLE_CODE = "105";
private static final String TAG = "rtm-invoker"; //$NON-NLS-1$
private static final DocumentBuilder builder;
static
{
// Done this way because the builder is marked "final"
DocumentBuilder aBuilder;
try
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
aBuilder = factory.newDocumentBuilder();
}
catch (Exception exception)
{
Log.e(TAG, "Unable to construct a document builder", exception);
aBuilder = null;
}
builder = aBuilder;
}
private static final String ENCODING = "UTF-8"; //$NON-NLS-1$
private static final String API_SIG_PARAM = "api_sig"; //$NON-NLS-1$
private static final long INVOCATION_INTERVAL = 400;
private long lastInvocation;
private final ApplicationInfo applicationInfo;
private final MessageDigest digest;
private final String serviceRelativeUri;
private final HttpClient httpClient;
public Invoker(@SuppressWarnings("unused") String serverHostName,
@SuppressWarnings("unused") int serverPortNumber,
String serviceRelativeUri, ApplicationInfo applicationInfo)
throws ServiceInternalException {
this.serviceRelativeUri = serviceRelativeUri;
httpClient = new DefaultHttpClient();
lastInvocation = System.currentTimeMillis();
this.applicationInfo = applicationInfo;
try {
digest = MessageDigest.getInstance("md5"); //$NON-NLS-1$
} catch (NoSuchAlgorithmException e) {
throw new ServiceInternalException(
"Could not create properly the MD5 digest", e);
}
}
private StringBuffer computeRequestUri(Param... params)
throws ServiceInternalException {
final StringBuffer requestUri = new StringBuffer(serviceRelativeUri);
if (params.length > 0) {
requestUri.append("?");
}
for (Param param : params) {
try {
requestUri.append(param.getName()).append("=").append(
URLEncoder.encode(param.getValue(), ENCODING)).append(
"&");
} catch (Exception exception) {
final StringBuffer message = new StringBuffer(
"Cannot encode properly the HTTP GET request URI: cannot execute query");
Log.e(TAG, message.toString(), exception);
throw new ServiceInternalException(message.toString());
}
}
requestUri.append(API_SIG_PARAM).append("=").append(calcApiSig(params));
return requestUri;
}
/** Call invoke with a false repeat */
public Element invoke(Param... params) throws ServiceException {
return invoke(false, params);
}
public Element invoke(boolean repeat, Param... params)
throws ServiceException {
long timeSinceLastInvocation = System.currentTimeMillis() -
lastInvocation;
if (timeSinceLastInvocation < INVOCATION_INTERVAL) {
// In order not to invoke the RTM service too often
try {
Thread.sleep(INVOCATION_INTERVAL - timeSinceLastInvocation);
} catch (InterruptedException e) {
return null;
}
}
// We compute the URI
final StringBuffer requestUri = computeRequestUri(params);
HttpResponse response = null;
final HttpGet request = new HttpGet("http://" //$NON-NLS-1$
+ ServiceImpl.SERVER_HOST_NAME + requestUri.toString());
final String methodUri = request.getRequestLine().getUri();
Element result;
try {
Log.i(TAG, "Executing the method:" + methodUri); //$NON-NLS-1$
response = httpClient.execute(request);
lastInvocation = System.currentTimeMillis();
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.e(TAG, "Method failed: " + response.getStatusLine()); //$NON-NLS-1$
// Tim: HTTP error. Let's wait a little bit
if (!repeat) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// ignore
}
response.getEntity().consumeContent();
return invoke(true, params);
}
throw new ServiceInternalException("method failed: "
+ response.getStatusLine());
}
final Document responseDoc = builder.parse(response.getEntity()
.getContent());
final Element wrapperElt = responseDoc.getDocumentElement();
if (!wrapperElt.getNodeName().equals("rsp")) {
throw new ServiceInternalException(
"unexpected response returned by RTM service: "
+ wrapperElt.getNodeName());
} else {
String stat = wrapperElt.getAttribute("stat");
if (stat.equals("fail")) {
Node errElt = wrapperElt.getFirstChild();
while (errElt != null
&& (errElt.getNodeType() != Node.ELEMENT_NODE || !errElt
.getNodeName().equals("err"))) {
errElt = errElt.getNextSibling();
}
if (errElt == null) {
throw new ServiceInternalException(
"unexpected response returned by RTM service: "
+ wrapperElt.getNodeValue());
} else {
if (SERVICE_UNAVAILABLE_CODE.equals(((Element) errElt)
.getAttribute("code")) && !repeat) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// ignore
}
return invoke(true, params);
}
throw new ServiceException(Integer
.parseInt(((Element) errElt)
.getAttribute("code")),
((Element) errElt).getAttribute("msg"));
}
} else {
Node dataElt = wrapperElt.getFirstChild();
while (dataElt != null
&& (dataElt.getNodeType() != Node.ELEMENT_NODE || dataElt
.getNodeName().equals("transaction") == true)) {
try {
Node nextSibling = dataElt.getNextSibling();
if (nextSibling == null) {
break;
} else {
dataElt = nextSibling;
}
} catch (IndexOutOfBoundsException exception) {
// Some implementation may throw this exception,
// instead of returning a null sibling
break;
}
}
if (dataElt == null) {
throw new ServiceInternalException(
"unexpected response returned by RTM service: "
+ wrapperElt.getNodeValue());
} else {
result = (Element) dataElt;
}
}
}
} catch (IOException e) {
throw new ServiceInternalException("Error making connection: " +
e.getMessage(), e);
} catch (SAXException e) {
// repeat call if possible.
if(!repeat)
return invoke(true, params);
else
throw new ServiceInternalException("Error parsing response. " +
"Please try sync again!", e);
} finally {
httpClient.getConnectionManager().closeExpiredConnections();
}
return result;
}
final String calcApiSig(Param... params) throws ServiceInternalException {
try {
digest.reset();
digest.update(applicationInfo.getSharedSecret().getBytes(ENCODING));
List<Param> sorted = Arrays.asList(params);
Collections.sort(sorted);
for (Param param : sorted) {
digest.update(param.getName().getBytes(ENCODING));
digest.update(param.getValue().getBytes(ENCODING));
}
return convertToHex(digest.digest());
} catch (UnsupportedEncodingException e) {
throw new ServiceInternalException(
"cannot hahdle properly the encoding", e);
}
}
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while (two_halfs++ < 1);
}
return buf.toString();
}
}

@ -1,59 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
import java.util.Date;
import org.weloveastrid.rmilk.api.data.RtmData;
/**
*
* @author Will Ross Jun 21, 2007
*/
public class Param implements Comparable<Param> {
private final String name;
private final String value;
public Param(String name, String value) {
this.name = name;
this.value = value;
}
public Param(String name, Date value) {
this.name = name;
this.value = RtmData.formatDate(value);
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public int compareTo(Param p) {
return name.compareTo(p.getName());
}
}

@ -1,47 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
import java.util.prefs.Preferences;
/**
*
* @author Will Ross Jun 21, 2007
*/
public class Prefs {
Preferences preferences;
public enum PrefKey {
AuthToken
}
public Prefs() {
preferences = Preferences.userNodeForPackage(Prefs.class);
}
public String getAuthToken() {
return preferences.get(PrefKey.AuthToken.toString(), null);
}
public void setAuthToken(String authToken) {
preferences.put(PrefKey.AuthToken.toString(), authToken);
}
}

@ -1,50 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
import java.io.IOException;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class ServiceException extends IOException {
private static final long serialVersionUID = -6711156026040643361L;
int responseCode;
String responseMessage;
public ServiceException(int responseCode, String responseMessage) {
super("Service invocation failed. Code: " + responseCode + "; message: " + responseMessage);
this.responseCode = responseCode;
this.responseMessage = responseMessage;
}
int getResponseCode() {
return responseCode;
}
String getResponseMessage() {
return responseMessage;
}
}

@ -1,617 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.w3c.dom.Element;
import org.weloveastrid.rmilk.api.data.RtmAuth;
import org.weloveastrid.rmilk.api.data.RtmData;
import org.weloveastrid.rmilk.api.data.RtmFrob;
import org.weloveastrid.rmilk.api.data.RtmList;
import org.weloveastrid.rmilk.api.data.RtmLists;
import org.weloveastrid.rmilk.api.data.RtmLocation;
import org.weloveastrid.rmilk.api.data.RtmTask;
import org.weloveastrid.rmilk.api.data.RtmTask.Priority;
import org.weloveastrid.rmilk.api.data.RtmTaskList;
import org.weloveastrid.rmilk.api.data.RtmTaskNote;
import org.weloveastrid.rmilk.api.data.RtmTaskSeries;
import org.weloveastrid.rmilk.api.data.RtmTasks;
import org.weloveastrid.rmilk.api.data.RtmTimeline;
/**
* A major part of the RTM API implementation is here.
*
* @author Will Ross Jun 21, 2007
* @author Edouard Mercier, since 2008.04.15
* @author timsu January 2009
*/
@SuppressWarnings("nls")
public class ServiceImpl
{
public final static String SERVER_HOST_NAME = "api.rememberthemilk.com"; //$NON-NLS-1$
public final static int SERVER_PORT_NUMBER = 80;
public final static String REST_SERVICE_URL_POSTFIX = "/services/rest/"; //$NON-NLS-1$
private final ApplicationInfo applicationInfo;
private final Invoker invoker;
private final Prefs prefs;
private String currentAuthToken;
RtmFrob tempFrob;
public ServiceImpl(ApplicationInfo applicationInfo)
throws ServiceInternalException
{
invoker = new Invoker(SERVER_HOST_NAME, SERVER_PORT_NUMBER, REST_SERVICE_URL_POSTFIX, applicationInfo);
this.applicationInfo = applicationInfo;
prefs = new Prefs();
if (applicationInfo.getAuthToken() != null)
{
currentAuthToken = applicationInfo.getAuthToken();
}
else
{
currentAuthToken = prefs.getAuthToken();
}
}
public boolean isServiceAuthorized()
throws ServiceException
{
if (currentAuthToken == null)
return false;
try
{
/* RtmAuth auth = */auth_checkToken(currentAuthToken);
return true;
}
catch (ServiceException e)
{
if (e.getResponseCode() != 98)
{
throw e;
}
else
{
// Bad token.
currentAuthToken = null;
return false;
}
}
}
public String beginAuthorization(RtmAuth.Perms permissions)
throws ServiceException
{
// Instructions from the "User authentication for desktop applications"
// section at http://www.rememberthemilk.com/services/api/authentication.rtm
tempFrob = auth_getFrob();
return beginAuthorization(tempFrob, permissions);
}
public String beginAuthorization(RtmFrob frob, RtmAuth.Perms permissions)
throws ServiceException
{
String authBaseUrl = "http://" + SERVER_HOST_NAME + "/services/auth/";
Param[] params = new Param[] { new Param("api_key", applicationInfo.getApiKey()), new Param("perms", permissions.toString()),
new Param("frob", frob.getValue()) };
Param sig = new Param("api_sig", invoker.calcApiSig(params));
StringBuilder authUrl = new StringBuilder(authBaseUrl);
authUrl.append("?");
for (Param param : params)
{
authUrl.append(param.getName()).append("=").append(param.getValue()).append("&");
}
authUrl.append(sig.getName()).append("=").append(sig.getValue());
return authUrl.toString();
}
public String completeAuthorization()
throws ServiceException
{
return completeAuthorization(tempFrob);
}
public String completeAuthorization(RtmFrob frob)
throws ServiceException
{
currentAuthToken = auth_getToken(frob.getValue());
prefs.setAuthToken(currentAuthToken);
return currentAuthToken;
}
public RtmAuth auth_checkToken(String authToken)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.auth.checkToken"), new Param("auth_token", authToken),
new Param("api_key", applicationInfo.getApiKey()));
return new RtmAuth(response);
}
public RtmFrob auth_getFrob()
throws ServiceException
{
return new RtmFrob(invoker.invoke(new Param("method", "rtm.auth.getFrob"), new Param("api_key", applicationInfo.getApiKey())));
}
public String auth_getToken(String frob)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.auth.getToken"), new Param("frob", frob), new Param("api_key", applicationInfo.getApiKey()));
return new RtmAuth(response).getToken();
}
public void contacts_add()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void contacts_delete()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void contacts_getList()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void groups_add()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void groups_addContact()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void groups_delete()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void groups_getList()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void groups_removeContact()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public RtmList lists_add(String timelineId, String listName)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.lists.add"), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()), new Param("name", listName), new Param("timeline", timelineId));
return new RtmList(response);
}
public void lists_archive()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void lists_delete(String timelineId, String listId)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.lists.delete"), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()),
new Param("timeline", timelineId), new Param("list_id", listId));
}
public RtmLists lists_getList()
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.lists.getList"), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
return new RtmLists(response);
}
public RtmList lists_getList(String listName)
throws ServiceException
{
RtmLists fullList = lists_getList();
for (Entry<String, RtmList> entry : fullList.getLists().entrySet())
{
if (entry.getValue().getName().equals(listName))
{
return entry.getValue();
}
}
return null;
}
public void lists_setDefaultList()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public RtmList lists_setName(String timelineId, String listId, String newName)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.lists.setName"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("name", newName), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
return new RtmList(response);
}
public void lists_unarchive()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void reflection_getMethodInfo()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void reflection_getMethods()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void settings_getList()
{
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Adds a task, name, to the list specified by list_id.
* @param timelineId
* @param listId can be null to omit this parameter (assumes Inbox)
* @param name
* @return
* @throws ServiceException
*/
public RtmTaskSeries tasks_add(String timelineId, String listId, String name)
throws ServiceException
{
Element response;
if(listId != null)
response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
else
response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId),
new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
RtmTaskList rtmTaskList = new RtmTaskList(response);
if (rtmTaskList.getSeries().size() == 1)
{
return rtmTaskList.getSeries().get(0);
}
else if (rtmTaskList.getSeries().size() > 1)
{
throw new ServiceInternalException("Internal error: more that one task (" + rtmTaskList.getSeries().size() + ") has been created");
}
throw new ServiceInternalException("Internal error: no task has been created");
}
public void tasks_addTags()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void tasks_complete(String timelineId, String listId, String taskSeriesId, String taskId)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.complete"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
public void tasks_delete(String timelineId, String listId, String taskSeriesId, String taskId)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.delete"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
public RtmTasks tasks_getList(String listId, String filter, Date lastSync)
throws ServiceException
{
Set<Param> params = new HashSet<Param>();
params.add(new Param("method", "rtm.tasks.getList"));
if (listId != null)
{
params.add(new Param("list_id", listId));
}
if (filter != null)
{
params.add(new Param("filter", filter));
}
if (lastSync != null)
{
params.add(new Param("last_sync", lastSync));
}
params.add(new Param("auth_token", currentAuthToken));
params.add(new Param("api_key", applicationInfo.getApiKey()));
return new RtmTasks(invoker.invoke(params.toArray(new Param[params.size()])));
}
public RtmTaskSeries tasks_getTask(String taskName)
throws ServiceException
{
return tasks_getTask(null, taskName);
}
public RtmTaskSeries tasks_getTask(String taskSeriesId, String taskName)
throws ServiceException
{
Set<Param> params = new HashSet<Param>();
params.add(new Param("method", "rtm.tasks.getList"));
params.add(new Param("auth_token", currentAuthToken));
params.add(new Param("api_key", applicationInfo.getApiKey()));
params.add(new Param("filter", "name:\"" + taskName+"\""));
RtmTasks rtmTasks = new RtmTasks(invoker.invoke(params.toArray(new Param[params.size()])));
return findTask(taskSeriesId, rtmTasks);
}
private RtmTaskSeries findTask(String taskId, RtmTasks rtmTasks)
{
for (RtmTaskList list : rtmTasks.getLists())
{
for (RtmTaskSeries series : list.getSeries())
{
if (taskId != null)
{
if (series.getId().equals(taskId))
{
return series;
}
}
else
{
return series;
}
}
}
return null;
}
public void tasks_movePriority()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public RtmTaskSeries tasks_moveTo(String timelineId, String fromListId, String toListId, String taskSeriesId, String taskId)
throws ServiceException
{
if(fromListId.equals(toListId))
return null;
Element elt = invoker.invoke(new Param("method", "rtm.tasks.moveTo"), new Param("timeline", timelineId), new Param("from_list_id", fromListId),
new Param("to_list_id", toListId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
RtmTaskList rtmTaskList = new RtmTaskList(elt);
return findTask(taskSeriesId, taskId, rtmTaskList);
}
public void tasks_postpone()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void tasks_removeTags()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void tasks_setDueDate(String timelineId, String listId, String taskSeriesId, String taskId, Date due, boolean hasDueTime)
throws ServiceException
{
final boolean setDueDate = (due != null);
if (setDueDate == true)
{
invoker.invoke(new Param("method", "rtm.tasks.setDueDate"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("due", due), new Param("has_due_time", hasDueTime ? "1" : "0"),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
}
else
{
invoker.invoke(new Param("method", "rtm.tasks.setDueDate"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
}
public void tasks_setEstimate(String timelineId, String listId, String taskSeriesId, String taskId, String newEstimate)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.setEstimate"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("estimate", newEstimate), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
public void tasks_setName(String timelineId, String listId, String taskSeriesId, String taskId, String newName)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.setName"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("name", newName), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
private RtmTaskSeries findTask(String taskSeriesId, String taskId, RtmTaskList rtmTaskList)
{
for (RtmTaskSeries series : rtmTaskList.getSeries())
{
if (series.getId().equals(taskSeriesId) && series.getTask().getId().equals(taskId))
{
return series;
}
}
return null;
}
public void tasks_setPriority(String timelineId, String listId, String taskSeriesId, String taskId, Priority priority)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.setPriority"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("priority", RtmTask.convertPriority(priority)),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
}
public void tasks_setRecurrence(String timelineId, String listId, String taskSeriesId, String taskId, String repeat)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.setRecurrence"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("repeat", repeat),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
}
public void tasks_setTags(String timelineId, String listId,
String taskSeriesId, String taskId, String[] tags) throws ServiceException
{
StringBuilder tagString = new StringBuilder();
if(tags != null) {
for(int i = 0; i < tags.length; i++) {
tagString.append(tags[i].replace(" ", "_"));
if(i < tags.length - 1)
tagString.append(",");
}
}
invoker.invoke(new Param("method", "rtm.tasks.setTags"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("tags", tagString.toString()), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
public void tasks_setURL()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void tasks_uncomplete(String timelineId, String listId, String taskSeriesId, String taskId)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.uncomplete"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
}
public RtmTaskNote tasks_notes_add(String timelineId, String listId, String taskSeriesId, String taskId, String title, String text)
throws ServiceException
{
Element elt = invoker.invoke(new Param("method", "rtm.tasks.notes.add"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("note_title", title), new Param("note_text", text),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
return new RtmTaskNote(elt);
}
public void tasks_notes_delete(String timelineId, String noteId)
throws ServiceException
{
invoker.invoke(new Param("method", "rtm.tasks.notes.delete"), new Param("timeline", timelineId), new Param("note_id", noteId),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
}
public RtmTaskNote tasks_notes_edit(String timelineId, String noteId, String title, String text)
throws ServiceException
{
Element elt = invoker.invoke(new Param("method", "rtm.tasks.notes.edit"), new Param("timeline", timelineId), new Param("note_id", noteId),
new Param("note_title", title), new Param("note_text", text), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
return new RtmTaskNote(elt);
}
public RtmTaskSeries tasks_setLocation(String timelineId, String listId, String taskSeriesId, String taskId, String locationId)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.tasks.setLocation"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("location_id", locationId),
new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey()));
RtmTaskList rtmTaskList = new RtmTaskList(response);
return findTask(taskSeriesId, taskId, rtmTaskList);
}
public RtmTaskSeries tasks_setURL(String timelineId, String listId, String taskSeriesId, String taskId, String url)
throws ServiceException
{
Element response = invoker.invoke(new Param("method", "rtm.tasks.setURL"), new Param("timeline", timelineId), new Param("list_id", listId),
new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("url", url), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
RtmTaskList rtmTaskList = new RtmTaskList(response);
return findTask(taskSeriesId, taskId, rtmTaskList);
}
public void test_echo()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void test_login()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void time_convert()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void time_parse()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public String timelines_create()
throws ServiceException
{
return new RtmTimeline(invoker.invoke(new Param("method", "rtm.timelines.create"), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()))).getId();
}
public void timezones_getList()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public void transactions_undo()
{
throw new UnsupportedOperationException("Not supported yet.");
}
public List<RtmLocation> locations_getList()
throws ServiceException
{
Element result = invoker.invoke(new Param("method", "rtm.locations.getList"), new Param("auth_token", currentAuthToken),
new Param("api_key", applicationInfo.getApiKey()));
List<RtmLocation> locations = new ArrayList<RtmLocation>();
for (Element child : RtmData.children(result, "location"))
{
locations.add(new RtmLocation(child));
}
return locations;
}
}

@ -1,53 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api;
/**
* Introduced in order to get rid of the {@link RuntimeException}, and have only one time of regular exception to cope with, from the API end-user
* point of view.
*
* @author Edouard Mercier
* @since 2008.04.23
*/
public class ServiceInternalException
extends ServiceException
{
private static final long serialVersionUID = -423838945284984432L;
private final Exception enclosedException;
public ServiceInternalException(String message)
{
this(message, null);
}
public ServiceInternalException(String message, Exception exception)
{
super(-1, message);
this.enclosedException = exception;
}
public Exception getEnclosedException()
{
return enclosedException;
}
}

@ -1,66 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class RtmAuth extends RtmData {
public enum Perms {
read, write, delete
}
private final String token;
private final Perms perms;
private final RtmUser user;
public RtmAuth(String token, Perms perms, RtmUser user) {
this.token = token;
this.perms = perms;
this.user = user;
}
public RtmAuth(Element elt) {
if (!elt.getNodeName().equals("auth")) { throw new IllegalArgumentException("Element " + elt.getNodeName() + " does not represent an Auth object."); }
this.token = text(child(elt, "token"));
this.perms = Enum.valueOf(Perms.class, text(child(elt, "perms")));
this.user = new RtmUser(child(elt, "user"));
}
public String getToken() {
return token;
}
public Perms getPerms() {
return perms;
}
public RtmUser getUser() {
return user;
}
}

@ -1,120 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public abstract class RtmData
{
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
public RtmData() {
//
}
/**
* The method is not optimized at most, but circumvents a bug in Android runtime.
*/
public static Element child(Element elt, String nodeName)
{
NodeList childNodes = elt.getChildNodes();
for (int index = 0; index < childNodes.getLength(); index++)
{
Node child = childNodes.item(index);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(nodeName))
{
return (Element) child;
}
}
return null;
}
/**
* The method is not optimized at most, but circumvents a bug in Android runtime.
*/
public static List<Element> children(Element elt, String nodeName)
{
List<Element> result = new ArrayList<Element>();
NodeList childNodes = elt.getChildNodes();
for (int index = 0; index < childNodes.getLength(); index++)
{
Node child = childNodes.item(index);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(nodeName))
{
result.add((Element) child);
}
}
return result;
}
protected String text(Element elt)
{
StringBuilder result = new StringBuilder();
Node child = elt.getFirstChild();
while (child != null)
{
switch (child.getNodeType())
{
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
result.append(child.getNodeValue());
break;
default:
break;
}
child = child.getNextSibling();
}
return result.toString();
}
public synchronized static Date parseDate(String s)
{
try
{
Date d = DATE_FORMAT.parse(s);
return new Date(d.getTime() + TimeZone.getDefault().getOffset(d.getTime()));
}
catch (ParseException e)
{
throw new RuntimeException(e);
}
}
public synchronized static String formatDate(Date d)
{
return DATE_FORMAT.format(new Date(d.getTime() - TimeZone.getDefault().getOffset(d.getTime()))) + "Z";
}
}

@ -1,44 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 21, 2007
*/
public class RtmFrob extends RtmData {
private final String value;
public RtmFrob(String value) {
this.value = value;
}
public RtmFrob(Element elt) {
this.value = text(elt);
}
public String getValue() {
return value;
}
}

@ -1,72 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
@SuppressWarnings("nls")
public class RtmList extends RtmData {
private final String id;
private final boolean smart;
private final boolean archived;
private final int position;
private final String name;
public RtmList(String id, String name, boolean smart, boolean archived, int position) {
this.id = id;
this.name = name;
this.smart = smart;
this.archived = archived;
this.position = position;
}
public RtmList(Element elt) {
id = elt.getAttribute("id");
name = elt.getAttribute("name");
smart = elt.getAttribute("smart").equals("1");
archived = elt.getAttribute("archived").equals("1");
position = Integer.parseInt(elt.getAttribute("position"));
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public boolean isSmart() {
return smart;
}
public boolean isArchived() {
return archived;
}
public int getPosition() {
return position;
}
public boolean isInbox() {
return position == -1;
}
}

@ -1,52 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Element;
@SuppressWarnings("nls")
public class RtmLists extends RtmData {
private final Map<String, RtmList> lists;
public RtmLists() {
this.lists = new HashMap<String, RtmList>();
}
public RtmLists(Element elt) {
this.lists = new HashMap<String, RtmList>();
for (Element listElt : children(elt, "list")) {
RtmList list = new RtmList(listElt);
lists.put(list.getId(), list);
}
}
public RtmList getList(String id) {
return lists.get(id);
}
public Map<String, RtmList> getLists() {
return Collections.unmodifiableMap(lists);
}
}

@ -1,60 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
/**
* Represents a location.
*
* @author Edouard Mercier
* @since 2008.05.22
*/
@SuppressWarnings("nls")
public class RtmLocation
extends RtmData
{
public final String id;
public final String name;
public final float longitude;
public final float latitude;
public final String address;
public final boolean viewable;
public int zoom;
public RtmLocation(Element element)
{
id = element.getAttribute("id");
name = element.getAttribute("name");
longitude = Float.parseFloat(element.getAttribute("longitude"));
latitude = Float.parseFloat(element.getAttribute("latitude"));
address = element.getAttribute("address");
zoom = Integer.parseInt(element.getAttribute("zoom"));
viewable = element.getAttribute("viewable").equals("1") ? true : false;
}
}

@ -1,199 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.Date;
import org.w3c.dom.Element;
import android.util.Log;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class RtmTask
extends RtmData
{
private static final String TAG = "rtm-task";
private final String id;
private final Date due;
private final boolean hasDueTime;
private final Date added;
private final Date completed;
private final Date deleted;
private final Priority priority;
private final int postponed;
private final String estimate;
public enum Priority
{
High, Medium, Low, None;
public static Priority values(Integer value) {
value = Math.min(values().length - 1, value);
return values()[value];
}
}
public static String convertPriority(Priority priority)
{
switch (priority)
{
case None:
return new String(new char[] { 'n' });
case Low:
return new String(new char[] { '3' });
case Medium:
return new String(new char[] { '2' });
case High:
return new String(new char[] { '1' });
default:
Log.e(TAG, "Unrecognized RTM task priority: '" + priority + "'");
return new String(new char[] { 'n' });
}
}
public RtmTask(String id, Date due, boolean hasDueTime, Date added, Date completed, Date deleted, Priority priority, int postponed, String estimate)
{
this.id = id;
this.due = due;
this.hasDueTime = hasDueTime;
this.added = added;
this.completed = completed;
this.deleted = deleted;
this.priority = priority;
this.postponed = postponed;
this.estimate = estimate;
}
public RtmTask(Element elt)
{
id = elt.getAttribute("id");
String dueStr = elt.getAttribute("due");
due = (dueStr == null || dueStr.length() == 0) ? null : parseDate(dueStr);
hasDueTime = Integer.parseInt(elt.getAttribute("has_due_time")) != 0;
String addedStr = elt.getAttribute("added");
added = (addedStr == null || addedStr.length() == 0) ? null : parseDate(addedStr);
String completedStr = elt.getAttribute("completed");
completed = (completedStr == null || completedStr.length() == 0) ? null : parseDate(completedStr);
String deletedStr = elt.getAttribute("deleted");
deleted = (deletedStr == null || deletedStr.length() == 0) ? null : parseDate(deletedStr);
String priorityStr = elt.getAttribute("priority");
if (priorityStr.length() > 0)
{
switch (priorityStr.charAt(0))
{
case 'N':
case 'n':
priority = Priority.None;
break;
case '3':
priority = Priority.Low;
break;
case '2':
priority = Priority.Medium;
break;
case '1':
priority = Priority.High;
break;
default:
System.err.println("Unrecognized RTM task priority: '" + priorityStr + "'");
priority = Priority.Medium;
}
}
else
{
priority = Priority.None;
}
if (elt.hasAttribute("postponed") == true && elt.getAttribute("postponed").length() > 0)
{
postponed = Integer.parseInt(elt.getAttribute("postponed"));
}
else
{
postponed = 0;
}
estimate = elt.getAttribute("estimate");
}
public String getId()
{
return id;
}
public Date getDue()
{
return due;
}
public boolean getHasDueTime()
{
return hasDueTime;
}
public Date getAdded()
{
return added;
}
public Date getCompleted()
{
return completed;
}
public Date getDeleted()
{
return deleted;
}
public Priority getPriority()
{
return priority;
}
public int getPostponed()
{
return postponed;
}
public String getEstimate()
{
return estimate;
}
@Override
public String toString()
{
return "Task<" + id + ">";
}
}

@ -1,61 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 22, 2007
*/
@SuppressWarnings("nls")
public class RtmTaskList extends RtmData {
private final String id;
private final List<RtmTaskSeries> series;
public RtmTaskList(String id) {
this.id = id;
this.series = new ArrayList<RtmTaskSeries>();
}
public RtmTaskList(Element elt) {
id = elt.getAttribute("id");
series = new ArrayList<RtmTaskSeries>();
for (Element seriesElt : children(elt, "taskseries")) {
series.add(new RtmTaskSeries(this, seriesElt));
}
if (id == null || id.length() == 0) { throw new RuntimeException("No id found in task list."); }
}
public String getId() {
return id;
}
public List<RtmTaskSeries> getSeries() {
return Collections.unmodifiableList(series);
}
}

@ -1,106 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.Date;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Text;
import android.util.Log;
/**
* Represents a single task note.
*
* @author Edouard Mercier
* @since 2008.04.22
*/
@SuppressWarnings("nls")
public class RtmTaskNote
extends RtmData
{
private final String id;
private final Date created;
private final Date modified;
private final String title;
private String text;
public RtmTaskNote(Element element)
{
id = element.getAttribute("id");
created = parseDate(element.getAttribute("created"));
modified = parseDate(element.getAttribute("modified"));
title = element.getAttribute("title");
// The note text itself might be split across multiple children of the
// note element, so get all of the children.
for (int i=0; i < element.getChildNodes().getLength(); i++) {
Object innerNote = element.getChildNodes().item(i);
if(innerNote instanceof EntityReference) // this node is empty
continue;
if(!(innerNote instanceof Text)) {
Log.w("rtm-note", "Expected text type, got " + innerNote.getClass());
continue;
}
Text innerText = (Text) innerNote;
if (text == null)
text = innerText.getData();
else
text = text.concat(innerText.getData());
}
}
public String getId()
{
return id;
}
public Date getCreated()
{
return created;
}
public Date getModified()
{
return modified;
}
public String getTitle()
{
return title;
}
public String getText()
{
return text;
}
}

@ -1,54 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
/**
* Represents the notes of a task.
*
* @author Edouard Mercier
* @since 2008.04.22
*/
@SuppressWarnings("nls")
public class RtmTaskNotes
extends RtmData
{
private List<RtmTaskNote> notes;
public RtmTaskNotes(Element element)
{
notes = new ArrayList<RtmTaskNote>();
for (Element child : children(element, "note"))
{
notes.add(new RtmTaskNote(child));
}
}
public List<RtmTaskNote> getNotes()
{
return notes;
}
}

@ -1,170 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 22, 2007
*/
@SuppressWarnings("nls")
public class RtmTaskSeries extends RtmData {
private final RtmTaskList list;
private final String id;
private final Date created;
private final Date modified;
private final String name;
private final String source;
private final RtmTask task;
private final LinkedList<String> tags;
private final RtmTaskNotes notes;
private final String locationId;
private final String url;
private final boolean hasRecurrence;
public RtmTaskSeries(RtmTaskList list, String id, Date created, Date modified, String name,
String source, RtmTask task) {
this.list = list;
this.id = id;
this.created = created;
this.modified = modified;
this.name = name;
this.source = source;
this.task = task;
this.locationId = null;
notes = null;
url = null;
tags = null;
hasRecurrence = false;
}
public RtmTaskSeries(RtmTaskList list, Element elt) {
this.list = list;
id = elt.getAttribute("id");
created = parseDate(elt.getAttribute("created"));
modified = parseDate(elt.getAttribute("modified"));
name = elt.getAttribute("name");
source = elt.getAttribute("source");
List<Element> children = children(elt, "task");
if (children.size() > 1) {
// assume it's a repeating task - pick the child with nearest
// but not expired due date
RtmTask selectedTask = new RtmTask(children.get(0));
for(Element element : children) {
RtmTask childTask = new RtmTask(element);
if(childTask.getCompleted() == null) {
selectedTask = childTask;
break;
}
}
task = selectedTask;
} else {
task = new RtmTask(child(elt, "task"));
}
notes = new RtmTaskNotes(child(elt, "notes"));
locationId = elt.getAttribute("location_id");
url = elt.getAttribute("url");
hasRecurrence = children(elt, "rrule").size() > 0;
Element elementTags = child(elt, "tags");
if (elementTags.getChildNodes().getLength() > 0) {
List<Element> elementTagList = children(elementTags, "tag");
tags = new LinkedList<String>();
for (Element elementTag : elementTagList) {
String tag = text(elementTag);
if (tag != null)
tags.add(tag);
}
} else {
tags = null;
}
}
public String getId() {
return id;
}
public Date getCreated() {
return created;
}
public Date getModified() {
return modified;
}
public String getName() {
return name;
}
public String getSource() {
return source;
}
public RtmTask getTask() {
return task;
}
public LinkedList<String> getTags() {
return tags;
}
public RtmTaskNotes getNotes() {
return notes;
}
public String getLocationId() {
return locationId;
}
@Override
public String toString() {
return "TaskSeries<" + id + "," + name + ">";
}
public String getURL() {
return url;
}
public boolean hasRecurrence() {
return hasRecurrence;
}
public RtmTaskList getList() {
return list;
}
}

@ -1,51 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class RtmTasks extends RtmData {
private final List<RtmTaskList> lists;
public RtmTasks() {
this.lists = new ArrayList<RtmTaskList>();
}
public RtmTasks(Element elt) {
this.lists = new ArrayList<RtmTaskList>();
for (Element listElt : children(elt, "list")) {
lists.add(new RtmTaskList(listElt));
}
}
public List<RtmTaskList> getLists() {
return Collections.unmodifiableList(lists);
}
}

@ -1,39 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
public class RtmTimeline extends RtmData {
private final String id;
public RtmTimeline(String id) {
this.id = id;
}
public RtmTimeline(Element elt) {
id = text(elt);
}
public String getId() {
return id;
}
}

@ -1,63 +0,0 @@
/*
* Copyright 2007, MetaDimensional Technologies Inc.
*
*
* This file is part of the RememberTheMilk Java API.
*
* The RememberTheMilk Java API is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.weloveastrid.rmilk.api.data;
import org.w3c.dom.Element;
/**
*
* @author Will Ross Jun 21, 2007
*/
@SuppressWarnings("nls")
public class RtmUser extends RtmData {
private final String id;
private final String username;
private final String fullname;
public RtmUser(String id, String username, String fullname) {
this.id = id;
this.username = username;
this.fullname = fullname;
}
public RtmUser(Element elt) {
if (!elt.getNodeName().equals("user")) { throw new IllegalArgumentException("Element " + elt.getNodeName() + " does not represent a User object."); }
this.id = elt.getAttribute("id");
this.username = elt.getAttribute("username");
this.fullname = elt.getAttribute("fullname");
}
public String getId() {
return id;
}
public String getUsername() {
return username;
}
public String getFullname() {
return fullname;
}
}

@ -1,42 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.data;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.data.StoreObject;
/**
* Data Model which represents a list in RTM
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkListFields {
/** type*/
public static final String TYPE = "rmilk-list"; //$NON-NLS-1$
// --- properties
/** Remote ID */
public static final LongProperty REMOTE_ID = new LongProperty(
StoreObject.TABLE, StoreObject.ITEM.name);
/** Name */
public static final StringProperty NAME = StoreObject.VALUE1;
/** Position */
public static final IntegerProperty POSITION = new IntegerProperty(
StoreObject.TABLE, StoreObject.VALUE2.name);
/** Archived (0 or 1) */
public static final IntegerProperty ARCHIVED = new IntegerProperty(
StoreObject.TABLE, StoreObject.VALUE3.name);
}

@ -1,127 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.data;
import java.util.Map;
import org.weloveastrid.rmilk.MilkDependencyInjector;
import org.weloveastrid.rmilk.api.data.RtmList;
import org.weloveastrid.rmilk.api.data.RtmLists;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.StoreObjectApiDao;
import com.todoroo.astrid.data.StoreObjectApiDao.StoreObjectCriteria;
/**
* Service for reading and writing Milk lists
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkListService {
static {
MilkDependencyInjector.initialize();
}
private StoreObject[] lists = null;
private final StoreObjectApiDao storeObjectDao;
public MilkListService() {
storeObjectDao = new StoreObjectApiDao(ContextManager.getContext());
}
/**
* Reads lists
*/
private void readLists() {
if(lists != null)
return;
TodorooCursor<StoreObject> cursor = storeObjectDao.query(Query.select(StoreObject.PROPERTIES).
where(StoreObjectCriteria.byType(MilkListFields.TYPE)).orderBy(Order.asc(MilkListFields.POSITION)));
try {
lists = new StoreObject[cursor.getCount()];
for(int i = 0; i < lists.length; i++) {
cursor.moveToNext();
StoreObject list = new StoreObject(cursor);
lists[i] = list;
}
} finally {
cursor.close();
}
}
/**
* @return a list of lists
*/
public StoreObject[] getLists() {
readLists();
return lists;
}
/**
* Clears current cache of RTM lists and loads data from RTM into
* database. Returns the inbox list.
*
* @param remoteLists
* @return list with the name "inbox"
*/
public StoreObject setLists(RtmLists remoteLists) {
readLists();
StoreObject inbox = null;
for(Map.Entry<String, RtmList> remote : remoteLists.getLists().entrySet()) {
if(remote.getValue().isSmart() || "All Tasks".equals(remote.getValue().getName())) //$NON-NLS-1$
continue;
long id = Long.parseLong(remote.getValue().getId());
StoreObject local = null;
for(StoreObject list : lists) {
if(list.getValue(MilkListFields.REMOTE_ID).equals(id)) {
local = list;
break;
}
}
if(local == null)
local = new StoreObject();
local.setValue(StoreObject.TYPE, MilkListFields.TYPE);
local.setValue(MilkListFields.REMOTE_ID, id);
local.setValue(MilkListFields.NAME, remote.getValue().getName());
local.setValue(MilkListFields.POSITION, remote.getValue().getPosition());
local.setValue(MilkListFields.ARCHIVED, remote.getValue().isArchived() ? 1 : 0);
storeObjectDao.save(local);
if(remote.getValue().isInbox()) {
inbox = local;
}
}
// clear list cache
lists = null;
return inbox;
}
/**
* Get list name by list id
* @param listId
* @return null if no list by this id exists, otherwise list name
*/
public String getListName(long listId) {
readLists();
for(StoreObject list : lists)
if(list.getValue(MilkListFields.REMOTE_ID).equals(listId))
return list.getValue(MilkListFields.NAME);
return null;
}
}

@ -1,79 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.data;
import java.util.ArrayList;
import org.weloveastrid.rmilk.MilkDependencyInjector;
import org.weloveastrid.rmilk.MilkUtilities;
import org.weloveastrid.rmilk.sync.MilkTaskContainer;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.notes.NoteMetadata;
import com.todoroo.astrid.sync.SyncProviderUtilities;
import com.todoroo.astrid.utility.SyncMetadataService;
public final class MilkMetadataService extends SyncMetadataService<MilkTaskContainer>{
static {
MilkDependencyInjector.initialize();
}
public MilkMetadataService() {
super(ContextManager.getContext());
}
@Override
public MilkTaskContainer createContainerFromLocalTask(Task task,
ArrayList<Metadata> metadata) {
return new MilkTaskContainer(task, metadata);
}
@Override
public Criterion getLocalMatchCriteria(MilkTaskContainer remoteTask) {
return Criterion.and(MilkTaskFields.TASK_SERIES_ID.eq(remoteTask.taskSeriesId),
MilkTaskFields.TASK_ID.eq(remoteTask.taskId));
}
@Override
public Criterion getMetadataCriteria() {
return Criterion.or(MetadataCriteria.withKey(TAG_KEY),
MetadataCriteria.withKey(MilkTaskFields.METADATA_KEY),
Criterion.and(MetadataCriteria.withKey(NoteMetadata.METADATA_KEY),
NoteMetadata.EXT_PROVIDER.eq(MilkNoteHelper.PROVIDER)));
}
@Override
public String getMetadataKey() {
return MilkTaskFields.METADATA_KEY;
}
@Override
public SyncProviderUtilities getUtilities() {
return MilkUtilities.INSTANCE;
}
/**
* Reads task notes out of a task
*/
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(MetadataCriteria.byTaskAndwithKey(taskId, NoteMetadata.METADATA_KEY)));
return cursor;
}
@Override
public Criterion getMetadataWithRemoteId() {
return MilkTaskFields.TASK_ID.gt(0);
}
}

@ -1,82 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.data;
import org.weloveastrid.rmilk.api.data.RtmTaskNote;
import android.text.TextUtils;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.notes.NoteMetadata;
/**
* Metadata entries for a Remember the Milk note. The first RMilk note becomes
* Astrid's note field, subsequent notes are stored in metadata in this
* format.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkNoteHelper {
/** metadata key */
public static final String PROVIDER = "rmilk"; //$NON-NLS-1$
public static Metadata create(RtmTaskNote note) {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, NoteMetadata.METADATA_KEY);
metadata.setValue(NoteMetadata.EXT_ID, note.getId());
metadata.setValue(NoteMetadata.EXT_PROVIDER, PROVIDER);
metadata.setValue(NoteMetadata.TITLE, note.getTitle());
metadata.setValue(NoteMetadata.BODY, note.getText());
metadata.setValue(Metadata.CREATION_DATE, note.getCreated().getTime());
return metadata;
}
/**
* Turn a note's title and text into a string
* @param title
* @param text
* @return
*/
@SuppressWarnings("nls")
public static String toNoteField(RtmTaskNote note) {
String title = note.getTitle();
String text = note.getText();
if(TextUtils.isEmpty(text) && TextUtils.isEmpty(title))
return "";
StringBuilder result = new StringBuilder();
if(!TextUtils.isEmpty(title)) {
result.append(title);
if(!TextUtils.isEmpty(text))
result.append("\n");
}
if(!TextUtils.isEmpty(text)) {
result.append(text);
}
return result.toString();
}
/**
* Turn a string into a note's title and text
* @param value
* @return
*/
@SuppressWarnings("nls")
public static String[] fromNoteField(String value) {
String[] result = new String[2];
int firstLineBreak = value.indexOf('\n');
if(firstLineBreak > -1 && firstLineBreak + 1 < value.length()) {
result[0] = value.substring(0, firstLineBreak);
result[1] = value.substring(firstLineBreak + 1, value.length());
} else {
result[0] = "";
result[1] = value;
}
return result;
}
}

@ -1,56 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.data;
import org.weloveastrid.rmilk.sync.MilkTaskContainer;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.data.Metadata;
/**
* Metadata entries for a Remember The Milk Task
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkTaskFields {
/** metadata key */
public static final String METADATA_KEY = "rmilk"; //$NON-NLS-1$
/** {@link MilkListFields} id */
public static final LongProperty LIST_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
/** RTM Task Series Id */
public static final LongProperty TASK_SERIES_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE2.name);
/** RTM Task Id */
public static final LongProperty TASK_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE3.name);
/** Whether task repeats in RTM (1 or 0) */
public static final IntegerProperty REPEATING = new IntegerProperty(Metadata.TABLE,
Metadata.VALUE4.name);
/**
* Creates a piece of metadata from a remote task
* @param rtmTaskSeries
* @return
*/
public static Metadata create(MilkTaskContainer container) {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, METADATA_KEY);
metadata.setValue(MilkTaskFields.LIST_ID, container.listId);
metadata.setValue(MilkTaskFields.TASK_SERIES_ID, container.taskSeriesId);
metadata.setValue(MilkTaskFields.TASK_ID, container.taskId);
metadata.setValue(MilkTaskFields.REPEATING, container.repeating ? 1 : 0);
return metadata;
}
}

@ -1,590 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.sync;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import org.weloveastrid.rmilk.MilkBackgroundService;
import org.weloveastrid.rmilk.MilkDependencyInjector;
import org.weloveastrid.rmilk.MilkLoginActivity;
import org.weloveastrid.rmilk.MilkLoginActivity.SyncLoginCallback;
import org.weloveastrid.rmilk.MilkPreferences;
import org.weloveastrid.rmilk.MilkUtilities;
import org.weloveastrid.rmilk.api.ApplicationInfo;
import org.weloveastrid.rmilk.api.ServiceImpl;
import org.weloveastrid.rmilk.api.ServiceInternalException;
import org.weloveastrid.rmilk.api.data.RtmAuth.Perms;
import org.weloveastrid.rmilk.api.data.RtmList;
import org.weloveastrid.rmilk.api.data.RtmLists;
import org.weloveastrid.rmilk.api.data.RtmTask;
import org.weloveastrid.rmilk.api.data.RtmTask.Priority;
import org.weloveastrid.rmilk.api.data.RtmTaskList;
import org.weloveastrid.rmilk.api.data.RtmTaskNote;
import org.weloveastrid.rmilk.api.data.RtmTaskSeries;
import org.weloveastrid.rmilk.api.data.RtmTasks;
import org.weloveastrid.rmilk.data.MilkListService;
import org.weloveastrid.rmilk.data.MilkMetadataService;
import org.weloveastrid.rmilk.data.MilkNoteHelper;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.text.TextUtils;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.sync.SyncProvider;
import com.todoroo.astrid.sync.SyncProviderUtilities;
public class MilkSyncProvider extends SyncProvider<MilkTaskContainer> {
private ServiceImpl rtmService = null;
private String timeline = null;
@Autowired private MilkMetadataService milkMetadataService;
@Autowired private MilkListService milkListService;
static {
MilkDependencyInjector.initialize();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------- public methods
// ----------------------------------------------------------------------
/**
* Sign out of RTM, deleting all synchronization metadata
*/
public void signOut(Context context) {
ContextManager.setContext(context);
MilkUtilities.INSTANCE.setToken(null);
MilkUtilities.INSTANCE.clearLastSyncDate();
DependencyInjectionService.getInstance().inject(this);
milkMetadataService.clearMetadata();
}
@Override
protected SyncProviderUtilities getUtilities() {
return MilkUtilities.INSTANCE;
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ initiating sync
// ----------------------------------------------------------------------
/**
* set up service
*/
@SuppressWarnings("nls")
private void initializeService(String authToken) throws ServiceInternalException {
String appName = null;
String z = stripslashes(0,"q9883o3384n21snq17501qn38oo1r689", "b");
String v = stripslashes(16,"19o2n020345219os","a");
if(authToken == null)
rtmService = new ServiceImpl(new ApplicationInfo(
z, v, appName));
else
rtmService = new ServiceImpl(new ApplicationInfo(
z, v, appName, authToken));
}
/**
* initiate sync in background
*/
@Override
@SuppressWarnings("nls")
protected void initiateBackground() {
DependencyInjectionService.getInstance().inject(this);
try {
String authToken = MilkUtilities.INSTANCE.getToken();
// check if we have a token & it works
if(authToken != null) {
initializeService(authToken);
if(!rtmService.isServiceAuthorized()) // re-do login
authToken = null;
}
if(authToken == null) {
// try completing the authorization if it was partial
if(rtmService != null) {
try {
String token = rtmService.completeAuthorization();
MilkUtilities.INSTANCE.setToken(token);
performSync();
return;
} catch (Exception e) {
// didn't work. do the process again.
}
}
// can't do anything, user not logged in
} else {
performSync();
}
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("rtm-authenticate", e, true);
} finally {
MilkUtilities.INSTANCE.stopOngoing();
}
}
/**
* If user isn't already signed in, show sign in dialog. Else perform sync.
*/
@SuppressWarnings("nls")
@Override
protected void initiateManual(final Activity activity) {
final Resources r = activity.getResources();
String authToken = MilkUtilities.INSTANCE.getToken();
MilkUtilities.INSTANCE.stopOngoing();
// check if we have a token & it works
if(authToken == null) {
// open up a dialog and have the user go to browser
final String url;
try {
initializeService(null);
url = rtmService.beginAuthorization(Perms.delete);
} catch (Exception e) {
handleException("rmilk-auth", e, true);
return;
}
Intent intent = new Intent(activity, MilkLoginActivity.class);
MilkLoginActivity.setCallback(new SyncLoginCallback() {
public String verifyLogin(final Handler syncLoginHandler) {
if(rtmService == null) {
return null;
}
try {
String token = rtmService.completeAuthorization();
MilkUtilities.INSTANCE.setToken(token);
synchronize(activity);
return null;
} catch (Exception e) {
// didn't work
handleException("rtm-verify-login", e, false);
rtmService = null;
if(e instanceof ServiceInternalException)
e = ((ServiceInternalException)e).getEnclosedException();
return r.getString(R.string.rmilk_MLA_error, e.getMessage());
}
}
});
intent.putExtra(MilkLoginActivity.URL_TOKEN, url);
activity.startActivityForResult(intent, 0);
} else {
activity.startService(new Intent(MilkBackgroundService.SYNC_ACTION, null,
activity, MilkBackgroundService.class));
activity.finish();
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------- synchronization!
// ----------------------------------------------------------------------
protected void performSync() {
try {
// get RTM timeline
timeline = rtmService.timelines_create();
// load RTM lists
RtmLists lists = rtmService.lists_getList();
milkListService.setLists(lists);
// read all tasks
ArrayList<MilkTaskContainer> remoteChanges = new ArrayList<MilkTaskContainer>();
Date lastSyncDate = new Date(MilkUtilities.INSTANCE.getLastSyncDate());
boolean shouldSyncIndividualLists = false;
String filter = null;
if(lastSyncDate.getTime() == 0)
filter = "status:incomplete"; //$NON-NLS-1$ // 1st time sync: get unfinished tasks
// try the quick synchronization
try {
Thread.sleep(1000); // throttle
RtmTasks tasks = rtmService.tasks_getList(null, filter, lastSyncDate);
addTasksToList(tasks, remoteChanges);
} catch (Exception e) {
handleException("rtm-quick-sync", e, false); //$NON-NLS-1$
remoteChanges.clear();
shouldSyncIndividualLists = true;
}
if(shouldSyncIndividualLists) {
for(RtmList list : lists.getLists().values()) {
if(list.isSmart())
continue;
try {
Thread.sleep(1500);
RtmTasks tasks = rtmService.tasks_getList(list.getId(),
filter, lastSyncDate);
addTasksToList(tasks, remoteChanges);
} catch (Exception e) {
handleException("rtm-indiv-sync", e, true); //$NON-NLS-1$
continue;
}
}
}
SyncData<MilkTaskContainer> syncData = populateSyncData(remoteChanges);
try {
synchronizeTasks(syncData);
} finally {
syncData.localCreated.close();
syncData.localUpdated.close();
}
MilkUtilities.INSTANCE.recordSuccessfulSync();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("rtm-sync", e, true); //$NON-NLS-1$
}
}
// ----------------------------------------------------------------------
// ------------------------------------------------------------ sync data
// ----------------------------------------------------------------------
// all synchronized properties
private static final Property<?>[] PROPERTIES = new Property<?>[] {
Task.ID,
Task.TITLE,
Task.IMPORTANCE,
Task.DUE_DATE,
Task.CREATION_DATE,
Task.COMPLETION_DATE,
Task.DELETION_DATE,
Task.NOTES,
};
/**
* Populate SyncData data structure
*/
private SyncData<MilkTaskContainer> populateSyncData(ArrayList<MilkTaskContainer> remoteTasks) {
// fetch locally created tasks
TodorooCursor<Task> localCreated = milkMetadataService.getLocallyCreated(PROPERTIES);
// fetch locally updated tasks
TodorooCursor<Task> localUpdated = milkMetadataService.getLocallyUpdated(PROPERTIES);
return new SyncData<MilkTaskContainer>(remoteTasks, localCreated, localUpdated);
}
/**
* Add the tasks read from RTM to the given list
*/
private void addTasksToList(RtmTasks tasks, ArrayList<MilkTaskContainer> list) {
for (RtmTaskList taskList : tasks.getLists()) {
for (RtmTaskSeries taskSeries : taskList.getSeries()) {
MilkTaskContainer remote = parseRemoteTask(taskSeries);
// update reminder flags for incoming remote tasks to prevent annoying
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
milkMetadataService.findLocalMatch(remote);
list.add(remote);
}
}
}
// ----------------------------------------------------------------------
// ------------------------------------------------- create / push / pull
// ----------------------------------------------------------------------
@Override
protected MilkTaskContainer create(MilkTaskContainer task) throws IOException {
String listId = null;
if(task.listId > 0)
listId = Long.toString(task.listId);
RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId,
task.task.getValue(Task.TITLE));
MilkTaskContainer newRemoteTask = parseRemoteTask(rtmTask);
transferIdentifiers(newRemoteTask, task);
push(task, newRemoteTask);
return newRemoteTask;
}
/**
* Determine whether this task's property should be transmitted
* @param task task to consider
* @param property property to consider
* @param remoteTask remote task proxy
* @return
*/
private boolean shouldTransmit(MilkTaskContainer task, Property<?> property, MilkTaskContainer remoteTask) {
if(!task.task.containsValue(property))
return false;
if(remoteTask == null)
return true;
if(!remoteTask.task.containsValue(property))
return true;
// special cases - match if they're zero or nonzero
if(property == Task.COMPLETION_DATE ||
property == Task.DELETION_DATE)
return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0,
(Long)remoteTask.task.getValue(property) == 0);
return !AndroidUtilities.equals(task.task.getValue(property),
remoteTask.task.getValue(property));
}
/**
* Send changes for the given Task across the wire. If a remoteTask is
* supplied, we attempt to intelligently only transmit the values that
* have changed.
*/
@Override
protected MilkTaskContainer push(MilkTaskContainer local, MilkTaskContainer remote) throws IOException {
boolean remerge = false;
// fetch remote task for comparison
if(remote == null)
remote = pull(local);
String listId = Long.toString(local.listId);
String taskSeriesId = Long.toString(local.taskSeriesId);
String taskId = Long.toString(local.taskId);
if(remote != null && !AndroidUtilities.equals(local.listId, remote.listId))
rtmService.tasks_moveTo(timeline, Long.toString(remote.listId),
listId, taskSeriesId, taskId);
// either delete or re-create if necessary
if(shouldTransmit(local, Task.DELETION_DATE, remote)) {
if(local.task.getValue(Task.DELETION_DATE) > 0)
rtmService.tasks_delete(timeline, listId, taskSeriesId, taskId);
else if(remote == null) {
RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId,
local.task.getValue(Task.TITLE));
remote = parseRemoteTask(rtmTask);
transferIdentifiers(remote, local);
}
}
if(shouldTransmit(local, Task.TITLE, remote))
rtmService.tasks_setName(timeline, listId, taskSeriesId,
taskId, local.task.getValue(Task.TITLE));
if(shouldTransmit(local, Task.IMPORTANCE, remote))
rtmService.tasks_setPriority(timeline, listId, taskSeriesId,
taskId, Priority.values(local.task.getValue(Task.IMPORTANCE)));
if(shouldTransmit(local, Task.DUE_DATE, remote))
rtmService.tasks_setDueDate(timeline, listId, taskSeriesId,
taskId, DateUtilities.unixtimeToDate(local.task.getValue(Task.DUE_DATE)),
local.task.hasDueTime());
if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) {
if(local.task.getValue(Task.COMPLETION_DATE) == 0)
rtmService.tasks_uncomplete(timeline, listId, taskSeriesId,
taskId);
else {
rtmService.tasks_complete(timeline, listId, taskSeriesId,
taskId);
// if repeating, pull and merge
if(local.repeating)
remerge = true;
}
}
// tags
HashSet<String> localTags = new HashSet<String>();
HashSet<String> remoteTags = new HashSet<String>();
for(Metadata item : local.metadata)
if(MilkMetadataService.TAG_KEY.equals(item.getValue(Metadata.KEY)))
localTags.add(item.getValue(Metadata.VALUE1));
if(remote != null && remote.metadata != null) {
for(Metadata item : remote.metadata)
if(MilkMetadataService.TAG_KEY.equals(item.getValue(Metadata.KEY)))
remoteTags.add(item.getValue(Metadata.VALUE1));
}
if(!localTags.equals(remoteTags)) {
String[] tags = localTags.toArray(new String[localTags.size()]);
rtmService.tasks_setTags(timeline, listId, taskSeriesId,
taskId, tags);
}
// notes
if(shouldTransmit(local, Task.NOTES, remote)) {
String[] titleAndText = MilkNoteHelper.fromNoteField(local.task.getValue(Task.NOTES));
List<RtmTaskNote> notes = null;
if(remote != null && remote.remote.getNotes() != null)
notes = remote.remote.getNotes().getNotes();
if(notes != null && notes.size() > 0) {
String remoteNoteId = notes.get(0).getId();
rtmService.tasks_notes_edit(timeline, remoteNoteId, titleAndText[0],
titleAndText[1]);
} else {
rtmService.tasks_notes_add(timeline, listId, taskSeriesId,
taskId, titleAndText[0], titleAndText[1]);
}
}
remote = pull(local);
remote.task.setId(local.task.getId());
if(remerge) {
// transform local into remote
local.task = remote.task;
local.listId = remote.listId;
local.taskId = remote.taskId;
local.repeating = remote.repeating;
local.taskSeriesId = remote.taskSeriesId;
}
return remote;
}
/** Create a task container for the given RtmTaskSeries */
private MilkTaskContainer parseRemoteTask(RtmTaskSeries rtmTaskSeries) {
Task task = new Task();
RtmTask rtmTask = rtmTaskSeries.getTask();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
task.setValue(Task.TITLE, rtmTaskSeries.getName());
task.setValue(Task.CREATION_DATE, DateUtilities.dateToUnixtime(rtmTask.getAdded()));
task.setValue(Task.COMPLETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getCompleted()));
task.setValue(Task.DELETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getDeleted()));
if(rtmTask.getDue() != null) {
task.setValue(Task.DUE_DATE,
Task.createDueDate(rtmTask.getHasDueTime() ? Task.URGENCY_SPECIFIC_DAY_TIME :
Task.URGENCY_SPECIFIC_DAY, DateUtilities.dateToUnixtime(rtmTask.getDue())));
} else {
task.setValue(Task.DUE_DATE, 0L);
}
task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal());
if(rtmTaskSeries.getTags() != null) {
for(String tag : rtmTaskSeries.getTags()) {
Metadata tagData = new Metadata();
tagData.setValue(Metadata.KEY, MilkMetadataService.TAG_KEY);
tagData.setValue(Metadata.VALUE1, tag);
metadata.add(tagData);
}
}
task.setValue(Task.NOTES, ""); //$NON-NLS-1$
if(rtmTaskSeries.getNotes() != null && rtmTaskSeries.getNotes().getNotes().size() > 0) {
boolean firstNote = true;
Collections.reverse(rtmTaskSeries.getNotes().getNotes()); // reverse so oldest is first
for(RtmTaskNote note : rtmTaskSeries.getNotes().getNotes()) {
if(firstNote) {
firstNote = false;
task.setValue(Task.NOTES, MilkNoteHelper.toNoteField(note));
} else
metadata.add(MilkNoteHelper.create(note));
}
}
MilkTaskContainer container = new MilkTaskContainer(task, metadata, rtmTaskSeries);
return container;
}
@Override
protected MilkTaskContainer pull(MilkTaskContainer task) throws IOException {
if(task.taskSeriesId == 0)
throw new ServiceInternalException("Tried to read an invalid task"); //$NON-NLS-1$
RtmTaskSeries rtmTask = rtmService.tasks_getTask(Long.toString(task.taskSeriesId),
task.task.getValue(Task.TITLE));
if(rtmTask != null)
return parseRemoteTask(rtmTask);
return null;
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- read / write
// ----------------------------------------------------------------------
@Override
protected MilkTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
return milkMetadataService.readTaskAndMetadata(cursor);
}
@Override
protected void write(MilkTaskContainer task) throws IOException {
milkMetadataService.saveTaskAndMetadata(task);
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- misc helpers
// ----------------------------------------------------------------------
@Override
protected int matchTask(ArrayList<MilkTaskContainer> tasks, MilkTaskContainer target) {
int length = tasks.size();
for(int i = 0; i < length; i++) {
MilkTaskContainer task = tasks.get(i);
if(AndroidUtilities.equals(task.listId, target.listId) &&
AndroidUtilities.equals(task.taskSeriesId, target.taskSeriesId) &&
AndroidUtilities.equals(task.taskId, target.taskId))
return i;
}
return -1;
}
@Override
protected int updateNotification(Context context, Notification notification) {
String notificationTitle = context.getString(R.string.rmilk_notification_title);
Intent intent = new Intent(context, MilkPreferences.class);
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
intent, 0);
notification.setLatestEventInfo(context,
notificationTitle, context.getString(R.string.SyP_progress),
notificationIntent);
return 0;
}
@Override
protected void transferIdentifiers(MilkTaskContainer source,
MilkTaskContainer destination) {
destination.listId = source.listId;
destination.taskSeriesId = source.taskSeriesId;
destination.taskId = source.taskId;
}
// ----------------------------------------------------------------------
// ------------------------------------------------------- helper classes
// ----------------------------------------------------------------------
private static final String stripslashes(int ____,String __,String ___) {
int _=__.charAt(____/92);_=_==115?_-1:_;_=((_>=97)&&(_<=123)?
((_-83)%27+97):_);return TextUtils.htmlEncode(____==31?___:
stripslashes(____+1,__.substring(1),___+((char)_)));
}
}

@ -1,73 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.weloveastrid.rmilk.sync;
import java.util.ArrayList;
import java.util.Iterator;
import org.weloveastrid.rmilk.api.data.RtmTaskSeries;
import org.weloveastrid.rmilk.data.MilkTaskFields;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.sync.SyncContainer;
/**
* RTM Task Container
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MilkTaskContainer extends SyncContainer {
public long listId, taskSeriesId, taskId;
public boolean repeating;
public RtmTaskSeries remote;
public MilkTaskContainer(Task task, ArrayList<Metadata> metadata,
long listId, long taskSeriesId, long taskId, boolean repeating,
RtmTaskSeries remote) {
this.task = task;
this.metadata = metadata;
this.listId = listId;
this.taskSeriesId = taskSeriesId;
this.taskId = taskId;
this.repeating = repeating;
this.remote = remote;
}
public MilkTaskContainer(Task task, ArrayList<Metadata> metadata,
RtmTaskSeries rtmTaskSeries) {
this(task, metadata, Long.parseLong(rtmTaskSeries.getList().getId()),
Long.parseLong(rtmTaskSeries.getId()), Long.parseLong(rtmTaskSeries.getTask().getId()),
rtmTaskSeries.hasRecurrence(), rtmTaskSeries);
}
public MilkTaskContainer(Task task, ArrayList<Metadata> metadata) {
this(task, metadata, 0, 0, 0, false, null);
for(Iterator<Metadata> iterator = metadata.iterator(); iterator.hasNext(); ) {
Metadata item = iterator.next();
if(MilkTaskFields.METADATA_KEY.equals(item.getValue(Metadata.KEY))) {
if(item.containsNonNullValue(MilkTaskFields.LIST_ID))
listId = item.getValue(MilkTaskFields.LIST_ID);
if(item.containsNonNullValue(MilkTaskFields.TASK_SERIES_ID))
taskSeriesId = item.getValue(MilkTaskFields.TASK_SERIES_ID);
if(item.containsNonNullValue(MilkTaskFields.TASK_ID))
taskId = item.getValue(MilkTaskFields.TASK_ID);
if(item.containsNonNullValue(MilkTaskFields.REPEATING))
repeating = item.getValue(MilkTaskFields.REPEATING) == 1;
iterator.remove();
break;
}
}
}
@Override
public void prepareForSaving() {
super.prepareForSaving();
metadata.add(MilkTaskFields.create(this));
}
}

@ -11,9 +11,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import org.weloveastrid.rmilk.MilkPreferences;
import org.weloveastrid.rmilk.MilkUtilities;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
@ -352,10 +349,6 @@ public class EditPreferences extends TodorooPreferenceActivity {
intent.setClassName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name);
if(MilkPreferences.class.getName().equals(resolveInfo.activityInfo.name) &&
!MilkUtilities.INSTANCE.isLoggedIn())
continue;
if (GtasksPreferences.class.getName().equals(resolveInfo.activityInfo.name)
&& AmazonMarketStrategy.isKindleFire())
continue;

@ -9,9 +9,6 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import org.weloveastrid.rmilk.MilkPreferences;
import org.weloveastrid.rmilk.MilkUtilities;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@ -200,11 +197,6 @@ public class SyncActionHelper {
String category = MetadataHelper.resolveActivityCategoryName(
resolveInfo, pm);
if (MilkPreferences.class.getName().equals(
resolveInfo.activityInfo.name)
&& !MilkUtilities.INSTANCE.isLoggedIn())
continue;
if (GtasksPreferences.class.getName().equals(
resolveInfo.activityInfo.name)
&& AmazonMarketStrategy.isKindleFire())

@ -8,7 +8,6 @@ package com.todoroo.astrid.service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import android.content.Context;
import android.content.SharedPreferences;
@ -17,7 +16,6 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import com.google.ical.values.RRule;
@ -192,9 +190,6 @@ public class Astrid2To3UpgradeHelper {
alarmsDatabase.getDao());
alarmsDatabase.close();
// --- upgrade RTM sync mappings
migrateSyncMappingToMetadata();
// --- clean up database
metadataService.cleanup();
@ -432,51 +427,6 @@ public class Astrid2To3UpgradeHelper {
}
}
/**
* Move data from sync table into metadata table.
*/
@SuppressWarnings("nls")
private void migrateSyncMappingToMetadata() {
Context context = ContextManager.getContext();
if(!checkIfDatabaseExists(context, syncTable))
return;
SQLiteDatabase syncDb = new Astrid2UpgradeHelper(context, syncTable,
null, 1).getReadableDatabase();
Cursor cursor = syncDb.rawQuery("SELECT task, remoteId FROM " + syncTable, null);
try {
if(cursor.getCount() == 0)
return;
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "rmilk");
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long task = cursor.getLong(0);
String id = cursor.getString(1);
if(TextUtils.isEmpty(id))
continue;
StringTokenizer strtok = new StringTokenizer(id, "|");
String taskId = strtok.nextToken();
String taskSeriesId = strtok.nextToken();
String listId = strtok.nextToken();
metadata.setValue(Metadata.TASK, task);
metadata.setValue(Metadata.VALUE1, (listId));
metadata.setValue(Metadata.VALUE2, (taskSeriesId));
metadata.setValue(Metadata.VALUE3, (taskId));
metadata.setValue(Metadata.VALUE4, "0"); // not accurate, but not important
metadataDao.createNew(metadata);
metadata.clearValue(Metadata.ID);
}
} finally {
cursor.close();
syncDb.close();
}
}
/**
* Move data from alert table into metadata table.
*/

@ -8,8 +8,6 @@ package com.todoroo.astrid.service;
import java.io.File;
import java.util.List;
import org.weloveastrid.rmilk.MilkUtilities;
import android.Manifest;
import android.app.Activity;
import android.app.AlarmManager;
@ -243,7 +241,6 @@ public class StartupService {
taskService.cleanup();
// if sync ongoing flag was set, clear it
MilkUtilities.INSTANCE.stopOngoing();
gtasksPreferenceService.stopOngoing();
actFmPreferenceService.stopOngoing();
OpencrxCoreUtils.INSTANCE.stopOngoing();

@ -9,8 +9,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import org.weloveastrid.rmilk.data.MilkTaskFields;
import android.content.ContentValues;
import android.text.TextUtils;
@ -200,10 +198,6 @@ public class TaskService {
if(GtasksMetadata.METADATA_KEY.equals(metadata.getValue(Metadata.KEY)))
metadata.setValue(GtasksMetadata.ID, ""); //$NON-NLS-1$
if(MilkTaskFields.METADATA_KEY.equals(metadata.getValue(Metadata.KEY))) {
metadata.setValue(MilkTaskFields.TASK_ID, 0L);
metadata.setValue(MilkTaskFields.TASK_SERIES_ID, 0L);
}
if(OpencrxCoreUtils.OPENCRX_ACTIVITY_METADATA_KEY.equals(metadata.getValue(Metadata.KEY)))
metadata.setValue(OpencrxCoreUtils.ACTIVITY_ID, 0L);

@ -13,7 +13,6 @@ import java.util.Locale;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.weloveastrid.rmilk.MilkUtilities;
import android.app.Activity;
import android.app.Dialog;
@ -61,9 +60,7 @@ public class UpdateMessageService {
private static final String URL = "http://www.weloveastrid.com/updates.php";
private static final String PLUGIN_PDV = "pdv";
private static final String PLUGIN_GTASKS = "gtasks";
private static final String PLUGIN_RMILK = "rmilk";
@Autowired protected RestClient restClient;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@ -303,12 +300,8 @@ public class UpdateMessageService {
private boolean pluginConditionMatches(String plugin) {
// handle internal plugin specially
if(PLUGIN_GTASKS.equals(plugin)) {
if(PLUGIN_GTASKS.equals(plugin))
return gtasksPreferenceService.isLoggedIn();
}
else if(PLUGIN_RMILK.equals(plugin)) {
return MilkUtilities.INSTANCE.isLoggedIn();
}
else
return addOnService.isInstalled(plugin);
}

@ -5,8 +5,6 @@
*/
package com.todoroo.astrid.service;
import org.weloveastrid.rmilk.data.MilkNoteHelper;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
@ -17,8 +15,6 @@ import android.os.Bundle;
import com.timsu.astrid.GCMIntentService;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
@ -33,11 +29,9 @@ import com.todoroo.astrid.activity.Eula;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.helper.DueDateTimeMigrator;
import com.todoroo.astrid.notes.NoteMetadata;
import com.todoroo.astrid.service.abtesting.ABChooser;
import com.todoroo.astrid.subtasks.SubtasksMetadataMigration;
import com.todoroo.astrid.tags.TagCaseMigrator;
@ -733,41 +727,6 @@ public final class UpgradeService {
}
}
t.close();
TodorooCursor<Metadata> m = metadataService.query(Query.select(Metadata.PROPERTIES).
where(Metadata.KEY.eq("rmilk-note")));
StringProperty RTM_NOTE_ID = Metadata.VALUE1;
StringProperty RTM_NOTE_TITLE = Metadata.VALUE2;
StringProperty RTM_NOTE_TEXT = Metadata.VALUE3;
LongProperty RTM_NOTE_CREATED = new LongProperty(Metadata.TABLE, Metadata.VALUE4.name);
Metadata metadata = new Metadata();
for(m.moveToFirst(); !m.isAfterLast(); m.moveToNext()) {
metadata.readFromCursor(m);
String id, body, title, provider;
long created;
if("rmilk-note".equals(metadata.getValue(Metadata.KEY))) {
id = metadata.getValue(RTM_NOTE_ID);
body = metadata.getValue(RTM_NOTE_TEXT);
title = metadata.getValue(RTM_NOTE_TITLE);
created = metadata.getValue(RTM_NOTE_CREATED);
provider = MilkNoteHelper.PROVIDER;
metadata.setValue(Metadata.KEY, NoteMetadata.METADATA_KEY);
metadata.setValue(Metadata.CREATION_DATE, created);
metadata.setValue(NoteMetadata.BODY, body);
metadata.setValue(NoteMetadata.TITLE, title);
metadata.setValue(NoteMetadata.THUMBNAIL, null);
metadata.setValue(NoteMetadata.EXT_PROVIDER, provider);
metadata.setValue(NoteMetadata.EXT_ID, id);
metadata.clearValue(Metadata.ID);
metadataService.save(metadata);
}
}
m.close();
}
/**

Loading…
Cancel
Save