From 5b756f693949712efb5adbe15c80a96148a8f08d Mon Sep 17 00:00:00 2001 From: Logan Date: Mon, 2 Feb 2015 15:25:47 +0800 Subject: [PATCH] download a folder --- AndroidManifest.xml | 13 +- .../list_item_download_finished.png | Bin 0 -> 3722 bytes .../list_item_download_waiting.png | Bin 0 -> 1438 bytes res/layout-sw600dp/list_item_entry.xml | 80 ++++ res/layout-xlarge/list_item_entry.xml | 80 ++++ res/layout/list_item_entry.xml | 116 +++--- ...d_list_item.xml => transfer_list_item.xml} | 43 +- res/layout/transfer_task_fragment.xml | 43 ++ res/layout/upload_tasks_activity.xml | 17 - res/menu-sw480dp/browser_menu.xml | 11 +- res/menu/browser_menu.xml | 11 +- res/menu/transfer_list_menu.xml | 19 + ...d_task_menu.xml => transfer_task_menu.xml} | 4 + res/values-es-rAR/strings.xml | 2 + res/values-es/strings.xml | 2 + res/values-fr/strings.xml | 2 + res/values-hu/strings.xml | 2 + res/values-ko/strings.xml | 2 + res/values-pl-rPL/strings.xml | 2 + res/values-sw320dp/dimens.xml | 9 - res/values-sw600dp/dimens.xml | 6 +- res/values-tr/strings.xml | 2 + res/values-zh-rCN/strings.xml | 2 + res/values/colors.xml | 2 +- res/values/dimens.xml | 7 + res/values/strings.xml | 11 +- .../cameraupload/CameraUploadService.java | 20 +- .../seafile/seadroid2/data/DataManager.java | 3 +- .../seadroid2/transfer/DownloadTaskInfo.java | 42 +- .../seadroid2/transfer/TransferManager.java | 379 +++++++++++++++++- .../seadroid2/transfer/TransferService.java | 93 ++++- .../seadroid2/transfer/TransferTaskInfo.java | 65 +++ .../seadroid2/transfer/UploadTaskInfo.java | 57 ++- .../ui/activity/BrowserActivity.java | 254 +++++++----- .../ui/activity/TransferActivity.java | 175 ++++++++ .../ui/activity/UploadTasksActivity.java | 319 --------------- .../seadroid2/ui/adapter/SeafItemAdapter.java | 139 ++++++- .../ui/adapter/TransferTaskAdapter.java | 257 ++++++++++++ .../ui/adapter/UploadTasksAdapter.java | 247 ------------ .../ui/fragment/DownloadTaskFragment.java | 181 +++++++++ .../seadroid2/ui/fragment/ReposFragment.java | 31 +- .../ui/fragment/TransferTaskFragment.java | 190 +++++++++ .../ui/fragment/UploadTaskFragment.java | 178 ++++++++ 43 files changed, 2253 insertions(+), 865 deletions(-) create mode 100644 res/drawable-xhdpi/list_item_download_finished.png create mode 100644 res/drawable-xhdpi/list_item_download_waiting.png create mode 100644 res/layout-sw600dp/list_item_entry.xml create mode 100644 res/layout-xlarge/list_item_entry.xml rename res/layout/{upload_list_item.xml => transfer_list_item.xml} (55%) create mode 100644 res/layout/transfer_task_fragment.xml delete mode 100644 res/layout/upload_tasks_activity.xml create mode 100644 res/menu/transfer_list_menu.xml rename res/menu/{upload_task_menu.xml => transfer_task_menu.xml} (80%) delete mode 100644 res/values-sw320dp/dimens.xml create mode 100644 src/com/seafile/seadroid2/transfer/TransferTaskInfo.java create mode 100644 src/com/seafile/seadroid2/ui/activity/TransferActivity.java delete mode 100644 src/com/seafile/seadroid2/ui/activity/UploadTasksActivity.java create mode 100644 src/com/seafile/seadroid2/ui/adapter/TransferTaskAdapter.java delete mode 100644 src/com/seafile/seadroid2/ui/adapter/UploadTasksAdapter.java create mode 100644 src/com/seafile/seadroid2/ui/fragment/DownloadTaskFragment.java create mode 100644 src/com/seafile/seadroid2/ui/fragment/TransferTaskFragment.java create mode 100644 src/com/seafile/seadroid2/ui/fragment/UploadTaskFragment.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 663499dde..e715ee6c2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -34,12 +34,6 @@ android:label="@string/app_name"> - - - + + + + KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>RI+y?e7jKeZ#YO-C z1Gh;;K~#9!%-B(A8+9DO@$dg~xu$J;)AdTH*%WIx=XBP<)S&~Ng@IN2$- zq*?tvUM|1;Dxn4n5!;|ly3`Khv9uiMU!K2do6VLj_ zpI-+XJ-4wX#JM>Z^$J12iLpLjKt=QmxujeE|2ZK$14WN z70n^3wsCUBYMws)ygBmX5wDm)E*`$#3j^dFD{U8nOkGLWM0QMg%^-d;Vt)^{8`-I$__YZ3zC zVf?D5bjyrssB4ce=do0Kh2eXy{S zRuESL8y_ADwLCtl#*D4Nps6cUZky|Jn_UDL1U94+ok_>Gh4tIm)55tL@c~JrZTnJL z-*QQf8QThb)}W~?$Co>f5i6Y!Y5i6h*az%fxRy$EChc4%kemMQ5n<;d;DidTj15?h z2+WC_TE17B?wSKAKmcZd1E#JdOPvp011&_`{!a^MCIW;&1Y{&R$mH7{yMb4LTgn)& z4DdB@C_3(n?99xJ zL?VH4py>Sk{KCRQPfrinFxTtXuOSzV#>U3}{{HFdX)vHwjYb1wc6N4ZwOY`?2a&Fb zTCIlhK~yRgG$0m>0eQJ1;DXG}%|VpGU`$RT;DWkZ``h83?My-x2CWXc&kg&?LPL~MpiOSjY5 z=PbuasI@E2eKHS3&SxXJ%dPQ0fKtF2y5(mPQOzLl-;T8KGPSy_7%75`#=3Er5ahFm zDfJD443)3B%2Xg9<2KS3)eV)K!stz^OU945M3cfdOO!2KsrH#I>xJ}#N~ikT{XtR1 zS=tmCzWHB#eQ*JX1R0XW+v1~ED1|Gjs2jZ%oxsi=llX-CGp>fQ#ne}IiyIbsDQtho z^sQ(`h(l6L`R2YF<*aip_7N)iNW(~idVKh;oa?wtJ+jjtccJNz`;T{IpUMewjnh2L zY4R$Q6`NfCLcacKU{D^pTJ-xo)@-W%b2|alD=g&q*H(`u7 z?4_1$>P&FYv=k-ozWi>gP3{wO&*;RTX4}Uu7knwgy{x%b`z*jGB{w8H&&9IkJd0a% zEXcvgF{p0nV*Z&O^X#As6KiUyoz;Q#gtf8LyB$J;_f_K`W}W%xl0^U`CH3wVS;>oV zp7(EppvaoID7F#ZJByNfjCa8828Qt_{17n`Tjrb3Z%8ZPqHEa1TukIQ zkzulJ!kDmk9Z!Qj7j5$GK1@;$Wxk(gO3C* zxFD3pY!y;TxEXT&YW3`7Bc3rFEWAS+9c@M7G6|WVY=!PO9n>lkZip^TBzWx$_X=wc z-m`~LSRkEN*f8^lj}7DLzeY@%xNf*vxjszit*{Ga-17Y056hf0z7!uDTKMlkO%O?J zlX4YF@2!eXK7s!shBR4F{UT?EN2L9fn0WmLKK^u;6GP4Nd)T<0HmwgTtdGp310O goG78EwbNIKaXjS{hHZn{x%}Js;{AMRUJ-=<0Ay%{ga7~l literal 0 HcmV?d00001 diff --git a/res/layout-sw600dp/list_item_entry.xml b/res/layout-sw600dp/list_item_entry.xml new file mode 100644 index 000000000..8e9f58227 --- /dev/null +++ b/res/layout-sw600dp/list_item_entry.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout-xlarge/list_item_entry.xml b/res/layout-xlarge/list_item_entry.xml new file mode 100644 index 000000000..8f93b59ba --- /dev/null +++ b/res/layout-xlarge/list_item_entry.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/list_item_entry.xml b/res/layout/list_item_entry.xml index 154324163..8f93b59ba 100644 --- a/res/layout/list_item_entry.xml +++ b/res/layout/list_item_entry.xml @@ -2,55 +2,79 @@ + android:padding="@dimen/lv_item_padding"> - + - + - + - + + + + + + + + \ No newline at end of file diff --git a/res/layout/upload_list_item.xml b/res/layout/transfer_list_item.xml similarity index 55% rename from res/layout/upload_list_item.xml rename to res/layout/transfer_list_item.xml index 3244641ab..3cfe2f23c 100644 --- a/res/layout/upload_list_item.xml +++ b/res/layout/transfer_list_item.xml @@ -5,60 +5,61 @@ android:padding="10dp" > + android:textSize="@dimen/lv_subtitle_txt_size" /> + android:textSize="@dimen/lv_title_txt_size" /> + android:textSize="@dimen/lv_download_state_txt_size" /> + android:textSize="@dimen/lv_download_state_txt_size" /> diff --git a/res/layout/transfer_task_fragment.xml b/res/layout/transfer_task_fragment.xml new file mode 100644 index 000000000..a3beda444 --- /dev/null +++ b/res/layout/transfer_task_fragment.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + diff --git a/res/layout/upload_tasks_activity.xml b/res/layout/upload_tasks_activity.xml deleted file mode 100644 index a020ac586..000000000 --- a/res/layout/upload_tasks_activity.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/res/menu-sw480dp/browser_menu.xml b/res/menu-sw480dp/browser_menu.xml index 9451ce6b2..dcfeb9ad7 100644 --- a/res/menu-sw480dp/browser_menu.xml +++ b/res/menu-sw480dp/browser_menu.xml @@ -21,7 +21,12 @@ android:showAsAction="always" android:title="More"> - + + @@ -36,9 +41,9 @@ android:title="@string/camera" android:showAsAction="never"/> - - + + + - + + + + + + Cancel + + + + \ No newline at end of file diff --git a/res/menu/upload_task_menu.xml b/res/menu/transfer_task_menu.xml similarity index 80% rename from res/menu/upload_task_menu.xml rename to res/menu/transfer_task_menu.xml index e638d797e..919630e1b 100644 --- a/res/menu/upload_task_menu.xml +++ b/res/menu/transfer_task_menu.xml @@ -13,6 +13,10 @@ android:visible="true" android:title="@string/task_remove" /> + + diff --git a/res/values-es-rAR/strings.xml b/res/values-es-rAR/strings.xml index ac228a42e..7b844d4be 100644 --- a/res/values-es-rAR/strings.xml +++ b/res/values-es-rAR/strings.xml @@ -133,6 +133,8 @@ Bibliotecas Actividades Favoritos + Lista de descarga + Lista de subida Provea la contraseña para la biblioteca \"%s\" Esperando Finalizado diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index ac228a42e..7b844d4be 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -133,6 +133,8 @@ Bibliotecas Actividades Favoritos + Lista de descarga + Lista de subida Provea la contraseña para la biblioteca \"%s\" Esperando Finalizado diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index db967bb09..798f8b159 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -133,6 +133,8 @@ Bibliothèques Activités Préférés + Liste de téléchargement + Liste d\'envois Mot de passe pour la bibliothèque \"%s\" En attente Terminé diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 051d617e0..4f99bdfc8 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -133,6 +133,8 @@ Kötetek Tevékenységek Csillagozott + Letöltési lista + Feltöltési lista Jelszó megadása ehhez: \"%s\" Vár Kész diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 66c7d9eb2..e46c6cea6 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -133,6 +133,8 @@ 라이브러리 활동 별표 표시 + 다운로드 목록 + 업로드 목록 \"%s\" 라이브러리에 대한 암호를 입력하세요 기다리는 중 끝냈습니다 diff --git a/res/values-pl-rPL/strings.xml b/res/values-pl-rPL/strings.xml index 618401444..2e49b9f51 100644 --- a/res/values-pl-rPL/strings.xml +++ b/res/values-pl-rPL/strings.xml @@ -133,6 +133,8 @@ Biblioteki Aktywność Ulubione + Lista pobierania + Lista wysyłania Podaj hasło do biblioteki \"%s\" Oczekiwanie Zakończono diff --git a/res/values-sw320dp/dimens.xml b/res/values-sw320dp/dimens.xml deleted file mode 100644 index f18bd2308..000000000 --- a/res/values-sw320dp/dimens.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - 0dp - - 16dp - - diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index e10656f4d..1aa4c653a 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -12,11 +12,13 @@ 60dp 10dp 60dp + 14dp 60dp 12dp 18sp 16sp 12dp + 14sp 70dp 70dp @@ -58,8 +60,8 @@ 16sp 0dp - 16dp + 32dp - \ No newline at end of file + diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 452274301..341ac5118 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -133,6 +133,8 @@ Kütüphaneler Etkinlikler Yıldızlı + İndirme Listesi + Yükleme Listesi \"%s\" kütüphanesi için şifreyi giriniz Bekliyor Bitti diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index ff52fe153..635c2562b 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -134,6 +134,8 @@ 资料库 最近修改 星标文件 + 下载列表 + 上传列表 请提供资料库\"%s\"的密码 等待中. . . 已完成 diff --git a/res/values/colors.xml b/res/values/colors.xml index db4a1ed5e..433d1649d 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -29,7 +29,7 @@ #888 - #282828 + #282828 #555555 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 7d5325e58..f44d777c3 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -41,12 +41,19 @@ 4dp 11dp 60dp + 8dp 60dp 4dp 8dp 16sp 8dp 14sp + 14dp + 14dp + 4dp + 4dp + 2dp + 12sp 48dp 48dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 52c910feb..1e8ace3b7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -29,7 +29,7 @@ Activities Upload https - Uploads + Transfer List Accounts Settings @@ -62,6 +62,7 @@ Cancel Retry Remove + Remove all cancelled Remove all finished Download Remove Cache @@ -106,6 +107,7 @@ Failed to load libraries Failed to load contents of this folder This type of share is not supported + Download folder Create a new file Create a new folder Take a photo here @@ -117,6 +119,7 @@ The library have been deleted. Export this file No upload tasks yet + No download tasks yet Note: Server address should contain the port. For example, www.example.com:8000 Copy link Share a link to this file @@ -296,4 +299,10 @@ Input server... Login with Shibboleth loading... + + + Network connection error, please try again later! + Cancel all + Download List + Upload List diff --git a/src/com/seafile/seadroid2/cameraupload/CameraUploadService.java b/src/com/seafile/seadroid2/cameraupload/CameraUploadService.java index d5d37a105..dd3a8c0d6 100644 --- a/src/com/seafile/seadroid2/cameraupload/CameraUploadService.java +++ b/src/com/seafile/seadroid2/cameraupload/CameraUploadService.java @@ -79,14 +79,8 @@ public void onCreate() { } private void cancelUploadTasks(){ - List cameraUploadsTasksList = mTransferService.getAllUploadTaskInfos(); - for (UploadTaskInfo uploadTaskInfo : cameraUploadsTasksList) { - // use isCopyToLocal as a flag to mark a camera photo upload task if false - // mark a file upload task if true - if (!uploadTaskInfo.isCopyToLocal) { - mTransferService.cancelUploadTask(uploadTaskInfo.taskID); - } - } + + mTransferService.cancelAllCameraUploadTasks(); Intent localIntent = new Intent(TransferService.BROADCAST_ACTION).putExtra("type", BROADCAST_CAMERA_UPLOAD_SERVICE_STOPPED); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(localIntent); @@ -141,7 +135,7 @@ public void onServiceConnected(ComponentName className, IBinder service) { mTransferService = binder.getService(); for (PendingUploadInfo info : pendingUploads) { - mTransferService.addUploadTask(account, info.repoID, + mTransferService.addTaskToUploadQue(account, info.repoID, info.repoName, info.targetDir, info.localFilePath, info.isUpdate, info.isCopyToLocal); } @@ -154,11 +148,11 @@ public void onServiceDisconnected(ComponentName arg0) { } }; - private void addUploadCameraPhotoTask(String repoID, String repoName, String targetDir, String localFilePath) { + private void addCameraUploadTask(String repoID, String repoName, String targetDir, String localFilePath) { if (mTransferService != null) { // set the last parameter "isUpdate" to true to stop copying file into sd-card // if passed "false" will cause OOM when uploading photos - mTransferService.addUploadTask(account, repoID, repoName, targetDir, localFilePath, false, false); + mTransferService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, false, false); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, false, false); pendingUploads.add(info); @@ -260,7 +254,7 @@ protected void onPostExecute(List result) { SeafCachedPhoto cp = cUploadManager.getCachedPhoto(repoName, repoId, DIR, path); if (cp == null) { // add photos to uploading queue - addUploadCameraPhotoTask(repoId, repoName, CAMERA_UPLOAD_REMOTE_PARENTDIR + CAMERA_UPLOAD_REMOTE_DIR, photo.getAbsolutePath()); + addCameraUploadTask(repoId, repoName, CAMERA_UPLOAD_REMOTE_PARENTDIR + CAMERA_UPLOAD_REMOTE_DIR, photo.getAbsolutePath()); } } } @@ -281,7 +275,7 @@ protected void onPostExecute(File photo) { SeafCachedPhoto cachePhoto = cUploadManager.getCachedPhoto(repoName, repoId, DIR, photo.getName()); if (cachePhoto == null) { - addUploadCameraPhotoTask(repoId, repoName, CAMERA_UPLOAD_REMOTE_PARENTDIR + addCameraUploadTask(repoId, repoName, CAMERA_UPLOAD_REMOTE_PARENTDIR + CAMERA_UPLOAD_REMOTE_DIR, photo.getAbsolutePath()); } } diff --git a/src/com/seafile/seadroid2/data/DataManager.java b/src/com/seafile/seadroid2/data/DataManager.java index 51b9e809a..ac2f0f778 100644 --- a/src/com/seafile/seadroid2/data/DataManager.java +++ b/src/com/seafile/seadroid2/data/DataManager.java @@ -477,10 +477,11 @@ public List getCachedDirents(String repoID, String path) { } /** - * In two cases we need to visit the server for dirents + * In three cases we need to visit the server for dirents * * 1. No cached dirents * 2. User clicks "refresh" button. + * 3. Download all dirents within a folder * * In the second case, the local cache may still be valid. */ diff --git a/src/com/seafile/seadroid2/transfer/DownloadTaskInfo.java b/src/com/seafile/seadroid2/transfer/DownloadTaskInfo.java index e021206a8..a4aca5aa3 100644 --- a/src/com/seafile/seadroid2/transfer/DownloadTaskInfo.java +++ b/src/com/seafile/seadroid2/transfer/DownloadTaskInfo.java @@ -4,30 +4,32 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.transfer.TransferManager.TaskState; -public class DownloadTaskInfo { - public final Account account; - public final int taskID; - public final TaskState state; - public final String repoID; - public final String repoName; +public class DownloadTaskInfo extends TransferTaskInfo { + public final String pathInRepo; - public final String localFilePath; public final long fileSize, finished; - public final SeafException err; - public DownloadTaskInfo(Account account, int taskID, TaskState state, String repoID, - String repoName, String path, String localPath, - long fileSize, long finished, - SeafException err) { - this.account = account; - this.taskID = taskID; - this.state = state; - this.repoID = repoID; - this.repoName = repoName; - this.pathInRepo = path; - this.localFilePath = localPath; + /** + * Constructor of DownloadTaskInfo + * + * @param account Current login Account instance + * @param taskID TransferTask id + * @param state TransferTask state, value is one of INIT, TRANSFERRING, FINISHED, CANCELLED, FAILED of {@link TaskState} + * @param repoID Repository id + * @param repoName Repository name + * @param pathInRepo File path in Repository + * @param localPath Local path + * @param fileSize File total size + * @param finished File downloaded size + * @param err Exception instance of {@link SeafException} + */ + public DownloadTaskInfo(Account account, int taskID, TaskState state, + String repoID, String repoName, String pathInRepo, + String localPath, long fileSize, long finished, SeafException err) { + super(account, taskID, state, repoID, repoName, localPath, err); + + this.pathInRepo = pathInRepo; this.fileSize = fileSize; this.finished = finished; - this.err = err; } } diff --git a/src/com/seafile/seadroid2/transfer/TransferManager.java b/src/com/seafile/seadroid2/transfer/TransferManager.java index 8701e1129..14174fa29 100644 --- a/src/com/seafile/seadroid2/transfer/TransferManager.java +++ b/src/com/seafile/seadroid2/transfer/TransferManager.java @@ -1,18 +1,19 @@ package com.seafile.seadroid2.transfer; -import java.io.File; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - import android.os.AsyncTask; import android.util.Log; - import com.google.common.collect.Lists; +import com.seafile.seadroid2.ConcurrentAsyncTask; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.data.DataManager; import com.seafile.seadroid2.data.ProgressMonitor; +import com.seafile.seadroid2.util.Utils; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; /** * Manages file downloading and uploading. @@ -40,6 +41,14 @@ public interface TransferListener { private int notificationID; TransferListener listener; + private List uploadWaitingList = Lists.newArrayList(); + private List uploadingList = Lists.newArrayList(); + private static final int UPLOAD_MAX_COUNT = 2; + + private List downloadingList = Lists.newArrayList(); + private List downloadWaitingList = Lists.newArrayList(); + private static final int DOWNLOAD_MAX_COUNT = 2; + public TransferManager() { notificationID = 0; uploadTasks = Lists.newArrayList(); @@ -57,11 +66,12 @@ public void unsetListener() { /** * Add a new upload task + * this method is deprecated. */ public int addUploadTask(Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal) { Iterator iter = uploadTasks.iterator(); - + while (iter.hasNext()) { UploadTask task = iter.next(); if (task.myRepoID.equals(repoID) && task.myPath.equals(filePath)) { @@ -83,8 +93,97 @@ public int addUploadTask(Account account, String repoID, String repoName, return task.getTaskID(); } + + /** + * add upload task to queue + * @param account + * @param repoID + * @param repoName + * @param dir + * @param filePath + * @param isUpdate + * @param isCopyToLocal + */ + public int addTaskToUploadQue(Account account, + String repoID, + String repoName, + String dir, + String filePath, + boolean isUpdate, + boolean isCopyToLocal) { + + int taskID = -1; + synchronized (uploadWaitingList) { + boolean hasInQue = false; + for (UploadTask uploadTask : uploadWaitingList) { + if (uploadTask.myPath.equals(filePath)) { + hasInQue = true; + taskID = uploadTask.getTaskID(); + // Log.d(DEBUG_TAG, "in Que " + index + " " + filePath + "in waiting list"); + break; + } + } + + for (UploadTask uploadTask : uploadingList) { + if (uploadTask.myPath.equals(filePath)) { + hasInQue = true; + taskID = uploadTask.getTaskID(); + // Log.d(DEBUG_TAG, "in Que " + index + " " + filePath + "in uploading list"); + break; + } + } + + if (!hasInQue) { + UploadTask task = new UploadTask(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal); + // Log.d(DEBUG_TAG, "add Que " + task.getTaskID() + " " + filePath); + taskID = task.getTaskID(); + + uploadWaitingList.add(task); + + uploadNext(); + } + } + return taskID; + } + + private void uploadNextInQue(int taskID) { + synchronized (uploadWaitingList) { + removeCurrentUploadItem(taskID); + uploadNext(); + } + } + + public void uploadNext() { + // Log.d(DEBUG_TAG, "UploadingCount:" + uploadingList.size()); + if (!uploadWaitingList.isEmpty() + && uploadingList.size() < UPLOAD_MAX_COUNT) { + // Log.d(DEBUG_TAG, "do next!"); + + UploadTask item = uploadWaitingList.get(0); + uploadingList.add(item); + uploadWaitingList.remove(0); + ConcurrentAsyncTask.execute(item); + } + } + + private void removeCurrentUploadItem(int taskID) { + // Log.d(DEBUG_TAG, "removeCurrentUploadItem:" + taskID); + if (!uploadingList.isEmpty()) { + UploadTask toRemove = getUploadTaskByID(taskID); + for (int i = 0; i < uploadingList.size(); i++) { + if (uploadingList.get(i).myPath.equals(toRemove.myPath)) { + // Log.d(DEBUG_TAG, "Done Que " + taskID + " " + uploadingList.get(i).myPath); + uploadingList.remove(i); + break; + } + } + } + + } + /** * Add a new download task + * this method is deprecated. */ public int addDownloadTask(Account account, String repoName, @@ -112,6 +211,141 @@ public int addDownloadTask(Account account, return task.getTaskID(); } + public int addTaskToDownloadQue(Account account, + String repoName, + String repoID, + String path) { + int taskID = -1; + synchronized (downloadWaitingList) { + boolean hasInQue = false; + for (DownloadTask downloadTask : downloadWaitingList) { + if (downloadTask.myRepoName.equals(repoName) && + downloadTask.myPath.equals(path)) { + hasInQue = true; + taskID = downloadTask.getTaskID(); + // Log.d(DEBUG_TAG, "in Que " + index + " " + repoName + path + "in waiting list"); + break; + } + } + + for (DownloadTask downloadTask : downloadingList) { + if (downloadTask.myRepoName.equals(repoName) && + downloadTask.myPath.equals(path)) { + hasInQue = true; + taskID = downloadTask.getTaskID(); + // Log.d(DEBUG_TAG, "in Que " + index + " " + repoName + path + " in downloading list" ); + break; + } + } + + if (!hasInQue) { + DownloadTask task = new DownloadTask(account, repoName, repoID, path); + + taskID = task.getTaskID(); + // Log.d(DEBUG_TAG, "add Que " + task.getTaskID() + " " + repoName + path); + downloadWaitingList.add(task); + downloadNext(); + } + } + return taskID; + } + + public void downloadNext() { + // Log.d(DEBUG_TAG, "DownloadingCount:" + downloadWaitingList.size()); + if (!downloadWaitingList.isEmpty() + && downloadingList.size() < DOWNLOAD_MAX_COUNT) { + // Log.d(DEBUG_TAG, "do next!"); + + DownloadTask item = downloadWaitingList.get(0); + downloadingList.add(item); + downloadWaitingList.remove(0); + ConcurrentAsyncTask.execute(item); + } + } + + private void downloadNextInQue(int taskID) { + synchronized (downloadWaitingList) { + removeCurrentDownloadItem(taskID); + downloadNext(); + } + } + + private void removeCurrentDownloadItem(int taskID) { + // Log.d(DEBUG_TAG, "removeCurrentDownloadItem:" + taskID); + + if (!downloadingList.isEmpty()) { + DownloadTask toRemove = getDownloadTaskByID(taskID); + for (int i = 0; i < downloadingList.size(); i++) { + if (downloadingList.get(i).myRepoName.equals(toRemove.myRepoName) && + downloadingList.get(i).myPath.equals(toRemove.myPath)) { + // Log.d(DEBUG_TAG, "Done Que " + taskID + " " + toRemove.myRepoName + toRemove.myPath); + downloadingList.remove(i); + break; + } + } + } + + } + + public void removeCancelledDownloadItemInQue(int taskID) { + + synchronized (downloadWaitingList) { + + DownloadTask toCancel = getDownloadTaskByID(taskID); + if (!downloadWaitingList.isEmpty()) { + for (int i = 0; i < downloadWaitingList.size(); i++) { + if (downloadWaitingList.get(i).myRepoName.equals(toCancel.myRepoName) && + downloadWaitingList.get(i).myPath.equals(toCancel.myPath)) { + // Log.d(DEBUG_TAG, "CancelQue " + taskID + " " + toRemove.myRepoName + toRemove.myPath + " in waiting list"); + downloadWaitingList.remove(i); + break; + } + } + } + + if (!downloadingList.isEmpty()) { + for (int i = 0; i< downloadingList.size(); i++) { + if (downloadingList.get(i).myRepoName.equals(toCancel.myRepoName) && + downloadingList.get(i).myPath.equals(toCancel.myPath)) { + // Log.d(DEBUG_TAG, "CancelQue " + taskID + " " + toRemove.myRepoName + toRemove.myPath + " in downloading list"); + downloadingList.remove(i); + break; + } + } + } + } + + } + + public void removeCancelledUploadItemInQue(int taskID) { + + synchronized (uploadWaitingList) { + + UploadTask toCancel = getUploadTaskByID(taskID); + + if (!uploadWaitingList.isEmpty()) { + for (int i = 0; i < uploadWaitingList.size(); i++) { + if (uploadWaitingList.get(i).myPath.equals(toCancel.myPath)) { + // Log.d(DEBUG_TAG, "CancelQue " + taskID + " " + toCancel.myPath + " in waiting list"); + uploadWaitingList.remove(i); + break; + } + } + } + + if (!uploadingList.isEmpty()) { + for (int i = 0; i< uploadingList.size(); i++) { + if (uploadingList.get(i).myPath.equals(toCancel.myPath)) { + // Log.d(DEBUG_TAG, "CancelQue " + taskID + " " + toCancel.myPath + " in uploading list"); + uploadingList.remove(i); + break; + } + } + } + } + + } + private UploadTask getUploadTaskByID(int taskID) { for (UploadTask task : uploadTasks) { if (task.getTaskID() == taskID) { @@ -120,7 +354,7 @@ private UploadTask getUploadTaskByID(int taskID) { } return null; } - + public UploadTaskInfo getUploadTaskInfo (int taskID) { UploadTask task = getUploadTaskByID(taskID); if (task != null) { @@ -138,14 +372,83 @@ public List getAllUploadTaskInfos() { return infos; } - + + public List getAllDownloadTaskInfos() { + ArrayList infos = Lists.newArrayList(); + for (DownloadTask task : downloadTasks) { + infos.add(task.getTaskInfo()); + } + + return infos; + } + + /** + * get all download task info under a specific directory. + * + * @param repoID + * @param dir valid dir should be something like this "/DIRNAME/", instead of "/DIRNAME", + * in order to ensure the param being consistent with its caller + * @return List + */ + public List getDownloadTaskInfosByPath(String repoID, String dir) { + ArrayList infos = Lists.newArrayList(); + for (DownloadTask task : downloadTasks) { + String parentDir = Utils.getParentPath(task.myPath); + String validDir; + + if (!parentDir.equals("/")) + validDir = parentDir + "/"; + else + validDir = parentDir; + + if (task.myRepoID.equals(repoID) && validDir.equals(dir)) + infos.add(task.getTaskInfo()); + } + + return infos; + } + + public boolean isDownloading() { + if (downloadTasks.isEmpty()) { + return false; + } + + for (DownloadTask task : downloadTasks) { + if (task.getState().equals(TaskState.TRANSFERRING) || task.getState().equals(TaskState.INIT)) { + return true; + } + } + + return false; + } + + public boolean isUploading() { + if (uploadTasks.isEmpty()) + return false; + + for (UploadTask task : uploadTasks) { + if (task.getState().equals(TaskState.INIT) || task.getState().equals(TaskState.TRANSFERRING)) { + return true; + } + } + + return false; + } + public void removeUploadTask(int taskID) { UploadTask task = getUploadTaskByID(taskID); if (task != null) { uploadTasks.remove(task); } } - + + public void removeDownloadTask(int taskID) { + DownloadTask task = getDownloadTaskByID(taskID); + if (task != null) { + downloadTasks.remove(task); + } + } + public void removeFinishedUploadTasks() { Iterator iter = uploadTasks.iterator(); while (iter.hasNext()) { @@ -156,6 +459,26 @@ public void removeFinishedUploadTasks() { } } + public void removeAllDownloadTasksByState(TaskState taskState) { + Iterator iter = downloadTasks.iterator(); + while (iter.hasNext()) { + DownloadTask task = iter.next(); + if (task.getState().equals(taskState)) { + iter.remove(); + } + } + } + + public void removeAllUploadTasksByState(TaskState taskState) { + Iterator iter = uploadTasks.iterator(); + while (iter.hasNext()) { + UploadTask task = iter.next(); + if (task.getState().equals(taskState)) { + iter.remove(); + } + } + } + public void cancelUploadTask(int taskID) { UploadTask task = getUploadTaskByID(taskID); if (task != null) { @@ -177,6 +500,13 @@ public void retryUploadTask(int taskID) { } } + public void retryDownloadTask(int taskID) { + DownloadTask task = getDownloadTaskByID(taskID); + if (task != null) { + task.retryDownload(); + } + } + private DownloadTask getDownloadTaskByID(int taskID) { for (DownloadTask task : downloadTasks) { if (task.getTaskID() == taskID) { @@ -194,7 +524,7 @@ public DownloadTaskInfo getDownloadTaskInfo (int taskID) { return null; } - + private class UploadTask extends AsyncTask { private String myRepoID; private String myRepoName; @@ -202,7 +532,7 @@ private class UploadTask extends AsyncTask { private String myPath; // local file path private boolean isUpdate; // true if update an existing file private boolean isCopyToLocal; // false to turn off copy operation - + private TaskState myState; private int myID; private long myUploaded; @@ -245,7 +575,7 @@ public TaskState getState() { } public UploadTaskInfo getTaskInfo() { - UploadTaskInfo info = new UploadTaskInfo(myID, account, myState, myRepoID, + UploadTaskInfo info = new UploadTaskInfo(account, myID, myState, myRepoID, myRepoName, myDir, myPath, isUpdate, isCopyToLocal, myUploaded, mySize, err); return info; @@ -256,7 +586,7 @@ public void retryUpload() { return; } uploadTasks.remove(this); - addUploadTask(account, myRepoID, myRepoName, myDir, myPath, isUpdate, isCopyToLocal); + addTaskToUploadQue(account, myRepoID, myRepoName, myDir, myPath, isUpdate, isCopyToLocal); } public void cancelUpload() { @@ -264,6 +594,8 @@ public void cancelUpload() { return; } myState = TaskState.CANCELLED; + + removeCancelledUploadItemInQue(myID); super.cancel(true); } @@ -297,7 +629,7 @@ public boolean isCancelled() { if (isUpdate) { dataManager.updateFile(myRepoName, myRepoID, myDir, myPath, monitor, isCopyToLocal); } else { - Log.d(DEBUG_TAG, "Upload path: " + myPath); + // Log.d(DEBUG_TAG, "Upload path: " + myPath); dataManager.uploadFile(myRepoName, myRepoID, myDir, myPath, monitor, isCopyToLocal); } } catch (SeafException e) { @@ -319,6 +651,8 @@ protected void onPostExecute(Void v) { listener.onFileUploadFailed(myID); } } + + uploadNextInQue(myID); } @Override @@ -409,6 +743,8 @@ protected void onPostExecute(File file) { listener.onFileDownloadFailed(taskID); } } + + downloadNextInQue(taskID); } @Override @@ -431,7 +767,20 @@ public void cancelDownload() { return; } myState = TaskState.CANCELLED; + removeCancelledDownloadItemInQue(taskID); super.cancel(true); } + + public void retryDownload() { + if (myState != TaskState.CANCELLED && myState != TaskState.FAILED) { + return; + } + downloadTasks.remove(this); + addTaskToDownloadQue(account, myRepoName, myRepoID, myPath); + } + + public TaskState getState() { + return myState; + } } } diff --git a/src/com/seafile/seadroid2/transfer/TransferService.java b/src/com/seafile/seadroid2/transfer/TransferService.java index 69c470587..e6cdf4b79 100644 --- a/src/com/seafile/seadroid2/transfer/TransferService.java +++ b/src/com/seafile/seadroid2/transfer/TransferService.java @@ -10,6 +10,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.util.Log; +import com.google.common.collect.Lists; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.transfer.TransferManager.TransferListener; import com.seafile.seadroid2.util.Utils; @@ -62,9 +63,9 @@ public IBinder onBind(Intent intent) { } /** - * handle two types of upload tasks request, including files upload tasks and camera photo upload tasks. + * call this method to handle upload request, like file upload or camera upload. * - * Note: use isCopyToLocal to mark camera photo upload tasks, if true + * Note: use isCopyToLocal to mark automatic camera upload if true, or file upload if false. * @param account * @param repoID * @param repoName @@ -76,11 +77,28 @@ public IBinder onBind(Intent intent) { */ public int addUploadTask(Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal) { - return txManager.addUploadTask(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal); + return addTaskToUploadQue(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal); } - + + public int addTaskToUploadQue(Account account, String repoID, String repoName, String dir, + String filePath, boolean isUpdate, boolean isCopyToLocal) { + return txManager.addTaskToUploadQue(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal); + } + public int addDownloadTask(Account account, String repoName, String repoID, String path) { - return txManager.addDownloadTask(account, repoName, repoID, path); + return addTaskToDownloadQue(account, repoName, repoID, path); + } + + public int addTaskToDownloadQue(Account account, String repoName, String repoID, String path) { + return txManager.addTaskToDownloadQue(account, repoName, repoID, path); + } + + public boolean isDownloading() { + return txManager.isDownloading(); + } + + public boolean isUploading() { + return txManager.isUploading(); } public UploadTaskInfo getUploadTaskInfo(int taskID) { @@ -90,11 +108,33 @@ public UploadTaskInfo getUploadTaskInfo(int taskID) { public List getAllUploadTaskInfos() { return txManager.getAllUploadTaskInfos(); } + + public List getAllDownloadTaskInfos() { + return txManager.getAllDownloadTaskInfos(); + } + + public List getDownloadTaskInfosByPath(String repoID, String dir) { + return txManager.getDownloadTaskInfosByPath(repoID, dir); + } public void removeUploadTask(int taskID) { txManager.removeUploadTask(taskID); } + public void removeDownloadTask(int taskID) { + txManager.removeDownloadTask(taskID); + } + + public void removeAllDownloadTasksByState(TransferManager.TaskState taskState) { + txManager.removeAllDownloadTasksByState(taskState); + + } + + public void removeAllUploadTasksByState(TransferManager.TaskState taskState) { + txManager.removeAllUploadTasksByState(taskState); + + } + public void removeFinishedUploadTasks() { txManager.removeFinishedUploadTasks(); } @@ -103,10 +143,39 @@ public void cancelUploadTask(int taskID) { txManager.cancelUploadTask(taskID); } + public void cancelUploadTaskInQue(int taskID) { + cancelUploadTask(taskID); + txManager.removeCancelledUploadItemInQue(taskID); + txManager.uploadNext(); + } + + public void cancelAllUploadTasks() { + List uploadTaskInfos = txManager.getAllUploadTaskInfos(); + for (TransferTaskInfo uploadTaskInfo : uploadTaskInfos) { + cancelUploadTask(uploadTaskInfo.taskID); + } + + } + + public void cancelAllCameraUploadTasks() { + List uploadTaskInfos = getAllUploadTaskInfos(); + for (UploadTaskInfo uploadTaskInfo : uploadTaskInfos) { + // use isCopyToLocal as a flag to mark a camera photo upload task if false + // mark a file upload task if true + if (!uploadTaskInfo.isCopyToLocal) { + cancelUploadTask(uploadTaskInfo.taskID); + } + } + } + public void retryUploadTask(int taskID) { txManager.retryUploadTask(taskID); } + public void retryDownloadTask(int taskID) { + txManager.retryDownloadTask(taskID); + } + public DownloadTaskInfo getDownloadTaskInfo(int taskID) { return txManager.getDownloadTaskInfo(taskID); } @@ -163,6 +232,20 @@ public void onFileDownloadFailed(int taskID) { public void cancelDownloadTask(int taskID) { txManager.cancelDownloadTask(taskID); } + + public void cancelDownloadTaskInQue(int taskID) { + cancelDownloadTask(taskID); + txManager.removeCancelledDownloadItemInQue(taskID); + txManager.downloadNext(); + } + + public void cancellAllDownloadTasks() { + List downloadTaskInfos = txManager.getAllDownloadTaskInfos(); + for (DownloadTaskInfo downloadTaskInfo : downloadTaskInfos) { + cancelDownloadTask(downloadTaskInfo.taskID); + } + } + } interface TransferDBHelper { diff --git a/src/com/seafile/seadroid2/transfer/TransferTaskInfo.java b/src/com/seafile/seadroid2/transfer/TransferTaskInfo.java new file mode 100644 index 000000000..215f50ccd --- /dev/null +++ b/src/com/seafile/seadroid2/transfer/TransferTaskInfo.java @@ -0,0 +1,65 @@ +package com.seafile.seadroid2.transfer; + +import com.seafile.seadroid2.SeafException; +import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.transfer.TransferManager.TaskState; + +/* + * base class for {@link DownloadTaskInfo} and {@link UploadTaskInfo} + */ +public class TransferTaskInfo{ + public final Account account; + public final int taskID; + public final TaskState state; + public final String repoID; + public final String repoName; + public final String localFilePath; + public final SeafException err; + + /** + * Construct a Transfer Task Info instance + * + * @param account Current login Account instance + * @param taskID TransferTask id + * @param state TransferTask state, value is one of INIT, TRANSFERRING, FINISHED, CANCELLED, FAILED of {@link TaskState} + * @param repoID Repository id + * @param repoName Repository name + * @param localPath Local path + * @param err Exception instance of {@link SeafException} + */ + public TransferTaskInfo(Account account, int taskID, TaskState state, String repoID, + String repoName, String localPath, + SeafException err) { + this.account = account; + this.taskID = taskID; + this.state = state; + this.repoID = repoID; + this.repoName = repoName; + this.localFilePath = localPath; + this.err = err; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TransferTaskInfo)) + return false; + if (obj == this) + return true; + + TransferTaskInfo tti = (TransferTaskInfo) obj; + return tti.account.getSignature().equals(account.getSignature()) + && Integer.compare(tti.taskID, taskID) == 0 + && tti.repoID.equals(repoID); + } + + @Override + public String toString() { + return "email " + account.getEmail() + " server " + account.getServer() + " taskID " + taskID + " repoID " + repoID + + " repoName " + repoName + " localFilePath " + localFilePath; + } + + @Override + public int hashCode() { + return String.format("%s%s%d", taskID, repoName, localFilePath).hashCode(); + } +} diff --git a/src/com/seafile/seadroid2/transfer/UploadTaskInfo.java b/src/com/seafile/seadroid2/transfer/UploadTaskInfo.java index 8977a9cdb..ed942bb8b 100644 --- a/src/com/seafile/seadroid2/transfer/UploadTaskInfo.java +++ b/src/com/seafile/seadroid2/transfer/UploadTaskInfo.java @@ -4,34 +4,47 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.transfer.TransferManager.TaskState; -public class UploadTaskInfo { - public final int taskID; - public final TaskState state; - public final String repoID; - public final String repoName; +public class UploadTaskInfo extends TransferTaskInfo { + public final String parentDir; - public final String localFilePath; - public final boolean isUpdate, isCopyToLocal; public final long uploadedSize, totalSize; - public final SeafException err; - public final Account account; + public final boolean isUpdate, isCopyToLocal; - public UploadTaskInfo(int taskID, Account account, TaskState state, String repoID, - String repoName, String parentDir, - String localFilePath, boolean isUpdate, boolean isCopyToLocal, - long uploadedSize, long totalSize, + /** + * Constructor of UploadTaskInfo + * + * @param account Current login Account instance + * @param taskID TransferTask id + * @param state TransferTask state, value is one of INIT, TRANSFERRING, FINISHED, CANCELLED, FAILED of {@link TaskState} + * @param repoID Repository id + * @param repoName Repository name + * @param parentDir Parent directory of the file + * @param localPath Local path + * @param isUpdate Force to update files if true + * @param isCopyToLocal Copy files to SD card if true + * @param uploadedSize File uploaded size + * @param totalSize File total size + * @param err Exception instance of {@link SeafException} + */ + public UploadTaskInfo(Account account, + int taskID, + TaskState state, + String repoID, + String repoName, + String parentDir, + String localPath, + boolean isUpdate, + boolean isCopyToLocal, + long uploadedSize, + long totalSize, SeafException err) { - this.taskID = taskID; - this.account = account; - this.state = state; - this.repoID = repoID; - this.repoName = repoName; + + super(account, taskID, state, repoID, repoName, localPath, err); + this.parentDir = parentDir; - this.localFilePath = localFilePath; - this.isUpdate = isUpdate; - this.isCopyToLocal = isCopyToLocal; this.uploadedSize = uploadedSize; this.totalSize = totalSize; - this.err = err; + this.isUpdate = isUpdate; + this.isCopyToLocal = isCopyToLocal; } } diff --git a/src/com/seafile/seadroid2/ui/activity/BrowserActivity.java b/src/com/seafile/seadroid2/ui/activity/BrowserActivity.java index 029f38ff3..22da9272e 100644 --- a/src/com/seafile/seadroid2/ui/activity/BrowserActivity.java +++ b/src/com/seafile/seadroid2/ui/activity/BrowserActivity.java @@ -22,7 +22,9 @@ import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.preference.PreferenceManager; import android.provider.MediaStore; @@ -47,11 +49,7 @@ import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; import com.google.common.collect.Lists; -import com.seafile.seadroid2.NavContext; -import com.seafile.seadroid2.R; -import com.seafile.seadroid2.SeafConnection; -import com.seafile.seadroid2.SeafException; -import com.seafile.seadroid2.SettingsManager; +import com.seafile.seadroid2.*; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.AccountManager; import com.seafile.seadroid2.cameraupload.CameraUploadService; @@ -61,13 +59,9 @@ import com.seafile.seadroid2.data.SeafStarredFile; import com.seafile.seadroid2.fileschooser.MultiFileChooserActivity; import com.seafile.seadroid2.monitor.FileMonitorService; -import com.seafile.seadroid2.transfer.DownloadTaskInfo; -import com.seafile.seadroid2.transfer.PendingUploadInfo; -import com.seafile.seadroid2.transfer.TransferService; +import com.seafile.seadroid2.transfer.*; import com.seafile.seadroid2.transfer.TransferService.TransferBinder; -import com.seafile.seadroid2.transfer.UploadTaskInfo; import com.seafile.seadroid2.ui.CopyMoveContext; -import com.seafile.seadroid2.ui.adapter.UploadTasksAdapter; import com.seafile.seadroid2.ui.dialog.AppChoiceDialog; import com.seafile.seadroid2.ui.dialog.CopyMoveDialog; import com.seafile.seadroid2.ui.dialog.DeleteFileDialog; @@ -142,7 +136,7 @@ public DataManager getDataManager() { public void addUpdateTask(String repoID, String repoName, String targetDir, String localFilePath) { if (txService != null) { - txService.addUploadTask(account, repoID, repoName, targetDir, localFilePath, true, true); + txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, true, true); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, true, true); pendingUploads.add(info); @@ -151,7 +145,7 @@ public void addUpdateTask(String repoID, String repoName, String targetDir, Stri private void addUploadTask(String repoID, String repoName, String targetDir, String localFilePath) { if (txService != null) { - txService.addUploadTask(account, repoID, repoName, targetDir, localFilePath, false, true); + txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, false, true); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, false, true); pendingUploads.add(info); @@ -431,9 +425,13 @@ public void onServiceConnected(ComponentName className, IBinder service) { Log.d(DEBUG_TAG, "bind TransferService"); for (PendingUploadInfo info : pendingUploads) { - txService.addUploadTask(account, info.repoID, - info.repoName, info.targetDir, - info.localFilePath, info.isUpdate, info.isCopyToLocal); + txService.addTaskToUploadQue(account, + info.repoID, + info.repoName, + info.targetDir, + info.localFilePath, + info.isUpdate, + info.isCopyToLocal); } pendingUploads.clear(); } @@ -532,39 +530,44 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) { MenuItem menuUpload = menu.findItem(R.id.upload); MenuItem menuRefresh = menu.findItem(R.id.refresh); + MenuItem menuDownloadFolder = menu.findItem(R.id.download_folder); MenuItem menuNewDir = menu.findItem(R.id.newdir); MenuItem menuNewFile = menu.findItem(R.id.newfile); MenuItem menuCamera = menu.findItem(R.id.camera); - MenuItem menuUploadTasks = menu.findItem(R.id.upload_tasks); + MenuItem menuTransferTasks = menu.findItem(R.id.transfer_tasks); MenuItem menuAccounts = menu.findItem(R.id.accounts); MenuItem menuSettings = menu.findItem(R.id.settings); // Libraries Tab if (currentPosition == 0) { menuUpload.setVisible(true); + menuDownloadFolder.setVisible(true); if (navContext.inRepo() && hasRepoWritePermission()) { menuUpload.setEnabled(true); - } - else + menuDownloadFolder.setEnabled(true); + } else { menuUpload.setEnabled(false); + menuDownloadFolder.setEnabled(false); + } } else { menuUpload.setVisible(false); + menuDownloadFolder.setVisible(false); } // Libraries Tab if (currentPosition == 0) { menuRefresh.setVisible(true); - menuUploadTasks.setVisible(true); + menuTransferTasks.setVisible(true); menuAccounts.setVisible(true); menuSettings.setVisible(true); } else if (currentPosition == 2) { // ACTIVITY_TAB menuRefresh.setVisible(true); - menuUploadTasks.setVisible(true); + menuTransferTasks.setVisible(true); menuAccounts.setVisible(true); menuSettings.setVisible(true); } else { menuRefresh.setVisible(false); - menuUploadTasks.setVisible(false); + menuTransferTasks.setVisible(false); menuAccounts.setVisible(false); menuSettings.setVisible(false); } @@ -592,7 +595,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { menuNewDir.setVisible(false); menuNewFile.setVisible(false); menuCamera.setVisible(false); - menuUploadTasks.setVisible(false); + menuTransferTasks.setVisible(false); menuAccounts.setVisible(false); menuSettings.setVisible(false); } @@ -604,7 +607,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { menuNewFile.setVisible(false); menuCamera.setVisible(false); menuRefresh.setVisible(true); - menuUploadTasks.setVisible(true); + menuTransferTasks.setVisible(true); menuAccounts.setVisible(true); menuSettings.setVisible(true); } @@ -612,17 +615,6 @@ public boolean onPrepareOptionsMenu(Menu menu) { return true; } - public static UploadTasksAdapter uploadTasksAdapter; - - private List getUploadTaskInfos() { - if (txService == null) { - // In case the service is not ready - return Lists.newArrayList(); - } - - return txService.getAllUploadTaskInfos(); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -635,9 +627,8 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.upload: pickFile(); return true; - case R.id.upload_tasks: - Intent newIntent = new Intent(this, UploadTasksActivity.class); - uploadTasksAdapter = new UploadTasksAdapter(this, getUploadTaskInfos()); + case R.id.transfer_tasks: + Intent newIntent = new Intent(this, TransferActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(newIntent); return true; @@ -675,6 +666,10 @@ public void onTaskSuccess() { getStarredFragment().refresh(); } return true; + case R.id.download_folder: + String parentPath = Utils.getParentPath(navContext.getDirPath()); + downloadDir(parentPath); + break; case R.id.newdir: showNewDirDialog(); return true; @@ -948,6 +943,82 @@ public void onFileSelected(SeafDirent dirent) { startFileActivity(repoName, repoID, filePath); } + public void downloadDir(String direntName) { + if (!Utils.isNetworkOn()) { + showToast(R.string.network_down); + return; + } + + String fileName = direntName; + final String repoName = navContext.getRepoName(); + final String repoID = navContext.getRepoID(); + final String filePath = Utils.pathJoin(navContext.getDirPath(), fileName); + ConcurrentAsyncTask.execute(new DownloadDirTask(), repoName, repoID, filePath); + // Log.d(DEBUG_TAG, "download >> " + repoName + navContext.getDirPath()); + + } + + private class DownloadDirTask extends AsyncTask > { + + String repoName; + String repoID; + String filePath; + + SeafException err = null; + + @Override + protected List doInBackground(String... params) { + if (params.length != 3) { + Log.d(DEBUG_TAG, "Wrong params to LoadDirTask"); + return null; + } + + repoName = params[0]; + repoID = params[1]; + filePath = params[2]; + + try { + return dataManager.getDirentsFromServer(repoID, filePath); + } catch (SeafException e) { + err = e; + e.printStackTrace(); + return null; + } + } + + @Override + protected void onPostExecute(List dirents) { + + if (dirents == null) { + if (err != null) + showToast(R.string.transfer_list_network_error); + + return; + } + + for (SeafDirent seafDirent : dirents) { + if (!seafDirent.isDir()) { + File localCachedFile = dataManager.getLocalCachedFile(repoName, + repoID, + Utils.pathJoin(filePath, + seafDirent.name), + seafDirent.id); + if (localCachedFile == null) { + // Log.d(DEBUG_TAG, Utils.pathJoin(repoName, filePath, seafDirent.name)); + txService.addTaskToDownloadQue(account, + repoName, + repoID, + Utils.pathJoin(filePath, + seafDirent.name)); + } + } + } + // set download tasks info to adapter in order to update download progress in UI thread + getReposFragment().getAdapter().setDownloadTaskList(txService.getDownloadTaskInfosByPath(repoID, filePath)); + getReposFragment().getAdapter().notifyDataSetChanged(); + } + } + private void startFileActivity(String repoName, String repoID, String filePath) { int taskID = txService.addDownloadTask(account, repoName, repoID, filePath); Intent intent = new Intent(this, FileActivity.class); @@ -1097,8 +1168,7 @@ public void onAppSelected(ResolveInfo appInfo) { startActivity(sendIntent); return; } - fetchFileAndExport(appInfo, sendIntent, repoName, repoID, - path); + fetchFileAndExport(appInfo, sendIntent, repoName, repoID, path); } }); @@ -1130,6 +1200,8 @@ public void onFailure(SeafException err) { /** * Share a file. Generating a file share link and send the link to someone * through some app. + * @param repoID + * @param path */ public void shareFile(String repoID, String path) { chooseShareApp(repoID, path, false); @@ -1308,32 +1380,6 @@ public void onTaskSuccess() { dialog.show(getSupportFragmentManager(), "DialogFragment"); } - private void onFileUploaded(int taskID) { - if (txService == null) { - return; - } - - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - - String repoID = info.repoID; - String dir = info.parentDir; - if (currentPosition == 0 - && repoID.equals(navContext.getRepoID()) - && dir.equals(navContext.getDirPath())) { - getReposFragment().refreshView(true); - String verb = getString(info.isUpdate ? R.string.updated : R.string.uploaded); - showToast(verb + " " + Utils.fileNameFromPath(info.localFilePath)); - } - } - - private void onFileUploadFailed(int taskID) { - if (txService == null) { - return; - } - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - showToast(getString(R.string.upload_failed) + " " + Utils.fileNameFromPath(info.localFilePath)); - } - private void onFileDownloadProgress(int taskID) { if (txService == null) { return; @@ -1345,23 +1391,6 @@ private void onFileDownloadProgress(int taskID) { } } - private void onFileDownloaded(int taskID) { - if (txService == null) { - return; - } - - DownloadTaskInfo info = txService.getDownloadTaskInfo(taskID); - if (fetchFileDialog != null && fetchFileDialog.getTaskID() == taskID) { - fetchFileDialog.handleDownloadTaskInfo(info); - } else { - if (currentPosition == 0 - && info.repoID.equals(navContext.getRepoID()) - && Utils.getParentPath(info.pathInRepo).equals(navContext.getDirPath())) { - getReposFragment().getAdapter().notifyChanged(); - } - } - } - private void onFileDownloadFailed(int taskID) { if (txService == null) { return; @@ -1369,7 +1398,7 @@ private void onFileDownloadFailed(int taskID) { DownloadTaskInfo info = txService.getDownloadTaskInfo(taskID); if (fetchFileDialog != null && fetchFileDialog.getTaskID() == taskID) { - fetchFileDialog.handleDownloadTaskInfo(info); + fetchFileDialog.handleDownloadTaskInfo(info); return; } @@ -1378,16 +1407,22 @@ private void onFileDownloadFailed(int taskID) { final String repoID = info.repoID; final String path = info.pathInRepo; - if (err != null && err.getCode() == SeafConnection.HTTP_STATUS_REPO_PASSWORD_REQUIRED) { + if (err != null + && err.getCode() == SeafConnection.HTTP_STATUS_REPO_PASSWORD_REQUIRED) { if (currentPosition == 0 - && repoID.equals(navContext.getRepoID()) - && Utils.getParentPath(path).equals(navContext.getDirPath())) { - showPasswordDialog(repoName, repoID, new TaskDialog.TaskDialogListener() { - @Override - public void onTaskSuccess() { - txService.addDownloadTask(account, repoName, repoID, path); - } - }); + && repoID.equals(navContext.getRepoID()) + && Utils.getParentPath(path) + .equals(navContext.getDirPath())) { + showPasswordDialog(repoName, repoID, + new TaskDialog.TaskDialogListener() { + @Override + public void onTaskSuccess() { + txService.addDownloadTask(account, + repoName, + repoID, + path); + } + }); return; } } @@ -1395,6 +1430,32 @@ public void onTaskSuccess() { showToast(getString(R.string.download_failed) + " " + Utils.fileNameFromPath(path)); } + private void onFileUploaded(int taskID) { + if (txService == null) { + return; + } + + UploadTaskInfo info = txService.getUploadTaskInfo(taskID); + + String repoID = info.repoID; + String dir = info.parentDir; + if (currentPosition == 0 + && repoID.equals(navContext.getRepoID()) + && dir.equals(navContext.getDirPath())) { + getReposFragment().refreshView(true); + String verb = getString(info.isUpdate ? R.string.updated : R.string.uploaded); + showToast(verb + " " + Utils.fileNameFromPath(info.localFilePath)); + } + } + + private void onFileUploadFailed(int taskID) { + if (txService == null) { + return; + } + UploadTaskInfo info = txService.getUploadTaskInfo(taskID); + showToast(getString(R.string.upload_failed) + " " + Utils.fileNameFromPath(info.localFilePath)); + } + public PasswordDialog showPasswordDialog(String repoName, String repoID, TaskDialog.TaskDialogListener listener) { return showPasswordDialog(repoName, repoID, listener, null); @@ -1424,7 +1485,6 @@ public boolean onKeyUp(int keycode, KeyEvent e) { return super.onKeyUp(keycode, e); } - // for receive broadcast from TransferService private class TransferReceiver extends BroadcastReceiver { @@ -1435,23 +1495,15 @@ public void onReceive(Context context, Intent intent) { if (type.equals(TransferService.BROADCAST_FILE_DOWNLOAD_PROGRESS)) { int taskID = intent.getIntExtra("taskID", 0); onFileDownloadProgress(taskID); - - } else if (type.equals(TransferService.BROADCAST_FILE_DOWNLOAD_SUCCESS)) { - int taskID = intent.getIntExtra("taskID", 0); - onFileDownloaded(taskID); - } else if (type.equals(TransferService.BROADCAST_FILE_DOWNLOAD_FAILED)) { int taskID = intent.getIntExtra("taskID", 0); onFileDownloadFailed(taskID); - } else if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_SUCCESS)) { int taskID = intent.getIntExtra("taskID", 0); onFileUploaded(taskID); - } else if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_FAILED)) { int taskID = intent.getIntExtra("taskID", 0); onFileUploadFailed(taskID); - } } diff --git a/src/com/seafile/seadroid2/ui/activity/TransferActivity.java b/src/com/seafile/seadroid2/ui/activity/TransferActivity.java new file mode 100644 index 000000000..5ba05a5ba --- /dev/null +++ b/src/com/seafile/seadroid2/ui/activity/TransferActivity.java @@ -0,0 +1,175 @@ +package com.seafile.seadroid2.ui.activity; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v4.view.ViewPager.OnPageChangeListener; +import android.util.Log; +import android.view.KeyEvent; +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; +import com.seafile.seadroid2.R; +import com.seafile.seadroid2.ui.fragment.DownloadTaskFragment; +import com.seafile.seadroid2.ui.fragment.UploadTaskFragment; +import com.viewpagerindicator.TabPageIndicator; +import com.seafile.seadroid2.ui.adapter.TransferTaskAdapter; + +public class TransferActivity extends SherlockFragmentActivity { + private static final String DEBUG_TAG = "TransferActivity"; + + /** 0 mark as Download Fragment, 1 mark as Upload Fragment, the same convention with {@link TransferTaskAdapter #mTransferTaskType} */ + private int currentPosition = 0; + private TransferTabsAdapter tabsAdapter; + private ViewPager pager; + + private Menu overFlowMenu = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.tabs_main); + + tabsAdapter = new TransferTabsAdapter(getSupportFragmentManager()); + + pager = (ViewPager) findViewById(R.id.pager); + pager.setAdapter(tabsAdapter); + + TabPageIndicator indicator = (TabPageIndicator) findViewById(R.id.indicator); + indicator.setViewPager(pager); + indicator.setOnPageChangeListener(new OnPageChangeListener() { + @Override + public void onPageSelected(final int position) { + Log.d(DEBUG_TAG, "current tab index " + position); + currentPosition = position; + supportInvalidateOptionsMenu(); + pager.setCurrentItem(position); + } + + @Override + public void onPageScrollStateChanged(int arg0) { + // TODO Auto-generated method stub + } + + @Override + public void onPageScrolled(int arg0, float arg1, int arg2) { + // TODO Auto-generated method stub + } + }); + + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_MENU: + if (overFlowMenu != null) { + overFlowMenu.performIdentifierAction(R.id.transfer_overflow_menu, 0); + } + } + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getSupportMenuInflater(); + inflater.inflate(R.menu.transfer_list_menu, menu); + overFlowMenu = menu; + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + // MenuItem cancel = menu.findItem(R.id.cancel_transfer_tasks); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.cancel_transfer_tasks: + if (currentPosition == 0) { + getDownloadTaskFragment().cancelDownloadTasks(); + + } else getUploadTaskFragment().cancelUploadTasks(); + + return true; + } + return super.onOptionsItemSelected(item); + } + + public DownloadTaskFragment getDownloadTaskFragment() { + return (DownloadTaskFragment)getFragment(0); + } + + public UploadTaskFragment getUploadTaskFragment() { + return (UploadTaskFragment)getFragment(1); + } + + public Fragment getFragment(int index) { + return getSupportFragmentManager().findFragmentByTag(makeFragmentName(index)); + } + + private String makeFragmentName(int index) { + return "android:switcher:" + R.id.pager + ":" + index; + } + + /* + * Adapter for {@link ViewPager} to bind DownloadTaskFragment and UploadTaskFragment + */ + class TransferTabsAdapter extends FragmentPagerAdapter { + public TransferTabsAdapter(FragmentManager fm) { + super(fm); + } + + private DownloadTaskFragment downloadsFragment = null; + private UploadTaskFragment uploadsFragment = null; + + @Override + public Fragment getItem(int position) { + switch (position) { + case 0: + if (downloadsFragment == null) { + downloadsFragment = new DownloadTaskFragment(); + } + return downloadsFragment; + case 1: + if (uploadsFragment == null) { + uploadsFragment = new UploadTaskFragment(); + } + return uploadsFragment; + default: + return new Fragment(); + } + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case 0: + return getString(R.string.transfer_tabs_downloads); + case 1: + return getString(R.string.transfer_tabs_uploads); + + default: + return null; + } + } + + @Override + public int getCount() { + return 2; + } + + } +} \ No newline at end of file diff --git a/src/com/seafile/seadroid2/ui/activity/UploadTasksActivity.java b/src/com/seafile/seadroid2/ui/activity/UploadTasksActivity.java deleted file mode 100644 index aa1602d4b..000000000 --- a/src/com/seafile/seadroid2/ui/activity/UploadTasksActivity.java +++ /dev/null @@ -1,319 +0,0 @@ -package com.seafile.seadroid2.ui.activity; - -import java.util.List; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.support.v4.content.LocalBroadcastManager; -import android.util.Log; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.View; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.ListView; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; -import com.google.common.collect.Lists; -import com.seafile.seadroid2.R; -import com.seafile.seadroid2.transfer.TransferManager.TaskState; -import com.seafile.seadroid2.transfer.TransferService; -import com.seafile.seadroid2.transfer.TransferService.TransferBinder; -import com.seafile.seadroid2.transfer.UploadTaskInfo; -import com.seafile.seadroid2.ui.adapter.UploadTasksAdapter; - -public class UploadTasksActivity extends SherlockFragmentActivity { - private static final String DEBUG_TAG = "UploadTasksActivity"; - - private ListView uploadTasksView; - private UploadTasksAdapter adapter; - private TransferService txService; - private TransferReceiver mTransferReceiver; - private ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - TransferBinder binder = (TransferBinder) service; - txService = binder.getService(); - Log.d(DEBUG_TAG, "bind TransferService"); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - txService = null; - } - }; - - protected void onCreate(Bundle savedInstanceState) { - Log.d(DEBUG_TAG, "UploadTasksActivity.onCreate is called"); - super.onCreate(savedInstanceState); - - setContentView(R.layout.upload_tasks_activity); - this.supportInvalidateOptionsMenu(); - adapter = BrowserActivity.uploadTasksAdapter; - uploadTasksView = (ListView) findViewById(R.id.upload_tasks_list); - uploadTasksView.setAdapter(adapter); - if (adapter != null && adapter.isEmpty()) { - uploadTasksView.setVisibility(View.GONE); - } - if (adapter == null) { - finish(); - } - registerForContextMenu(uploadTasksView); - - Intent bIntent = new Intent(this, TransferService.class); - bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE); - Log.d(DEBUG_TAG, "try bind TransferService"); - - ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - } - - @Override - protected void onResume() { - super.onResume(); - Log.d(DEBUG_TAG, "datasetChanged"); - adapter.notifyDataSetChanged(); - } - - @Override - public void onStart() { - Log.d(DEBUG_TAG, "onStart"); - super.onStart(); - - if (mTransferReceiver == null) { - mTransferReceiver = new TransferReceiver(); - } - - IntentFilter filter = new IntentFilter(TransferService.BROADCAST_ACTION); - LocalBroadcastManager.getInstance(this).registerReceiver(mTransferReceiver, filter); - - } - - @Override - protected void onStop() { - Log.d(DEBUG_TAG, "onStop"); - super.onStop(); - - if (mTransferReceiver != null) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(mTransferReceiver); - } - } - - @Override - protected void onDestroy() { - Log.d(DEBUG_TAG, "onDestroy is called"); - if (txService != null) { - unbindService(mConnection); - txService = null; - } - - super.onDestroy(); - } - - private List getUploadTaskInfos() { - if (txService == null) { - // In case the service is not ready - return Lists.newArrayList(); - } - - return txService.getAllUploadTaskInfos(); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - android.view.MenuInflater inflater = this.getMenuInflater(); - inflater.inflate(R.menu.upload_task_menu, menu); - - ListView listView = (ListView)v; - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - UploadTaskInfo taskInfo = (UploadTaskInfo)listView.getItemAtPosition(info.position); - - android.view.MenuItem itemCancel = menu.findItem(R.id.cancel); - android.view.MenuItem itemRetry = menu.findItem(R.id.retry); - android.view.MenuItem itemRemove = menu.findItem(R.id.remove); - android.view.MenuItem itemRemoveAll = menu.findItem(R.id.remove_all_finished); - - itemCancel.setVisible(false); - itemRetry.setVisible(false); - itemRemove.setVisible(false); - itemRemoveAll.setVisible(false); - - switch (taskInfo.state) { - case INIT: - itemCancel.setVisible(true); - break; - case TRANSFERRING: - itemCancel.setVisible(true); - break; - case CANCELLED: - itemRetry.setVisible(true); - itemRemove.setVisible(true); - break; - case FAILED: - itemRetry.setVisible(true); - itemRemove.setVisible(true); - break; - case FINISHED: - itemRemove.setVisible(true); - itemRemoveAll.setVisible(true); - break; - } - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); - - if (txService == null) { - return false; - } - - ListView listView = uploadTasksView; - UploadTaskInfo taskInfo = (UploadTaskInfo)listView.getItemAtPosition(info.position); - TaskState state = taskInfo.state; - int taskID = taskInfo.taskID; - - boolean needRefresh = false; - - switch (item.getItemId()) { - case R.id.cancel: - if (state == TaskState.INIT || state == TaskState.TRANSFERRING) { - txService.cancelUploadTask(taskID); - needRefresh = true; - } - break; - case R.id.retry: - if (state == TaskState.FAILED || state == TaskState.CANCELLED) { - txService.retryUploadTask(taskID); - needRefresh = true; - } - break; - case R.id.remove: - if (state == TaskState.FINISHED || state == TaskState.FAILED || state == TaskState.CANCELLED) { - txService.removeUploadTask(taskID); - needRefresh = true; - } - break; - case R.id.remove_all_finished: - if (state == TaskState.FINISHED) { - txService.removeFinishedUploadTasks(); - needRefresh = true; - } - break; - default: - return super.onContextItemSelected(item); - } - - if (needRefresh) { - refreshView(); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - this.finish(); - default: - return super.onOptionsItemSelected(item); - } - } - - public boolean isReady() { - return adapter != null; - } - - public void refreshView() { - adapter.setTaskInfos(getUploadTaskInfos()); - adapter.notifyDataSetChanged(); - this.supportInvalidateOptionsMenu(); - } - - public void onTaskProgressUpdate(UploadTaskInfo info) { - adapter.onTaskProgressUpdate(info); - } - - public void onTaskFinished(UploadTaskInfo info) { - adapter.onTaskFinished(info); - } - - public void onTaskFailed(UploadTaskInfo info) { - adapter.onTaskFailed(info); - } - - public void onTaskCancelled(UploadTaskInfo info) { - adapter.onTaskCancelled(info); - } - - private void onFileUploadProgress(int taskID) { - if (txService == null) { - return; - } - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - if (isReady()) - onTaskProgressUpdate(info); - } - - private void onFileUploaded(int taskID) { - if (txService == null) { - return; - } - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - if (isReady()) - onTaskFinished(info); - } - - private void onFileUploadCancelled(int taskID) { - if (txService == null) { - return; - } - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - if (isReady()) - onTaskCancelled(info); - } - - private void onFileUploadFailed(int taskID) { - if (txService == null) { - return; - } - UploadTaskInfo info = txService.getUploadTaskInfo(taskID); - if (isReady()) - onTaskFailed(info); - } - - // for receive broadcast from TransferService - private class TransferReceiver extends BroadcastReceiver { - - private TransferReceiver() {} - - public void onReceive(Context context, Intent intent) { - String type = intent.getStringExtra("type"); - if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_SUCCESS)) { - int taskID = intent.getIntExtra("taskID", 0); - onFileUploaded(taskID); - - } else if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_FAILED)) { - int taskID = intent.getIntExtra("taskID", 0); - onFileUploadFailed(taskID); - - } else if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_PROGRESS)) { - int taskID = intent.getIntExtra("taskID", 0); - onFileUploadProgress(taskID); - } else if (type.equals(TransferService.BROADCAST_FILE_UPLOAD_CANCELLED)) { - int taskID = intent.getIntExtra("taskID", 0); - onFileUploadCancelled(taskID); - } - } - - } // TransferReceiver -} diff --git a/src/com/seafile/seadroid2/ui/adapter/SeafItemAdapter.java b/src/com/seafile/seadroid2/ui/adapter/SeafItemAdapter.java index dd4c5ecc7..f3655aacd 100644 --- a/src/com/seafile/seadroid2/ui/adapter/SeafItemAdapter.java +++ b/src/com/seafile/seadroid2/ui/adapter/SeafItemAdapter.java @@ -4,7 +4,9 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.List; +import android.widget.ProgressBar; import net.londatiga.android.ActionItem; import net.londatiga.android.QuickAction; import android.content.res.Resources; @@ -27,6 +29,7 @@ import com.seafile.seadroid2.data.SeafGroup; import com.seafile.seadroid2.data.SeafItem; import com.seafile.seadroid2.data.SeafRepo; +import com.seafile.seadroid2.transfer.DownloadTaskInfo; import com.seafile.seadroid2.ui.activity.BrowserActivity; import com.seafile.seadroid2.util.Utils; @@ -36,6 +39,9 @@ public class SeafItemAdapter extends BaseAdapter { private BrowserActivity mActivity; private boolean repoIsEncrypted; + /** DownloadTask instance container **/ + private List mDownloadTaskInfos; + public SeafItemAdapter(BrowserActivity activity) { this.mActivity = activity; items = Lists.newArrayList(); @@ -60,6 +66,48 @@ public boolean isEmpty() { return items.isEmpty(); } + /** + * DownloadTask list should not be empty + * + * @return true if files added to download list, false otherwise + */ + public boolean isDownloadTaskListEmpty() { + if (mDownloadTaskInfos == null || mDownloadTaskInfos.isEmpty()) { + return true; + } + return false; + } + + /** + * To refresh download status icons of {@link com.seafile.seadroid2.ui.fragment.ReposFragment #mPullRefreshListView} instantly, + * use this method to update data set. + *

