1 /* ownCloud Android client application
2 * Copyright (C) 2012-2013 ownCloud Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package com
.owncloud
.android
.ui
.activity
;
20 import java
.util
.ArrayList
;
21 import java
.util
.List
;
23 import android
.accounts
.Account
;
24 import android
.app
.Activity
;
25 import android
.content
.Intent
;
26 import android
.database
.Cursor
;
27 import android
.graphics
.Bitmap
;
28 import android
.graphics
.BitmapFactory
;
29 import android
.os
.Bundle
;
30 import android
.util
.Log
;
31 import android
.util
.SparseArray
;
32 import android
.view
.Gravity
;
33 import android
.view
.View
;
34 import android
.view
.View
.OnClickListener
;
35 import android
.view
.View
.OnLongClickListener
;
36 import android
.view
.ViewGroup
;
37 import android
.widget
.Button
;
38 import android
.widget
.CheckBox
;
39 import android
.widget
.CompoundButton
;
40 import android
.widget
.CompoundButton
.OnCheckedChangeListener
;
41 import android
.widget
.ImageButton
;
42 import android
.widget
.LinearLayout
;
43 import android
.widget
.TextView
;
44 import android
.widget
.Toast
;
46 import com
.owncloud
.android
.AccountUtils
;
47 import com
.owncloud
.android
.Log_OC
;
48 import com
.owncloud
.android
.R
;
49 import com
.owncloud
.android
.db
.DbHandler
;
50 import com
.owncloud
.android
.files
.InstantUploadBroadcastReceiver
;
51 import com
.owncloud
.android
.files
.services
.FileUploader
;
52 import com
.owncloud
.android
.utils
.FileStorageUtils
;
55 * This Activity is used to display a list with images they could not be
56 * uploaded instantly. The images can be selected for delete or for a try again
59 * The entry-point for this activity is the 'Failed upload Notification" and a
60 * sub-menu underneath the 'Upload' menu-item
62 * @author andomaex / Matthias Baumann
64 * This program is free software: you can redistribute it and/or modify
65 * it under the terms of the GNU General Public License as published by
66 * the Free Software Foundation, either version 3 of the License, or (at
67 * your option) any later version.
69 * This program is distributed in the hope that it will be useful, but
70 * WITHOUT ANY WARRANTY; without even the implied warranty of
71 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
72 * General Public License for more de/
74 public class InstantUploadActivity
extends Activity
{
76 private static final String LOG_TAG
= InstantUploadActivity
.class.getSimpleName();
77 private LinearLayout listView
;
78 private static final String retry_chexbox_tag
= "retry_chexbox_tag";
79 public static final boolean IS_ENABLED
= false
;
80 private static int MAX_LOAD_IMAGES
= 5;
81 private int lastLoadImageIdx
= 0;
83 private SparseArray
<String
> fileList
= null
;
84 CheckBox failed_upload_all_cb
;
87 protected void onCreate(Bundle savedInstanceState
) {
88 super.onCreate(savedInstanceState
);
89 setContentView(R
.layout
.failed_upload_files
);
91 Button delete_all_btn
= (Button
) findViewById(R
.id
.failed_upload_delete_all_btn
);
92 delete_all_btn
.setOnClickListener(getDeleteListner());
93 Button retry_all_btn
= (Button
) findViewById(R
.id
.failed_upload_retry_all_btn
);
94 retry_all_btn
.setOnClickListener(getRetryListner());
95 this.failed_upload_all_cb
= (CheckBox
) findViewById(R
.id
.failed_upload_headline_cb
);
96 failed_upload_all_cb
.setOnCheckedChangeListener(getCheckAllListener());
97 listView
= (LinearLayout
) findViewById(R
.id
.failed_upload_scrollviewlayout
);
104 * init the listview with ImageButtons, checkboxes and filename for every
105 * Image that was not successfully uploaded
107 * this method is call at Activity creation and on delete one ore more
108 * list-entry an on retry the upload by clicking the ImageButton or by click
109 * to the 'retry all' button
112 private void loadListView(boolean reset
) {
113 DbHandler db
= new DbHandler(getApplicationContext());
114 Cursor c
= db
.getFailedFiles();
117 fileList
= new SparseArray
<String
>();
118 listView
.removeAllViews();
119 lastLoadImageIdx
= 0;
123 c
.moveToPosition(lastLoadImageIdx
);
125 while (c
.moveToNext()) {
128 String imp_path
= c
.getString(1);
129 String message
= c
.getString(4);
130 fileList
.put(lastLoadImageIdx
, imp_path
);
131 LinearLayout rowLayout
= getHorizontalLinearLayout(lastLoadImageIdx
);
132 rowLayout
.addView(getFileCheckbox(lastLoadImageIdx
));
133 rowLayout
.addView(getImageButton(imp_path
, lastLoadImageIdx
));
134 rowLayout
.addView(getFileButton(imp_path
, message
, lastLoadImageIdx
));
135 listView
.addView(rowLayout
);
136 Log_OC
.d(LOG_TAG
, imp_path
+ " on idx: " + lastLoadImageIdx
);
137 if (lastLoadImageIdx
% MAX_LOAD_IMAGES
== 0) {
141 if (lastLoadImageIdx
> 0) {
142 addLoadMoreButton(listView
);
150 private void addLoadMoreButton(LinearLayout listView
) {
151 if (listView
!= null
) {
152 Button loadmoreBtn
= null
;
153 View oldButton
= listView
.findViewById(42);
154 if (oldButton
!= null
) {
155 // remove existing button
156 listView
.removeView(oldButton
);
157 // to add the button at the end
158 loadmoreBtn
= (Button
) oldButton
;
160 // create a new button to add to the scoll view
161 loadmoreBtn
= new Button(this);
162 loadmoreBtn
.setId(42);
163 loadmoreBtn
.setText(getString(R
.string
.failed_upload_load_more_images
));
164 loadmoreBtn
.setBackgroundResource(R
.color
.owncloud_white
);
165 loadmoreBtn
.setTextSize(12);
166 loadmoreBtn
.setOnClickListener(new OnClickListener() {
168 public void onClick(View v
) {
174 listView
.addView(loadmoreBtn
);
179 * provide a list of CheckBox instances, looked up from parent listview this
180 * list ist used to select/deselect all checkboxes at the list
182 * @return List<CheckBox>
184 private List
<CheckBox
> getCheckboxList() {
185 List
<CheckBox
> list
= new ArrayList
<CheckBox
>();
186 for (int i
= 0; i
< listView
.getChildCount(); i
++) {
187 Log_OC
.d(LOG_TAG
, "ListView has Childs: " + listView
.getChildCount());
188 View childView
= listView
.getChildAt(i
);
189 if (childView
!= null
&& childView
instanceof ViewGroup
) {
190 View checkboxView
= getChildViews((ViewGroup
) childView
);
191 if (checkboxView
!= null
&& checkboxView
instanceof CheckBox
) {
192 Log_OC
.d(LOG_TAG
, "found Child: " + checkboxView
.getId() + " " + checkboxView
.getClass());
193 list
.add((CheckBox
) checkboxView
);
201 * recursive called method, used from getCheckboxList method
206 private View
getChildViews(ViewGroup view
) {
208 for (int i
= 0; i
< view
.getChildCount(); i
++) {
209 View cb
= view
.getChildAt(i
);
210 if (cb
!= null
&& cb
instanceof ViewGroup
) {
211 return getChildViews((ViewGroup
) cb
);
212 } else if (cb
instanceof CheckBox
) {
221 * create a new OnCheckedChangeListener for the 'check all' checkbox *
223 * @return OnCheckedChangeListener to select all checkboxes at the list
225 private OnCheckedChangeListener
getCheckAllListener() {
226 return new OnCheckedChangeListener() {
228 public void onCheckedChanged(CompoundButton buttonView
, boolean isChecked
) {
229 List
<CheckBox
> list
= getCheckboxList();
230 for (CheckBox checkbox
: list
) {
231 ((CheckBox
) checkbox
).setChecked(isChecked
);
239 * Button click Listener for the retry button at the headline
241 * @return a Listener to perform a retry for all selected images
243 private OnClickListener
getRetryListner() {
244 return new OnClickListener() {
247 public void onClick(View v
) {
251 List
<CheckBox
> list
= getCheckboxList();
252 for (CheckBox checkbox
: list
) {
253 boolean to_retry
= checkbox
.isChecked();
255 Log_OC
.d(LOG_TAG
, "Checkbox for " + checkbox
.getId() + " was checked: " + to_retry
);
256 String img_path
= fileList
.get(checkbox
.getId());
259 final String msg
= "Image-Path " + checkbox
.getId() + " was checked: " + img_path
;
260 Log_OC
.d(LOG_TAG
, msg
);
261 startUpload(img_path
);
267 listView
.removeAllViews();
269 if (failed_upload_all_cb
!= null
) {
270 failed_upload_all_cb
.setChecked(false
);
279 * Button click Listener for the delete button at the headline
281 * @return a Listener to perform a delete for all selected images
283 private OnClickListener
getDeleteListner() {
285 return new OnClickListener() {
288 public void onClick(View v
) {
290 final DbHandler dbh
= new DbHandler(getApplicationContext());
292 List
<CheckBox
> list
= getCheckboxList();
293 for (CheckBox checkbox
: list
) {
294 boolean to_be_delete
= checkbox
.isChecked();
296 Log_OC
.d(LOG_TAG
, "Checkbox for " + checkbox
.getId() + " was checked: " + to_be_delete
);
297 String img_path
= fileList
.get(checkbox
.getId());
298 Log_OC
.d(LOG_TAG
, "Image-Path " + checkbox
.getId() + " was checked: " + img_path
);
300 boolean deleted
= dbh
.removeIUPendingFile(img_path
);
301 Log_OC
.d(LOG_TAG
, "removing " + checkbox
.getId() + " was : " + deleted
);
309 listView
.removeAllViews();
311 if (failed_upload_all_cb
!= null
) {
312 failed_upload_all_cb
.setChecked(false
);
320 private LinearLayout
getHorizontalLinearLayout(int id
) {
321 LinearLayout linearLayout
= new LinearLayout(getApplicationContext());
322 linearLayout
.setId(id
);
323 linearLayout
.setLayoutParams(new LinearLayout
.LayoutParams(LinearLayout
.LayoutParams
.WRAP_CONTENT
,
324 LinearLayout
.LayoutParams
.MATCH_PARENT
));
325 linearLayout
.setGravity(Gravity
.RIGHT
);
326 linearLayout
.setOrientation(LinearLayout
.HORIZONTAL
);
330 private LinearLayout
getVerticalLinearLayout() {
331 LinearLayout linearLayout
= new LinearLayout(getApplicationContext());
332 linearLayout
.setLayoutParams(new LinearLayout
.LayoutParams(LinearLayout
.LayoutParams
.WRAP_CONTENT
,
333 LinearLayout
.LayoutParams
.MATCH_PARENT
));
334 linearLayout
.setGravity(Gravity
.TOP
);
335 linearLayout
.setOrientation(LinearLayout
.VERTICAL
);
339 private View
getFileButton(final String img_path
, String message
, int id
) {
341 TextView failureTextView
= new TextView(this);
342 failureTextView
.setText(getString(R
.string
.failed_upload_failure_text
) + message
);
343 failureTextView
.setBackgroundResource(R
.color
.owncloud_white
);
344 failureTextView
.setTextSize(8);
345 failureTextView
.setOnLongClickListener(getOnLongClickListener(message
));
346 failureTextView
.setPadding(5, 5, 5, 10);
347 TextView retryButton
= new TextView(this);
348 retryButton
.setId(id
);
349 retryButton
.setText(img_path
);
350 retryButton
.setBackgroundResource(R
.color
.owncloud_white
);
351 retryButton
.setTextSize(8);
352 retryButton
.setOnClickListener(getImageButtonOnClickListener(img_path
));
353 retryButton
.setOnLongClickListener(getOnLongClickListener(message
));
354 retryButton
.setPadding(5, 5, 5, 10);
355 LinearLayout verticalLayout
= getVerticalLinearLayout();
356 verticalLayout
.addView(retryButton
);
357 verticalLayout
.addView(failureTextView
);
359 return verticalLayout
;
362 private OnLongClickListener
getOnLongClickListener(final String message
) {
363 return new OnLongClickListener() {
366 public boolean onLongClick(View v
) {
367 Log
.d(LOG_TAG
, message
);
368 Toast toast
= Toast
.makeText(InstantUploadActivity
.this, getString(R
.string
.failed_upload_retry_text
)
369 + message
, Toast
.LENGTH_LONG
);
377 private CheckBox
getFileCheckbox(int id
) {
378 CheckBox retryCB
= new CheckBox(this);
380 retryCB
.setBackgroundResource(R
.color
.owncloud_white
);
381 retryCB
.setTextSize(8);
382 retryCB
.setTag(retry_chexbox_tag
);
386 private ImageButton
getImageButton(String img_path
, int id
) {
387 ImageButton imageButton
= new ImageButton(this);
388 imageButton
.setId(id
);
389 imageButton
.setClickable(true
);
390 imageButton
.setOnClickListener(getImageButtonOnClickListener(img_path
));
392 // scale and add a thumbnail to the imagebutton
393 int base_scale_size
= 32;
394 if (img_path
!= null
) {
395 Log_OC
.d(LOG_TAG
, "add " + img_path
+ " to Image Button");
396 BitmapFactory
.Options options
= new BitmapFactory
.Options();
397 options
.inJustDecodeBounds
= true
;
398 Bitmap bitmap
= BitmapFactory
.decodeFile(img_path
, options
);
399 int width_tpm
= options
.outWidth
, height_tmp
= options
.outHeight
;
402 if (width_tpm
/ 2 < base_scale_size
|| height_tmp
/ 2 < base_scale_size
) {
410 Log_OC
.d(LOG_TAG
, "scale Imgae with: " + scale
);
411 BitmapFactory
.Options options2
= new BitmapFactory
.Options();
412 options2
.inSampleSize
= scale
;
413 bitmap
= BitmapFactory
.decodeFile(img_path
, options2
);
415 if (bitmap
!= null
) {
416 Log_OC
.d(LOG_TAG
, "loaded Bitmap Bytes: " + bitmap
.getRowBytes());
417 imageButton
.setImageBitmap(bitmap
);
419 Log_OC
.d(LOG_TAG
, "could not load imgage: " + img_path
);
425 private OnClickListener
getImageButtonOnClickListener(final String img_path
) {
426 return new OnClickListener() {
429 public void onClick(View v
) {
430 startUpload(img_path
);
438 * start uploading a file to the INSTANT_UPLOD_DIR
442 private void startUpload(String img_path
) {
444 String filename
= FileStorageUtils
.getInstantUploadFilePath(img_path
);
445 if (canInstantUpload()) {
446 Account account
= AccountUtils
.getCurrentOwnCloudAccount(InstantUploadActivity
.this);
447 // add file again to upload queue
448 DbHandler db
= new DbHandler(InstantUploadActivity
.this);
450 db
.updateFileState(img_path
, DbHandler
.UPLOAD_STATUS_UPLOAD_LATER
, null
);
455 Intent i
= new Intent(InstantUploadActivity
.this, FileUploader
.class);
456 i
.putExtra(FileUploader
.KEY_ACCOUNT
, account
);
457 i
.putExtra(FileUploader
.KEY_LOCAL_FILE
, img_path
);
458 i
.putExtra(FileUploader
.KEY_REMOTE_FILE
, filename
);
459 i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_SINGLE_FILE
);
460 i
.putExtra(com
.owncloud
.android
.files
.services
.FileUploader
.KEY_INSTANT_UPLOAD
, true
);
462 final String msg
= "try to upload file with name :" + filename
;
463 Log_OC
.d(LOG_TAG
, msg
);
464 Toast toast
= Toast
.makeText(InstantUploadActivity
.this, getString(R
.string
.failed_upload_retry_text
)
465 + filename
, Toast
.LENGTH_LONG
);
470 Toast toast
= Toast
.makeText(InstantUploadActivity
.this,
471 getString(R
.string
.failed_upload_retry_do_nothing_text
) + filename
, Toast
.LENGTH_LONG
);
476 private boolean canInstantUpload() {
478 if (!InstantUploadBroadcastReceiver
.isOnline(this)
479 || (InstantUploadBroadcastReceiver
.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
480 .isConnectedViaWiFi(this))) {