FileDisplayActivity.java 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425
  1. /* ownCloud Android client application
  2. * Copyright (C) 2011 Bartek Przybylski
  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 as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. */
  18. package com.owncloud.android.ui.activity;
  19. import java.io.File;
  20. import android.accounts.Account;
  21. import android.app.AlertDialog;
  22. import android.app.ProgressDialog;
  23. import android.app.AlertDialog.Builder;
  24. import android.app.Dialog;
  25. import android.content.BroadcastReceiver;
  26. import android.content.ComponentName;
  27. import android.content.ContentResolver;
  28. import android.content.Context;
  29. import android.content.DialogInterface;
  30. import android.content.DialogInterface.OnClickListener;
  31. import android.content.Intent;
  32. import android.content.IntentFilter;
  33. import android.content.ServiceConnection;
  34. import android.content.SharedPreferences;
  35. import android.content.SharedPreferences.Editor;
  36. import android.content.pm.PackageInfo;
  37. import android.content.pm.PackageManager.NameNotFoundException;
  38. import android.content.res.Resources.NotFoundException;
  39. import android.database.Cursor;
  40. import android.graphics.Bitmap;
  41. import android.graphics.drawable.BitmapDrawable;
  42. import android.net.Uri;
  43. import android.os.Bundle;
  44. import android.os.Handler;
  45. import android.os.IBinder;
  46. import android.preference.PreferenceManager;
  47. import android.provider.MediaStore;
  48. import android.support.v4.app.Fragment;
  49. import android.support.v4.app.FragmentTransaction;
  50. import android.util.Log;
  51. import android.view.View;
  52. import android.view.ViewGroup;
  53. import android.widget.ArrayAdapter;
  54. import android.widget.EditText;
  55. import android.widget.TextView;
  56. import android.widget.Toast;
  57. import com.actionbarsherlock.app.ActionBar;
  58. import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
  59. import com.actionbarsherlock.app.SherlockFragmentActivity;
  60. import com.actionbarsherlock.view.Menu;
  61. import com.actionbarsherlock.view.MenuInflater;
  62. import com.actionbarsherlock.view.MenuItem;
  63. import com.actionbarsherlock.view.Window;
  64. import com.owncloud.android.AccountUtils;
  65. import com.owncloud.android.authenticator.AccountAuthenticator;
  66. import com.owncloud.android.datamodel.DataStorageManager;
  67. import com.owncloud.android.datamodel.FileDataStorageManager;
  68. import com.owncloud.android.datamodel.OCFile;
  69. import com.owncloud.android.files.services.FileDownloader;
  70. import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  71. import com.owncloud.android.files.services.FileObserverService;
  72. import com.owncloud.android.files.services.FileUploader;
  73. import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  74. import com.owncloud.android.network.OwnCloudClientUtils;
  75. import com.owncloud.android.operations.OnRemoteOperationListener;
  76. import com.owncloud.android.operations.RemoteOperation;
  77. import com.owncloud.android.operations.RemoteOperationResult;
  78. import com.owncloud.android.operations.RemoveFileOperation;
  79. import com.owncloud.android.operations.RenameFileOperation;
  80. import com.owncloud.android.operations.SynchronizeFileOperation;
  81. import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  82. import com.owncloud.android.syncadapter.FileSyncService;
  83. import com.owncloud.android.ui.dialog.ChangelogDialog;
  84. import com.owncloud.android.ui.dialog.SslValidatorDialog;
  85. import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
  86. import com.owncloud.android.ui.fragment.FileDetailFragment;
  87. import com.owncloud.android.ui.fragment.FileFragment;
  88. import com.owncloud.android.ui.fragment.OCFileListFragment;
  89. import com.owncloud.android.ui.preview.PreviewImageActivity;
  90. import com.owncloud.android.ui.preview.PreviewImageFragment;
  91. import com.owncloud.android.ui.preview.PreviewMediaFragment;
  92. import com.owncloud.android.R;
  93. import eu.alefzero.webdav.WebdavClient;
  94. /**
  95. * Displays, what files the user has available in his ownCloud.
  96. *
  97. * @author Bartek Przybylski
  98. *
  99. */
  100. public class FileDisplayActivity extends SherlockFragmentActivity implements
  101. OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener {
  102. private ArrayAdapter<String> mDirectories;
  103. private OCFile mCurrentDir = null;
  104. private OCFile mCurrentFile = null;
  105. private DataStorageManager mStorageManager;
  106. private SyncBroadcastReceiver mSyncBroadcastReceiver;
  107. private UploadFinishReceiver mUploadFinishReceiver;
  108. private DownloadFinishReceiver mDownloadFinishReceiver;
  109. private FileDownloaderBinder mDownloaderBinder = null;
  110. private FileUploaderBinder mUploaderBinder = null;
  111. private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
  112. private RemoteOperationResult mLastSslUntrustedServerResult = null;
  113. private OCFileListFragment mFileList;
  114. private boolean mDualPane;
  115. private static final int DIALOG_SETUP_ACCOUNT = 0;
  116. private static final int DIALOG_CREATE_DIR = 1;
  117. private static final int DIALOG_ABOUT_APP = 2;
  118. public static final int DIALOG_SHORT_WAIT = 3;
  119. private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4;
  120. private static final int DIALOG_SSL_VALIDATOR = 5;
  121. private static final int DIALOG_CERT_NOT_SAVED = 6;
  122. private static final String DIALOG_CHANGELOG_TAG = "DIALOG_CHANGELOG";
  123. private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
  124. private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
  125. private static final String TAG = "FileDisplayActivity";
  126. private static int[] mMenuIdentifiersToPatch = {R.id.action_about_app};
  127. private OCFile mWaitingToPreview;
  128. private Handler mHandler;
  129. @Override
  130. public void onCreate(Bundle savedInstanceState) {
  131. Log.d(getClass().toString(), "onCreate() start");
  132. super.onCreate(savedInstanceState);
  133. /// Load of parameters from received intent
  134. mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); // no check necessary, mCurrenDir == null if the parameter is not in the intent
  135. Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
  136. if (account != null)
  137. AccountUtils.setCurrentOwnCloudAccount(this, account.name);
  138. /// Load of saved instance state: keep this always before initDataFromCurrentAccount()
  139. if(savedInstanceState != null) {
  140. // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER, or SOME TIMES
  141. mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
  142. mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW);
  143. } else {
  144. mWaitingToPreview = null;
  145. }
  146. if (!AccountUtils.accountsAreSetup(this)) {
  147. /// no account available: FORCE ACCOUNT CREATION
  148. mStorageManager = null;
  149. createFirstAccount();
  150. } else { /// at least an account is available
  151. initDataFromCurrentAccount(); // it checks mCurrentDir and mCurrentFile with the current account
  152. }
  153. mUploadConnection = new ListServiceConnection();
  154. mDownloadConnection = new ListServiceConnection();
  155. bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
  156. bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
  157. // PIN CODE request ; best location is to decide, let's try this first
  158. if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
  159. requestPinCode();
  160. }
  161. // file observer
  162. Intent observer_intent = new Intent(this, FileObserverService.class);
  163. observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
  164. startService(observer_intent);
  165. /// USER INTERFACE
  166. requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
  167. // Drop-down navigation
  168. mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
  169. OCFile currFile = mCurrentDir;
  170. while(currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {
  171. mDirectories.add(currFile.getFileName());
  172. currFile = mStorageManager.getFileById(currFile.getParentId());
  173. }
  174. mDirectories.add(OCFile.PATH_SEPARATOR);
  175. // Inflate and set the layout view
  176. setContentView(R.layout.files);
  177. mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  178. mDualPane = (findViewById(R.id.file_details_container) != null);
  179. if (mDualPane) {
  180. initFileDetailsInDualPane();
  181. }
  182. // Action bar setup
  183. ActionBar actionBar = getSupportActionBar();
  184. actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
  185. actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getParentId() != 0);
  186. actionBar.setDisplayShowTitleEnabled(false);
  187. actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
  188. actionBar.setListNavigationCallbacks(mDirectories, this);
  189. setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to workaround bug in its implementation
  190. // show changelog, if needed
  191. showChangeLog();
  192. Log.d(getClass().toString(), "onCreate() end");
  193. }
  194. /**
  195. * Shows a dialog with the change log of the current version after each app update
  196. *
  197. * TODO make it permanent; by now, only to advice the workaround app for 4.1.x
  198. */
  199. private void showChangeLog() {
  200. if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.JELLY_BEAN) {
  201. final String KEY_VERSION = "version";
  202. SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  203. int currentVersionNumber = 0;
  204. int savedVersionNumber = sharedPref.getInt(KEY_VERSION, 0);
  205. try {
  206. PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
  207. currentVersionNumber = pi.versionCode;
  208. } catch (Exception e) {}
  209. if (currentVersionNumber > savedVersionNumber) {
  210. ChangelogDialog.newInstance(true).show(getSupportFragmentManager(), DIALOG_CHANGELOG_TAG);
  211. Editor editor = sharedPref.edit();
  212. editor.putInt(KEY_VERSION, currentVersionNumber);
  213. editor.commit();
  214. }
  215. }
  216. }
  217. /**
  218. * Launches the account creation activity. To use when no ownCloud account is available
  219. */
  220. private void createFirstAccount() {
  221. Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
  222. intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
  223. startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished;
  224. }
  225. /**
  226. * Load of state dependent of the existence of an ownCloud account
  227. */
  228. private void initDataFromCurrentAccount() {
  229. /// Storage manager initialization - access to local database
  230. mStorageManager = new FileDataStorageManager(
  231. AccountUtils.getCurrentOwnCloudAccount(this),
  232. getContentResolver());
  233. /// Check if mCurrentDir is a directory
  234. if(mCurrentDir != null && !mCurrentDir.isDirectory()) {
  235. mCurrentFile = mCurrentDir;
  236. mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
  237. }
  238. /// Check if mCurrentDir and mCurrentFile are in the current account, and update them
  239. if (mCurrentDir != null) {
  240. mCurrentDir = mStorageManager.getFileByPath(mCurrentDir.getRemotePath()); // mCurrentDir == null if it is not in the current account
  241. }
  242. if (mCurrentFile != null) {
  243. if (mCurrentFile.fileExists()) {
  244. mCurrentFile = mStorageManager.getFileByPath(mCurrentFile.getRemotePath()); // mCurrentFile == null if it is not in the current account
  245. } // else : keep mCurrentFile with the received value; this is currently the case of an upload in progress, when the user presses the status notification in a landscape tablet
  246. }
  247. /// Default to root if mCurrentDir was not found
  248. if (mCurrentDir == null) {
  249. mCurrentDir = mStorageManager.getFileByPath("/"); // will be NULL if the database was never synchronized
  250. }
  251. }
  252. private void initFileDetailsInDualPane() {
  253. if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {
  254. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  255. if (mCurrentFile != null) {
  256. if (PreviewMediaFragment.canBePreviewed(mCurrentFile)) {
  257. if (mCurrentFile.isDown()) {
  258. transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  259. } else {
  260. transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  261. mWaitingToPreview = mCurrentFile;
  262. }
  263. } else {
  264. transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  265. }
  266. mCurrentFile = null;
  267. } else {
  268. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
  269. }
  270. transaction.commit();
  271. }
  272. }
  273. @Override
  274. public void onDestroy() {
  275. super.onDestroy();
  276. if (mDownloadConnection != null)
  277. unbindService(mDownloadConnection);
  278. if (mUploadConnection != null)
  279. unbindService(mUploadConnection);
  280. }
  281. @Override
  282. public boolean onCreateOptionsMenu(Menu menu) {
  283. MenuInflater inflater = getSherlock().getMenuInflater();
  284. inflater.inflate(R.menu.main_menu, menu);
  285. patchHiddenAccents(menu);
  286. return true;
  287. }
  288. /**
  289. * Workaround for this: <a href="http://code.google.com/p/android/issues/detail?id=3974">http://code.google.com/p/android/issues/detail?id=3974</a>
  290. *
  291. * @param menu Menu to patch
  292. */
  293. private void patchHiddenAccents(Menu menu) {
  294. for (int i = 0; i < mMenuIdentifiersToPatch.length ; i++) {
  295. MenuItem aboutItem = menu.findItem(mMenuIdentifiersToPatch[i]);
  296. if (aboutItem != null && aboutItem.getIcon() instanceof BitmapDrawable) {
  297. // Clip off the bottom three (density independent) pixels of transparent padding
  298. Bitmap original = ((BitmapDrawable) aboutItem.getIcon()).getBitmap();
  299. float scale = getResources().getDisplayMetrics().density;
  300. int clippedHeight = (int) (original.getHeight() - (3 * scale));
  301. Bitmap scaled = Bitmap.createBitmap(original, 0, 0, original.getWidth(), clippedHeight);
  302. aboutItem.setIcon(new BitmapDrawable(getResources(), scaled));
  303. }
  304. }
  305. }
  306. @Override
  307. public boolean onOptionsItemSelected(MenuItem item) {
  308. boolean retval = true;
  309. switch (item.getItemId()) {
  310. case R.id.action_create_dir: {
  311. showDialog(DIALOG_CREATE_DIR);
  312. break;
  313. }
  314. case R.id.action_sync_account: {
  315. startSynchronization();
  316. break;
  317. }
  318. case R.id.action_upload: {
  319. showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
  320. break;
  321. }
  322. case R.id.action_settings: {
  323. Intent settingsIntent = new Intent(this, Preferences.class);
  324. startActivity(settingsIntent);
  325. break;
  326. }
  327. case R.id.action_about_app: {
  328. showDialog(DIALOG_ABOUT_APP);
  329. break;
  330. }
  331. case android.R.id.home: {
  332. if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
  333. onBackPressed();
  334. }
  335. break;
  336. }
  337. default:
  338. retval = super.onOptionsItemSelected(item);
  339. }
  340. return retval;
  341. }
  342. private void startSynchronization() {
  343. ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); // cancel the current synchronizations of any ownCloud account
  344. Bundle bundle = new Bundle();
  345. bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
  346. ContentResolver.requestSync(
  347. AccountUtils.getCurrentOwnCloudAccount(this),
  348. AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);
  349. }
  350. @Override
  351. public boolean onNavigationItemSelected(int itemPosition, long itemId) {
  352. int i = itemPosition;
  353. while (i-- != 0) {
  354. onBackPressed();
  355. }
  356. // the next operation triggers a new call to this method, but it's necessary to
  357. // ensure that the name exposed in the action bar is the current directory when the
  358. // user selected it in the navigation list
  359. if (itemPosition != 0)
  360. getSupportActionBar().setSelectedNavigationItem(0);
  361. return true;
  362. }
  363. /**
  364. * Called, when the user selected something for uploading
  365. */
  366. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  367. if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
  368. requestSimpleUpload(data, resultCode);
  369. } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
  370. requestMultipleUpload(data, resultCode);
  371. }
  372. }
  373. private void requestMultipleUpload(Intent data, int resultCode) {
  374. String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
  375. if (filePaths != null) {
  376. String[] remotePaths = new String[filePaths.length];
  377. String remotePathBase = "";
  378. for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
  379. remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
  380. }
  381. if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
  382. remotePathBase += OCFile.PATH_SEPARATOR;
  383. for (int j = 0; j< remotePaths.length; j++) {
  384. remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
  385. }
  386. Intent i = new Intent(this, FileUploader.class);
  387. i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  388. i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
  389. i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
  390. i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
  391. if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
  392. i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
  393. startService(i);
  394. } else {
  395. Log.d("FileDisplay", "User clicked on 'Update' with no selection");
  396. Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
  397. t.show();
  398. return;
  399. }
  400. }
  401. private void requestSimpleUpload(Intent data, int resultCode) {
  402. String filepath = null;
  403. try {
  404. Uri selectedImageUri = data.getData();
  405. String filemanagerstring = selectedImageUri.getPath();
  406. String selectedImagePath = getPath(selectedImageUri);
  407. if (selectedImagePath != null)
  408. filepath = selectedImagePath;
  409. else
  410. filepath = filemanagerstring;
  411. } catch (Exception e) {
  412. Log.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
  413. e.printStackTrace();
  414. } finally {
  415. if (filepath == null) {
  416. Log.e("FileDisplay", "Couldnt resolve path to file");
  417. Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
  418. t.show();
  419. return;
  420. }
  421. }
  422. Intent i = new Intent(this, FileUploader.class);
  423. i.putExtra(FileUploader.KEY_ACCOUNT,
  424. AccountUtils.getCurrentOwnCloudAccount(this));
  425. String remotepath = new String();
  426. for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
  427. remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
  428. }
  429. if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
  430. remotepath += OCFile.PATH_SEPARATOR;
  431. remotepath += new File(filepath).getName();
  432. i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
  433. i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
  434. i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
  435. if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
  436. i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
  437. startService(i);
  438. }
  439. @Override
  440. public void onBackPressed() {
  441. if (mDirectories.getCount() <= 1) {
  442. finish();
  443. return;
  444. }
  445. popDirname();
  446. mFileList.onNavigateUp();
  447. mCurrentDir = mFileList.getCurrentFile();
  448. if (mDualPane) {
  449. // Resets the FileDetailsFragment on Tablets so that it always displays
  450. Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  451. if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
  452. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  453. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
  454. transaction.commit();
  455. }
  456. }
  457. if(mCurrentDir.getParentId() == 0){
  458. ActionBar actionBar = getSupportActionBar();
  459. actionBar.setDisplayHomeAsUpEnabled(false);
  460. }
  461. }
  462. @Override
  463. protected void onSaveInstanceState(Bundle outState) {
  464. // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
  465. Log.d(getClass().toString(), "onSaveInstanceState() start");
  466. super.onSaveInstanceState(outState);
  467. outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);
  468. if (mDualPane) {
  469. FileFragment fragment = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  470. if (fragment != null) {
  471. OCFile file = fragment.getFile();
  472. if (file != null) {
  473. outState.putParcelable(FileDetailFragment.EXTRA_FILE, file);
  474. }
  475. }
  476. }
  477. outState.putParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
  478. Log.d(getClass().toString(), "onSaveInstanceState() end");
  479. }
  480. @Override
  481. protected void onResume() {
  482. Log.d(getClass().toString(), "onResume() start");
  483. super.onResume();
  484. if (AccountUtils.accountsAreSetup(this)) {
  485. if (mStorageManager == null) {
  486. // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created
  487. initDataFromCurrentAccount();
  488. if (mDualPane) {
  489. initFileDetailsInDualPane();
  490. }
  491. }
  492. // Listen for sync messages
  493. IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
  494. mSyncBroadcastReceiver = new SyncBroadcastReceiver();
  495. registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
  496. // Listen for upload messages
  497. IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
  498. mUploadFinishReceiver = new UploadFinishReceiver();
  499. registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
  500. // Listen for download messages
  501. IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
  502. //downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
  503. mDownloadFinishReceiver = new DownloadFinishReceiver();
  504. registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
  505. // List current directory
  506. mFileList.listDirectory(mCurrentDir); // TODO we should find the way to avoid the need of this (maybe it's not necessary yet; to check)
  507. } else {
  508. mStorageManager = null; // an invalid object will be there if all the ownCloud accounts are removed
  509. showDialog(DIALOG_SETUP_ACCOUNT);
  510. }
  511. Log.d(getClass().toString(), "onResume() end");
  512. }
  513. @Override
  514. protected void onPause() {
  515. Log.d(getClass().toString(), "onPause() start");
  516. super.onPause();
  517. if (mSyncBroadcastReceiver != null) {
  518. unregisterReceiver(mSyncBroadcastReceiver);
  519. mSyncBroadcastReceiver = null;
  520. }
  521. if (mUploadFinishReceiver != null) {
  522. unregisterReceiver(mUploadFinishReceiver);
  523. mUploadFinishReceiver = null;
  524. }
  525. if (mDownloadFinishReceiver != null) {
  526. unregisterReceiver(mDownloadFinishReceiver);
  527. mDownloadFinishReceiver = null;
  528. }
  529. if (!AccountUtils.accountsAreSetup(this)) {
  530. dismissDialog(DIALOG_SETUP_ACCOUNT);
  531. }
  532. Log.d(getClass().toString(), "onPause() end");
  533. }
  534. @Override
  535. protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
  536. if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
  537. ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
  538. }
  539. }
  540. @Override
  541. protected Dialog onCreateDialog(int id) {
  542. Dialog dialog = null;
  543. AlertDialog.Builder builder;
  544. switch (id) {
  545. case DIALOG_SETUP_ACCOUNT: {
  546. builder = new AlertDialog.Builder(this);
  547. builder.setTitle(R.string.main_tit_accsetup);
  548. builder.setMessage(R.string.main_wrn_accsetup);
  549. builder.setCancelable(false);
  550. builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
  551. public void onClick(DialogInterface dialog, int which) {
  552. createFirstAccount();
  553. dialog.dismiss();
  554. }
  555. });
  556. String message = String.format(getString(R.string.common_exit), getString(R.string.app_name));
  557. builder.setNegativeButton(message, new OnClickListener() {
  558. public void onClick(DialogInterface dialog, int which) {
  559. dialog.dismiss();
  560. finish();
  561. }
  562. });
  563. //builder.setNegativeButton(android.R.string.cancel, this);
  564. dialog = builder.create();
  565. break;
  566. }
  567. case DIALOG_ABOUT_APP: {
  568. builder = new AlertDialog.Builder(this);
  569. builder.setTitle(getString(R.string.about_title));
  570. PackageInfo pkg;
  571. try {
  572. pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
  573. builder.setMessage(String.format(getString(R.string.about_message), getString(R.string.app_name), pkg.versionName));
  574. builder.setIcon(android.R.drawable.ic_menu_info_details);
  575. dialog = builder.create();
  576. } catch (NameNotFoundException e) {
  577. builder = null;
  578. dialog = null;
  579. Log.e(TAG, "Error while showing about dialog", e);
  580. }
  581. break;
  582. }
  583. case DIALOG_CREATE_DIR: {
  584. builder = new Builder(this);
  585. final EditText dirNameInput = new EditText(getBaseContext());
  586. builder.setView(dirNameInput);
  587. builder.setTitle(R.string.uploader_info_dirname);
  588. int typed_color = getResources().getColor(R.color.setup_text_typed);
  589. dirNameInput.setTextColor(typed_color);
  590. builder.setPositiveButton(android.R.string.ok,
  591. new OnClickListener() {
  592. public void onClick(DialogInterface dialog, int which) {
  593. String directoryName = dirNameInput.getText().toString();
  594. if (directoryName.trim().length() == 0) {
  595. dialog.cancel();
  596. return;
  597. }
  598. // Figure out the path where the dir needs to be created
  599. String path;
  600. if (mCurrentDir == null) {
  601. // this is just a patch; we should ensure that mCurrentDir never is null
  602. if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
  603. OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
  604. mStorageManager.saveFile(file);
  605. }
  606. mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
  607. }
  608. path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
  609. // Create directory
  610. path += directoryName + OCFile.PATH_SEPARATOR;
  611. Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));
  612. thread.start();
  613. dialog.dismiss();
  614. showDialog(DIALOG_SHORT_WAIT);
  615. }
  616. });
  617. builder.setNegativeButton(R.string.common_cancel,
  618. new OnClickListener() {
  619. public void onClick(DialogInterface dialog, int which) {
  620. dialog.cancel();
  621. }
  622. });
  623. dialog = builder.create();
  624. break;
  625. }
  626. case DIALOG_SHORT_WAIT: {
  627. ProgressDialog working_dialog = new ProgressDialog(this);
  628. working_dialog.setMessage(getResources().getString(
  629. R.string.wait_a_moment));
  630. working_dialog.setIndeterminate(true);
  631. working_dialog.setCancelable(false);
  632. dialog = working_dialog;
  633. break;
  634. }
  635. case DIALOG_CHOOSE_UPLOAD_SOURCE: {
  636. final String [] items = { getString(R.string.actionbar_upload_files),
  637. getString(R.string.actionbar_upload_from_apps) };
  638. builder = new AlertDialog.Builder(this);
  639. builder.setTitle(R.string.actionbar_upload);
  640. builder.setItems(items, new DialogInterface.OnClickListener() {
  641. public void onClick(DialogInterface dialog, int item) {
  642. if (item == 0) {
  643. //if (!mDualPane) {
  644. Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
  645. action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this));
  646. startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
  647. //} else {
  648. // TODO create and handle new fragment LocalFileListFragment
  649. //}
  650. } else if (item == 1) {
  651. Intent action = new Intent(Intent.ACTION_GET_CONTENT);
  652. action = action.setType("*/*")
  653. .addCategory(Intent.CATEGORY_OPENABLE);
  654. startActivityForResult(
  655. Intent.createChooser(action, getString(R.string.upload_chooser_title)),
  656. ACTION_SELECT_CONTENT_FROM_APPS);
  657. }
  658. }
  659. });
  660. dialog = builder.create();
  661. break;
  662. }
  663. case DIALOG_SSL_VALIDATOR: {
  664. dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
  665. break;
  666. }
  667. case DIALOG_CERT_NOT_SAVED: {
  668. builder = new AlertDialog.Builder(this);
  669. builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
  670. builder.setCancelable(false);
  671. builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
  672. @Override
  673. public void onClick(DialogInterface dialog, int which) {
  674. dialog.dismiss();
  675. };
  676. });
  677. dialog = builder.create();
  678. break;
  679. }
  680. default:
  681. dialog = null;
  682. }
  683. return dialog;
  684. }
  685. /**
  686. * Translates a content URI of an image to a physical path
  687. * on the disk
  688. * @param uri The URI to resolve
  689. * @return The path to the image or null if it could not be found
  690. */
  691. public String getPath(Uri uri) {
  692. String[] projection = { MediaStore.Images.Media.DATA };
  693. Cursor cursor = managedQuery(uri, projection, null, null, null);
  694. if (cursor != null) {
  695. int column_index = cursor
  696. .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
  697. cursor.moveToFirst();
  698. return cursor.getString(column_index);
  699. }
  700. return null;
  701. }
  702. /**
  703. * Pushes a directory to the drop down list
  704. * @param directory to push
  705. * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
  706. */
  707. public void pushDirname(OCFile directory) {
  708. if(!directory.isDirectory()){
  709. throw new IllegalArgumentException("Only directories may be pushed!");
  710. }
  711. mDirectories.insert(directory.getFileName(), 0);
  712. mCurrentDir = directory;
  713. }
  714. /**
  715. * Pops a directory name from the drop down list
  716. * @return True, unless the stack is empty
  717. */
  718. public boolean popDirname() {
  719. mDirectories.remove(mDirectories.getItem(0));
  720. return !mDirectories.isEmpty();
  721. }
  722. private class DirectoryCreator implements Runnable {
  723. private String mTargetPath;
  724. private Account mAccount;
  725. private Handler mHandler;
  726. public DirectoryCreator(String targetPath, Account account, Handler handler) {
  727. mTargetPath = targetPath;
  728. mAccount = account;
  729. mHandler = handler;
  730. }
  731. @Override
  732. public void run() {
  733. WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
  734. boolean created = wdc.createDirectory(mTargetPath);
  735. if (created) {
  736. mHandler.post(new Runnable() {
  737. @Override
  738. public void run() {
  739. dismissDialog(DIALOG_SHORT_WAIT);
  740. // Save new directory in local database
  741. OCFile newDir = new OCFile(mTargetPath);
  742. newDir.setMimetype("DIR");
  743. newDir.setParentId(mCurrentDir.getFileId());
  744. mStorageManager.saveFile(newDir);
  745. // Display the new folder right away
  746. mFileList.listDirectory();
  747. }
  748. });
  749. } else {
  750. mHandler.post(new Runnable() {
  751. @Override
  752. public void run() {
  753. dismissDialog(DIALOG_SHORT_WAIT);
  754. try {
  755. Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
  756. msg.show();
  757. } catch (NotFoundException e) {
  758. Log.e(TAG, "Error while trying to show fail message " , e);
  759. }
  760. }
  761. });
  762. }
  763. }
  764. }
  765. // Custom array adapter to override text colors
  766. private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
  767. public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
  768. super(ctx, view);
  769. }
  770. public View getView(int position, View convertView, ViewGroup parent) {
  771. View v = super.getView(position, convertView, parent);
  772. ((TextView) v).setTextColor(getResources().getColorStateList(
  773. android.R.color.white));
  774. return v;
  775. }
  776. public View getDropDownView(int position, View convertView,
  777. ViewGroup parent) {
  778. View v = super.getDropDownView(position, convertView, parent);
  779. ((TextView) v).setTextColor(getResources().getColorStateList(
  780. android.R.color.white));
  781. return v;
  782. }
  783. }
  784. private class SyncBroadcastReceiver extends BroadcastReceiver {
  785. /**
  786. * {@link BroadcastReceiver} to enable syncing feedback in UI
  787. */
  788. @Override
  789. public void onReceive(Context context, Intent intent) {
  790. boolean inProgress = intent.getBooleanExtra(
  791. FileSyncService.IN_PROGRESS, false);
  792. String accountName = intent
  793. .getStringExtra(FileSyncService.ACCOUNT_NAME);
  794. Log.d("FileDisplay", "sync of account " + accountName
  795. + " is in_progress: " + inProgress);
  796. if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {
  797. String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
  798. boolean fillBlankRoot = false;
  799. if (mCurrentDir == null) {
  800. mCurrentDir = mStorageManager.getFileByPath("/");
  801. fillBlankRoot = (mCurrentDir != null);
  802. }
  803. if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
  804. || fillBlankRoot ) {
  805. if (!fillBlankRoot)
  806. mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
  807. OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()
  808. .findFragmentById(R.id.fileList);
  809. if (fileListFragment != null) {
  810. fileListFragment.listDirectory(mCurrentDir);
  811. }
  812. }
  813. setSupportProgressBarIndeterminateVisibility(inProgress);
  814. removeStickyBroadcast(intent);
  815. }
  816. RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
  817. if (synchResult != null) {
  818. if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
  819. mLastSslUntrustedServerResult = synchResult;
  820. showDialog(DIALOG_SSL_VALIDATOR);
  821. }
  822. }
  823. }
  824. }
  825. private class UploadFinishReceiver extends BroadcastReceiver {
  826. /**
  827. * Once the file upload has finished -> update view
  828. * @author David A. Velasco
  829. * {@link BroadcastReceiver} to enable upload feedback in UI
  830. */
  831. @Override
  832. public void onReceive(Context context, Intent intent) {
  833. String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
  834. String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
  835. boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);
  836. boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
  837. if (sameAccount && isDescendant) {
  838. OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  839. if (fileListFragment != null) {
  840. fileListFragment.listDirectory();
  841. }
  842. }
  843. }
  844. }
  845. /**
  846. * Once the file download has finished -> update view
  847. */
  848. private class DownloadFinishReceiver extends BroadcastReceiver {
  849. @Override
  850. public void onReceive(Context context, Intent intent) {
  851. String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
  852. String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
  853. if (accountName != null && AccountUtils.getCurrentOwnCloudAccount(context) != null) {
  854. boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);
  855. boolean isDescendant = (mCurrentDir != null) && (downloadedRemotePath != null) && (downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
  856. if (sameAccount && isDescendant) {
  857. OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  858. if (fileListFragment != null) {
  859. fileListFragment.listDirectory();
  860. if ( mWaitingToPreview != null &&
  861. mWaitingToPreview.getRemotePath().equals(downloadedRemotePath) &&
  862. intent.getAction().equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE) ) {
  863. Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  864. if (fragment != null && fragment instanceof FileDetailFragment ) {
  865. FileDetailFragment detailFragment = (FileDetailFragment) fragment;
  866. if (detailFragment.getFile().getRemotePath().equals(downloadedRemotePath)) {
  867. detailFragment.listenForTransferProgress();
  868. detailFragment.updateFileDetails(true);
  869. }
  870. }
  871. }
  872. }
  873. }
  874. }
  875. }
  876. }
  877. /**
  878. * {@inheritDoc}
  879. */
  880. @Override
  881. public DataStorageManager getStorageManager() {
  882. return mStorageManager;
  883. }
  884. /**
  885. * {@inheritDoc}
  886. */
  887. @Override
  888. public void onDirectoryClick(OCFile directory) {
  889. pushDirname(directory);
  890. ActionBar actionBar = getSupportActionBar();
  891. actionBar.setDisplayHomeAsUpEnabled(true);
  892. if (mDualPane) {
  893. // Resets the FileDetailsFragment on Tablets so that it always displays
  894. Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  895. if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
  896. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  897. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
  898. transaction.commit();
  899. }
  900. }
  901. }
  902. /**
  903. * {@inheritDoc}
  904. */
  905. @Override
  906. public void onFileClick(OCFile file) {
  907. if (file != null && PreviewImageFragment.canBePreviewed(file)) {
  908. // preview image - it handles the download, if needed
  909. startPreviewImage(file);
  910. } else if (file != null && PreviewMediaFragment.canBePreviewed(file)) {
  911. if (file.isDown()) {
  912. // general preview
  913. startMediaPreview(file);
  914. } else {
  915. // automatic download, preview on finish
  916. startDownloadForPreview(file);
  917. }
  918. } else {
  919. // details view
  920. startDetails(file);
  921. }
  922. }
  923. private void startPreviewImage(OCFile file) {
  924. Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
  925. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  926. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  927. startActivity(showDetailsIntent);
  928. }
  929. private void startMediaPreview(OCFile file) {
  930. if (mDualPane) {
  931. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  932. transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  933. transaction.commit();
  934. } else {
  935. Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
  936. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  937. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  938. startActivity(showDetailsIntent);
  939. }
  940. }
  941. private void startDownloadForPreview(OCFile file) {
  942. if (mDualPane) {
  943. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  944. transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  945. transaction.commit();
  946. mWaitingToPreview = file;
  947. requestForDownload();
  948. } else {
  949. Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
  950. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  951. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  952. startActivity(showDetailsIntent);
  953. }
  954. }
  955. private void startDetails(OCFile file) {
  956. if (mDualPane && !file.isImage()) {
  957. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  958. transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  959. transaction.commit();
  960. } else {
  961. Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
  962. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  963. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  964. startActivity(showDetailsIntent);
  965. }
  966. }
  967. /**
  968. * {@inheritDoc}
  969. */
  970. @Override
  971. public OCFile getInitialDirectory() {
  972. return mCurrentDir;
  973. }
  974. /**
  975. * {@inheritDoc}
  976. */
  977. @Override
  978. public void onFileStateChanged() {
  979. OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  980. if (fileListFragment != null) {
  981. fileListFragment.listDirectory();
  982. }
  983. }
  984. /**
  985. * {@inheritDoc}
  986. */
  987. @Override
  988. public FileDownloaderBinder getFileDownloaderBinder() {
  989. return mDownloaderBinder;
  990. }
  991. /**
  992. * {@inheritDoc}
  993. */
  994. @Override
  995. public FileUploaderBinder getFileUploaderBinder() {
  996. return mUploaderBinder;
  997. }
  998. /** Defines callbacks for service binding, passed to bindService() */
  999. private class ListServiceConnection implements ServiceConnection {
  1000. @Override
  1001. public void onServiceConnected(ComponentName component, IBinder service) {
  1002. if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
  1003. Log.d(TAG, "Download service connected");
  1004. mDownloaderBinder = (FileDownloaderBinder) service;
  1005. if (mWaitingToPreview != null) {
  1006. requestForDownload();
  1007. }
  1008. } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
  1009. Log.d(TAG, "Upload service connected");
  1010. mUploaderBinder = (FileUploaderBinder) service;
  1011. } else {
  1012. return;
  1013. }
  1014. // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
  1015. if (mFileList != null)
  1016. mFileList.listDirectory();
  1017. if (mDualPane) {
  1018. Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  1019. if (fragment != null && fragment instanceof FileDetailFragment) {
  1020. FileDetailFragment detailFragment = (FileDetailFragment)fragment;
  1021. detailFragment.listenForTransferProgress();
  1022. detailFragment.updateFileDetails(false);
  1023. }
  1024. }
  1025. }
  1026. @Override
  1027. public void onServiceDisconnected(ComponentName component) {
  1028. if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
  1029. Log.d(TAG, "Download service disconnected");
  1030. mDownloaderBinder = null;
  1031. } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
  1032. Log.d(TAG, "Upload service disconnected");
  1033. mUploaderBinder = null;
  1034. }
  1035. }
  1036. };
  1037. /**
  1038. * Launch an intent to request the PIN code to the user before letting him use the app
  1039. */
  1040. private void requestPinCode() {
  1041. boolean pinStart = false;
  1042. SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  1043. pinStart = appPrefs.getBoolean("set_pincode", false);
  1044. if (pinStart) {
  1045. Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
  1046. i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
  1047. startActivity(i);
  1048. }
  1049. }
  1050. @Override
  1051. public void onSavedCertificate() {
  1052. startSynchronization();
  1053. }
  1054. @Override
  1055. public void onFailedSavingCertificate() {
  1056. showDialog(DIALOG_CERT_NOT_SAVED);
  1057. }
  1058. /**
  1059. * Updates the view associated to the activity after the finish of some operation over files
  1060. * in the current account.
  1061. *
  1062. * @param operation Removal operation performed.
  1063. * @param result Result of the removal.
  1064. */
  1065. @Override
  1066. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  1067. if (operation instanceof RemoveFileOperation) {
  1068. onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
  1069. } else if (operation instanceof RenameFileOperation) {
  1070. onRenameFileOperationFinish((RenameFileOperation)operation, result);
  1071. } else if (operation instanceof SynchronizeFileOperation) {
  1072. onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
  1073. }
  1074. }
  1075. /**
  1076. * Updates the view associated to the activity after the finish of an operation trying to remove a
  1077. * file.
  1078. *
  1079. * @param operation Removal operation performed.
  1080. * @param result Result of the removal.
  1081. */
  1082. private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
  1083. dismissDialog(DIALOG_SHORT_WAIT);
  1084. if (result.isSuccess()) {
  1085. Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
  1086. msg.show();
  1087. OCFile removedFile = operation.getFile();
  1088. if (mDualPane) {
  1089. FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  1090. if (details != null && removedFile.equals(details.getFile())) {
  1091. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  1092. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
  1093. transaction.commit();
  1094. }
  1095. }
  1096. if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {
  1097. mFileList.listDirectory();
  1098. }
  1099. } else {
  1100. Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG);
  1101. msg.show();
  1102. if (result.isSslRecoverableException()) {
  1103. mLastSslUntrustedServerResult = result;
  1104. showDialog(DIALOG_SSL_VALIDATOR);
  1105. }
  1106. }
  1107. }
  1108. /**
  1109. * Updates the view associated to the activity after the finish of an operation trying to rename a
  1110. * file.
  1111. *
  1112. * @param operation Renaming operation performed.
  1113. * @param result Result of the renaming.
  1114. */
  1115. private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
  1116. dismissDialog(DIALOG_SHORT_WAIT);
  1117. OCFile renamedFile = operation.getFile();
  1118. if (result.isSuccess()) {
  1119. if (mDualPane) {
  1120. FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  1121. if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
  1122. ((FileDetailFragment) details).updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));
  1123. }
  1124. }
  1125. if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {
  1126. mFileList.listDirectory();
  1127. }
  1128. } else {
  1129. if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
  1130. Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
  1131. msg.show();
  1132. // TODO throw again the new rename dialog
  1133. } else {
  1134. Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
  1135. msg.show();
  1136. if (result.isSslRecoverableException()) {
  1137. mLastSslUntrustedServerResult = result;
  1138. showDialog(DIALOG_SSL_VALIDATOR);
  1139. }
  1140. }
  1141. }
  1142. }
  1143. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
  1144. dismissDialog(DIALOG_SHORT_WAIT);
  1145. OCFile syncedFile = operation.getLocalFile();
  1146. if (!result.isSuccess()) {
  1147. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  1148. Intent i = new Intent(this, ConflictsResolveActivity.class);
  1149. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
  1150. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  1151. startActivity(i);
  1152. } else {
  1153. Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
  1154. msg.show();
  1155. }
  1156. } else {
  1157. if (operation.transferWasRequested()) {
  1158. mFileList.listDirectory();
  1159. onTransferStateChanged(syncedFile, true, true);
  1160. } else {
  1161. Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
  1162. msg.show();
  1163. }
  1164. }
  1165. }
  1166. /**
  1167. * {@inheritDoc}
  1168. */
  1169. @Override
  1170. public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
  1171. /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  1172. if (fileListFragment != null) {
  1173. fileListFragment.listDirectory();
  1174. }*/
  1175. if (mDualPane) {
  1176. FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  1177. if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
  1178. if (downloading || uploading) {
  1179. ((FileDetailFragment)details).updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));
  1180. } else {
  1181. ((FileDetailFragment)details).updateFileDetails(downloading || uploading);
  1182. }
  1183. }
  1184. }
  1185. }
  1186. @Override
  1187. public void showFragmentWithDetails(OCFile file) {
  1188. if (mDualPane) {
  1189. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  1190. transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  1191. transaction.commit();
  1192. } else {
  1193. Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
  1194. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  1195. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  1196. showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
  1197. startActivity(showDetailsIntent);
  1198. }
  1199. }
  1200. @Override
  1201. public void notifySuccessfulDownload(OCFile file, Intent intent, boolean success) {
  1202. if (success) {
  1203. if (mWaitingToPreview != null) {
  1204. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  1205. transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  1206. transaction.commit();
  1207. mWaitingToPreview = null;
  1208. }
  1209. }
  1210. mDownloadFinishReceiver.onReceive(this, intent);
  1211. }
  1212. private void requestForDownload() {
  1213. Account account = AccountUtils.getCurrentOwnCloudAccount(this);
  1214. if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
  1215. Intent i = new Intent(this, FileDownloader.class);
  1216. i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
  1217. i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
  1218. startService(i);
  1219. }
  1220. }
  1221. }