Merge branch 'develop' into branding_configurable_URL_input_field
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / InstantUploadActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012-2013 ownCloud Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17 package com.owncloud.android.ui.activity;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import android.accounts.Account;
23 import android.app.Activity;
24 import android.content.Intent;
25 import android.database.Cursor;
26 import android.graphics.Bitmap;
27 import android.graphics.BitmapFactory;
28 import android.os.Bundle;
29 import android.util.SparseArray;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.View.OnClickListener;
33 import android.view.View.OnLongClickListener;
34 import android.view.ViewGroup;
35 import android.widget.Button;
36 import android.widget.CheckBox;
37 import android.widget.CompoundButton;
38 import android.widget.CompoundButton.OnCheckedChangeListener;
39 import android.widget.ImageButton;
40 import android.widget.LinearLayout;
41 import android.widget.TextView;
42 import android.widget.Toast;
43
44 import com.owncloud.android.Log_OC;
45 import com.owncloud.android.R;
46 import com.owncloud.android.authentication.AccountUtils;
47 import com.owncloud.android.db.DbHandler;
48 import com.owncloud.android.files.InstantUploadBroadcastReceiver;
49 import com.owncloud.android.files.services.FileUploader;
50 import com.owncloud.android.utils.FileStorageUtils;
51
52 /**
53 * This Activity is used to display a list with images they could not be
54 * uploaded instantly. The images can be selected for delete or for a try again
55 * upload
56 *
57 * The entry-point for this activity is the 'Failed upload Notification" and a
58 * sub-menu underneath the 'Upload' menu-item
59 *
60 * @author andomaex / Matthias Baumann
61 */
62 public class InstantUploadActivity extends Activity {
63
64 private static final String LOG_TAG = InstantUploadActivity.class.getSimpleName();
65 private LinearLayout listView;
66 private static final String retry_chexbox_tag = "retry_chexbox_tag";
67 public static final boolean IS_ENABLED = false;
68 private static int MAX_LOAD_IMAGES = 5;
69 private int lastLoadImageIdx = 0;
70
71 private SparseArray<String> fileList = null;
72 CheckBox failed_upload_all_cb;
73
74 @Override
75 protected void onCreate(Bundle savedInstanceState) {
76 super.onCreate(savedInstanceState);
77 setContentView(R.layout.failed_upload_files);
78
79 Button delete_all_btn = (Button) findViewById(R.id.failed_upload_delete_all_btn);
80 delete_all_btn.setOnClickListener(getDeleteListner());
81 Button retry_all_btn = (Button) findViewById(R.id.failed_upload_retry_all_btn);
82 retry_all_btn.setOnClickListener(getRetryListner());
83 this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
84 failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener());
85 listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout);
86
87 loadListView(true);
88
89 }
90
91 /**
92 * init the listview with ImageButtons, checkboxes and filename for every
93 * Image that was not successfully uploaded
94 *
95 * this method is call at Activity creation and on delete one ore more
96 * list-entry an on retry the upload by clicking the ImageButton or by click
97 * to the 'retry all' button
98 *
99 */
100 private void loadListView(boolean reset) {
101 DbHandler db = new DbHandler(getApplicationContext());
102 Cursor c = db.getFailedFiles();
103
104 if (reset) {
105 fileList = new SparseArray<String>();
106 listView.removeAllViews();
107 lastLoadImageIdx = 0;
108 }
109 if (c != null) {
110 try {
111 c.moveToPosition(lastLoadImageIdx);
112
113 while (c.moveToNext()) {
114
115 lastLoadImageIdx++;
116 String imp_path = c.getString(1);
117 String message = c.getString(4);
118 fileList.put(lastLoadImageIdx, imp_path);
119 LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx);
120 rowLayout.addView(getFileCheckbox(lastLoadImageIdx));
121 rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx));
122 rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx));
123 listView.addView(rowLayout);
124 Log_OC.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx);
125 if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) {
126 break;
127 }
128 }
129 if (lastLoadImageIdx > 0) {
130 addLoadMoreButton(listView);
131 }
132 } finally {
133 db.close();
134 }
135 }
136 }
137
138 private void addLoadMoreButton(LinearLayout listView) {
139 if (listView != null) {
140 Button loadmoreBtn = null;
141 View oldButton = listView.findViewById(42);
142 if (oldButton != null) {
143 // remove existing button
144 listView.removeView(oldButton);
145 // to add the button at the end
146 loadmoreBtn = (Button) oldButton;
147 } else {
148 // create a new button to add to the scoll view
149 loadmoreBtn = new Button(this);
150 loadmoreBtn.setId(42);
151 loadmoreBtn.setText(getString(R.string.failed_upload_load_more_images));
152 loadmoreBtn.setBackgroundResource(R.color.owncloud_white);
153 loadmoreBtn.setTextSize(12);
154 loadmoreBtn.setOnClickListener(new OnClickListener() {
155 @Override
156 public void onClick(View v) {
157 loadListView(false);
158 }
159
160 });
161 }
162 listView.addView(loadmoreBtn);
163 }
164 }
165
166 /**
167 * provide a list of CheckBox instances, looked up from parent listview this
168 * list ist used to select/deselect all checkboxes at the list
169 *
170 * @return List<CheckBox>
171 */
172 private List<CheckBox> getCheckboxList() {
173 List<CheckBox> list = new ArrayList<CheckBox>();
174 for (int i = 0; i < listView.getChildCount(); i++) {
175 Log_OC.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount());
176 View childView = listView.getChildAt(i);
177 if (childView != null && childView instanceof ViewGroup) {
178 View checkboxView = getChildViews((ViewGroup) childView);
179 if (checkboxView != null && checkboxView instanceof CheckBox) {
180 Log_OC.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass());
181 list.add((CheckBox) checkboxView);
182 }
183 }
184 }
185 return list;
186 }
187
188 /**
189 * recursive called method, used from getCheckboxList method
190 *
191 * @param View
192 * @return View
193 */
194 private View getChildViews(ViewGroup view) {
195 if (view != null) {
196 for (int i = 0; i < view.getChildCount(); i++) {
197 View cb = view.getChildAt(i);
198 if (cb != null && cb instanceof ViewGroup) {
199 return getChildViews((ViewGroup) cb);
200 } else if (cb instanceof CheckBox) {
201 return cb;
202 }
203 }
204 }
205 return null;
206 }
207
208 /**
209 * create a new OnCheckedChangeListener for the 'check all' checkbox *
210 *
211 * @return OnCheckedChangeListener to select all checkboxes at the list
212 */
213 private OnCheckedChangeListener getCheckAllListener() {
214 return new OnCheckedChangeListener() {
215 @Override
216 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
217 List<CheckBox> list = getCheckboxList();
218 for (CheckBox checkbox : list) {
219 ((CheckBox) checkbox).setChecked(isChecked);
220 }
221 }
222
223 };
224 }
225
226 /**
227 * Button click Listener for the retry button at the headline
228 *
229 * @return a Listener to perform a retry for all selected images
230 */
231 private OnClickListener getRetryListner() {
232 return new OnClickListener() {
233
234 @Override
235 public void onClick(View v) {
236
237 try {
238
239 List<CheckBox> list = getCheckboxList();
240 for (CheckBox checkbox : list) {
241 boolean to_retry = checkbox.isChecked();
242
243 Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry);
244 String img_path = fileList.get(checkbox.getId());
245 if (to_retry) {
246
247 final String msg = "Image-Path " + checkbox.getId() + " was checked: " + img_path;
248 Log_OC.d(LOG_TAG, msg);
249 startUpload(img_path);
250 }
251
252 }
253 } finally {
254 // refresh the List
255 listView.removeAllViews();
256 loadListView(true);
257 if (failed_upload_all_cb != null) {
258 failed_upload_all_cb.setChecked(false);
259 }
260 }
261
262 }
263 };
264 }
265
266 /**
267 * Button click Listener for the delete button at the headline
268 *
269 * @return a Listener to perform a delete for all selected images
270 */
271 private OnClickListener getDeleteListner() {
272
273 return new OnClickListener() {
274
275 @Override
276 public void onClick(View v) {
277
278 final DbHandler dbh = new DbHandler(getApplicationContext());
279 try {
280 List<CheckBox> list = getCheckboxList();
281 for (CheckBox checkbox : list) {
282 boolean to_be_delete = checkbox.isChecked();
283
284 Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete);
285 String img_path = fileList.get(checkbox.getId());
286 Log_OC.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path);
287 if (to_be_delete) {
288 boolean deleted = dbh.removeIUPendingFile(img_path);
289 Log_OC.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted);
290
291 }
292
293 }
294 } finally {
295 dbh.close();
296 // refresh the List
297 listView.removeAllViews();
298 loadListView(true);
299 if (failed_upload_all_cb != null) {
300 failed_upload_all_cb.setChecked(false);
301 }
302 }
303
304 }
305 };
306 }
307
308 private LinearLayout getHorizontalLinearLayout(int id) {
309 LinearLayout linearLayout = new LinearLayout(getApplicationContext());
310 linearLayout.setId(id);
311 linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
312 LinearLayout.LayoutParams.MATCH_PARENT));
313 linearLayout.setGravity(Gravity.RIGHT);
314 linearLayout.setOrientation(LinearLayout.HORIZONTAL);
315 return linearLayout;
316 }
317
318 private LinearLayout getVerticalLinearLayout() {
319 LinearLayout linearLayout = new LinearLayout(getApplicationContext());
320 linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
321 LinearLayout.LayoutParams.MATCH_PARENT));
322 linearLayout.setGravity(Gravity.TOP);
323 linearLayout.setOrientation(LinearLayout.VERTICAL);
324 return linearLayout;
325 }
326
327 private View getFileButton(final String img_path, String message, int id) {
328
329 TextView failureTextView = new TextView(this);
330 failureTextView.setText(getString(R.string.failed_upload_failure_text) + message);
331 failureTextView.setBackgroundResource(R.color.owncloud_white);
332 failureTextView.setTextSize(8);
333 failureTextView.setOnLongClickListener(getOnLongClickListener(message));
334 failureTextView.setPadding(5, 5, 5, 10);
335 TextView retryButton = new TextView(this);
336 retryButton.setId(id);
337 retryButton.setText(img_path);
338 retryButton.setBackgroundResource(R.color.owncloud_white);
339 retryButton.setTextSize(8);
340 retryButton.setOnClickListener(getImageButtonOnClickListener(img_path));
341 retryButton.setOnLongClickListener(getOnLongClickListener(message));
342 retryButton.setPadding(5, 5, 5, 10);
343 LinearLayout verticalLayout = getVerticalLinearLayout();
344 verticalLayout.addView(retryButton);
345 verticalLayout.addView(failureTextView);
346
347 return verticalLayout;
348 }
349
350 private OnLongClickListener getOnLongClickListener(final String message) {
351 return new OnLongClickListener() {
352
353 @Override
354 public boolean onLongClick(View v) {
355 Log_OC.d(LOG_TAG, message);
356 Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
357 + message, Toast.LENGTH_LONG);
358 toast.show();
359 return true;
360 }
361
362 };
363 }
364
365 private CheckBox getFileCheckbox(int id) {
366 CheckBox retryCB = new CheckBox(this);
367 retryCB.setId(id);
368 retryCB.setBackgroundResource(R.color.owncloud_white);
369 retryCB.setTextSize(8);
370 retryCB.setTag(retry_chexbox_tag);
371 return retryCB;
372 }
373
374 private ImageButton getImageButton(String img_path, int id) {
375 ImageButton imageButton = new ImageButton(this);
376 imageButton.setId(id);
377 imageButton.setClickable(true);
378 imageButton.setOnClickListener(getImageButtonOnClickListener(img_path));
379
380 // scale and add a thumbnail to the imagebutton
381 int base_scale_size = 32;
382 if (img_path != null) {
383 Log_OC.d(LOG_TAG, "add " + img_path + " to Image Button");
384 BitmapFactory.Options options = new BitmapFactory.Options();
385 options.inJustDecodeBounds = true;
386 Bitmap bitmap = BitmapFactory.decodeFile(img_path, options);
387 int width_tpm = options.outWidth, height_tmp = options.outHeight;
388 int scale = 3;
389 while (true) {
390 if (width_tpm / 2 < base_scale_size || height_tmp / 2 < base_scale_size) {
391 break;
392 }
393 width_tpm /= 2;
394 height_tmp /= 2;
395 scale++;
396 }
397
398 Log_OC.d(LOG_TAG, "scale Imgae with: " + scale);
399 BitmapFactory.Options options2 = new BitmapFactory.Options();
400 options2.inSampleSize = scale;
401 bitmap = BitmapFactory.decodeFile(img_path, options2);
402
403 if (bitmap != null) {
404 Log_OC.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes());
405 imageButton.setImageBitmap(bitmap);
406 } else {
407 Log_OC.d(LOG_TAG, "could not load imgage: " + img_path);
408 }
409 }
410 return imageButton;
411 }
412
413 private OnClickListener getImageButtonOnClickListener(final String img_path) {
414 return new OnClickListener() {
415
416 @Override
417 public void onClick(View v) {
418 startUpload(img_path);
419 loadListView(true);
420 }
421
422 };
423 }
424
425 /**
426 * start uploading a file to the INSTANT_UPLOD_DIR
427 *
428 * @param img_path
429 */
430 private void startUpload(String img_path) {
431 // extract filename
432 String filename = FileStorageUtils.getInstantUploadFilePath(this, img_path);
433 if (canInstantUpload()) {
434 Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this);
435 // add file again to upload queue
436 DbHandler db = new DbHandler(InstantUploadActivity.this);
437 try {
438 db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null);
439 } finally {
440 db.close();
441 }
442
443 Intent i = new Intent(InstantUploadActivity.this, FileUploader.class);
444 i.putExtra(FileUploader.KEY_ACCOUNT, account);
445 i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path);
446 i.putExtra(FileUploader.KEY_REMOTE_FILE, filename);
447 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
448 i.putExtra(com.owncloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true);
449
450 final String msg = "try to upload file with name :" + filename;
451 Log_OC.d(LOG_TAG, msg);
452 Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
453 + filename, Toast.LENGTH_LONG);
454 toast.show();
455
456 startService(i);
457 } else {
458 Toast toast = Toast.makeText(InstantUploadActivity.this,
459 getString(R.string.failed_upload_retry_do_nothing_text) + filename, Toast.LENGTH_LONG);
460 toast.show();
461 }
462 }
463
464 private boolean canInstantUpload() {
465
466 if (!InstantUploadBroadcastReceiver.isOnline(this)
467 || (InstantUploadBroadcastReceiver.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
468 .isConnectedViaWiFi(this))) {
469 return false;
470 } else {
471 return true;
472 }
473 }
474
475 }