From: David A. Velasco Date: Mon, 24 Mar 2014 09:23:05 +0000 (+0100) Subject: Merge branch 'develop' into release-1.5.5 X-Git-Tag: oc-android-1.5.5~5 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/d2ff7d47553944206bcb904b423fa33b91ef0977?hp=a9c0d1c62bd03e77322349356eaf6dd210e95ca1 Merge branch 'develop' into release-1.5.5 --- diff --git a/owncloud-android-library b/owncloud-android-library index 30acd487..25c53f3b 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 30acd4875dda3fd0bec83daaad522f3d5a02ead6 +Subproject commit 25c53f3bf978f53cceed125edfb9a37eecfa15a8 diff --git a/res/drawable-hdpi-v11/notification_icon.png b/res/drawable-hdpi-v11/notification_icon.png new file mode 100644 index 00000000..9e634a8c Binary files /dev/null and b/res/drawable-hdpi-v11/notification_icon.png differ diff --git a/res/drawable-hdpi/notification_icon.png b/res/drawable-hdpi/notification_icon.png new file mode 100644 index 00000000..9872053a Binary files /dev/null and b/res/drawable-hdpi/notification_icon.png differ diff --git a/res/drawable-mdpi-v11/notification_icon.png b/res/drawable-mdpi-v11/notification_icon.png new file mode 100644 index 00000000..e33e6535 Binary files /dev/null and b/res/drawable-mdpi-v11/notification_icon.png differ diff --git a/res/drawable-mdpi/notification_icon.png b/res/drawable-mdpi/notification_icon.png new file mode 100644 index 00000000..79bcf2ae Binary files /dev/null and b/res/drawable-mdpi/notification_icon.png differ diff --git a/res/drawable-xhdpi-v11/notification_icon.png b/res/drawable-xhdpi-v11/notification_icon.png new file mode 100644 index 00000000..1ad49714 Binary files /dev/null and b/res/drawable-xhdpi-v11/notification_icon.png differ diff --git a/res/drawable-xhdpi/notification_icon.png b/res/drawable-xhdpi/notification_icon.png new file mode 100644 index 00000000..656977dd Binary files /dev/null and b/res/drawable-xhdpi/notification_icon.png differ diff --git a/res/layout-v11/notification_with_progress_bar.xml b/res/layout-v11/notification_with_progress_bar.xml new file mode 100644 index 00000000..f4d4fab3 --- /dev/null +++ b/res/layout-v11/notification_with_progress_bar.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/notification_with_progress_bar.xml b/res/layout/notification_with_progress_bar.xml new file mode 100644 index 00000000..1df31dc5 --- /dev/null +++ b/res/layout/notification_with_progress_bar.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + diff --git a/res/layout/progressbar_layout.xml b/res/layout/progressbar_layout.xml deleted file mode 100644 index e5a5afed..00000000 --- a/res/layout/progressbar_layout.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 64bada03..506f66ae 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -7,18 +7,18 @@ محتويات من تطبيقات أخرى الملفات فتح باستخدام - إنشاء دليل + إنشاء مجلد إعدادات تفاصيل أرسل عام المزيد حسابات - ادارة الحسابات - كلمه السر للتطبيق - حمايه العميل + إدارة الحسابات + كلمة سر التطبيق + حماية العميل تفعيل الرفع الفوري - رفع الصور الماخوذة عن طريق الكاميرا تلقائياً + رفع الصور فور التقاطها بالكاميرا تفعيل الدخول يستخدم هذا لتسجيل المشاكل تاريخ الدخول @@ -34,113 +34,124 @@ عنوان الخادم https://… إسم المستخدم كلمة السر - جديد لـ %1$s ؟ + جديد في %1$s ؟ الملفات اتصال - إرفع - اختر مجلد الرفع: + رفع + اختر مجلد الرفع : لم يتم العثور على أي حساب - لا يوجد جسابات للـ %1$s في جهازك. يرجى إعداد حساب أولاَ. - إعداد + لا توجد حسابات %1$s على جهازك. يرجى تهيئة حساب أولاً. + تهيئة خروج - لا يوجد محتويات للرفع - لم يتم استلام أي محتويات. - %1$s غير مسموح له بالوصول للمحتوى المشترك + لا يوجد محتوى للرفع + لم يتم استلام أي محتوى. لا شيء للرفع. + %1$s غير مسموح له بالوصول للمحتوى المشارك يتم الرفع - لا يوجد ملفات في هذا المجلد.\nيمكن انشاء ملف جديد عن طريق خيارات قائمة \"رفع\". + لا توجد ملفات في هذا المجلد.\nيمكن إنشاء ملف جديد باختيار \"رفع\" القائمة. اضغظ على الملف ليتم عرض خيارات أكثر - الحجم: - النوع: - انشئ: - عُدل: - انزال - تحديث ملف + الحجم : + النوع : + انشئ في : + عُدل في : + تحميل + تحديث الملف تم تغيير اسم الملف إلى %1$s أثناء الرفع شارك الرابط الغاء مشاركة الرابط نعم لا تم - إلغاء تحميل - إلغاء رفع الملفات - الغاء - احفظ & خروج + إلغاء التحميل + إلغاء الرفع + إلغاء + حفظ + خروج خطأ تحميل ... - حدث خطأ غير معروف. + خطأ غير معروف. حول عدل كلمة السر حذف الحساب حساب جديد - رفع من + الرفع من ... اسم المجلد يتم الرفع ... %1$d%% رفع %2$s تم الرفع بنجاح تم رفع %1$s بنجاح - عملية الرفع فشلت - رفع %1$s قد لا يكون كاملاً - يتم التحميل - %1$d%% تنزيل %2$s + فشل الرفع + لم يكتمل رفع %1$s + يتم التحميل ... + %1$d%% تحميل %2$s تم التحميل بنجاح تم تحميل %1$s بنجاح فشل التحميل - تحميل %1$s قد لا يكون كاملاَ + لم يكتمل تحميل %1$s لم يتم تحميلها بعد - اختر حساب - فشل في المزامنة - تعذر إكمال التزامن لـ %1$s + اختر حسابا + فشلت المزامنة. + لم تكتمل مزامنة %1$s كلمة السر غير صالحة لـ %1$s - يوجد تعارض - جهات الاتصال لـ %1$d لا يمكن مزامنتها ( %2$d تعارض) + هناك تعارض + لم تنجح المزامنة الدائمة لـ %1$d ملفات + لم تنجح المزامنة التلقائية للملفات + لا يمكن مزامنة جهات اتصال %1$d ( %2$d تعارض) + تم نسيان بعض الملفات المحلية + لم ينجح نقل %1$d ملفات إلى المجلد %2$s + اعتبارا من اﻹصدار 1.3.16, الملفات المرفوعة من هذا الجهاز يتم نسخها إلى المجلد المحلي %1$s تفاديا لفقدان البيانات حينما تتم مزامنة ملف واحد مع عدة حسابات.\n\nنظرا لهذا التغيير، تم نسخ كل الملفات المرفوعة في اﻹصدارات السابقة إلى المجلد %2$s. لكن خطأً ما حال دون إتمام العملية أثناء مزامنة الحسابات. يمكنك إما ترك هذه الملفات و حذف الرابط إلى %3$s، و إما نقل الملفات إلى المجلد %1$s و الاحتفاظ بالرابط إلى %4$s \n\nفيما يلي الملفات المحلية، و المفات الخارجية المرتبطة بها في %5$s لا يوجد مجلد %1$s بعد الان نقل الكل تم نقل جميع الملفات - بعض الملفات لا يمكن نقلها - محلي:%1$s - لا يوجد مساحة كافية لنسخ الملفات المحددة لمجلد %1$s . هل ترغب بنقلهم للمجلد بدلاَ من ذلك؟ - فضلا, ادخل كلمة السر - فضلا, ادخل كلمة السر + لم ينجح نقل بعض الملفات + محلي :%1$s + خارجي : %1$s + لا يوجد مساحة كافية لنسخ الملفات المحددة إلى مجلد %1$s . هل ترغب بنقلها بدلاَ من ذلك؟ + يرجى إدخال كلمة السر + أدخل كلمة السر سيتم طلب PIN في كل مرة يتم فيها تشغيل التطبيق - فضلا, ادخل كلمة السر مره اخري - ازاله كلمة السر - كلمات السر غير متطابقه - كلمه السر غير صحيحه - تم ازاله كلمه السر - تم تسجيل كلمه السر - %1$s مشغل الموسيقى - %1$s (عرض) - %1$s (تحميل) - تم الانتهاء من تشغيل %1$s + يرجى إدخال كلمة السر مرة أخرى + إزالة كلمة السر + كلمتا السر غير متطابقتين + كلمه السر غير صحيحة + تمت إزالة كلمه السر + تم تسجيل كلمت السر + مشغل الموسيقى %1$s + %1$s (يتم التشغيل) + %1$s (يتم التحميل) + انتهى تشغيل %1$s لا يوجد ملف وسائط - لم يتم تقديم اي حساب - الملف ليس في حساب فعال - ترميز غير مدعوم + لم يتم تقديم أي حساب + الملف ليس في حساب صحيح + ترميز غير مدعّم لا يمكن قراءة ملف الوسائط - لم يتم فك ترميز ملف الوسائط بشكل صحيح + الملف غير مرمز بشكل صحيح انتهت المهلة أثناء محاولة العرض + لا يمكن بث ملف الوسائط لا يمكن عرض ملف الوسائط مع عارض الوسائط المستعمل خطا امني اثناء محاولة عرض %1$s خطا في المدخلات اثناء محاولة عرض %1$s خطا غير متوقع اثناء محاولة عرض %1$s + زر الترجيع + زر التشغيل أو الإيقاف + زر التقدم للأمام محاولة الدخول ... لا يتوفر اتصال الاتصال الآمن غير متاح - يتم إنشاء الاتصال + تم الاتصال اختبار الاتصال ... - اعدادات الخاد تالفة + إعداد الخادم غير صحيحة الحساب لنفس المستخدم والخادم موجود مسبقا على الجهاز المستخدم المدخل لا يتوافق مع المستخدم الموجود في الحساب - حدث خطأ غير معروف! + حدث خطأ غير معروف ! فشل في العثور على المضيف - تعذر إيجاد جهة الاتصال للسيرفر. - الخادم اخذ الكثير من الوقت للرد - رابط تالف - فشل في انشاء SSL - لم يتم التعرف على اصدار الخادم - لا يمكن إنشاء اتصال - تم إنشاء اتصال آمن - خطا في الاسم او كلمة المرور + لم يتم إيجاد الخادم + الخادم أخذ كثيرا من الوقت للرد + رابط غير سليم + فشل في تهيئة SSL + لا يمكن التحقق من هوية خادم SSL + إصدار الخادم غير معروف + لم ينجح الاتصال + نجح الاتصال آمن + اسم المستخدم أو كلمة المرور خاطئة فشل في التحقق تم رفض الوصول من قبل الخادم المرخص حالة غير متوقعة: الرجاء, ادخال عنوان الخادم مرة اخرى @@ -150,58 +161,71 @@ يتم الاتصال بالخادم للتحقق الخادم لا يدعم طريقة التحقق هذه %1$s لا يدعم الحسابات المتعددة - اجعل الملف محدث - إعادة تسميه - الغى + الخادم لا يجيب بمعرف صحيح للمستخدم، الرجاء الاتصال بالمدير + جعل الملف محدثا + إعادة التسمية + حذف هل تود حقاَ إزالة %1$s ؟ - هل ترغب في إزالة %1$s و جهات الاتصال التابعة له؟ - محلي فقط + هل ترغب حقا في إزالة %1$s و محتوياته ؟ + محليا فقط المحتويات المحلية فقط - حذف من الخادم - شبكي و محلي - يتم الحذف بنجاح - لقد فشل الحذف + الحذف من الخادم + محليا و عن بعد + تم الحذف بنجاح + فشل الحذف أدخل اسما جديدا - لايمكن اعادة تسمية النسخ المحلي ,حاول باسم آخر - اعادة التسمية لم تكتمل - محتويات الملفات متزامنة سابقا - لم يتمكن من انشاء المجلد + لم تنجح إعادة تسمية النسخة المحلية، جرب اسما آخر + لم تكتمل إعادة التسمية + لا يمكن التحقق من الملف الخارجي + تمت مزامنة محتويات الملفات من قبل + لم ينجح إنشاء المجلد رموز ممنوعة: / \\ < > : \" | ? * - فضلاً, انتظر - خطا غير متوقع : الرجاء اختيار الملف من برنامج آخر + انتظر للحظة + خطا غير متوقع : الرجاء اختيار الملف من تطبيق آخر لم يتم اختيار أي ملف ارسل الرابط الى ... تسجيل الدخول باستخدام oAuth2 الاتصال مع خادم oAuth2 - تعذر التحقق من هوية الموقع + لا يمكن التحقق من هوية الموقع شهادة الخادم غير موثوقة - شهادة الخادم منتهية - تاريخ صلاحية شهادة الخادم في المستقبل - هذه الوصله غير متطابقه مع السيرفر فى شهاده الحمايه - هل تريد ان تثق في هذه الشهادة على اي حال ؟ - لم يتمكن من حفظ الشهادة + شهادة الخادم منتهية الصلاحية + وقت صلاحية شهادة الخادم لم يحن بعد + الرابط لا يوافق اسم المضيف فى شهاده الحماية + هل تريد أن تثق في هذه الشهادة على اي حال ؟ + لم ينجح حفظ الشهادة تفاصيل إخفاء - أصدرت ل: - الاسم الشائع: - منظمة: - الوحدة التنظيمية: - البلد: - حالة: - المكان: - من: - إلى: - التوقيع: - الخوارزمية: + أصدرت لـ : + أصدرت بواسطة + الاسم الشائع : + منظمة : + الوحدة التنظيمية : + البلد : + الحالة : + المكان : + الصلاحية : + من : + إلى : + التوقيع : + الخوارزمية : + لا يمكن إظهار الشهادة + - لا معلومات عن الخطأ. + هذه مساحة محجوزة + placeholder.txt صورة PNG 389 KB + 2012/05/18 12:23 مساء 12:23:45 - رفع الصور من خلال الواي فاي فقط + رفع الصور من خلال الـ WiFi فقط + /InstantUpload تعارض في التحديث + الملف %s غير + الاحتفاظ بالنسختين + استبدال عدم الرفع معاينة الصورة هذه الصورة لا يمكن أن تظهر - %1$s لم يتمكن من النسخ الى %2$s في المجلد المحلي + لم ينجح نسخ %1$s إلى المجلد المحلي %2$s فشل في محاولة الرفع الفوري فشل في الرفع الفوري ملخص لكل الاخطاء في عملية الرفع الفوري @@ -210,6 +234,7 @@ حذف كل المختارات من قائمة انتظار الرفع اعادة المحاولة لرفع الصورة: تحميل المزيد من الصور + do nothing you are not online for instant upload رسالة خطا: الرجاء التاكد من اعدادات الخادم, من الممكن انك تعديت الحد في quota عذرا , المشاركة غير مفعلة في الخادم. الرجاء التواصل مع المدير @@ -218,4 +243,6 @@ غير قادر على إلغاء مشاركة هذا الملف أو المجلد.لا وجود له حدث خطأ ما أثناء محاولة إلغاء مشاركة هذا الملف أو المجلد أرسل + نسخ الرابط + تم النسخ للحافظة diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 85c4acc0..67d7e297 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -161,6 +161,7 @@ S\'està connectant a un servidor d\'autenticació... El serivdor no permet aquest mètode d\'autenticació %1$s no permet comptes múltiples + El servidor no retorna una id d\'usuari correcta, contacteu amb l\'administrador. Mantén el fitxer actualitzat Reanomena Elimina @@ -207,6 +208,8 @@ A: Signatura: Algoritme: + No s\'ha pogut mostrar el certificat. + - No hi ha informació de l\'error Això és un text variable placeholder.txt Imatge PNG diff --git a/res/values-cs-rCZ/strings.xml b/res/values-cs-rCZ/strings.xml index 787f3f3a..7e6875d1 100644 --- a/res/values-cs-rCZ/strings.xml +++ b/res/values-cs-rCZ/strings.xml @@ -97,7 +97,7 @@ Obsah %1$d souborů nemohl být synchronizován (počet konfliktů: %2$d) Některé místní soubory byly zapomenuty %1$d souborů z adresáře %2$s nelze zkopírovat do - Od verze 1.3.16 jsou soubory nahrané z tohoto zařízení kopírovány do místní %1$s složky, aby se zabránilo ztrátě dat při synchronizaci jednoho souboru s více účty.\n\nVšechny soubory nahrané předchozími verzemi aplikace byly z tohoto důvodu překopírovány do složky %2$s. Přesto se objevila chyba zabraňující dokončení této operace v průběhu synchronizace účtu. Buď můžete soubor(y) ponechat jak jsou a odebrat odkaz do složky %3$s nebo přesunout soubor(y) do složky %1$s a zachovat odkaz na %4$s.\n\nNíže je seznam místních a vzdálených souborů ve složce %5$s, do které byly odkázány. + Od verze 1.3.16 jsou soubory nahrané z tohoto zařízení kopírovány do místní %1$s složky, aby se zabránilo ztrátě dat při synchronizaci jednoho souboru s více účty.\n\nVšechny soubory nahrané předchozími verzemi aplikace byly z tohoto důvodu překopírovány do složky %2$s. Bohužel se objevila chyba zabraňující dokončení této operace v průběhu synchronizace účtu. Buď můžete soubor(y) ponechat jak jsou a odebrat odkaz do složky %3$s nebo přesunout soubor(y) do složky %1$s a zachovat odkaz na %4$s.\n\nNíže je seznam místních a vzdálených souborů ve složce %5$s, do které byly odkázány. Složka %1$s již neexistuje Přesunout vše Všechny soubory byly přesunuty @@ -161,6 +161,7 @@ Připojuji se k přihlašovacímu serveru... Server nepodporuje tuto přihlašovací metodu %1$s nepodporuje více účtů + Váš server nevrací správné přihlašovací ID, kontaktujte vašeho administrátora Udržovat soubor aktuální Přejmenovat Odstranit @@ -207,6 +208,8 @@ Pro: Podpis: Alogritmus: + Certifikát nemohl být zobrazen. + - Žádné informace o této chybě Zástupný text placeholder.txt Obrázek PNG diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 7fae754f..31d216bc 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -97,6 +97,7 @@ Indholdet af %1$d filer ikke kunne synkroniseres (%2$d konflikter) Visse lokale filer blev glemt %1$d filer ud af %2$s mappe kunne ikke kopieres ind i + Fra version 1.3.16 bliver filer uploadet fra denne enhed kopieret til mappen %1$s for at forhindre datatab når en enkelt fil synkroniseres med flere konti.\n\nPå grund af denne ændring er alle filer som var uploadet i tidligere versioner af denne app kopieret til mappen %2$s. Imidlertid forhindrede en fejl færdiggørelsen af denne operation under konto-synkronisering. Du kan enten lade filerne være som de er og fjerne linket til %3$s eller flytte filerne til mappen %1$s og beholde linket til %4$s.\n\nHerunder er en liste med de lokale og eksterne filer i %5$s, som de var knyttet til. Mappen %1$s eksistere ikke længere Flyt alle Alle filer blev flyttet @@ -160,6 +161,7 @@ Forbinder til godkendelsesserver ... Serveren understøtter ikke denne godkendelsesmetode %1$s understøtter ikke multiple konti + Din server retunere ikke et korrekt bruger-id. Kontakt venligst din administrator Hold fil opdateret Omdøb Fjern @@ -206,6 +208,8 @@ Til: Signatur: Algoritme: + Certifikatet kunne ikke vises. + - Ingen information om fejlen Dette er en pladsholder stedfortræder.txt PNG Billede @@ -236,6 +240,7 @@ Beklager, deling er ikke slået til på din server. Kontakt venligst din administrator. Kan ikke dele denne fil eller mappe. Find venligst ud af om den eksisterer Der opstod en fejl ved deling af denne fil eller mappe + Kan ikke fjerne delingen af denne fil eller mappe. Den findes ikke. Der opstod en fejl ved stopning af deling af denne mappe. Send Kopier link diff --git a/res/values-v11/versioned_styles.xml b/res/values-v11/versioned_styles.xml index 75cfb9b7..ab237e84 100644 --- a/res/values-v11/versioned_styles.xml +++ b/res/values-v11/versioned_styles.xml @@ -6,4 +6,17 @@ @color/button_text_color + + + + + + diff --git a/res/values-v9/versioned_styles.xml b/res/values-v9/versioned_styles.xml new file mode 100644 index 00000000..57bb396f --- /dev/null +++ b/res/values-v9/versioned_styles.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index a7f76f5b..4c6ee40d 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -25,13 +25,13 @@ android:title="@string/prefs_select_oc_account" android:summary="@string/prefs_summary_select_oc_account" / --> - - + - - diff --git a/src/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/com/owncloud/android/authentication/AuthenticatorActivity.java index 9dcddcee..e5e80144 100644 --- a/src/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -63,6 +63,8 @@ import com.owncloud.android.lib.common.accounts.AccountTypeUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.operations.DetectAuthenticationMethodOperation; +import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod; import com.owncloud.android.operations.OAuth2GetAccessToken; import com.owncloud.android.lib.common.network.CertificateCombinedException; @@ -73,7 +75,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation; import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation; - + import com.owncloud.android.ui.dialog.SamlWebViewDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; @@ -87,8 +89,8 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; * @author David A. Velasco */ public class AuthenticatorActivity extends AccountAuthenticatorActivity - implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener, - SsoWebViewClientListener, OnSslUntrustedCertListener { +implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener, +SsoWebViewClientListener, OnSslUntrustedCertListener { private static final String TAG = AuthenticatorActivity.class.getSimpleName(); @@ -119,7 +121,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private static final String AUTH_ON = "on"; private static final String AUTH_OFF = "off"; private static final String AUTH_OPTIONAL = "optional"; - + private static final int DIALOG_LOGIN_PROGRESS = 0; private static final int DIALOG_CERT_NOT_SAVED = 1; private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 2; @@ -128,7 +130,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity public static final byte ACTION_UPDATE_TOKEN = 1; private static final String TAG_SAML_DIALOG = "samlWebViewDialog"; - + private String mHostBaseUrl; private OwnCloudVersion mDiscoveredVersion; @@ -151,31 +153,33 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private Account mAccount; private TextView mAuthMessage; - + private EditText mHostUrlInput; private boolean mHostUrlInputEnabled; private View mRefreshButton; private String mAuthTokenType; - + private EditText mUsernameInput; private EditText mPasswordInput; - + private CheckBox mOAuth2Check; - + private TextView mOAuthAuthEndpointText; private TextView mOAuthTokenEndpointText; - + private SamlWebViewDialog mSamlDialog; - + private View mOkButton; - + private String mAuthToken; - + private boolean mResumed; // Control if activity is resumed public static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT"; + private DetectAuthenticationMethodOperation mDetectAuthenticationOperation; + /** * {@inheritDoc} @@ -199,10 +203,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); mOkButton = findViewById(R.id.buttonOK); mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); - + /// set Host Url Input Enabled mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input); - + /// set visibility of link for new users boolean accountRegisterVisibility = getResources().getBoolean(R.bool.show_welcome_link); Button welcomeLink = (Button) findViewById(R.id.welcome_link); @@ -222,7 +226,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAccount = null; mHostBaseUrl = ""; boolean refreshButtonEnabled = false; - + // URL input configuration applied if (!mHostUrlInputEnabled) { @@ -255,15 +259,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mHostUrlInput.setText(mHostBaseUrl); String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@')); mUsernameInput.setText(userName); - + } initAuthorizationMethod(); // checks intent and setup.xml to determine mCurrentAuthorizationMethod mJustCreated = true; - + if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) { checkOcServer(); } - + } else { mResumed = true; /// connection state and info @@ -279,7 +283,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) { showPassword(); } - + /// server data String ocVersion = savedInstanceState.getString(KEY_OC_VERSION); String ocVersionString = savedInstanceState.getString(KEY_OC_VERSION_STRING); @@ -293,17 +297,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE); if (mAuthTokenType == null) { mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); - + } // check if server check was interrupted by a configuration change if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) { checkOcServer(); } - + // refresh button enabled refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED); - + } @@ -316,7 +320,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity adaptViewAccordingToAuthenticationMethod(); showServerStatus(); showAuthStatus(); - + if (mAction == ACTION_UPDATE_TOKEN) { /// lock things that should not change mHostUrlInput.setEnabled(false); @@ -325,7 +329,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mUsernameInput.setFocusable(false); mOAuth2Check.setVisibility(View.GONE); } - + //if (mServerIsChecked && !mServerIsValid && mRefreshButtonEnabled) showRefreshButton(); if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton(); mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes @@ -364,7 +368,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mResumed = false; } }); - + mPasswordInput.setOnFocusChangeListener(this); mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE); mPasswordInput.setOnEditorActionListener(this); @@ -377,7 +381,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity return true; } }); - + findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { @@ -391,8 +395,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } }); } - - + + private void initAuthorizationMethod() { boolean oAuthRequired = false; @@ -400,15 +404,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE); mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT); - + // TODO could be a good moment to validate the received token type, if not null - + if (mAuthTokenType == null) { if (mAccount != null) { /// same authentication method than the one used to create the account to update oAuthRequired = (mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2) != null); samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null); - + } else { /// use the one set in setup.xml oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2)); @@ -422,14 +426,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); } } - + if (mAccount != null) { String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@')); mUsernameInput.setText(userName); } - + mOAuth2Check.setChecked(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)); - + } /** @@ -469,10 +473,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity outState.putParcelable(KEY_ACCOUNT, mAccount); } outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType); - + // refresh button enabled outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE)); - + } @@ -519,7 +523,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } mJustCreated = false; - + } @@ -590,11 +594,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private void checkOcServer() { String uri = trimUrlWebdav(mHostUrlInput.getText().toString().trim()); - + if (!mHostUrlInputEnabled){ uri = getString(R.string.server_url); } - + mServerIsValid = false; mServerIsChecked = false; mOkButton.setEnabled(false); @@ -648,7 +652,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private boolean isPasswordVisible() { return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); } - + private void hidePasswordButton() { mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } @@ -657,13 +661,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); showViewPasswordButton(); } - + private void hidePassword() { mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); showViewPasswordButton(); } - - + + /** * Cancels the authenticator activity * @@ -747,7 +751,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthStatusIcon = R.drawable.progress_small; mAuthStatusText = R.string.oauth_login_connection; showAuthStatus(); - + // GET AUTHORIZATION request //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth)); @@ -775,7 +779,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthStatusText = R.string.auth_connecting_auth_server; showAuthStatus(); showDialog(DIALOG_LOGIN_PROGRESS); - + /// get the path to the root folder through WebDAV from the version server String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType); @@ -783,7 +787,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthCheckOperation = new ExistenceCheckRemoteOperation("", this, false); OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false); mOperationThread = mAuthCheckOperation.execute(client, this, mHandler); - + } /** @@ -803,28 +807,60 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } else if (operation instanceof ExistenceCheckRemoteOperation) { if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result); - + } else { onAuthorizationCheckFinish((ExistenceCheckRemoteOperation)operation, result); } } else if (operation instanceof GetRemoteUserNameOperation) { onGetUserNameFinish((GetRemoteUserNameOperation) operation, result); - + + } else if (operation instanceof DetectAuthenticationMethodOperation) { + onDetectAutheticationFinish((DetectAuthenticationMethodOperation) operation, result); + } + + } + + private void onDetectAutheticationFinish(DetectAuthenticationMethodOperation operation, RemoteOperationResult result) { + // Read authentication method + if (result.getData().size() > 0) { + AuthenticationMethod authMethod = (AuthenticationMethod) result.getData().get(0); + String basic = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); + String oAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()); + String saml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()); + + if ( ( mAuthTokenType.equals(basic) && !authMethod.equals(AuthenticationMethod.BASIC_HTTP_AUTH) ) || + ( mAuthTokenType.equals(oAuth) && !authMethod.equals(AuthenticationMethod.BEARER_TOKEN) ) || + ( mAuthTokenType.equals(saml) && !authMethod.equals(AuthenticationMethod.SAML_WEB_SSO) ) ) { + + mOkButton.setEnabled(false); + mServerIsValid = false; + //show an alert message ( Server Status ) + updateServerStatusIconNoRegularAuth(); + showServerStatus(); + + } else { + mOkButton.setEnabled(true); + + // Show server status + showServerStatus(); + } + } - } + + private void onGetUserNameFinish(GetRemoteUserNameOperation operation, RemoteOperationResult result) { - + if (result.isSuccess()) { boolean success = false; String username = operation.getUserName(); - + if ( mAction == ACTION_CREATE) { mUsernameInput.setText(username); success = createAccount(); } else { - + if (!mUsernameInput.getText().toString().equals(username)) { // fail - not a new account, but an existing one; disallow result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); @@ -832,11 +868,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity showAuthStatus(); Log_OC.d(TAG, result.getLogMessage()); } else { - updateToken(); - success = true; + updateToken(); + success = true; } } - + if (success) finish(); } else { @@ -844,7 +880,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity showAuthStatus(); Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage()); } - + } private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) { @@ -853,23 +889,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } catch (IllegalArgumentException e) { // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens } - + //if (result.isTemporalRedirection() && result.isIdPRedirection()) { if (result.isIdPRedirection()) { String url = result.getRedirectedLocation(); String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType); - + // Show dialog mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl); mSamlDialog.show(getSupportFragmentManager(), TAG_SAML_DIALOG); - + mAuthStatusIcon = 0; mAuthStatusText = 0; - + } else { mAuthStatusIcon = R.drawable.common_error; mAuthStatusText = R.string.auth_unsupported_auth_method; - + } showAuthStatus(); } @@ -890,32 +926,54 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mIsSslConn = (result.getCode() == ResultCode.OK_SSL); mOcServerChkOperation = null; + + /// retrieve discovered version and normalize server URL + mDiscoveredVersion = operation.getDiscoveredVersion(); + mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString()); + + // Refresh server status, but don't show it + updateServerStatusIconAndText(result); + /// update status icon and text if (mServerIsValid) { hideRefreshButton(); + // Try to create an account with user and pass "", to know if it is a regular server + // Update connect button in the answer of this method + detectAuthorizationMethod(); } else { showRefreshButton(); + // Show server status + showServerStatus(); } - updateServerStatusIconAndText(result); - showServerStatus(); /// very special case (TODO: move to a common place for all the remote operations) if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) { showUntrustedCertDialog(result); } - /// retrieve discovered version and normalize server URL - mDiscoveredVersion = operation.getDiscoveredVersion(); - mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString()); - /// allow or not the user try to access the server - mOkButton.setEnabled(mServerIsValid); - } // else nothing ; only the last check operation is considered; // multiple can be triggered if the user amends a URL before a previous check can be triggered } + /** + * Try to access with user/pass ""/"", to know if it is a regular server + */ + private void detectAuthorizationMethod() { + + Log_OC.d(TAG, "Trying empty authorization to detect authentication method"); + + /// get the path to the root folder through WebDAV from the version server + String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType); + + /// test credentials + mDetectAuthenticationOperation = new DetectAuthenticationMethodOperation(this); + OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); + mOperationThread = mDetectAuthenticationOperation.execute(client, this, mHandler); + } + + private String normalizeUrl(String url) { if (url != null && url.length() > 0) { url = url.trim(); @@ -950,8 +1008,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity } return (url != null ? url : ""); } - - + + /** * Chooses the right icon and text to show to the user for the received operation result. * @@ -1113,10 +1171,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private void updateStatusIconFailUserName(){ - mAuthStatusIcon = android.R.drawable.ic_secure; + mAuthStatusIcon = R.drawable.common_error; mAuthStatusText = R.string.auth_fail_get_user_name; } - + + private void updateServerStatusIconNoRegularAuth(){ + mServerStatusIcon = R.drawable.common_error; + mServerStatusText = R.string.auth_can_not_auth_against_server; + } + /** * Processes the result of the request for and access token send * to an OAuth authorization server. @@ -1182,8 +1245,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (success) { finish(); } - - } else if (result.isServerFail() || result.isException()) { + + } else if (result.isServerFail() || result.isException()) { /// if server fail or exception in authorization, the UI is updated as when a server check failed mServerIsChecked = true; mServerIsValid = false; @@ -1198,7 +1261,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAuthStatusIcon = 0; mAuthStatusText = 0; showAuthStatus(); - + // update input controls state showRefreshButton(); mOkButton.setEnabled(false); @@ -1213,10 +1276,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity showAuthStatus(); Log_OC.d(TAG, "Access failed: " + result.getLogMessage()); } - } + + /** * Sets the proper response to get that the Account Authenticator that started this activity saves * a new authorization token for mAccount. @@ -1225,24 +1289,24 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity Bundle response = new Bundle(); response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type); - + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken); // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken); - + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { - + response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken); // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken); - + } else { response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString()); mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString()); } setAccountAuthenticatorResult(response); - + } @@ -1275,15 +1339,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity showAuthStatus(); Log_OC.d(TAG, result.getLogMessage()); return false; - + } else { - + if (isOAuth || isSaml) { mAccountMgr.addAccountExplicitly(mAccount, "", null); // with external authorizations, the password is never input in the app } else { mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null); } - + /// add the new account as default in preferences, if there is none already Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this); if (defaultAccount == null) { @@ -1292,7 +1356,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity editor.putString("select_oc_account", accountName); editor.commit(); } - + /// prepare result to return to the Authenticator // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done final Intent intent = new Intent(); @@ -1308,16 +1372,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mAccountMgr.setUserData(mAccount, Constants.KEY_OC_VERSION, mDiscoveredVersion.getVersion()); mAccountMgr.setUserData(mAccount, Constants.KEY_OC_VERSION_STRING, mDiscoveredVersion.getVersionString()); mAccountMgr.setUserData(mAccount, Constants.KEY_OC_BASE_URL, mHostBaseUrl); - + if (isSaml) { mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); } else if (isOAuth) { mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2, "TRUE"); } - + setAccountAuthenticatorResult(intent.getExtras()); setResult(RESULT_OK, intent); - + return true; } } @@ -1472,8 +1536,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity public void onRefreshClick(View view) { checkOcServer(); } - - + + /** * Called when the eye icon in the password field is clicked. * @@ -1508,7 +1572,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity adaptViewAccordingToAuthenticationMethod(); } - + /** * Changes the visibility of input elements depending on * the current authorization method. @@ -1520,7 +1584,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mOAuthTokenEndpointText.setVisibility(View.VISIBLE); mUsernameInput.setVisibility(View.GONE); mPasswordInput.setVisibility(View.GONE); - + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { // SAML-based web Single Sign On mOAuthAuthEndpointText.setVisibility(View.GONE); @@ -1535,7 +1599,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity mPasswordInput.setVisibility(View.VISIBLE); } } - + /** * Called when the 'action' button in an IME is pressed ('enter' in software keyboard). * @@ -1548,7 +1612,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity if (mOkButton.isEnabled()) { mOkButton.performClick(); } - + } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) { if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { checkOcServer(); @@ -1561,7 +1625,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private abstract static class RightDrawableOnTouchListener implements OnTouchListener { private int fuzz = 75; - + /** * {@inheritDoc} */ @@ -1579,8 +1643,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity final int y = (int) event.getY(); final Rect bounds = rightDrawable.getBounds(); if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz) - && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) { - + && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) { + return onDrawableTouch(event); } } @@ -1593,7 +1657,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity public void onSamlDialogSuccess(String sessionCookie) { mAuthToken = sessionCookie; - + if (sessionCookie != null && sessionCookie.length() > 0) { mAuthToken = sessionCookie; @@ -1603,7 +1667,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity getUserOperation.execute(client, this, mHandler); } - + } @@ -1626,18 +1690,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity // TODO - show fail Log_OC.d(TAG, "SSO failed"); } - + } - + /** Show auth_message * * @param message */ private void showAuthMessage(String message) { - mAuthMessage.setVisibility(View.VISIBLE); - mAuthMessage.setText(message); + mAuthMessage.setVisibility(View.VISIBLE); + mAuthMessage.setText(message); } - + private void hideAuthMessage() { mAuthMessage.setVisibility(View.GONE); } @@ -1668,7 +1732,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity ft.addToBackStack(null); dialog.show(ft, DIALOG_UNTRUSTED_CERT); } - + /** * Show untrusted cert dialog */ @@ -1679,9 +1743,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity FragmentTransaction ft = fm.beginTransaction(); ft.addToBackStack(null); dialog.show(ft, DIALOG_UNTRUSTED_CERT); - + } - + /** * Dismiss untrusted cert dialog */ @@ -1691,9 +1755,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity SslErrorViewAdapter dialog = (SslErrorViewAdapter) frag; dialog.dismiss(); } - */ + */ } - + /** * Called from SslValidatorDialog when a new server certificate was correctly saved. */ @@ -1719,7 +1783,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity public void onCancelCertificate() { cancelWebView(); } - + public void cancelWebView() { Fragment fd = getSupportFragmentManager().findFragmentByTag(TAG_SAML_DIALOG); @@ -1729,7 +1793,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity d.dismiss(); } } - + } } diff --git a/src/com/owncloud/android/files/managers/OCNotificationManager.java b/src/com/owncloud/android/files/managers/OCNotificationManager.java deleted file mode 100644 index 47fb491d..00000000 --- a/src/com/owncloud/android/files/managers/OCNotificationManager.java +++ /dev/null @@ -1,156 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package com.owncloud.android.files.managers; - -import java.util.HashMap; -import java.util.Map; - -import com.owncloud.android.R; -import com.owncloud.android.utils.DisplayUtils; - -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Context; -import android.widget.RemoteViews; - - -public class OCNotificationManager { - - enum NotificationType { - NOTIFICATION_SIMPLE, - NOTIFICATION_PROGRESS - } - - static public class NotificationData { - private String mText, mSubtitle; - private int mPercent; - private boolean mOngoing; - - public NotificationData(String text, String subtitle, boolean ongoing) { - this(text, subtitle, -1, ongoing); - } - - public NotificationData(int percent, boolean ongoing) { - this(null, null, percent, ongoing); - } - - public NotificationData(String text, int percent, boolean ongoing) { - this(text, null, percent, ongoing); - } - - public NotificationData(String text, String subtitle, int percent, boolean ongoing) { - mText = text; - mPercent = percent; - mSubtitle = subtitle; - mOngoing = ongoing; - } - - public String getText() { return mText; } - public int getPercent() { return mPercent; } - public String getSubtitle() { return mSubtitle; } - public boolean getOngoing() { return mOngoing; } - } - - static private OCNotificationManager mInstance = null; - - private class NotificationTypePair { - public Notification mNotificaiton; - public NotificationType mType; - public NotificationTypePair(Notification n, NotificationType type) { - mNotificaiton = n; - mType = type; - } - } - - private Context mContext; - private Map mNotificationMap; - private int mNotificationCounter; - NotificationManager mNM; - - static OCNotificationManager getInstance(Context context) { - if (mInstance == null) - mInstance = new OCNotificationManager(context); - return mInstance; - } - - OCNotificationManager(Context context) { - mContext = context; - mNotificationMap = new HashMap(); - mNM = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationCounter = 0; - } - - public int postNotification(NotificationType type, NotificationData data) { - mNotificationCounter++; - Notification notification = null; - - switch (type) { - case NOTIFICATION_SIMPLE: - notification = new Notification(DisplayUtils.getSeasonalIconId(), data.getText(), System.currentTimeMillis()); - break; - case NOTIFICATION_PROGRESS: - notification = new Notification(); - notification.contentView = new RemoteViews(mContext.getPackageName(), R.layout.progressbar_layout); - notification.contentView.setTextViewText(R.id.status_text, - data.getText()); - notification.contentView.setImageViewResource(R.id.status_icon, - R.id.icon); - notification.contentView.setProgressBar(R.id.status_progress, - 100, - data.getPercent(), - false); - break; - default: - return -1; - } - if (data.getOngoing()) { - notification.flags |= notification.flags | Notification.FLAG_ONGOING_EVENT; - } - - mNotificationMap.put(mNotificationCounter, new NotificationTypePair(notification, type)); - return mNotificationCounter; - } - - public boolean updateNotification(int notification_id, NotificationData data) { - if (!mNotificationMap.containsKey(notification_id)) { - return false; - } - NotificationTypePair pair = mNotificationMap.get(notification_id); - switch (pair.mType) { - case NOTIFICATION_PROGRESS: - pair.mNotificaiton.contentView.setProgressBar(R.id.status_text, - 100, - data.getPercent(), - false); - return true; - case NOTIFICATION_SIMPLE: - pair.mNotificaiton = new Notification(DisplayUtils.getSeasonalIconId(), - data.getText(), System.currentTimeMillis()); - mNM.notify(notification_id, pair.mNotificaiton); - return true; - default: - return false; - } - } - - public void discardNotification(int notification_id) { - mNM.cancel(notification_id); - mNotificationMap.remove(notification_id); - } -} diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java index e59e37d9..2645511b 100644 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@ -44,12 +44,11 @@ import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.Log_OC; +import com.owncloud.android.utils.NotificationBuilderWithProgressBar; import android.accounts.Account; import android.accounts.AccountsException; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; @@ -61,7 +60,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; -import android.widget.RemoteViews; +import android.support.v4.app.NotificationCompat; public class FileDownloader extends Service implements OnDatatransferProgressListener { @@ -88,7 +87,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis private DownloadFileOperation mCurrentDownload = null; private NotificationManager mNotificationManager; - private Notification mNotification; + private NotificationCompat.Builder mNotificationBuilder; private int mLastPercent; @@ -404,13 +403,19 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis private void notifyDownloadStart(DownloadFileOperation download) { /// create status notification with a progress bar mLastPercent = 0; - mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis()); - mNotification.flags |= Notification.FLAG_ONGOING_EVENT; - mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout); - mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0); - mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName())); - mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId()); - + mNotificationBuilder = + NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this); + mNotificationBuilder + .setSmallIcon(R.drawable.notification_icon) + .setTicker(getString(R.string.downloader_download_in_progress_ticker)) + .setContentTitle(getString(R.string.downloader_download_in_progress_ticker)) + .setOngoing(true) + .setProgress(100, 0, download.getSize() < 0) + .setContentText( + String.format(getString(R.string.downloader_download_in_progress_content), 0, + new File(download.getSavePath()).getName()) + ); + /// includes a pending intent in the notification showing the details view of the file Intent showDetailsIntent = null; if (PreviewImageFragment.canBePreviewed(download.getFile())) { @@ -421,9 +426,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile()); showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount()); showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); - mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); + mNotificationBuilder.setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), showDetailsIntent, 0 + )); + + mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build()); } @@ -434,11 +442,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) { int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); if (percent != mLastPercent) { - mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0); + mNotificationBuilder.setProgress(100, percent, totalToTransfer < 0); String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName); - mNotification.contentView.setTextViewText(R.id.status_text, text); - mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); + mNotificationBuilder.setContentText(text); + mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build()); } mLastPercent = percent; } @@ -455,8 +463,12 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis if (!downloadResult.isCancelled()) { int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker; int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content; - Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(tickerId), System.currentTimeMillis()); - finalNotification.flags |= Notification.FLAG_AUTO_CANCEL; + mNotificationBuilder + .setTicker(getString(tickerId)) + .setContentTitle(getString(tickerId)) + .setAutoCancel(true) + .setOngoing(false) + .setProgress(0, 0, false); boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED || // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection() (downloadResult.isIdPRedirection() @@ -471,11 +483,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND); - finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT); - finalNotification.setLatestEventInfo( getApplicationContext(), - getString(tickerId), - String.format(getString(contentId), new File(download.getSavePath()).getName()), - finalNotification.contentIntent); + mNotificationBuilder + .setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT + )) + .setContentText(String.format(getString(contentId), new File(download.getSavePath()).getName())); mDownloadClient = null; // grant that future retries on the same account will get the fresh credentials } else { @@ -494,10 +506,13 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis // TODO put something smart in showDetailsIntent showDetailsIntent = new Intent(); } - finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); - finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent); + mNotificationBuilder + .setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), showDetailsIntent, 0 + )) + .setContentText(String.format(getString(contentId), new File(download.getSavePath()).getName())); } - mNotificationManager.notify(tickerId, finalNotification); + mNotificationManager.notify(tickerId, mNotificationBuilder.build()); } } diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index 2f963562..b5470669 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -53,13 +53,12 @@ import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.InstantUploadActivity; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.Log_OC; +import com.owncloud.android.utils.NotificationBuilderWithProgressBar; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountsException; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; @@ -71,8 +70,8 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; +import android.support.v4.app.NotificationCompat; import android.webkit.MimeTypeMap; -import android.widget.RemoteViews; @@ -117,9 +116,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe private UploadFileOperation mCurrentUpload = null; private NotificationManager mNotificationManager; - private Notification mNotification; + private NotificationCompat.Builder mNotificationBuilder; private int mLastPercent; - private RemoteViews mDefaultNotificationContentView; public static String getUploadFinishMessage() { @@ -678,30 +676,30 @@ public class FileUploader extends Service implements OnDatatransferProgressListe * * @param upload Upload operation starting. */ - @SuppressWarnings("deprecation") private void notifyUploadStart(UploadFileOperation upload) { // / create status notification with a progress bar mLastPercent = 0; - mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.uploader_upload_in_progress_ticker), - System.currentTimeMillis()); - mNotification.flags |= Notification.FLAG_ONGOING_EVENT; - mDefaultNotificationContentView = mNotification.contentView; - mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), - R.layout.progressbar_layout); - mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false); - mNotification.contentView.setTextViewText(R.id.status_text, - String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName())); - mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId()); - + mNotificationBuilder = + NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this); + mNotificationBuilder + .setOngoing(true) + .setSmallIcon(R.drawable.notification_icon) + .setTicker(getString(R.string.uploader_upload_in_progress_ticker)) + .setContentTitle(getString(R.string.uploader_upload_in_progress_ticker)) + .setProgress(100, 0, false) + .setContentText( + String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName())); + /// includes a pending intent in the notification showing the details view of the file Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class); showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile()); showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount()); showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), - (int) System.currentTimeMillis(), showDetailsIntent, 0); + mNotificationBuilder.setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), showDetailsIntent, 0 + )); - mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); + mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build()); } /** @@ -711,11 +709,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) { int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer)); if (percent != mLastPercent) { - mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, false); + mNotificationBuilder.setProgress(100, percent, false); String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName); - mNotification.contentView.setTextViewText(R.id.status_text, text); - mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); + mNotificationBuilder.setContentText(text); + mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build()); } mLastPercent = percent; } @@ -735,12 +733,10 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } else if (uploadResult.isSuccess()) { // / success -> silent update of progress notification to success // message - mNotification.flags ^= Notification.FLAG_ONGOING_EVENT; // remove - // the - // ongoing - // flag - mNotification.flags |= Notification.FLAG_AUTO_CANCEL; - mNotification.contentView = mDefaultNotificationContentView; + mNotificationBuilder + .setOngoing(false) + .setAutoCancel(true) + .setProgress(0, 0, false); /// includes a pending intent in the notification showing the details view of the file Intent showDetailsIntent = null; @@ -751,18 +747,20 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile()); showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount()); - showDetailsIntent.putExtra(FileActivity.EXTRA_FROM_NOTIFICATION, true); - showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), - (int) System.currentTimeMillis(), showDetailsIntent, 0); - - mNotification.setLatestEventInfo(getApplicationContext(), - getString(R.string.uploader_upload_succeeded_ticker), - String.format(getString(R.string.uploader_upload_succeeded_content_single), upload.getFileName()), - mNotification.contentIntent); - - mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); // NOT - // AN + showDetailsIntent.putExtra(FileActivity.EXTRA_FROM_NOTIFICATION, true);; + mNotificationBuilder + .setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), showDetailsIntent, 0 + )) + .setTicker(getString(R.string.uploader_upload_succeeded_ticker)) + .setContentTitle(getString(R.string.uploader_upload_succeeded_ticker)) + .setContentText( + String.format(getString(R.string.uploader_upload_succeeded_content_single), + upload.getFileName()) + ); + + mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build()); // NOT + // AN DbHandler db = new DbHandler(this.getBaseContext()); db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath()); db.close(); @@ -771,9 +769,12 @@ public class FileUploader extends Service implements OnDatatransferProgressListe // / fail -> explicit failure notification mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker); - Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(), - getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis()); - finalNotification.flags |= Notification.FLAG_AUTO_CANCEL; + NotificationCompat.Builder errorBuilder = new NotificationCompat.Builder(this); + errorBuilder + .setSmallIcon(R.drawable.notification_icon) + .setTicker(getString(R.string.uploader_upload_failed_ticker)) + .setContentTitle(getString(R.string.uploader_upload_failed_ticker)) + .setAutoCancel(true); String content = null; boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED || @@ -790,15 +791,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND); - finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT); + errorBuilder.setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT + )); content = String.format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName()); - finalNotification.setLatestEventInfo(getApplicationContext(), - getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent); mUploadClient = null; // grant that future retries on the same account will get the fresh credentials } else { // TODO put something smart in the contentIntent below - // finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); - //} if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL || uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) { @@ -823,10 +822,12 @@ public class FileUploader extends Service implements OnDatatransferProgressListe detailUploadIntent = new Intent(this, FailedUploadActivity.class); detailUploadIntent.putExtra(FailedUploadActivity.MESSAGE, content); } - finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), - (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT - | PendingIntent.FLAG_ONE_SHOT); - + errorBuilder + .setContentIntent(PendingIntent.getActivity( + this, (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT + )) + .setContentText(content); + if (upload.isInstant()) { DbHandler db = null; try { @@ -847,10 +848,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } } - finalNotification.setLatestEventInfo(getApplicationContext(), - getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent); - mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification); + mNotificationManager.notify(R.string.uploader_upload_failed_ticker, errorBuilder.build()); } } diff --git a/src/com/owncloud/android/operations/CreateShareOperation.java b/src/com/owncloud/android/operations/CreateShareOperation.java index 7277b3a7..896ecca7 100644 --- a/src/com/owncloud/android/operations/CreateShareOperation.java +++ b/src/com/owncloud/android/operations/CreateShareOperation.java @@ -30,7 +30,9 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation; import com.owncloud.android.lib.resources.files.FileUtils; @@ -40,6 +42,7 @@ import com.owncloud.android.utils.Log_OC; public class CreateShareOperation extends SyncOperation { private static final String TAG = CreateShareOperation.class.getSimpleName(); + protected FileDataStorageManager mStorageManager; @@ -83,39 +86,24 @@ public class CreateShareOperation extends SyncOperation { @Override protected RemoteOperationResult run(OwnCloudClient client) { - CreateRemoteShareOperation operation = new CreateRemoteShareOperation(mPath, mShareType, mShareWith, mPublicUpload, mPassword, mPermissions); - RemoteOperationResult result = operation.execute(client); - + RemoteOperation operation = null; + + // Check if the share link already exists + operation = new GetRemoteSharesForFileOperation(mPath, false, false); + RemoteOperationResult result = ((GetRemoteSharesForFileOperation)operation).execute(client); + + if (!result.isSuccess() || result.getData().size() <= 0) { + operation = new CreateRemoteShareOperation(mPath, mShareType, mShareWith, mPublicUpload, mPassword, mPermissions); + result = ((CreateRemoteShareOperation)operation).execute(client); + } + if (result.isSuccess()) { - if (result.getData().size() > 0) { OCShare share = (OCShare) result.getData().get(0); - - // Update DB with the response - share.setPath(mPath); - if (mPath.endsWith(FileUtils.PATH_SEPARATOR)) { - share.setIsFolder(true); - } else { - share.setIsFolder(false); - } - share.setPermissions(mPermissions); - - getStorageManager().saveShare(share); - - // Update OCFile with data from share: ShareByLink and publicLink - OCFile file = getStorageManager().getFileByPath(mPath); - if (file!=null) { - mSendIntent.putExtra(Intent.EXTRA_TEXT, share.getShareLink()); - file.setPublicLink(share.getShareLink()); - file.setShareByLink(true); - getStorageManager().saveFile(file); - Log_OC.d(TAG, "Public Link = " + file.getPublicLink()); - - } - } + updateData(share); + } } - - + return result; } @@ -123,5 +111,29 @@ public class CreateShareOperation extends SyncOperation { public Intent getSendIntent() { return mSendIntent; } + + private void updateData(OCShare share) { + // Update DB with the response + share.setPath(mPath); + if (mPath.endsWith(FileUtils.PATH_SEPARATOR)) { + share.setIsFolder(true); + } else { + share.setIsFolder(false); + } + share.setPermissions(mPermissions); + + getStorageManager().saveShare(share); + + // Update OCFile with data from share: ShareByLink and publicLink + OCFile file = getStorageManager().getFileByPath(mPath); + if (file!=null) { + mSendIntent.putExtra(Intent.EXTRA_TEXT, share.getShareLink()); + file.setPublicLink(share.getShareLink()); + file.setShareByLink(true); + getStorageManager().saveFile(file); + Log_OC.d(TAG, "Public Link = " + file.getPublicLink()); + + } + } } diff --git a/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java b/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java new file mode 100644 index 00000000..560109f0 --- /dev/null +++ b/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java @@ -0,0 +1,145 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.operations; + +import java.util.ArrayList; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +/** + * Operation to find out what authentication method requires + * the server to access files. + * + * Basically, tries to access to the root folder without authorization + * and analyzes the response. + * + * When successful, the instance of {@link RemoteOperationResult} passed + * through {@link OnRemoteOperationListener#onRemoteOperationFinish(RemoteOperation, + * RemoteOperationResult)} returns in {@link RemoteOperationResult#getData()} + * a value of {@link AuthenticationMethod}. + * + * @author David A. Velasco + */ +public class DetectAuthenticationMethodOperation extends RemoteOperation { + + private static final String TAG = DetectAuthenticationMethodOperation.class.getSimpleName(); + + public enum AuthenticationMethod { + UNKNOWN, + NONE, + BASIC_HTTP_AUTH, + SAML_WEB_SSO, + BEARER_TOKEN + } + + private Context mContext; + + /** + * Constructor + * + * @param context Android context of the caller. + */ + public DetectAuthenticationMethodOperation(Context context) { + mContext = context; + } + + + /** + * Performs the operation. + * + * Triggers a check of existence on the root folder of the server, granting + * that the request is not authenticated. + * + * Analyzes the result of check to find out what authentication method, if + * any, is requested by the server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result = null; + AuthenticationMethod authMethod = AuthenticationMethod.UNKNOWN; + + RemoteOperation operation = new ExistenceCheckRemoteOperation("", mContext, false); + client.setBasicCredentials("", ""); + client.setFollowRedirects(false); + + // try to access the root folder, following redirections but not SAML SSO redirections + result = operation.execute(client); + while (result.isTemporalRedirection() && !result.isIdPRedirection()) { + client.setWebdavUri(Uri.parse(result.getRedirectedLocation())); + result = operation.execute(client); + } + + // analyze response + if (result.getCode() == ResultCode.UNAUTHORIZED) { + String authRequest = ((result.getAuthenticateHeader()).trim()).toLowerCase(); + if (authRequest.startsWith("basic")) { + authMethod = AuthenticationMethod.BASIC_HTTP_AUTH; + + } else if (authRequest.startsWith("bearer")) { + authMethod = AuthenticationMethod.BEARER_TOKEN; + } + // else - fall back to UNKNOWN + + } else if (result.isSuccess()) { + authMethod = AuthenticationMethod.NONE; + + } else if (result.isIdPRedirection()) { + authMethod = AuthenticationMethod.SAML_WEB_SSO; + } + // else - fall back to UNKNOWN + Log.d(TAG, "Authentication method found: " + authenticationMethodToString(authMethod)); + + ArrayList data = new ArrayList(); + data.add(authMethod); + result.setData(data); + return result; // same result instance, so that other errors can be handled by the caller transparently + } + + + private String authenticationMethodToString(AuthenticationMethod value) { + switch (value){ + case NONE: + return "NONE"; + case BASIC_HTTP_AUTH: + return "BASIC_HTTP_AUTH"; + case BEARER_TOKEN: + return "BEARER_TOKEN"; + case SAML_WEB_SSO: + return "SAML_WEB_SSO"; + default: + return "UNKNOWN"; + } + } + +} diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 5d957071..68e78578 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -192,11 +192,8 @@ public class SynchronizeFolderOperation extends RemoteOperation { sendLocalBroadcast(EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result); } - if (result.isSuccess() && mIsShareSupported) { - RemoteOperationResult shareResult = refreshSharesForFolder(client); - if (shareResult.getCode() != ResultCode.FILE_NOT_FOUND) { - result = shareResult; - } // else , keep the previous result ; being conservative for servers where Sharing API is supported, but disabled + if (result.isSuccess() && mIsShareSupported && !mSyncFullAccount) { + refreshSharesForFolder(client); // share result is ignored } if (!mSyncFullAccount) { @@ -339,6 +336,8 @@ public class SynchronizeFolderOperation extends RemoteOperation { if (remoteFile.isFolder()) { remoteFile.setFileLength(localFile.getFileLength()); // TODO move operations about size of folders to FileContentProvider } + remoteFile.setPublicLink(localFile.getPublicLink()); + remoteFile.setShareByLink(localFile.isShareByLink()); } else { remoteFile.setEtag(""); // remote eTag will not be updated unless contents are synchronized (Synchronize[File|Folder]Operation with remoteFile as parameter) } diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 4f1cd68a..a3394e61 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -35,13 +35,10 @@ import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UpdateOCVersionOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity; -import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.Log_OC; - import android.accounts.Account; import android.accounts.AccountsException; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.AbstractThreadedSyncAdapter; @@ -51,7 +48,7 @@ import android.content.Context; import android.content.Intent; import android.content.SyncResult; import android.os.Bundle; -//import android.support.v4.content.LocalBroadcastManager; +import android.support.v4.app.NotificationCompat; /** * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing @@ -385,8 +382,8 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { * Notifies the user about a failed synchronization through the status notification bar */ private void notifyFailedSynchronization() { - Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis()); - notification.flags |= Notification.FLAG_AUTO_CANCEL; + NotificationCompat.Builder notificationBuilder = createNotificationBuilder(); + notificationBuilder.setTicker(i18n(R.string.sync_fail_ticker)); boolean needsToUpdateCredentials = (mLastFailedResult != null && ( mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED || ( mLastFailedResult.isIdPRedirection() && @@ -395,7 +392,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { ) ); // TODO put something smart in the contentIntent below for all the possible errors - notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); + notificationBuilder.setContentTitle(i18n(R.string.sync_fail_ticker)); if (needsToUpdateCredentials) { // let the user update credentials with one click Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class); @@ -405,18 +402,17 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND); - notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT); - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_fail_ticker), - String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name), - notification.contentIntent); + notificationBuilder + .setContentIntent(PendingIntent.getActivity( + getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT + )) + .setContentText(i18n(R.string.sync_fail_content_unauthorized, getAccount().name)); } else { - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_fail_ticker), - String.format(getContext().getString(R.string.sync_fail_content), getAccount().name), - notification.contentIntent); + notificationBuilder + .setContentText(i18n(R.string.sync_fail_content, getAccount().name)); } - ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification); + + showNotification(R.string.sync_fail_ticker, notificationBuilder); } @@ -427,26 +423,31 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { */ private void notifyFailsInFavourites() { if (mFailedResultsCounter > 0) { - Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis()); - notification.flags |= Notification.FLAG_AUTO_CANCEL; + NotificationCompat.Builder notificationBuilder = createNotificationBuilder(); + notificationBuilder.setTicker(i18n(R.string.sync_fail_in_favourites_ticker)); + // TODO put something smart in the contentIntent below - notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_fail_in_favourites_ticker), - String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound), - notification.contentIntent); - ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification); + notificationBuilder + .setContentIntent(PendingIntent.getActivity( + getContext(), (int) System.currentTimeMillis(), new Intent(), 0 + )) + .setContentTitle(i18n(R.string.sync_fail_in_favourites_ticker)) + .setContentText(i18n(R.string.sync_fail_in_favourites_content, mFailedResultsCounter + mConflictsFound, mConflictsFound)); + showNotification(R.string.sync_fail_in_favourites_ticker, notificationBuilder); } else { - Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis()); - notification.flags |= Notification.FLAG_AUTO_CANCEL; + NotificationCompat.Builder notificationBuilder = createNotificationBuilder(); + notificationBuilder.setTicker(i18n(R.string.sync_conflicts_in_favourites_ticker)); + // TODO put something smart in the contentIntent below - notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_conflicts_in_favourites_ticker), - String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound), - notification.contentIntent); - ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification); + notificationBuilder + .setContentIntent(PendingIntent.getActivity( + getContext(), (int) System.currentTimeMillis(), new Intent(), 0 + )) + .setContentTitle(i18n(R.string.sync_conflicts_in_favourites_ticker)) + .setContentText(i18n(R.string.sync_conflicts_in_favourites_ticker, mConflictsFound)); + + showNotification(R.string.sync_conflicts_in_favourites_ticker, notificationBuilder); } } @@ -460,9 +461,9 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory. */ private void notifyForgottenLocalFiles() { - Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis()); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - + NotificationCompat.Builder notificationBuilder = createNotificationBuilder(); + notificationBuilder.setTicker(i18n(R.string.sync_foreign_files_forgotten_ticker)); + /// includes a pending intent in the notification showing a more detailed explanation Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class); explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount()); @@ -474,14 +475,45 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths); explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0); - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_foreign_files_forgotten_ticker), - String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)), - notification.contentIntent); - ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification); + notificationBuilder + .setContentIntent(PendingIntent.getActivity( + getContext(), (int) System.currentTimeMillis(), explanationIntent, 0 + )) + .setContentTitle(i18n(R.string.sync_foreign_files_forgotten_ticker)) + .setContentText(i18n(R.string.sync_foreign_files_forgotten_content, mForgottenLocalFiles.size(), i18n(R.string.app_name))); + showNotification(R.string.sync_foreign_files_forgotten_ticker, notificationBuilder); } + /** + * Creates a notification builder with some commonly used settings + * + * @return + */ + private NotificationCompat.Builder createNotificationBuilder() { + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getContext()); + notificationBuilder.setSmallIcon(R.drawable.notification_icon).setAutoCancel(true); + return notificationBuilder; + } + /** + * Builds and shows the notification + * + * @param id + * @param builder + */ + private void showNotification(int id, NotificationCompat.Builder builder) { + ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)) + .notify(id, builder.build()); + } + /** + * Shorthand translation + * + * @param key + * @param args + * @return + */ + private String i18n(int key, Object... args) { + return getContext().getString(key, args); + } } diff --git a/src/com/owncloud/android/ui/CheckBoxPreferenceWithLongTitle.java b/src/com/owncloud/android/ui/CheckBoxPreferenceWithLongTitle.java new file mode 100644 index 00000000..dac083af --- /dev/null +++ b/src/com/owncloud/android/ui/CheckBoxPreferenceWithLongTitle.java @@ -0,0 +1,47 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; +import android.preference.CheckBoxPreference; + +public class CheckBoxPreferenceWithLongTitle extends CheckBoxPreference{ + + public CheckBoxPreferenceWithLongTitle(Context context) { + super(context); + } + + public CheckBoxPreferenceWithLongTitle(Context context, AttributeSet attrs) { + super(context, attrs); + } + public CheckBoxPreferenceWithLongTitle(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + TextView titleView = (TextView) view.findViewById(android.R.id.title); + titleView.setSingleLine(false); + titleView.setMaxLines(3); + titleView.setEllipsize(null); + } +} \ No newline at end of file diff --git a/src/com/owncloud/android/ui/PreferenceMultiline.java b/src/com/owncloud/android/ui/PreferenceMultiline.java new file mode 100644 index 00000000..28b3621e --- /dev/null +++ b/src/com/owncloud/android/ui/PreferenceMultiline.java @@ -0,0 +1,53 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.ui; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +/** + * Allow multiline titles in preferences + * + * @author masensio + * + */ +public class PreferenceMultiline extends Preference { + + public PreferenceMultiline(Context context) { + super(context); + } + + public PreferenceMultiline(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PreferenceMultiline(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + TextView titleView = (TextView) view.findViewById(android.R.id.title); + titleView.setSingleLine(false); + titleView.setMaxLines(3); + } +} diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 4593e3f0..633c0e7a 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -79,7 +79,6 @@ import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareLinkOperation; import com.owncloud.android.services.OperationsService; import com.owncloud.android.syncadapter.FileSyncAdapter; -import com.owncloud.android.ui.adapter.SslErrorViewAdapter; import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; diff --git a/src/com/owncloud/android/utils/NotificationBuilderWithProgressBar.java b/src/com/owncloud/android/utils/NotificationBuilderWithProgressBar.java new file mode 100644 index 00000000..c47e469e --- /dev/null +++ b/src/com/owncloud/android/utils/NotificationBuilderWithProgressBar.java @@ -0,0 +1,131 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.utils; + +import com.owncloud.android.R; + +import android.app.Notification; +import android.content.Context; +import android.os.Build; +import android.support.v4.app.NotificationCompat; +import android.view.View; +import android.widget.RemoteViews; + +/** + * Extends the support class {@link NotificationCompat.Builder} to grant that + * a progress bar is available in every Android version, because + * {@link NotificationCompat.Builder#setProgress(int, int, boolean)} has no + * real effect for Android < 4.0 + * + * @author David A. Velasco + */ +public class NotificationBuilderWithProgressBar extends NotificationCompat.Builder { + + /** + * Custom view to replace the original layout of the notifications + */ + private RemoteViews mContentView = null; + + /** + * Fatory method. + * + * Instances of this class will be only returned in Android versions needing it. + * + * @param context Context that will use the builder to create notifications + * @return An instance of this class, or of the regular + * {@link NotificationCompat.Builder}, when it is good enough. + */ + public static NotificationCompat.Builder newNotificationBuilderWithProgressBar(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new NotificationBuilderWithProgressBar(context); + } else { + return new NotificationCompat.Builder(context); + } + } + + /** + * Constructor. + * + * @param context Context that will use the builder to create notifications. + */ + private NotificationBuilderWithProgressBar(Context context) { + super(context); + mContentView = new RemoteViews(context.getPackageName(), R.layout.notification_with_progress_bar); + setContent(mContentView); + } + + /** + * {@inheritDoc} + */ + @Override + public NotificationCompat.Builder setProgress(int max, int progress, boolean indeterminate) { + mContentView.setProgressBar(R.id.progress, max, progress, indeterminate); + if (max > 0) { + mContentView.setViewVisibility(R.id.progressHolder, View.VISIBLE); + } else { + mContentView.setViewVisibility(R.id.progressHolder, View.GONE); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public NotificationCompat.Builder setSmallIcon(int icon) { + super.setSmallIcon(icon); // necessary + mContentView.setImageViewResource(R.id.icon, icon); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public NotificationCompat.Builder setContentTitle(CharSequence title) { + super.setContentTitle(title); + mContentView.setTextViewText(R.id.title, title); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public NotificationCompat.Builder setContentText(CharSequence text) { + super.setContentText(text); + mContentView.setTextViewText(R.id.text, text); + if (text != null && text.length() > 0) { + mContentView.setViewVisibility(R.id.text, View.VISIBLE); + } else { + mContentView.setViewVisibility(R.id.text, View.GONE); + } + return this; + } + + @Override + public Notification build() { + Notification result = super.build(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // super.build() in Android 2.x totally ruins whatever was made #setContent + result.contentView = mContentView; + } + return result; + } + +} diff --git a/third_party/android-support-library/android-support-v4.jar b/third_party/android-support-library/android-support-v4.jar index feaf44f8..96644edb 100644 Binary files a/third_party/android-support-library/android-support-v4.jar and b/third_party/android-support-library/android-support-v4.jar differ