FileDisplayActivity.java 63 KB

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