+ * This method should be called after the download folder button was clicked. + * + * @param downloadTaskInfos + */ + public void setDownloadTaskList(List downloadTaskInfos) { + this.mDownloadTaskInfos = downloadTaskInfos; + } + + /** + * To refresh download status icon of one specific item of {@link com.seafile.seadroid2.ui.fragment.ReposFragment #mPullRefreshListView} instantly, + * use this method to update data set. + * + * @param downloadTaskInfo + */ + public void setDownloadTask(DownloadTaskInfo downloadTaskInfo) { + if (mDownloadTaskInfos == null) { + mDownloadTaskInfos = Lists.newArrayList(); + } + // remove old data + if (mDownloadTaskInfos.contains(downloadTaskInfo)) { + mDownloadTaskInfos.remove(downloadTaskInfo); + } + + mDownloadTaskInfos.add(downloadTaskInfo); + } + public void addEntry(SeafItem entry) { items.add(entry); // Collections.sort(items); @@ -129,15 +177,15 @@ private View getRepoView(SeafRepo repo, View convertView, ViewGroup parent) { TextView subtitle = (TextView) view.findViewById(R.id.list_item_subtitle); ImageView icon = (ImageView) view.findViewById(R.id.list_item_icon); ImageView action = (ImageView) view.findViewById(R.id.list_item_action); - viewHolder = new Viewholder(title, subtitle, icon, action); + ImageView downloadStatusIcon = (ImageView) view.findViewById(R.id.list_item_download_status_icon); + ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.list_item_download_status_progressbar); + viewHolder = new Viewholder(title, subtitle, icon, action, downloadStatusIcon, progressBar); view.setTag(viewHolder); } else { viewHolder = (Viewholder) convertView.getTag(); } - /*viewHolder.title.setTextSize(16); - viewHolder.subtitle.setTextSize(12); - viewHolder.title.setPadding(12, 2, 10, 2); - viewHolder.subtitle.setPadding(12, 2, 10, 2);*/ + viewHolder.downloadStatusIcon.setVisibility(View.GONE); + viewHolder.progressBar.setVisibility(View.GONE); viewHolder.title.setText(repo.getTitle()); viewHolder.subtitle.setText(repo.getSubtitle()); viewHolder.icon.setImageResource(repo.getIcon()); @@ -162,7 +210,9 @@ private View getDirentView(SeafDirent dirent, View convertView, ViewGroup parent TextView subtitle = (TextView) view.findViewById(R.id.list_item_subtitle); ImageView icon = (ImageView) view.findViewById(R.id.list_item_icon); ImageView action = (ImageView) view.findViewById(R.id.list_item_action); - viewHolder = new Viewholder(title, subtitle, icon, action); + ImageView downloadStatusIcon = (ImageView) view.findViewById(R.id.list_item_download_status_icon); + ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.list_item_download_status_progressbar); + viewHolder = new Viewholder(title, subtitle, icon, action, downloadStatusIcon, progressBar); view.setTag(viewHolder); } else { viewHolder = (Viewholder) convertView.getTag(); @@ -170,17 +220,34 @@ private View getDirentView(SeafDirent dirent, View convertView, ViewGroup parent viewHolder.title.setText(dirent.getTitle()); if (dirent.isDir()) { + viewHolder.downloadStatusIcon.setVisibility(View.GONE); + viewHolder.progressBar.setVisibility(View.GONE); + viewHolder.subtitle.setText(dirent.getSubtitle()); viewHolder.icon.setImageResource(dirent.getIcon()); viewHolder.action.setVisibility(View.VISIBLE); setDirAction(dirent, viewHolder, position); } else { + viewHolder.downloadStatusIcon.setVisibility(View.GONE); setFileView(dirent, viewHolder, position); } return view; } + /** + * use to refresh view of {@link com.seafile.seadroid2.ui.fragment.ReposFragment #mPullRefreshListView} + *

+ *

when to show download status icons
+ * if the dirent is a file and already cached, show cached icon.
+ * if the dirent is a file and waiting to download, show downloading icon.
+ * if the dirent is a file and is downloading, show indeterminate progressbar.
+ * ignore directories and repos.
+ * + * @param dirent + * @param viewHolder + * @param position + */ private void setFileView(SeafDirent dirent, Viewholder viewHolder, int position) { NavContext nav = mActivity.getNavContext(); DataManager dataManager = mActivity.getDataManager(); @@ -196,9 +263,12 @@ private void setFileView(SeafDirent dirent, Viewholder viewHolder, int position) subtitle = dirent.getSubtitle(); if (cf != null) { cacheExists = true; - subtitle += "," + mActivity.getString(R.string.cached); } + // show file download finished + viewHolder.downloadStatusIcon.setVisibility(View.VISIBLE); + viewHolder.downloadStatusIcon.setImageResource(R.drawable.list_item_download_finished); viewHolder.subtitle.setText(subtitle); + viewHolder.progressBar.setVisibility(View.GONE); if (Utils.isViewableImage(file.getName())) { setImageThumbNail(file, dirent, dataManager, viewHolder); @@ -206,6 +276,43 @@ private void setFileView(SeafDirent dirent, Viewholder viewHolder, int position) viewHolder.icon.setImageResource(dirent.getIcon()); } else { + int downloadStatusIcon = R.drawable.list_item_download_waiting; + if (mDownloadTaskInfos != null) { + for (DownloadTaskInfo downloadTaskInfo : mDownloadTaskInfos) { + // use repoID and path to identify the task + if (downloadTaskInfo.repoID.equals(repoID) + && downloadTaskInfo.pathInRepo.equals(filePath)) { + switch (downloadTaskInfo.state) { + case INIT: + case CANCELLED: + case FAILED: + downloadStatusIcon = R.drawable.list_item_download_waiting; + viewHolder.downloadStatusIcon.setVisibility(View.VISIBLE); + viewHolder.progressBar.setVisibility(View.GONE); + break; + case TRANSFERRING: + viewHolder.downloadStatusIcon.setVisibility(View.GONE); + + viewHolder.progressBar.setVisibility(View.VISIBLE); + + break; + case FINISHED: + downloadStatusIcon = R.drawable.list_item_download_finished; + viewHolder.downloadStatusIcon.setVisibility(View.VISIBLE); + viewHolder.progressBar.setVisibility(View.GONE); + break; + default: + downloadStatusIcon = R.drawable.list_item_download_waiting; + break; + } + } + } + } else { + viewHolder.downloadStatusIcon.setVisibility(View.GONE); + viewHolder.progressBar.setVisibility(View.GONE); + } + + viewHolder.downloadStatusIcon.setImageResource(downloadStatusIcon); viewHolder.subtitle.setText(dirent.getSubtitle()); viewHolder.icon.setImageResource(dirent.getIcon()); } @@ -228,8 +335,8 @@ private void setImageThumbNail(File file, SeafDirent dirent, final int THUMBNAIL_SIZE = DataManager.caculateThumbnailSizeOfDevice(); try { // setImageURI does not work correctly under high screen density - //viewHolder.icon.setScaleType(ImageView.ScaleType.FIT_XY); - //viewHolder.icon.setImageURI(Uri.fromFile(thumbFile)); + // viewHolder.icon.setScaleType(ImageView.ScaleType.FIT_XY); + // viewHolder.icon.setImageURI(Uri.fromFile(thumbFile)); imageBitmap = BitmapFactory.decodeStream(new FileInputStream(thumbFile)); imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false); @@ -253,12 +360,17 @@ private View getCacheView(SeafCachedFile item, View convertView, ViewGroup paren TextView subtitle = (TextView) view.findViewById(R.id.list_item_subtitle); ImageView icon = (ImageView) view.findViewById(R.id.list_item_icon); ImageView action = (ImageView) view.findViewById(R.id.list_item_action); - viewHolder = new Viewholder(title, subtitle, icon, action); + ImageView downloadStatusIcon = (ImageView) view.findViewById(R.id.list_item_download_status_icon); + ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.list_item_download_status_progressbar); + viewHolder = new Viewholder(title, subtitle, icon, action, downloadStatusIcon, progressBar); view.setTag(viewHolder); } else { viewHolder = (Viewholder) convertView.getTag(); } + viewHolder.downloadStatusIcon.setVisibility(View.VISIBLE); + viewHolder.downloadStatusIcon.setImageResource(R.drawable.list_item_download_finished); + viewHolder.progressBar.setVisibility(View.GONE); viewHolder.title.setText(item.getTitle()); viewHolder.subtitle.setText(item.getSubtitle()); viewHolder.icon.setImageResource(item.getIcon()); @@ -282,14 +394,17 @@ public View getView(int position, View convertView, ViewGroup parent) { private class Viewholder { TextView title, subtitle; - ImageView icon, action; + ImageView icon, action, downloadStatusIcon; // downloadStatusIcon used to show file downloading status, it is invisible by default + ProgressBar progressBar; - public Viewholder(TextView title, TextView subtitle, ImageView icon, ImageView action) { + public Viewholder(TextView title, TextView subtitle, ImageView icon, ImageView action, ImageView downloadStatusIcon, ProgressBar progressBar) { super(); this.icon = icon; this.action = action; this.title = title; this.subtitle = subtitle; + this.downloadStatusIcon = downloadStatusIcon; + this.progressBar = progressBar; } } diff --git a/src/com/seafile/seadroid2/ui/adapter/TransferTaskAdapter.java b/src/com/seafile/seadroid2/ui/adapter/TransferTaskAdapter.java new file mode 100644 index 000000000..a330c6129 --- /dev/null +++ b/src/com/seafile/seadroid2/ui/adapter/TransferTaskAdapter.java @@ -0,0 +1,257 @@ +package com.seafile.seadroid2.ui.adapter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import android.content.Context; +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.google.common.collect.Maps; +import com.seafile.seadroid2.R; +import com.seafile.seadroid2.transfer.DownloadTaskInfo; +import com.seafile.seadroid2.transfer.TransferTaskInfo; +import com.seafile.seadroid2.transfer.UploadTaskInfo; +import com.seafile.seadroid2.ui.activity.TransferActivity; +import com.seafile.seadroid2.util.Utils; + +/* + * Adapter class for both uploading and downloading tasks + */ +public class TransferTaskAdapter extends BaseAdapter { + + private static final String DEBUG_TAG = "TransferTaskAdapter"; + + private List mUploadTaskInfos; + private List mDownloadTaskInfos; + private Context mContext; + /** 0 mark as Download Task, 1 mark as Upload Task, the same convention with {@link TransferActivity #currentPosition} */ + private int mTransferTaskType = -1; + public static final int DOWNLOAD_LIST_TAB = 0; + public static final int UPLOAD_LIST_TAB = 1; + + /** + * Constructor of {@link TransferTaskAdapter} + *

+ * set {@link TransferTaskAdapter #mDownloadTaskInfos} to null if the task is a uploading task
+ * set {@link TransferTaskAdapter #mUploadTaskInfos} to null if the task is a downloading task
+ * set {@link TransferTaskAdapter #mTransferTaskType} 0 to mark as Download Task, 1 mark to mark as Upload Task
+ * + * @param context + * @param uploadTaskInfos + * @param downloadTaskInfos + */ + public TransferTaskAdapter(Context context, + List uploadTaskInfos, + List downloadTaskInfos) { + this.mUploadTaskInfos = (List) uploadTaskInfos; + this.mDownloadTaskInfos = (List) downloadTaskInfos; + this.mContext = context; + } + + public void setCurrentTab(int whichTab) { + this.mTransferTaskType = whichTab; + } + + /* + * sort transfer list by task state, INIT goes to above, FINISHED goes to bottom. + */ + private class TaskInfoComparator implements Comparator { + private int taskStateToInteger(TransferTaskInfo info) { + switch (info.state) { + case TRANSFERRING: + return 0; + case INIT: + return 1; + case CANCELLED: + return 2; + case FAILED: + return 3; + case FINISHED: + return 4; + } + + return 0; + } + + @Override + public int compare(TransferTaskInfo infoA, TransferTaskInfo infoB) { + // sort task list, transferring < init < cancelled < failed < finished + return taskStateToInteger(infoA) - taskStateToInteger(infoB); + } + } + + public void setUploadTaskInfos(List infos) { + mUploadTaskInfos = infos; + Collections.sort(mUploadTaskInfos, new TaskInfoComparator()); + } + + public void setDownloadTaskInfos(List infos) { + mDownloadTaskInfos = infos; + Collections.sort(mDownloadTaskInfos, new TaskInfoComparator()); + } + + @Override + public int getCount() { + if (mTransferTaskType == 0) { + return mDownloadTaskInfos.size(); + } else if (mTransferTaskType == 1) { + return mUploadTaskInfos.size(); + } + return -1; + } + + @Override + public boolean isEmpty() { + if (mTransferTaskType == 0) { + return mDownloadTaskInfos.isEmpty(); + } else if (mTransferTaskType == 1) { + return mUploadTaskInfos.isEmpty(); + } + + return true; + } + + @Override + public TransferTaskInfo getItem(int position) { + if (mTransferTaskType == 0) { + return mDownloadTaskInfos.get(position); + } else if (mTransferTaskType == 1) { + return mUploadTaskInfos.get(position); + } + + return null; + } + + @Override + public long getItemId(int position) { + return position; + } + + private void updateTaskView(TransferTaskInfo info, Viewholder viewHolder) { + String stateStr = ""; + int stateColor = R.color.light_black; + long totalSize = 0l; + long transferedSize = 0l; + if (mTransferTaskType == 0) { + DownloadTaskInfo dti = (DownloadTaskInfo) info; + totalSize = dti.fileSize; + transferedSize = dti.finished; + } else if (mTransferTaskType == 1) { + UploadTaskInfo uti = (UploadTaskInfo) info; + totalSize = uti.totalSize; + transferedSize = uti.uploadedSize; + } + String sizeStr = Utils.readableFileSize(totalSize).toString(); + + switch (info.state) { + case INIT: + stateStr = mContext.getString(R.string.upload_waiting); + viewHolder.fileSize.setVisibility(View.INVISIBLE); + viewHolder.progressBar.setVisibility(View.INVISIBLE); + break; + case TRANSFERRING: + int percent; + if (totalSize == 0) + percent = 0; + else + percent = (int) (transferedSize * 100 / totalSize); + + viewHolder.progressBar.setProgress(percent); + sizeStr = String.format("%s / %s", + Utils.readableFileSize(transferedSize), + Utils.readableFileSize(totalSize)); + viewHolder.fileSize.setVisibility(View.VISIBLE); + viewHolder.progressBar.setVisibility(View.VISIBLE); + break; + case FINISHED: + stateStr = mContext.getString(R.string.upload_finished); + stateColor = Color.BLACK; + viewHolder.fileSize.setVisibility(View.VISIBLE); + viewHolder.progressBar.setVisibility(View.INVISIBLE); + break; + case CANCELLED: + stateStr = mContext.getString(R.string.upload_cancelled); + stateColor = Color.RED; + viewHolder.fileSize.setVisibility(View.INVISIBLE); + viewHolder.progressBar.setVisibility(View.INVISIBLE); + break; + case FAILED: + stateStr = mContext.getString(R.string.upload_failed); + stateColor = Color.RED; + viewHolder.fileSize.setVisibility(View.INVISIBLE); + viewHolder.progressBar.setVisibility(View.INVISIBLE); + break; + } + viewHolder.fileSize.setText(sizeStr); + viewHolder.state.setText(stateStr); + viewHolder.state.setTextColor(stateColor); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + Viewholder viewHolder; + + if (convertView == null) { + view = LayoutInflater.from(mContext).inflate(R.layout.transfer_list_item, null); + ImageView icon = (ImageView)view.findViewById(R.id.transfer_file_icon); + TextView state = (TextView)view.findViewById(R.id.transfer_file_state); + TextView targetPath = (TextView)view.findViewById(R.id.transfer_target_path); + TextView fileName = (TextView)view.findViewById(R.id.transfer_file_name); + TextView fileSize = (TextView)view.findViewById(R.id.transfer_file_size); + ProgressBar progressBar = (ProgressBar)view.findViewById(R.id.transfer_file_progress_bar); + viewHolder = new Viewholder(icon, state, targetPath, fileName, fileSize, progressBar); + view.setTag(viewHolder); + } else { + viewHolder = (Viewholder) convertView.getTag(); + } + + int iconID = 0; + if (mTransferTaskType == 0) { + DownloadTaskInfo taskInfo = mDownloadTaskInfos.get(position); + iconID = Utils.getFileIcon(taskInfo.pathInRepo); + // the three fileds is not dynamic + viewHolder.icon.setImageResource(iconID); + viewHolder.targetPath.setText(Utils.pathJoin(taskInfo.repoName, Utils.getParentPath(taskInfo.pathInRepo))); + viewHolder.fileName.setText(Utils.fileNameFromPath(taskInfo.pathInRepo)); + updateTaskView(taskInfo, viewHolder); + } else if (mTransferTaskType == 1) { + UploadTaskInfo taskInfo = mUploadTaskInfos.get(position); + iconID = Utils.getFileIcon(taskInfo.localFilePath); + String fullpath = taskInfo.repoName + taskInfo.parentDir; + // the three fileds is not dynamic + viewHolder.icon.setImageResource(iconID); + viewHolder.targetPath.setText(fullpath); + viewHolder.fileName.setText(Utils.fileNameFromPath(taskInfo.localFilePath)); + updateTaskView(taskInfo, viewHolder); + } + + return view; + } + + private class Viewholder { + ImageView icon; + TextView targetPath, fileName, fileSize, state; + ProgressBar progressBar; + + public Viewholder(ImageView icon, TextView state, TextView targetPath, + TextView fileName, TextView fileSize, ProgressBar progressBar) { + super(); + this.icon = icon; + this.state = state; + this.targetPath = targetPath; + this.fileName = fileName; + this.fileSize = fileSize; + this.progressBar = progressBar; + } + } +} \ No newline at end of file diff --git a/src/com/seafile/seadroid2/ui/adapter/UploadTasksAdapter.java b/src/com/seafile/seadroid2/ui/adapter/UploadTasksAdapter.java deleted file mode 100644 index dc32775d5..000000000 --- a/src/com/seafile/seadroid2/ui/adapter/UploadTasksAdapter.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.seafile.seadroid2.ui.adapter; - -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import android.content.Context; -import android.graphics.Color; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.google.common.collect.Maps; -import com.seafile.seadroid2.R; -import com.seafile.seadroid2.transfer.UploadTaskInfo; -import com.seafile.seadroid2.util.Utils; - -public class UploadTasksAdapter extends BaseAdapter { - private List mTaskInfos; - private Map mTaskViewMap; - private Context mContext; - - @SuppressWarnings("unused") - private static final String DEBUG_TAG = "UploadTasksAdapter"; - - public UploadTasksAdapter(Context context, List infos) { - setTaskInfos(infos); - mContext = context; - mTaskViewMap = Maps.newHashMap(); - } - - private class TaskInfoComparator implements Comparator { - private int taskStateToInteger(UploadTaskInfo info) { - switch (info.state) { - case INIT: - return 0; - case TRANSFERRING: - return 1; - case CANCELLED: - return 2; - case FAILED: - return 3; - case FINISHED: - return 4; - } - - return 0; - } - - @Override - public int compare(UploadTaskInfo infoA, UploadTaskInfo infoB) { - // sort task list, init < transferring < cancelled < failed < finished - return taskStateToInteger(infoA) - taskStateToInteger(infoB); - } - } - - public void setTaskInfos(List infos) { - mTaskInfos = infos; - Collections.sort(mTaskInfos, new TaskInfoComparator()); - } - - @Override - public int getCount() { - return mTaskInfos.size(); - } - - @Override - public boolean isEmpty() { - return mTaskInfos.isEmpty(); - } - - @Override - public UploadTaskInfo getItem(int position) { - return mTaskInfos.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - private void updateTaskView(UploadTaskInfo info, Viewholder viewHolder) { - String stateStr = ""; - int stateColor = R.color.light_black; - long total = info.totalSize; - long uploaded = info.uploadedSize; - String sizeStr = Utils.readableFileSize(total).toString(); - - switch (info.state) { - case INIT: - stateStr = mContext.getString(R.string.upload_waiting); - break; - case TRANSFERRING: - int percent; - if (total == 0) - percent = 0; - else - percent = (int)(uploaded * 100 / total); - - viewHolder.progressBar.setProgress(percent); - sizeStr = String.format("%s / %s", - Utils.readableFileSize(uploaded), - Utils.readableFileSize(total)); - viewHolder.progressBar.setVisibility(View.VISIBLE); - break; - case FINISHED: - stateStr = mContext.getString(R.string.upload_finished);; - stateColor = Color.BLACK; - viewHolder.progressBar.setVisibility(View.INVISIBLE); - break; - case CANCELLED: - stateStr = mContext.getString(R.string.upload_cancelled);; - stateColor = Color.RED; - viewHolder.progressBar.setVisibility(View.INVISIBLE); - break; - case FAILED: - stateStr = mContext.getString(R.string.upload_failed);; - stateColor = Color.RED; - viewHolder.progressBar.setVisibility(View.INVISIBLE); - break; - } - - viewHolder.fileSize.setText(sizeStr); - viewHolder.state.setText(stateStr); - viewHolder.state.setTextColor(stateColor); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - UploadTaskInfo taskInfo = mTaskInfos.get(position); - View view = convertView; - Viewholder viewHolder; - - if (convertView == null) { - view = LayoutInflater.from(mContext).inflate(R.layout.upload_list_item, null); - ImageView icon = (ImageView)view.findViewById(R.id.upload_file_icon); - TextView state = (TextView)view.findViewById(R.id.upload_file_state); - TextView targetPath = (TextView)view.findViewById(R.id.upload_target_path); - TextView fileName = (TextView)view.findViewById(R.id.upload_file_name); - TextView fileSize = (TextView)view.findViewById(R.id.upload_file_size); - ProgressBar progressBar = (ProgressBar)view.findViewById(R.id.upload_file_progress_bar); - viewHolder = new Viewholder(icon, state, targetPath, fileName, fileSize, progressBar); - view.setTag(viewHolder); - } else { - viewHolder = (Viewholder)convertView.getTag(); - } - - int iconID = Utils.getFileIcon(taskInfo.localFilePath); - String fullpath = taskInfo.repoName + taskInfo.parentDir; - - // the three fileds is not dynamic - viewHolder.icon.setImageResource(iconID); - viewHolder.targetPath.setText(fullpath); - viewHolder.fileName.setText(Utils.fileNameFromPath(taskInfo.localFilePath)); - - updateTaskView(taskInfo, viewHolder); - refreshTaskViewMap(taskInfo.taskID, viewHolder); - - return view; - } - - public void onTaskProgressUpdate(UploadTaskInfo info) { - handleUploadTaskUpdate(info); - } - - public void onTaskFailed (UploadTaskInfo info) { - handleUploadTaskUpdate(info); - } - - public void onTaskFinished (UploadTaskInfo info) { - handleUploadTaskUpdate(info); - } - - public void onTaskCancelled (UploadTaskInfo info) { - handleUploadTaskUpdate(info); - } - - private void refreshTaskViewMap(int taskID , Viewholder viewHolder) { - Iterator> iter; - iter = mTaskViewMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - if (viewHolder == entry.getValue()) { - iter.remove(); - } - } - - mTaskViewMap.put(taskID, viewHolder); - } - - private void handleUploadTaskUpdate(UploadTaskInfo info) { - int taskID; - Viewholder viewHolder = null; - for (Map.Entry entry: mTaskViewMap.entrySet()) { - taskID = entry.getKey(); - if (taskID == info.taskID) { - viewHolder = entry.getValue(); - break; - } - } - - if (viewHolder == null) { - return; - } - - updateTaskView(info, viewHolder); - updateTaskInfo(info); - } - - private void updateTaskInfo(UploadTaskInfo newInfo) { - int i, n = mTaskInfos.size(); - for (i = 0; i < n; i++) { - UploadTaskInfo info = mTaskInfos.get(i); - if (info.taskID == newInfo.taskID) { - mTaskInfos.set(i, newInfo); - return; - } - } - } - - private class Viewholder { - ImageView icon; - TextView targetPath, fileName, fileSize, state; - ProgressBar progressBar; - - public Viewholder(ImageView icon, TextView state, TextView targetPath, - TextView fileName, TextView fileSize, ProgressBar progressBar) { - super(); - this.icon = icon; - this.state = state; - this.targetPath = targetPath; - this.fileName = fileName; - this.fileSize = fileSize; - this.progressBar = progressBar; - } - } -} - - - diff --git a/src/com/seafile/seadroid2/ui/fragment/DownloadTaskFragment.java b/src/com/seafile/seadroid2/ui/fragment/DownloadTaskFragment.java new file mode 100644 index 000000000..7502af402 --- /dev/null +++ b/src/com/seafile/seadroid2/ui/fragment/DownloadTaskFragment.java @@ -0,0 +1,181 @@ +package com.seafile.seadroid2.ui.fragment; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import com.seafile.seadroid2.R; +import com.seafile.seadroid2.transfer.*; +import com.seafile.seadroid2.ui.adapter.TransferTaskAdapter; + +import java.util.List; + +/** + * Download tasks fragments + * + * Created by Logan on 14/12/22. + */ +public class DownloadTaskFragment extends TransferTaskFragment { + private static final String DEBUG_TAG = "DownloadTaskFragment"; + + private boolean isDownloadListVisible; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + Log.d(DEBUG_TAG, "onActivityCreated"); + super.onActivityCreated(savedInstanceState); + + emptyView.setText(getString(R.string.no_download_tasks)); + } + + @Override + void setUpTransferList(TransferService txService) { + + Log.d(DEBUG_TAG, "bind TransferService"); + List infos = txService.getAllDownloadTaskInfos(); + adapter = new TransferTaskAdapter(mActivity, null, infos); + adapter.setCurrentTab(TransferTaskAdapter.DOWNLOAD_LIST_TAB); + mTransferTaskListView.setAdapter(adapter); + + } + + @Override + public void onResume() { + Log.d(DEBUG_TAG, "onResume"); + isDownloadListVisible = true; + super.onResume(); + } + + @Override + public void onStop() { + Log.d(DEBUG_TAG, "onStop"); + super.onStop(); + isDownloadListVisible = false; + } + + boolean isNeedUpdateProgress() { + // first download list should at foreground + if (!isDownloadListVisible) { + return false; + } + + // second there are some downloading tasks + if (txService == null) + return false; + + if (!txService.isDownloading()) + return false; + + return true; + } + + // refresh download list by mTimer + void startTimer() { + Log.d(DEBUG_TAG, "timer started"); + mTimer.postDelayed(new Runnable() { + + @Override + public void run() { + adapter.setDownloadTaskInfos(txService.getAllDownloadTaskInfos()); + adapter.notifyDataSetChanged(); + Log.d(DEBUG_TAG, "timer post refresh signal " + System.currentTimeMillis()); + mTimer.postDelayed(this, 1 * 1000); + } + }, 1 * 1000); + } + + @Override + void refreshView() { + List infos = txService.getAllDownloadTaskInfos(); + if (infos == null || infos.isEmpty()) { + mTransferTaskListView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } else { + if (isNeedUpdateProgress()) + startTimer(); + + mTransferTaskListView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + adapter.setDownloadTaskInfos(infos); + adapter.notifyDataSetChanged(); + } + + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + if (getUserVisibleHint()) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + + if (txService == null) { + return false; + } + + ListView listView = mTransferTaskListView; + DownloadTaskInfo taskInfo = (DownloadTaskInfo) listView.getItemAtPosition(info.position); + TransferManager.TaskState state = taskInfo.state; + int taskID = taskInfo.taskID; + + boolean needRefresh = false; + + switch (item.getItemId()) { + case R.id.cancel: + if (state == TransferManager.TaskState.INIT || state == TransferManager.TaskState.TRANSFERRING) { + // txService.cancelDownloadTask(taskID); + txService.cancelDownloadTaskInQue(taskID); + needRefresh = true; + } + break; + case R.id.retry: + if (state == TransferManager.TaskState.FAILED || state == TransferManager.TaskState.CANCELLED) { + txService.retryDownloadTask(taskID); + needRefresh = true; + } + break; + case R.id.remove: + if (state == TransferManager.TaskState.FINISHED || state == TransferManager.TaskState.FAILED || state == TransferManager.TaskState.CANCELLED) { + txService.removeDownloadTask(taskID); + needRefresh = true; + } + break; + case R.id.remove_all_cancelled: + if (state == TransferManager.TaskState.CANCELLED) { + txService.removeAllDownloadTasksByState(TransferManager.TaskState.CANCELLED); + needRefresh = true; + } + break; + case R.id.remove_all_finished: + if (state == TransferManager.TaskState.FINISHED) { + txService.removeAllDownloadTasksByState(TransferManager.TaskState.FINISHED); + needRefresh = true; + } + break; + default: + return super.onContextItemSelected(item); + } + + if (needRefresh) { + refreshView(); + } + return true; + } + else + return false; + } + + /** + * cancel all download tasks + */ + public void cancelDownloadTasks() { + if (txService != null) { + txService.cancellAllDownloadTasks(); + } + + refreshView(); + + // stop timer + stopTimer(); + } + +} diff --git a/src/com/seafile/seadroid2/ui/fragment/ReposFragment.java b/src/com/seafile/seadroid2/ui/fragment/ReposFragment.java index 2037ad526..5c458a894 100644 --- a/src/com/seafile/seadroid2/ui/fragment/ReposFragment.java +++ b/src/com/seafile/seadroid2/ui/fragment/ReposFragment.java @@ -9,6 +9,7 @@ import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -60,6 +61,8 @@ public class ReposFragment extends SherlockListFragment { private View mListContainer; private TextView mErrorText; + private final Handler mTimer = new Handler(); + private DataManager getDataManager() { return mActivity.getDataManager(); } @@ -129,6 +132,7 @@ public void onStart() { public void onStop() { Log.d(DEBUG_TAG, "ReposFragment onStop"); super.onStop(); + stopTimer(); } @Override @@ -180,6 +184,8 @@ public void refreshView(boolean forceRefresh) { } public void navToReposView(boolean forceRefresh) { + stopTimer(); + mPullToRefreshStopRefreshing ++; if (mPullToRefreshStopRefreshing >1) { @@ -205,6 +211,8 @@ public void navToReposView(boolean forceRefresh) { } public void navToDirectory(boolean forceRefresh) { + startTimer(); + mPullToRefreshStopRefreshing ++; if (mPullToRefreshStopRefreshing > 1) { @@ -248,7 +256,28 @@ public void navToDirectory(boolean forceRefresh) { nav.getRepoID(), nav.getDirPath()); } - + + + // refresh download list by mTimer + public void startTimer() { + Log.d(DEBUG_TAG, "timer started"); + mTimer.postDelayed(new Runnable() { + + @Override + public void run() { + adapter.setDownloadTaskList(mActivity.getTransferService().getAllDownloadTaskInfos()); + adapter.notifyDataSetChanged(); + Log.d(DEBUG_TAG, "timer post refresh signal " + System.currentTimeMillis()); + mTimer.postDelayed(this, 1 * 1000); + } + }, 1 * 1000); + } + + public void stopTimer() { + Log.d(DEBUG_TAG, "timer stopped"); + mTimer.removeCallbacksAndMessages(null); + } + /** * calculate if repo refresh time is expired, the expiration is 10 mins */ diff --git a/src/com/seafile/seadroid2/ui/fragment/TransferTaskFragment.java b/src/com/seafile/seadroid2/ui/fragment/TransferTaskFragment.java new file mode 100644 index 000000000..2337642c3 --- /dev/null +++ b/src/com/seafile/seadroid2/ui/fragment/TransferTaskFragment.java @@ -0,0 +1,190 @@ +package com.seafile.seadroid2.ui.fragment; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.TextView; +import com.actionbarsherlock.app.SherlockListFragment; +import com.seafile.seadroid2.R; +import com.seafile.seadroid2.transfer.*; +import com.seafile.seadroid2.ui.activity.TransferActivity; +import com.seafile.seadroid2.ui.adapter.TransferTaskAdapter; + +/** + * Base class for transfer task fragments + * + * Created by Logan on 14/12/22. + */ +public abstract class TransferTaskFragment extends SherlockListFragment { + + protected TransferTaskAdapter adapter; + protected TransferActivity mActivity = null; + protected ListView mTransferTaskListView; + protected TextView emptyView; + private View mListContainer; + private View mProgressContainer; + protected final Handler mTimer = new Handler(); + protected TransferService txService = null; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mActivity = (TransferActivity) activity; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.transfer_task_fragment, container, false); + mTransferTaskListView = (ListView) root.findViewById(android.R.id.list); + mTransferTaskListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + + mListContainer = root.findViewById(R.id.listContainer); + mProgressContainer = root.findViewById(R.id.progressContainer); + emptyView = (TextView) root.findViewById(R.id.empty); + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + registerForContextMenu(mTransferTaskListView); + + // Toast.makeText(mActivity, "Loading animations", Toast.LENGTH_LONG).show(); + showLoading(true); + + // bind transfer service + Intent bIntent = new Intent(mActivity, TransferService.class); + mActivity.bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE); + } + + ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + TransferService.TransferBinder binder = (TransferService.TransferBinder) service; + txService = binder.getService(); + setUpTransferList(txService); + refreshView(); + + // Toast.makeText(mActivity, "Stop loading animations", Toast.LENGTH_LONG).show(); + showLoading(false); + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + txService = null; + } + }; + + abstract void setUpTransferList(TransferService txService); + + @Override + public void onResume() { + super.onResume(); + // refreshView(); + if (isNeedUpdateProgress()) + startTimer(); + } + + abstract boolean isNeedUpdateProgress(); + + @Override + public void onStop() { + super.onStop(); + stopTimer(); + if (txService != null) { + mActivity.unbindService(mConnection); + txService = null; + } + } + + // refresh list by mTimer + abstract void startTimer(); + + public void stopTimer() { + mTimer.removeCallbacksAndMessages(null); + } + + abstract void refreshView(); + + private void showLoading(boolean show) { + if (mActivity == null) + return; + + if (show) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation( + mActivity, android.R.anim.fade_in)); + mListContainer.startAnimation(AnimationUtils.loadAnimation( + mActivity, android.R.anim.fade_out)); + + mProgressContainer.setVisibility(View.VISIBLE); + mListContainer.setVisibility(View.INVISIBLE); + } else { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation( + mActivity, android.R.anim.fade_out)); + mListContainer.startAnimation(AnimationUtils.loadAnimation( + mActivity, android.R.anim.fade_in)); + + mProgressContainer.setVisibility(View.GONE); + mListContainer.setVisibility(View.VISIBLE); + } + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + android.view.MenuInflater inflater = mActivity.getMenuInflater(); + inflater.inflate(R.menu.transfer_task_menu, menu); + + ListView listView = (ListView)v; + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + TransferTaskInfo taskInfo = (TransferTaskInfo)listView.getItemAtPosition(info.position); + + android.view.MenuItem itemCancel = menu.findItem(R.id.cancel); + android.view.MenuItem itemRetry = menu.findItem(R.id.retry); + android.view.MenuItem itemRemove = menu.findItem(R.id.remove); + android.view.MenuItem itemRemoveAllCancelled = menu.findItem(R.id.remove_all_cancelled); + android.view.MenuItem itemRemoveAllFinished = menu.findItem(R.id.remove_all_finished); + + itemCancel.setVisible(false); + itemRetry.setVisible(false); + itemRemove.setVisible(false); + itemRemoveAllCancelled.setVisible(false); + itemRemoveAllFinished.setVisible(false); + + switch (taskInfo.state) { + case INIT: + itemCancel.setVisible(true); + break; + case TRANSFERRING: + itemCancel.setVisible(true); + break; + case CANCELLED: + itemRetry.setVisible(true); + itemRemove.setVisible(true); + itemRemoveAllCancelled.setVisible(true); + break; + case FAILED: + itemRetry.setVisible(true); + itemRemove.setVisible(true); + break; + case FINISHED: + itemRemove.setVisible(true); + itemRemoveAllFinished.setVisible(true); + break; + } + } +} diff --git a/src/com/seafile/seadroid2/ui/fragment/UploadTaskFragment.java b/src/com/seafile/seadroid2/ui/fragment/UploadTaskFragment.java new file mode 100644 index 000000000..d38246f9e --- /dev/null +++ b/src/com/seafile/seadroid2/ui/fragment/UploadTaskFragment.java @@ -0,0 +1,178 @@ +package com.seafile.seadroid2.ui.fragment; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import com.seafile.seadroid2.R; +import com.seafile.seadroid2.transfer.*; +import com.seafile.seadroid2.ui.adapter.TransferTaskAdapter; + +import java.util.List; + +/** + * Upload tasks fragments + * + * Created by Logan on 14/12/22. + */ +public class UploadTaskFragment extends TransferTaskFragment { + private static final String DEBUG_TAG = "UploadTaskFragment"; + + private boolean isUploadListVisible; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + emptyView.setText(getString(R.string.no_upload_tasks)); + + } + + @Override + void setUpTransferList(TransferService txService) { + List infos = txService.getAllUploadTaskInfos(); + adapter = new TransferTaskAdapter(mActivity, infos, null); + adapter.setCurrentTab(TransferTaskAdapter.UPLOAD_LIST_TAB); + mTransferTaskListView.setAdapter(adapter); + } + + @Override + public void onResume() { + isUploadListVisible = true; + super.onResume(); + } + + boolean isNeedUpdateProgress() { + // first upload list should at foreground + if (!isUploadListVisible) { + return false; + } + + // second there are some upload tasks + if(txService == null) + return false; + + if (!txService.isUploading()) + return false; + + return true; + } + + @Override + public void onStop() { + super.onStop(); + isUploadListVisible = false; + } + + // refresh upload list by mTimer + void startTimer() { + Log.d(DEBUG_TAG, "timer started"); + mTimer.postDelayed(new Runnable() { + + @Override + public void run() { + adapter.setUploadTaskInfos(txService.getAllUploadTaskInfos()); + adapter.notifyDataSetChanged(); + Log.d(DEBUG_TAG, "timer post refresh signal " + System.currentTimeMillis()); + mTimer.postDelayed(this, 1 * 1000); + } + }, 1 * 1000); + } + + void refreshView() { + + List infos = txService.getAllUploadTaskInfos(); + if (infos == null || infos.isEmpty()) { + mTransferTaskListView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } else { + if (isNeedUpdateProgress()) + startTimer(); + + mTransferTaskListView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + adapter.setUploadTaskInfos(infos); + adapter.notifyDataSetChanged(); + } + + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + if (getUserVisibleHint()) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + + if (txService == null) { + return false; + } + + ListView listView = mTransferTaskListView; + UploadTaskInfo taskInfo = (UploadTaskInfo) listView.getItemAtPosition(info.position); + TransferManager.TaskState state = taskInfo.state; + int taskID = taskInfo.taskID; + + boolean needRefresh = false; + + switch (item.getItemId()) { + case R.id.cancel: + if (state == TransferManager.TaskState.INIT || state == TransferManager.TaskState.TRANSFERRING) { + // txService.cancelUploadTask(taskID); + txService.cancelUploadTaskInQue(taskID); + needRefresh = true; + } + break; + case R.id.retry: + if (state == TransferManager.TaskState.FAILED || state == TransferManager.TaskState.CANCELLED) { + txService.retryUploadTask(taskID); + needRefresh = true; + } + break; + case R.id.remove: + if (state == TransferManager.TaskState.FINISHED || state == TransferManager.TaskState.FAILED || state == TransferManager.TaskState.CANCELLED) { + txService.removeUploadTask(taskID); + needRefresh = true; + } + break; + case R.id.remove_all_cancelled: + if (state == TransferManager.TaskState.CANCELLED) { + txService.removeAllUploadTasksByState(TransferManager.TaskState.CANCELLED); + needRefresh = true; + } + break; + case R.id.remove_all_finished: + if (state == TransferManager.TaskState.FINISHED) { + txService.removeFinishedUploadTasks(); + needRefresh = true; + } + break; + default: + return super.onContextItemSelected(item); + } + + if (needRefresh) { + refreshView(); + } + return true; + } + else + return false; + } + + /** + * cancel upload tasks + * Note that, this method only use to cancel file upload tasks rather than Camera Upload tasks. + * Because Camera Upload tasks will restarted again by Camera Upload Service. + * If want to persistently turn off Camera Upload tasks, should turn off the Camera upload service in Settings Menu. + */ + public void cancelUploadTasks() { + if (txService != null) { + txService.cancelAllUploadTasks(); + } + + refreshView(); + + // stop timer + stopTimer(); + } +} \ No newline at end of file