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