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