blob: 66c111fb776235d7e55ef8737b57cd49d2e04aac [file] [log] [blame]
Jose Pascoalb690af12014-10-06 18:29:26 +01001package com.fairphone.updater.fragments;
2
Jose Pascoal54b3ae62014-10-07 20:29:58 +01003import java.io.File;
4import java.io.IOException;
5import java.util.concurrent.TimeoutException;
6
Jose Pascoal75392162014-10-15 18:29:01 +01007import android.app.AlertDialog;
Jose Pascoalb690af12014-10-06 18:29:26 +01008import android.app.DownloadManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +01009import android.app.ProgressDialog;
10import android.content.BroadcastReceiver;
11import android.content.Context;
Jose Pascoal75392162014-10-15 18:29:01 +010012import android.content.DialogInterface;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010013import android.content.Intent;
14import android.content.IntentFilter;
Tiago Costa87925fe2014-12-02 17:57:51 +000015import android.content.SharedPreferences;
16import android.content.SharedPreferences.Editor;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010017import android.content.res.Resources;
18import android.content.res.Resources.NotFoundException;
Jose Pascoalb690af12014-10-06 18:29:26 +010019import android.database.Cursor;
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +000020import android.net.ConnectivityManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010021import android.os.AsyncTask;
Jose Pascoalb690af12014-10-06 18:29:26 +010022import android.os.Bundle;
23import android.os.Environment;
24import android.util.Log;
25import android.view.LayoutInflater;
26import android.view.View;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010027import android.view.View.OnClickListener;
Jose Pascoalb690af12014-10-06 18:29:26 +010028import android.view.ViewGroup;
29import android.widget.Button;
30import android.widget.LinearLayout;
31import android.widget.ProgressBar;
32import android.widget.TextView;
33import android.widget.Toast;
34
Tiago Costa87925fe2014-12-02 17:57:51 +000035import com.fairphone.updater.FairphoneUpdater;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010036import com.fairphone.updater.FairphoneUpdater.HeaderType;
37import com.fairphone.updater.FairphoneUpdater.UpdaterState;
Jose Pascoalb690af12014-10-06 18:29:26 +010038import com.fairphone.updater.R;
Jose Pascoal02d86242014-12-17 18:50:08 +000039import com.fairphone.updater.UpdaterService;
Jose Pascoal1c049e02014-12-17 13:03:09 +000040import com.fairphone.updater.data.DownloadableItem;
Tiago Costa198bf3d2014-12-16 15:23:18 +000041import com.fairphone.updater.data.Store;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010042import com.fairphone.updater.data.Version;
Jose Pascoalb690af12014-10-06 18:29:26 +010043import com.fairphone.updater.tools.Utils;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010044import com.stericson.RootTools.RootTools;
45import com.stericson.RootTools.exceptions.RootDeniedException;
46import com.stericson.RootTools.execution.CommandCapture;
47import com.stericson.RootTools.execution.Shell;
Jose Pascoalb690af12014-10-06 18:29:26 +010048
Jose Pascoal810950b2014-10-09 17:16:08 +010049public class DownloadAndRestartFragment extends BaseFragment
50{
Jose Pascoalb690af12014-10-06 18:29:26 +010051
Jose Pascoal810950b2014-10-09 17:16:08 +010052 protected static final String TAG = DownloadAndRestartFragment.class.getSimpleName();
Jose Pascoalb690af12014-10-06 18:29:26 +010053
Jose Pascoal810950b2014-10-09 17:16:08 +010054 private TextView mDownloadVersionName;
55 private LinearLayout mVersionDownloadingGroup;
56 private ProgressBar mVersionDownloadProgressBar;
57 private LinearLayout mVersionInstallGroup;
58 private Button mRestartButton;
59 private Button mCancelButton;
60 private Version mSelectedVersion;
Tiago Costa198bf3d2014-12-16 15:23:18 +000061 private Store mSelectedStore;
Jose Pascoalb690af12014-10-06 18:29:26 +010062
Tiago Costa198bf3d2014-12-16 15:23:18 +000063 private boolean mIsVersion;
Jose Pascoal1c049e02014-12-17 13:03:09 +000064
Jose Pascoal810950b2014-10-09 17:16:08 +010065 private DownloadManager mDownloadManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010066
Jose Pascoal810950b2014-10-09 17:16:08 +010067 private DownloadBroadCastReceiver mDownloadBroadCastReceiver;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010068
Tiago Costa73eda412014-11-18 14:37:42 +000069 private BroadcastReceiver mNetworkStateReceiver;
70
Jose Pascoal810950b2014-10-09 17:16:08 +010071 private long mLatestUpdateDownloadId;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010072
Jose Pascoal1c049e02014-12-17 13:03:09 +000073 public DownloadAndRestartFragment(boolean isVersion)
74 {
Tiago Costa198bf3d2014-12-16 15:23:18 +000075 super();
Jose Pascoal1c049e02014-12-17 13:03:09 +000076
Tiago Costa198bf3d2014-12-16 15:23:18 +000077 mIsVersion = isVersion;
78 }
Jose Pascoal1c049e02014-12-17 13:03:09 +000079
Jose Pascoal810950b2014-10-09 17:16:08 +010080 @Override
81 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
82 {
83 // Inflate the layout for this fragment
Tiago Costa198bf3d2014-12-16 15:23:18 +000084 View view = null;
Jose Pascoal1c049e02014-12-17 13:03:09 +000085 if (mIsVersion)
86 {
Tiago Costa198bf3d2014-12-16 15:23:18 +000087 mSelectedVersion = mainActivity.getSelectedVersion();
88 view = inflateViewByImageType(inflater, container);
Jose Pascoal1c049e02014-12-17 13:03:09 +000089 mSelectedStore = null;
90 }
91 else
92 {
Tiago Costa198bf3d2014-12-16 15:23:18 +000093 mSelectedStore = mainActivity.getSelectedStore();
94 view = inflateStoreView(inflater, container);
Jose Pascoal1c049e02014-12-17 13:03:09 +000095 mSelectedVersion = null;
Tiago Costa198bf3d2014-12-16 15:23:18 +000096 }
Jose Pascoal1c049e02014-12-17 13:03:09 +000097
Jose Pascoal810950b2014-10-09 17:16:08 +010098 setupLayout(view);
Jose Pascoalaa579a82014-11-05 22:17:16 +000099
Jose Pascoal810950b2014-10-09 17:16:08 +0100100 return view;
101 }
Jose Pascoal1c049e02014-12-17 13:03:09 +0000102
Tiago Costa198bf3d2014-12-16 15:23:18 +0000103 private View inflateViewByImageType(LayoutInflater inflater, ViewGroup container)
104 {
105 View view = inflater.inflate(R.layout.fragment_download_fairphone, container, false);
106 if (mSelectedVersion != null)
107 {
108 if (Version.IMAGE_TYPE_AOSP.equalsIgnoreCase(mSelectedVersion.getImageType()))
109 {
110 view = inflater.inflate(R.layout.fragment_download_android, container, false);
111 }
112 else if (Version.IMAGE_TYPE_FAIRPHONE.equalsIgnoreCase(mSelectedVersion.getImageType()))
113 {
114 view = inflater.inflate(R.layout.fragment_download_fairphone, container, false);
115 }
116 }
117 return view;
118 }
119
120 private View inflateStoreView(LayoutInflater inflater, ViewGroup container)
121 {
122 View view = inflater.inflate(R.layout.fragment_download_app_store, container, false);
Jose Pascoal1c049e02014-12-17 13:03:09 +0000123
Tiago Costa198bf3d2014-12-16 15:23:18 +0000124 return view;
125 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100126
Jose Pascoal810950b2014-10-09 17:16:08 +0100127 private void toggleDownloadProgressAndRestart()
128 {
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100129 UpdaterState state = mainActivity.getCurrentUpdaterState();
130 switch (state)
Jose Pascoal810950b2014-10-09 17:16:08 +0100131 {
132 case DOWNLOAD:
133 setupDownloadState();
Jose Pascoalb690af12014-10-06 18:29:26 +0100134
Jose Pascoal810950b2014-10-09 17:16:08 +0100135 mVersionInstallGroup.setVisibility(View.GONE);
136 mVersionDownloadingGroup.setVisibility(View.VISIBLE);
137 break;
Jose Pascoalb690af12014-10-06 18:29:26 +0100138
Jose Pascoal810950b2014-10-09 17:16:08 +0100139 case PREINSTALL:
140 setupPreInstallState();
Jose Pascoalb690af12014-10-06 18:29:26 +0100141
Jose Pascoal810950b2014-10-09 17:16:08 +0100142 mVersionDownloadingGroup.setVisibility(View.GONE);
143 mVersionInstallGroup.setVisibility(View.VISIBLE);
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100144
145 mRestartButton.setOnClickListener(new OnClickListener()
146 {
147
148 @Override
149 public void onClick(View v)
150 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000151 if (mIsVersion)
152 {
153 showEraseAllDataWarning();
154 }
155 else if (mSelectedStore != null)
156 {
157 startPreInstall();
158 }
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100159 }
160 });
161
Jose Pascoal810950b2014-10-09 17:16:08 +0100162 break;
Jose Pascoalb690af12014-10-06 18:29:26 +0100163
Jose Pascoal810950b2014-10-09 17:16:08 +0100164 default:
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100165 Log.w(TAG, "Wrong State: " + state + "\nOnly DOWNLOAD and PREINSTALL are supported");
Jose Pascoal3bc3a842014-10-16 19:03:21 +0100166 mainActivity.removeLastFragment(true);
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100167 return;
168
Jose Pascoal810950b2014-10-09 17:16:08 +0100169 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100170
Jose Pascoal810950b2014-10-09 17:16:08 +0100171 mCancelButton.setOnClickListener(new OnClickListener()
172 {
Jose Pascoalb690af12014-10-06 18:29:26 +0100173
Jose Pascoal810950b2014-10-09 17:16:08 +0100174 @Override
175 public void onClick(View v)
176 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000177 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100178 }
179 });
180 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100181
Jose Pascoal75392162014-10-15 18:29:01 +0100182 private void showEraseAllDataWarning()
183 {
Tiago Costa73eda412014-11-18 14:37:42 +0000184 if (mSelectedVersion != null && mSelectedVersion.hasEraseAllPartitionWarning())
Jose Pascoal75392162014-10-15 18:29:01 +0100185 {
186 new AlertDialog.Builder(mainActivity).setTitle(android.R.string.dialog_alert_title).setMessage(R.string.erase_all_partitions_warning_message)
187 .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
188 {
189
190 @Override
191 public void onClick(DialogInterface dialog, int which)
192 {
193 startPreInstall();
194 }
195 }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
196 {
197 public void onClick(DialogInterface dialog, int which)
198 {
199 // do nothing
200 }
201 }).show();
202 }
203 else
204 {
205 startPreInstall();
206 }
207 }
208
Jose Pascoal1c049e02014-12-17 13:03:09 +0000209 private void updateHeader()
210 {
Jose Pascoal02d86242014-12-17 18:50:08 +0000211 if (mIsVersion && mSelectedVersion != null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100212 {
213 if (Version.IMAGE_TYPE_FAIRPHONE.equalsIgnoreCase(mSelectedVersion.getImageType()))
214 {
Tiago Costa3855faa2014-11-14 17:55:05 +0000215 mainActivity.updateHeader(HeaderType.MAIN_FAIRPHONE, "", false);
Jose Pascoal810950b2014-10-09 17:16:08 +0100216 }
217 else if (Version.IMAGE_TYPE_AOSP.equalsIgnoreCase(mSelectedVersion.getImageType()))
218 {
Tiago Costa3855faa2014-11-14 17:55:05 +0000219 mainActivity.updateHeader(HeaderType.MAIN_ANDROID, "", false);
Jose Pascoal810950b2014-10-09 17:16:08 +0100220 }
221 }
Jose Pascoal02d86242014-12-17 18:50:08 +0000222 else if (mSelectedStore != null)
223 {
224 mainActivity.updateHeader(HeaderType.MAIN_APP_STORE, "", false);
225 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100226 else
227 {
Tiago Costa3855faa2014-11-14 17:55:05 +0000228 mainActivity.updateHeader(HeaderType.MAIN_FAIRPHONE, "", false);
Jose Pascoal810950b2014-10-09 17:16:08 +0100229 }
230 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100231
Jose Pascoal810950b2014-10-09 17:16:08 +0100232 private void startDownloadProgressUpdateThread()
233 {
234 new Thread(new Runnable()
235 {
Jose Pascoalb690af12014-10-06 18:29:26 +0100236
Jose Pascoal810950b2014-10-09 17:16:08 +0100237 @Override
238 public void run()
239 {
Jose Pascoal810950b2014-10-09 17:16:08 +0100240 boolean downloading = true;
Jose Pascoalb690af12014-10-06 18:29:26 +0100241
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000242 long latestUpdateDownloadId = 0;
243
244 int count = 3;
245
Jose Pascoal1c049e02014-12-17 13:03:09 +0000246 while (((latestUpdateDownloadId = mainActivity.getLatestDownloadId()) <= 0) && count > 0)
247 {
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000248 try
249 {
250 Thread.sleep(2000);
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000251 count--;
252 } catch (InterruptedException e)
253 {
254 e.printStackTrace();
255 }
256 }
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000257
Jose Pascoalaa579a82014-11-05 22:17:16 +0000258 while (mDownloadManager != null && latestUpdateDownloadId != 0 && downloading)
Jose Pascoal810950b2014-10-09 17:16:08 +0100259 {
Jose Pascoalb690af12014-10-06 18:29:26 +0100260
Jose Pascoal810950b2014-10-09 17:16:08 +0100261 DownloadManager.Query q = new DownloadManager.Query();
262 q.setFilterById(latestUpdateDownloadId);
Jose Pascoalb690af12014-10-06 18:29:26 +0100263
Jose Pascoalaa579a82014-11-05 22:17:16 +0000264 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(q) : null;
Jose Pascoal1c049e02014-12-17 13:03:09 +0000265
Jose Pascoalda015b12014-11-06 12:47:11 +0000266 if (cursor != null && cursor.moveToFirst())
Jose Pascoal810950b2014-10-09 17:16:08 +0100267 {
Jose Pascoal810950b2014-10-09 17:16:08 +0100268 try
269 {
270 int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
271 int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
Jose Pascoalb690af12014-10-06 18:29:26 +0100272
Jose Pascoal810950b2014-10-09 17:16:08 +0100273 if ((bytes_total + 10000) > Utils.getAvailablePartitionSizeInBytes(Environment.getExternalStorageDirectory()))
274 {
275 downloading = false;
Pedro Arelo773bd822014-10-10 11:57:34 +0100276 Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_sd_card_message), Toast.LENGTH_LONG).show();
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000277 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100278 }
Jose Pascoalaa579a82014-11-05 22:17:16 +0000279 else
Jose Pascoal810950b2014-10-09 17:16:08 +0100280 {
Jose Pascoalaa579a82014-11-05 22:17:16 +0000281 switch (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)))
282 {
283 case DownloadManager.STATUS_SUCCESSFUL:
284 case DownloadManager.STATUS_FAILED:
285 downloading = false;
Jose Pascoalb690af12014-10-06 18:29:26 +0100286
Jose Pascoalaa579a82014-11-05 22:17:16 +0000287 bytes_downloaded = 0;
288 bytes_total = 0;
289 break;
290 }
291
292 mVersionDownloadProgressBar.setProgress(bytes_downloaded);
293 mVersionDownloadProgressBar.setMax(bytes_total);
Jose Pascoal810950b2014-10-09 17:16:08 +0100294 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100295 } catch (Exception e)
296 {
297 downloading = false;
298 Log.e(TAG, "Error updating download progress: " + e.getMessage());
299 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100300
Jose Pascoal810950b2014-10-09 17:16:08 +0100301 cursor.close();
302 try
303 {
Jose Pascoalaa579a82014-11-05 22:17:16 +0000304 Thread.sleep(1000);
Jose Pascoal810950b2014-10-09 17:16:08 +0100305 } catch (InterruptedException e)
306 {
307 e.printStackTrace();
308 }
309 }
Jose Pascoalaa579a82014-11-05 22:17:16 +0000310 else
311 {
Jose Pascoalda015b12014-11-06 12:47:11 +0000312 if (cursor != null)
313 {
314 downloading = false;
315 cursor.close();
316 }
Jose Pascoalaa579a82014-11-05 22:17:16 +0000317 if (mDownloadManager == null)
318 {
319 downloading = false;
320 }
321 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100322 }
323 }
324 }).start();
325 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100326
Jose Pascoal810950b2014-10-09 17:16:08 +0100327 private void setupLayout(View view)
328 {
329 mDownloadVersionName = (TextView) view.findViewById(R.id.download_version_name_text);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100330
Jose Pascoal810950b2014-10-09 17:16:08 +0100331 // download in progress group
332 mVersionDownloadingGroup = (LinearLayout) view.findViewById(R.id.version_downloading_group);
333 mVersionDownloadProgressBar = (ProgressBar) view.findViewById(R.id.version_download_progress_bar);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100334
Jose Pascoal810950b2014-10-09 17:16:08 +0100335 // restart group
336 mVersionInstallGroup = (LinearLayout) view.findViewById(R.id.version_install_group);
337 mRestartButton = (Button) view.findViewById(R.id.restart_button);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100338
Jose Pascoal810950b2014-10-09 17:16:08 +0100339 mCancelButton = (Button) view.findViewById(R.id.cancel_button);
340 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100341
Jose Pascoal810950b2014-10-09 17:16:08 +0100342 @Override
343 public void onResume()
344 {
345 super.onResume();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100346
Jose Pascoal810950b2014-10-09 17:16:08 +0100347 setupInstallationReceivers();
348 registerDownloadBroadCastReceiver();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100349
Tiago Costa73eda412014-11-18 14:37:42 +0000350 registerNetworkStatusBoradcastReceiver();
351
Jose Pascoal8ed98d62014-10-09 20:41:11 +0100352 updateHeader();
Jose Pascoal1c049e02014-12-17 13:03:09 +0000353
Jose Pascoal02d86242014-12-17 18:50:08 +0000354 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
355 if (item != null)
Jose Pascoal1c049e02014-12-17 13:03:09 +0000356 {
Jose Pascoal02d86242014-12-17 18:50:08 +0000357 mDownloadVersionName.setText(mainActivity.getItemName(item));
Tiago Costa198bf3d2014-12-16 15:23:18 +0000358 }
Jose Pascoal1c049e02014-12-17 13:03:09 +0000359
Jose Pascoal810950b2014-10-09 17:16:08 +0100360 toggleDownloadProgressAndRestart();
361 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100362
Tiago Costa73eda412014-11-18 14:37:42 +0000363 private void registerNetworkStatusBoradcastReceiver()
364 {
365 // Setup monitoring for future connectivity status changes
366 if (mNetworkStateReceiver != null)
367 {
368 mNetworkStateReceiver = new BroadcastReceiver()
369 {
370 @Override
371 public void onReceive(Context context, Intent intent)
372 {
373 if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false))
374 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000375 abortUpdateProcess();
Tiago Costa73eda412014-11-18 14:37:42 +0000376 }
377 }
378 };
379 }
380
381 IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
382 mainActivity.registerReceiver(mNetworkStateReceiver, filter);
383 }
384
385 private void unregisterNetworkStatusBoradcastReceiver()
386 {
387 if (mNetworkStateReceiver != null)
388 {
389 mainActivity.unregisterReceiver(mNetworkStateReceiver);
390
391 mNetworkStateReceiver = null;
392 }
393 }
394
Jose Pascoal810950b2014-10-09 17:16:08 +0100395 @Override
396 public void onPause()
397 {
398 super.onPause();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100399
Jose Pascoal810950b2014-10-09 17:16:08 +0100400 unregisterBroadCastReceiver();
Tiago Costa73eda412014-11-18 14:37:42 +0000401
402 unregisterNetworkStatusBoradcastReceiver();
Jose Pascoal810950b2014-10-09 17:16:08 +0100403 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100404
Jose Pascoal810950b2014-10-09 17:16:08 +0100405 private void setupInstallationReceivers()
406 {
407 mDownloadManager = (DownloadManager) mainActivity.getSystemService(Context.DOWNLOAD_SERVICE);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100408
Jose Pascoal810950b2014-10-09 17:16:08 +0100409 mDownloadBroadCastReceiver = new DownloadBroadCastReceiver();
410 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100411
Jose Pascoal810950b2014-10-09 17:16:08 +0100412 private void registerDownloadBroadCastReceiver()
413 {
414 mainActivity.registerReceiver(mDownloadBroadCastReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
415 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100416
Jose Pascoal810950b2014-10-09 17:16:08 +0100417 private void unregisterBroadCastReceiver()
418 {
419 mainActivity.unregisterReceiver(mDownloadBroadCastReceiver);
420 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100421
Jose Pascoal810950b2014-10-09 17:16:08 +0100422 private class DownloadBroadCastReceiver extends BroadcastReceiver
423 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100424
Jose Pascoal810950b2014-10-09 17:16:08 +0100425 @Override
426 public void onReceive(Context context, Intent intent)
427 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100428
Jose Pascoal810950b2014-10-09 17:16:08 +0100429 mainActivity.getLatestUpdateDownloadIdFromSharedPreference();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100430
Jose Pascoal810950b2014-10-09 17:16:08 +0100431 updateDownloadFile();
432 }
433 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100434
Jose Pascoal810950b2014-10-09 17:16:08 +0100435 private void updateDownloadFile()
436 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100437
Jose Pascoal810950b2014-10-09 17:16:08 +0100438 long downloadId = mainActivity.getLatestDownloadId();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100439
Jose Pascoal810950b2014-10-09 17:16:08 +0100440 if (downloadId != 0)
441 {
442 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100443
Jose Pascoal810950b2014-10-09 17:16:08 +0100444 query.setFilterById(downloadId);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100445
Jose Pascoalaa579a82014-11-05 22:17:16 +0000446 Cursor cursor = mDownloadManager != null ? mDownloadManager.query(query) : null;
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100447
Jose Pascoalaa579a82014-11-05 22:17:16 +0000448 if (cursor != null && cursor.moveToFirst())
Jose Pascoal810950b2014-10-09 17:16:08 +0100449 {
450 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
451 int status = cursor.getInt(columnIndex);
452
453 switch (status)
454 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000455
Jose Pascoal810950b2014-10-09 17:16:08 +0100456 case DownloadManager.STATUS_SUCCESSFUL:
457 mainActivity.updateStatePreference(UpdaterState.PREINSTALL);
458 toggleDownloadProgressAndRestart();
459 break;
460 case DownloadManager.STATUS_RUNNING:
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000461 case DownloadManager.STATUS_PENDING:
Jose Pascoal810950b2014-10-09 17:16:08 +0100462 startDownloadProgressUpdateThread();
463 break;
464 case DownloadManager.STATUS_FAILED:
465 Resources resources = getResources();
Jose Pascoal02d86242014-12-17 18:50:08 +0000466 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
467 if (item != null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100468 {
Jose Pascoal02d86242014-12-17 18:50:08 +0000469 String downloadTitle = Utils.getDownloadTitleFromDownloadableItem(getResources(), item);
Jose Pascoal1c049e02014-12-17 13:03:09 +0000470 Toast.makeText(mainActivity, resources.getString(R.string.error_downloading) + " " + downloadTitle, Toast.LENGTH_LONG).show();
471 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100472 else
473 {
Pedro Arelo773bd822014-10-10 11:57:34 +0100474 Toast.makeText(mainActivity, resources.getString(R.string.error_downloading), Toast.LENGTH_LONG).show();
Jose Pascoal810950b2014-10-09 17:16:08 +0100475 }
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000476 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100477 break;
478 }
479 }
Jose Pascoal3bc3a842014-10-16 19:03:21 +0100480 else
481 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000482 abortUpdateProcess();
Jose Pascoal3bc3a842014-10-16 19:03:21 +0100483 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100484
Jose Pascoalaa579a82014-11-05 22:17:16 +0000485 if (cursor != null)
486 {
487 cursor.close();
488 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100489 }
490 }
491
492 // ************************************************************************************
493 // PRE INSTALL
494 // ************************************************************************************
495
Jose Pascoal1c049e02014-12-17 13:03:09 +0000496 private void setupPreInstallState()
Tiago Costa198bf3d2014-12-16 15:23:18 +0000497 {
498 Resources resources = mainActivity.getResources();
Jose Pascoal1c049e02014-12-17 13:03:09 +0000499 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
500 if (item != null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100501 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000502 File file = new File(getDownloadPath(item));
Jose Pascoal810950b2014-10-09 17:16:08 +0100503
504 if (file.exists())
505 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000506 if (Utils.checkMD5(item.getMd5Sum(), file))
Jose Pascoal810950b2014-10-09 17:16:08 +0100507 {
508 copyUpdateToCache(file);
509 return;
510 }
511 else
512 {
Pedro Arelo773bd822014-10-10 11:57:34 +0100513 Toast.makeText(mainActivity, resources.getString(R.string.invalid_md5_download_message), Toast.LENGTH_LONG).show();
Jose Pascoal810950b2014-10-09 17:16:08 +0100514 removeLastUpdateDownload();
515 }
516 }
517 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100518 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100519
Jose Pascoal810950b2014-10-09 17:16:08 +0100520 // ************************************************************************************
521 // DOWNLOAD UPDATE
522 // ************************************************************************************
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100523
Jose Pascoal810950b2014-10-09 17:16:08 +0100524 public void setupDownloadState()
525 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000526 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
Tiago Costa198bf3d2014-12-16 15:23:18 +0000527
Jose Pascoal810950b2014-10-09 17:16:08 +0100528 // setup the download state views
Jose Pascoal1c049e02014-12-17 13:03:09 +0000529 if (item == null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100530 {
531 Resources resources = getResources();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100532
Jose Pascoal810950b2014-10-09 17:16:08 +0100533 // we don't have the lastest.xml so get back to initial state
534 File updateDir = new File(Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder));
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100535
Jose Pascoal810950b2014-10-09 17:16:08 +0100536 updateDir.delete();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100537
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000538 abortUpdateProcess();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100539
Jose Pascoal810950b2014-10-09 17:16:08 +0100540 return;
541 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100542
Jose Pascoal810950b2014-10-09 17:16:08 +0100543 // if there is a download ID on the shared preferences
544 if (mLatestUpdateDownloadId == 0)
545 {
546 mLatestUpdateDownloadId = mainActivity.getLatestUpdateDownloadIdFromSharedPreference();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100547
Jose Pascoal810950b2014-10-09 17:16:08 +0100548 // invalid download Id
549 if (mLatestUpdateDownloadId == 0)
550 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000551 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100552 return;
553 }
554 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100555
Jose Pascoal810950b2014-10-09 17:16:08 +0100556 updateDownloadFile();
557 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100558
Jose Pascoal810950b2014-10-09 17:16:08 +0100559 private void startPreInstall()
560 {
Filipe Gonçalves72dac942014-12-15 18:05:58 +0000561 Resources resources = getResources();
Jose Pascoal1c049e02014-12-17 13:03:09 +0000562 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
563
Jose Pascoal02d86242014-12-17 18:50:08 +0000564 File f = new File("/" + resources.getString(R.string.recoveryCachePath) + "/" + Utils.getFilenameFromDownloadableItem(item));
Filipe Gonçalves72dac942014-12-15 18:05:58 +0000565 if (!f.exists())
566 {
567 abortUpdateProcess();
Jose Pascoal1c049e02014-12-17 13:03:09 +0000568 }
569 else if (item != null && RootTools.isAccessGiven())
Jose Pascoal810950b2014-10-09 17:16:08 +0100570 {
571 // set the command for the recovery
Jose Pascoal810950b2014-10-09 17:16:08 +0100572 try
573 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100574
Jose Pascoal810950b2014-10-09 17:16:08 +0100575 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100576
Jose Pascoal810950b2014-10-09 17:16:08 +0100577 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100578
Jose Pascoal810950b2014-10-09 17:16:08 +0100579 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100580
Jose Pascoal0a5be012014-11-17 16:55:40 +0000581 if (Utils.hasUnifiedPartition(resources))
Jose Pascoal810950b2014-10-09 17:16:08 +0100582 {
583 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=/" + resources.getString(R.string.recoveryCachePath) + "/"
Jose Pascoal02d86242014-12-17 18:50:08 +0000584 + Utils.getFilenameFromDownloadableItem(item) + "' >> /cache/recovery/command"));
Jose Pascoal810950b2014-10-09 17:16:08 +0100585 }
586 else
587 {
588 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=/" + resources.getString(R.string.recoverySdCardPath)
Jose Pascoal02d86242014-12-17 18:50:08 +0000589 + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item) + "' >> /cache/recovery/command"));
Jose Pascoal810950b2014-10-09 17:16:08 +0100590 }
591 } catch (IOException e)
592 {
593 // TODO Auto-generated catch block
594 e.printStackTrace();
595 } catch (NotFoundException e)
596 {
597 // TODO Auto-generated catch block
598 e.printStackTrace();
599 } catch (TimeoutException e)
600 {
601 // TODO Auto-generated catch block
602 e.printStackTrace();
603 } catch (RootDeniedException e)
604 {
605 // TODO Auto-generated catch block
606 e.printStackTrace();
607 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100608
Tiago Costa87925fe2014-12-02 17:57:51 +0000609 SharedPreferences sharedPreferences = getActivity().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, Context.MODE_PRIVATE);
Jose Pascoal1c049e02014-12-17 13:03:09 +0000610
Tiago Costa87925fe2014-12-02 17:57:51 +0000611 Editor editor = sharedPreferences.edit();
612 editor.remove(UpdaterService.PREFERENCE_REINSTALL_GAPPS);
Tiago Costa87925fe2014-12-02 17:57:51 +0000613 editor.commit();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100614
Jose Pascoal0a5be012014-11-17 16:55:40 +0000615 if (Utils.hasUnifiedPartition(resources))
Jose Pascoal810950b2014-10-09 17:16:08 +0100616 {
617 removeLastUpdateDownload();
618 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100619
Jose Pascoal810950b2014-10-09 17:16:08 +0100620 // remove the update files from data
621 removeUpdateFilesFromData();
Jose Pascoal733b84e2014-10-17 14:49:02 +0100622
Jose Pascoal810950b2014-10-09 17:16:08 +0100623 // reboot the device into recovery
Jose Pascoal810950b2014-10-09 17:16:08 +0100624 try
625 {
626 mainActivity.updateStatePreference(UpdaterState.NORMAL);
Jose Pascoal02d86242014-12-17 18:50:08 +0000627 mainActivity.clearSelectedItems();
Jose Pascoal810950b2014-10-09 17:16:08 +0100628 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
629 } catch (IOException e)
630 {
631 // TODO Auto-generated catch block
632 e.printStackTrace();
633 } catch (TimeoutException e)
634 {
635 // TODO Auto-generated catch block
636 e.printStackTrace();
637 } catch (RootDeniedException e)
638 {
639 // TODO Auto-generated catch block
640 e.printStackTrace();
641 }
642 }
643 else
644 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000645 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100646 }
647 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100648
Jose Pascoal810950b2014-10-09 17:16:08 +0100649 private void copyUpdateToCache(File file)
650 {
Jose Pascoal0a5be012014-11-17 16:55:40 +0000651 if (Utils.canCopyToCache(file))
Jose Pascoal810950b2014-10-09 17:16:08 +0100652 {
Jose Pascoal1c049e02014-12-17 13:03:09 +0000653 DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
Jose Pascoal733b84e2014-10-17 14:49:02 +0100654 CopyFileToCacheTask copyTask = new CopyFileToCacheTask();
Jose Pascoal1c049e02014-12-17 13:03:09 +0000655 if (item != null)
656 {
Jose Pascoal02d86242014-12-17 18:50:08 +0000657 copyTask.execute(file.getPath(), Environment.getDownloadCacheDirectory() + "/" + Utils.getFilenameFromDownloadableItem(item));
Jose Pascoal1c049e02014-12-17 13:03:09 +0000658 }
659 else
660 {
661 Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_cache_message), Toast.LENGTH_LONG).show();
662 abortUpdateProcess();
663 }
Jose Pascoal810950b2014-10-09 17:16:08 +0100664 }
665 else
666 {
Jose Pascoal0a5be012014-11-17 16:55:40 +0000667 if (Utils.hasUnifiedPartition(getResources()))
Jose Pascoal810950b2014-10-09 17:16:08 +0100668 {
Jose Pascoal0a5be012014-11-17 16:55:40 +0000669 Log.d(TAG, "No space on cache. Defaulting to Sdcard");
670 Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_cache_message), Toast.LENGTH_LONG).show();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100671
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000672 abortUpdateProcess();
Jose Pascoal810950b2014-10-09 17:16:08 +0100673 }
674 }
675 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100676
Jose Pascoal810950b2014-10-09 17:16:08 +0100677 // ************************************************************************************
678 // Update Removal
679 // ************************************************************************************
680 private void removeUpdateFilesFromData()
681 {
682 try
683 {
684 Shell.runRootCommand(new CommandCapture(0, getResources().getString(R.string.removePlayStoreCommand), getResources().getString(
685 R.string.removeGooglePlusCommand), getResources().getString(R.string.removeSoundSearchCommand), getResources().getString(
686 R.string.removeGmailCommand), getResources().getString(R.string.removePlayServicesCommand), getResources().getString(
687 R.string.removeQuicksearchCommand), getResources().getString(R.string.removeTalkbackCommand), getResources().getString(
688 R.string.removeText2SpeechCommand)));
689 } catch (IOException e)
690 {
691 e.printStackTrace();
692 } catch (TimeoutException e)
693 {
694 e.printStackTrace();
695 } catch (RootDeniedException e)
696 {
697 e.printStackTrace();
698 }
699 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100700
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000701 public boolean removeLastUpdateDownload()
Jose Pascoal810950b2014-10-09 17:16:08 +0100702 {
703 long latestUpdateDownloadId = mainActivity.getLatestUpdateDownloadIdFromSharedPreference();
Jose Pascoalaa579a82014-11-05 22:17:16 +0000704 if (latestUpdateDownloadId != 0 && mDownloadManager != null)
Jose Pascoal810950b2014-10-09 17:16:08 +0100705 {
706 // residue download ID
707 mDownloadManager.remove(latestUpdateDownloadId);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100708
Jose Pascoal810950b2014-10-09 17:16:08 +0100709 mainActivity.resetLastUpdateDownloadId();
710 }
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000711 return latestUpdateDownloadId != 0; // report if something was canceled
Jose Pascoal810950b2014-10-09 17:16:08 +0100712 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100713
Jose Pascoal810950b2014-10-09 17:16:08 +0100714 private class CopyFileToCacheTask extends AsyncTask<String, Integer, Integer>
715 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100716
Jose Pascoal810950b2014-10-09 17:16:08 +0100717 ProgressDialog mProgress;
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100718
Jose Pascoal810950b2014-10-09 17:16:08 +0100719 @Override
720 protected Integer doInBackground(String... params)
721 {
722 // check the correct number of
723 if (params.length != 2)
724 {
725 return -1;
726 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100727
Jose Pascoal810950b2014-10-09 17:16:08 +0100728 String originalFilePath = params[0];
729 String destinyFilePath = params[1];
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100730
Jose Pascoal810950b2014-10-09 17:16:08 +0100731 if (RootTools.isAccessGiven())
732 {
Jose Pascoal0a5be012014-11-17 16:55:40 +0000733 Utils.clearCache();
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100734
Jose Pascoal810950b2014-10-09 17:16:08 +0100735 File otaFilePath = new File(originalFilePath);
736 File otaFileCache = new File(destinyFilePath);
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100737
Jose Pascoal810950b2014-10-09 17:16:08 +0100738 if (!otaFileCache.exists())
739 {
740 RootTools.copyFile(otaFilePath.getPath(), otaFileCache.getPath(), false, false);
741 }
742 }
Jose Pascoal733b84e2014-10-17 14:49:02 +0100743 else
744 {
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000745 abortUpdateProcess();
Jose Pascoal733b84e2014-10-17 14:49:02 +0100746 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100747
Jose Pascoal810950b2014-10-09 17:16:08 +0100748 return 1;
749 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100750
Jose Pascoal810950b2014-10-09 17:16:08 +0100751 protected void onProgressUpdate(Integer... progress)
752 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100753
Jose Pascoal810950b2014-10-09 17:16:08 +0100754 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100755
Jose Pascoal810950b2014-10-09 17:16:08 +0100756 protected void onPreExecute()
757 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100758
Jose Pascoal810950b2014-10-09 17:16:08 +0100759 if (mProgress == null)
760 {
761 String title = "";
Pedro Arelo773bd822014-10-10 11:57:34 +0100762 String message = mainActivity.getResources().getString(R.string.please_be_patient);
Jose Pascoal810950b2014-10-09 17:16:08 +0100763 mProgress = ProgressDialog.show(mainActivity, title, message, true, false);
764 }
765 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100766
Jose Pascoal810950b2014-10-09 17:16:08 +0100767 protected void onPostExecute(Integer result)
768 {
769 // disable the spinner
770 if (mProgress != null)
771 {
772 mProgress.dismiss();
773 mProgress = null;
774 }
775 }
776 }
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100777
Jose Pascoal1c049e02014-12-17 13:03:09 +0000778 private String getDownloadPath(DownloadableItem item)
Jose Pascoal810950b2014-10-09 17:16:08 +0100779 {
780 Resources resources = mainActivity.getResources();
Jose Pascoal02d86242014-12-17 18:50:08 +0000781 return Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item);
Tiago Costa198bf3d2014-12-16 15:23:18 +0000782 }
Jose Pascoalb397dc62014-10-13 19:26:58 +0100783
Filipe Gonçalvescd0f33d2014-12-15 15:05:15 +0000784 public void abortUpdateProcess()
Jose Pascoalb397dc62014-10-13 19:26:58 +0100785 {
Tiago Costa73eda412014-11-18 14:37:42 +0000786 removeLastUpdateDownload();
787
788 mainActivity.runOnUiThread(new Runnable()
Filipe Gonçalvesd28bd622014-11-05 11:40:12 +0000789 {
Tiago Costa73eda412014-11-18 14:37:42 +0000790 @Override
791 public void run()
Jose Pascoalaa579a82014-11-05 22:17:16 +0000792 {
Tiago Costa73eda412014-11-18 14:37:42 +0000793 mainActivity.removeLastFragment(false);
794 if (mainActivity.getFragmentCount() == 1 && mainActivity.getBackStackSize() == 0)
795 {
796 mainActivity.changeState(UpdaterState.NORMAL);
Jose Pascoal02d86242014-12-17 18:50:08 +0000797 mainActivity.clearSelectedItems();
Tiago Costa73eda412014-11-18 14:37:42 +0000798 }
799 else
800 {
801 mainActivity.updateStatePreference(UpdaterState.NORMAL);
802 }
Jose Pascoalaa579a82014-11-05 22:17:16 +0000803 }
Tiago Costa73eda412014-11-18 14:37:42 +0000804 });
Jose Pascoalb397dc62014-10-13 19:26:58 +0100805 }
Jose Pascoalb690af12014-10-06 18:29:26 +0100806}