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