FileActivity.java 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. /**
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * Copyright (C) 2011 Bartek Przybylski
  6. * Copyright (C) 2016 ownCloud Inc.
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. package com.owncloud.android.ui.activity;
  22. import android.accounts.Account;
  23. import android.accounts.AccountManager;
  24. import android.accounts.AccountManagerCallback;
  25. import android.accounts.AccountManagerFuture;
  26. import android.accounts.AuthenticatorException;
  27. import android.accounts.OperationCanceledException;
  28. import android.content.ComponentName;
  29. import android.content.Context;
  30. import android.content.Intent;
  31. import android.content.ServiceConnection;
  32. import android.content.res.Configuration;
  33. import android.os.Bundle;
  34. import android.os.Handler;
  35. import android.os.IBinder;
  36. import android.support.v4.app.Fragment;
  37. import android.support.v4.app.FragmentManager;
  38. import android.support.v4.app.FragmentTransaction;
  39. import android.support.v4.view.GravityCompat;
  40. import android.support.v4.widget.DrawerLayout;
  41. import android.support.v7.app.ActionBar;
  42. import android.support.v7.app.ActionBarDrawerToggle;
  43. import android.support.v7.app.AppCompatActivity;
  44. import android.view.View;
  45. import android.widget.AdapterView;
  46. import android.widget.ListView;
  47. import android.widget.RelativeLayout;
  48. import android.widget.TextView;
  49. import android.widget.Toast;
  50. import com.owncloud.android.BuildConfig;
  51. import com.owncloud.android.MainApp;
  52. import com.owncloud.android.R;
  53. import com.owncloud.android.authentication.AccountUtils;
  54. import com.owncloud.android.authentication.AuthenticatorActivity;
  55. import com.owncloud.android.datamodel.FileDataStorageManager;
  56. import com.owncloud.android.datamodel.OCFile;
  57. import com.owncloud.android.files.FileOperationsHelper;
  58. import com.owncloud.android.files.services.FileDownloader;
  59. import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  60. import com.owncloud.android.files.services.FileUploader;
  61. import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  62. import com.owncloud.android.lib.common.OwnCloudAccount;
  63. import com.owncloud.android.lib.common.OwnCloudClient;
  64. import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
  65. import com.owncloud.android.lib.common.OwnCloudCredentials;
  66. import com.owncloud.android.lib.common.network.CertificateCombinedException;
  67. import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
  68. import com.owncloud.android.lib.common.operations.RemoteOperation;
  69. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  70. import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
  71. import com.owncloud.android.lib.common.utils.Log_OC;
  72. import com.owncloud.android.lib.resources.status.OCCapability;
  73. import com.owncloud.android.operations.CreateShareWithShareeOperation;
  74. import com.owncloud.android.operations.GetSharesForFileOperation;
  75. import com.owncloud.android.operations.SynchronizeFileOperation;
  76. import com.owncloud.android.operations.SynchronizeFolderOperation;
  77. import com.owncloud.android.operations.UnshareOperation;
  78. import com.owncloud.android.operations.UpdateSharePermissionsOperation;
  79. import com.owncloud.android.operations.UpdateShareViaLinkOperation;
  80. import com.owncloud.android.services.OperationsService;
  81. import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
  82. import com.owncloud.android.ui.NavigationDrawerItem;
  83. import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter;
  84. import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
  85. import com.owncloud.android.ui.dialog.LoadingDialog;
  86. import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
  87. import com.owncloud.android.utils.ErrorMessageAdapter;
  88. import java.util.ArrayList;
  89. /**
  90. * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud
  91. * {@link Account}s .
  92. */
  93. public class FileActivity extends AppCompatActivity
  94. implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener {
  95. public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
  96. public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
  97. public static final String EXTRA_FROM_NOTIFICATION =
  98. "com.owncloud.android.ui.activity.FROM_NOTIFICATION";
  99. public static final String TAG = FileActivity.class.getSimpleName();
  100. private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
  101. private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
  102. private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE";
  103. public static final int REQUEST_CODE__UPDATE_CREDENTIALS = 0;
  104. public static final int REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS;
  105. protected static final long DELAY_TO_REQUEST_OPERATIONS_LATER = 200;
  106. /* Dialog tags */
  107. private static final String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
  108. private static final String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED";
  109. /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/
  110. private Account mAccount;
  111. /** Capabilites of the server where {@link #mAccount} lives */
  112. private OCCapability mCapabilities;
  113. /** Main {@link OCFile} handled by the activity.*/
  114. private OCFile mFile;
  115. /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud
  116. * {@link Account} */
  117. private boolean mRedirectingToSetupAccount = false;
  118. /** Flag to signal when the value of mAccount was set */
  119. protected boolean mAccountWasSet;
  120. /** Flag to signal when the value of mAccount was restored from a saved state */
  121. protected boolean mAccountWasRestored;
  122. /** Flag to signal if the activity is launched by a notification */
  123. private boolean mFromNotification;
  124. /** Messages handler associated to the main thread and the life cycle of the activity */
  125. private Handler mHandler;
  126. /** Access point to the cached database for the current ownCloud {@link Account} */
  127. private FileDataStorageManager mStorageManager = null;
  128. private FileOperationsHelper mFileOperationsHelper;
  129. private ServiceConnection mOperationsServiceConnection = null;
  130. private OperationsServiceBinder mOperationsServiceBinder = null;
  131. private boolean mResumed = false;
  132. protected FileDownloaderBinder mDownloaderBinder = null;
  133. protected FileUploaderBinder mUploaderBinder = null;
  134. private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
  135. // Navigation Drawer
  136. protected DrawerLayout mDrawerLayout;
  137. protected ActionBarDrawerToggle mDrawerToggle;
  138. protected ListView mDrawerList;
  139. // Slide menu items
  140. protected String[] mDrawerTitles;
  141. protected String[] mDrawerContentDescriptions;
  142. protected ArrayList<NavigationDrawerItem> mDrawerItems;
  143. protected NavigationDrawerListAdapter mNavigationDrawerAdapter = null;
  144. // TODO re-enable when "Accounts" is available in Navigation Drawer
  145. // protected boolean mShowAccounts = false;
  146. /**
  147. * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of
  148. * the {@link FileActivity}.
  149. *
  150. * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user
  151. * is requested to create a new one.
  152. */
  153. @Override
  154. protected void onCreate(Bundle savedInstanceState) {
  155. super.onCreate(savedInstanceState);
  156. mHandler = new Handler();
  157. mFileOperationsHelper = new FileOperationsHelper(this);
  158. Account account = null;
  159. if(savedInstanceState != null) {
  160. mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
  161. mFromNotification = savedInstanceState.getBoolean(FileActivity.EXTRA_FROM_NOTIFICATION);
  162. mFileOperationsHelper.setOpIdWaitingFor(
  163. savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID, Long.MAX_VALUE)
  164. );
  165. if (getSupportActionBar() != null) {
  166. getSupportActionBar().setTitle(savedInstanceState.getString(KEY_ACTION_BAR_TITLE));
  167. }
  168. } else {
  169. account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
  170. mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
  171. mFromNotification = getIntent().getBooleanExtra(FileActivity.EXTRA_FROM_NOTIFICATION,
  172. false);
  173. }
  174. AccountUtils.updateAccountVersion(this); // best place, before any access to AccountManager
  175. // or database
  176. setAccount(account, savedInstanceState != null);
  177. mOperationsServiceConnection = new OperationsServiceConnection();
  178. bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection,
  179. Context.BIND_AUTO_CREATE);
  180. mDownloadServiceConnection = newTransferenceServiceConnection();
  181. if (mDownloadServiceConnection != null) {
  182. bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
  183. Context.BIND_AUTO_CREATE);
  184. }
  185. mUploadServiceConnection = newTransferenceServiceConnection();
  186. if (mUploadServiceConnection != null) {
  187. bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
  188. Context.BIND_AUTO_CREATE);
  189. }
  190. }
  191. @Override
  192. protected void onNewIntent (Intent intent) {
  193. Log_OC.v(TAG, "onNewIntent() start");
  194. Account current = AccountUtils.getCurrentOwnCloudAccount(this);
  195. if (current != null && mAccount != null && !mAccount.name.equals(current.name)) {
  196. mAccount = current;
  197. }
  198. Log_OC.v(TAG, "onNewIntent() stop");
  199. }
  200. /**
  201. * Since ownCloud {@link Account}s can be managed from the system setting menu,
  202. * the existence of the {@link Account} associated to the instance must be checked
  203. * every time it is restarted.
  204. */
  205. @Override
  206. protected void onRestart() {
  207. Log_OC.v(TAG, "onRestart() start");
  208. super.onRestart();
  209. boolean validAccount = (mAccount != null && AccountUtils.exists(mAccount, this));
  210. if (!validAccount) {
  211. swapToDefaultAccount();
  212. }
  213. Log_OC.v(TAG, "onRestart() end");
  214. }
  215. @Override
  216. protected void onStart() {
  217. super.onStart();
  218. if (mAccountWasSet) {
  219. onAccountSet(mAccountWasRestored);
  220. }
  221. }
  222. @Override
  223. protected void onResume() {
  224. super.onResume();
  225. mResumed = true;
  226. if (mOperationsServiceBinder != null) {
  227. doOnResumeAndBound();
  228. }
  229. }
  230. @Override
  231. protected void onPause() {
  232. if (mOperationsServiceBinder != null) {
  233. mOperationsServiceBinder.removeOperationListener(this);
  234. }
  235. mResumed = false;
  236. super.onPause();
  237. }
  238. @Override
  239. protected void onDestroy() {
  240. if (mOperationsServiceConnection != null) {
  241. unbindService(mOperationsServiceConnection);
  242. mOperationsServiceBinder = null;
  243. }
  244. if (mDownloadServiceConnection != null) {
  245. unbindService(mDownloadServiceConnection);
  246. mDownloadServiceConnection = null;
  247. }
  248. if (mUploadServiceConnection != null) {
  249. unbindService(mUploadServiceConnection);
  250. mUploadServiceConnection = null;
  251. }
  252. super.onDestroy();
  253. }
  254. @Override
  255. protected void onPostCreate(Bundle savedInstanceState) {
  256. super.onPostCreate(savedInstanceState);
  257. // Sync the toggle state after onRestoreInstanceState has occurred.
  258. if (mDrawerToggle != null) {
  259. mDrawerToggle.syncState();
  260. if (isDrawerOpen()) {
  261. getSupportActionBar().setTitle(R.string.app_name);
  262. mDrawerToggle.setDrawerIndicatorEnabled(true);
  263. }
  264. }
  265. }
  266. @Override
  267. public void onConfigurationChanged(Configuration newConfig) {
  268. super.onConfigurationChanged(newConfig);
  269. if (mDrawerToggle != null) {
  270. mDrawerToggle.onConfigurationChanged(newConfig);
  271. }
  272. }
  273. @Override
  274. public void onBackPressed() {
  275. if (isDrawerOpen()) {
  276. closeNavDrawer();
  277. return;
  278. }
  279. super.onBackPressed();
  280. }
  281. /**
  282. * checks if the drawer exists and is opened.
  283. *
  284. * @return <code>true</code> if the drawer is open, else <code>false</code>
  285. */
  286. public boolean isDrawerOpen() {
  287. if(mDrawerLayout != null) {
  288. return mDrawerLayout.isDrawerOpen(GravityCompat.START);
  289. } else {
  290. return false;
  291. }
  292. }
  293. /**
  294. * closes the navigation drawer.
  295. */
  296. public void closeNavDrawer() {
  297. if(mDrawerLayout != null) {
  298. mDrawerLayout.closeDrawer(GravityCompat.START);
  299. }
  300. }
  301. protected void initDrawer(){
  302. // constant settings for action bar when navigation drawer is inited
  303. getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
  304. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  305. // Notification Drawer
  306. RelativeLayout navigationDrawerLayout = (RelativeLayout) findViewById(R.id.left_drawer);
  307. mDrawerList = (ListView) navigationDrawerLayout.findViewById(R.id.drawer_list);
  308. // TODO re-enable when "Accounts" is available in Navigation Drawer
  309. // // load Account in the Drawer Title
  310. // // User-Icon
  311. // ImageView userIcon = (ImageView) navigationDrawerLayout.findViewById(R.id.drawer_userIcon);
  312. // userIcon.setImageResource(DisplayUtils.getSeasonalIconId());
  313. //
  314. // // Username
  315. // TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
  316. // Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
  317. //
  318. // if (account != null) {
  319. // int lastAtPos = account.name.lastIndexOf("@");
  320. // username.setText(account.name.substring(0, lastAtPos));
  321. // }
  322. // Display username in drawer
  323. setUsernameInDrawer(navigationDrawerLayout, AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()));
  324. // load slide menu items
  325. mDrawerTitles = getResources().getStringArray(R.array.drawer_items);
  326. // nav drawer content description from resources
  327. mDrawerContentDescriptions = getResources().
  328. getStringArray(R.array.drawer_content_descriptions);
  329. // nav drawer items
  330. mDrawerItems = new ArrayList<NavigationDrawerItem>();
  331. // adding nav drawer items to array
  332. // TODO re-enable when "Accounts" is available in Navigation Drawer
  333. // Accounts
  334. // mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0],
  335. // mDrawerContentDescriptions[0]));
  336. // All Files
  337. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], mDrawerContentDescriptions[0],
  338. R.drawable.ic_folder_open));
  339. // TODO Enable when "On Device" is recovered
  340. // On Device
  341. //mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2],
  342. // mDrawerContentDescriptions[2]));
  343. // Uploads
  344. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[2],
  345. R.drawable.ic_uploads));
  346. // Settings
  347. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], mDrawerContentDescriptions[1],
  348. R.drawable.ic_settings));
  349. // Logs
  350. if (BuildConfig.DEBUG) {
  351. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[3],
  352. mDrawerContentDescriptions[3], R.drawable.ic_log));
  353. }
  354. // setting the nav drawer list adapter
  355. mNavigationDrawerAdapter = new NavigationDrawerListAdapter(getApplicationContext(), this,
  356. mDrawerItems);
  357. mDrawerList.setAdapter(mNavigationDrawerAdapter);
  358. mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.string.drawer_open,R.string.drawer_close) {
  359. /** Called when a drawer has settled in a completely closed state. */
  360. public void onDrawerClosed(View view) {
  361. super.onDrawerClosed(view);
  362. updateActionBarTitleAndHomeButton(null);
  363. invalidateOptionsMenu();
  364. }
  365. /** Called when a drawer has settled in a completely open state. */
  366. public void onDrawerOpened(View drawerView) {
  367. super.onDrawerOpened(drawerView);
  368. getSupportActionBar().setTitle(R.string.app_name);
  369. mDrawerToggle.setDrawerIndicatorEnabled(true);
  370. invalidateOptionsMenu();
  371. }
  372. };
  373. // Set the list's click listener
  374. mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
  375. // Set the drawer toggle as the DrawerListener
  376. mDrawerLayout.setDrawerListener(mDrawerToggle);
  377. mDrawerToggle.setDrawerIndicatorEnabled(false);
  378. }
  379. /**
  380. * sets the given account name in the drawer in case the drawer is available. The account name
  381. * is shortened beginning from the @-sign in the username.
  382. *
  383. * @param navigationDrawerLayout the drawer layout to be used
  384. * @param account the account to be set in the drawer
  385. */
  386. protected void setUsernameInDrawer(View navigationDrawerLayout, Account account) {
  387. if (navigationDrawerLayout != null && account != null) {
  388. TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
  389. int lastAtPos = account.name.lastIndexOf("@");
  390. username.setText(account.name.substring(0, lastAtPos));
  391. }
  392. }
  393. /**
  394. * Updates title bar and home buttons (state and icon).
  395. *
  396. * Assumes that navigation drawer is NOT visible.
  397. */
  398. protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
  399. String title = getDefaultTitle(); // default
  400. boolean inRoot;
  401. /// choose the appropiate title
  402. if (chosenFile == null) {
  403. chosenFile = mFile; // if no file is passed, current file decides
  404. }
  405. inRoot = (
  406. chosenFile == null ||
  407. (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID)
  408. );
  409. if (!inRoot) {
  410. title = chosenFile.getFileName();
  411. }
  412. /// set the chosen title
  413. ActionBar actionBar = getSupportActionBar();
  414. actionBar.setTitle(title);
  415. /// also as content description
  416. View actionBarTitleView = getWindow().getDecorView().findViewById(
  417. getResources().getIdentifier("action_bar_title", "id", "android")
  418. );
  419. if (actionBarTitleView != null) { // it's null in Android 2.x
  420. actionBarTitleView.setContentDescription(title);
  421. }
  422. /// set home button properties
  423. mDrawerToggle.setDrawerIndicatorEnabled(inRoot);
  424. actionBar.setDisplayHomeAsUpEnabled(true);
  425. actionBar.setDisplayShowTitleEnabled(true);
  426. }
  427. protected String getDefaultTitle() {
  428. return getString(R.string.default_display_name_for_root_folder);
  429. }
  430. /**
  431. * Sets and validates the ownCloud {@link Account} associated to the Activity.
  432. *
  433. * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
  434. *
  435. * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
  436. *
  437. * @param account New {@link Account} to set.
  438. * @param savedAccount When 'true', account was retrieved from a saved instance state.
  439. */
  440. protected void setAccount(Account account, boolean savedAccount) {
  441. Account oldAccount = mAccount;
  442. boolean validAccount =
  443. (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(),
  444. account.name));
  445. if (validAccount) {
  446. mAccount = account;
  447. mAccountWasSet = true;
  448. mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
  449. } else {
  450. swapToDefaultAccount();
  451. }
  452. }
  453. /**
  454. * Tries to swap the current ownCloud {@link Account} for other valid and existing.
  455. *
  456. * If no valid ownCloud {@link Account} exists, the the user is requested
  457. * to create a new ownCloud {@link Account}.
  458. *
  459. * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
  460. */
  461. private void swapToDefaultAccount() {
  462. // default to the most recently used account
  463. Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
  464. if (newAccount == null) {
  465. /// no account available: force account creation
  466. createFirstAccount();
  467. mRedirectingToSetupAccount = true;
  468. mAccountWasSet = false;
  469. mAccountWasRestored = false;
  470. } else {
  471. mAccountWasSet = true;
  472. mAccountWasRestored = (newAccount.equals(mAccount));
  473. mAccount = newAccount;
  474. }
  475. }
  476. /**
  477. * Launches the account creation activity. To use when no ownCloud account is available
  478. */
  479. private void createFirstAccount() {
  480. AccountManager am = AccountManager.get(getApplicationContext());
  481. am.addAccount(MainApp.getAccountType(),
  482. null,
  483. null,
  484. null,
  485. this,
  486. new AccountCreationCallback(),
  487. null);
  488. }
  489. /**
  490. * {@inheritDoc}
  491. */
  492. @Override
  493. protected void onSaveInstanceState(Bundle outState) {
  494. super.onSaveInstanceState(outState);
  495. outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
  496. outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification);
  497. outState.putLong(KEY_WAITING_FOR_OP_ID, mFileOperationsHelper.getOpIdWaitingFor());
  498. if(getSupportActionBar() != null && getSupportActionBar().getTitle() != null) {
  499. // Null check in case the actionbar is used in ActionBar.NAVIGATION_MODE_LIST
  500. // since it doesn't have a title then
  501. outState.putString(KEY_ACTION_BAR_TITLE, getSupportActionBar().getTitle().toString());
  502. }
  503. }
  504. /**
  505. * Getter for the main {@link OCFile} handled by the activity.
  506. *
  507. * @return Main {@link OCFile} handled by the activity.
  508. */
  509. public OCFile getFile() {
  510. return mFile;
  511. }
  512. /**
  513. * Setter for the main {@link OCFile} handled by the activity.
  514. *
  515. * @param file Main {@link OCFile} to be handled by the activity.
  516. */
  517. public void setFile(OCFile file) {
  518. mFile = file;
  519. }
  520. /**
  521. * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity
  522. * is located.
  523. *
  524. * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity
  525. * is located.
  526. */
  527. public Account getAccount() {
  528. return mAccount;
  529. }
  530. protected void setAccount(Account account) {
  531. mAccount = account;
  532. }
  533. /**
  534. * Getter for the capabilities of the server where the current OC account lives.
  535. *
  536. * @return Capabilities of the server where the current OC account lives. Null if the account is not
  537. * set yet.
  538. */
  539. public OCCapability getCapabilities() {
  540. return mCapabilities;
  541. }
  542. /**
  543. * @return Value of mFromNotification: True if the Activity is launched by a notification
  544. */
  545. public boolean fromNotification() {
  546. return mFromNotification;
  547. }
  548. /**
  549. * @return 'True' when the Activity is finishing to enforce the setup of a new account.
  550. */
  551. protected boolean isRedirectingToSetupAccount() {
  552. return mRedirectingToSetupAccount;
  553. }
  554. public OperationsServiceBinder getOperationsServiceBinder() {
  555. return mOperationsServiceBinder;
  556. }
  557. protected ServiceConnection newTransferenceServiceConnection() {
  558. return null;
  559. }
  560. /**
  561. * Helper class handling a callback from the {@link AccountManager} after the creation of
  562. * a new ownCloud {@link Account} finished, successfully or not.
  563. *
  564. * At this moment, only called after the creation of the first account.
  565. */
  566. public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
  567. @Override
  568. public void run(AccountManagerFuture<Bundle> future) {
  569. FileActivity.this.mRedirectingToSetupAccount = false;
  570. boolean accountWasSet = false;
  571. if (future != null) {
  572. try {
  573. Bundle result;
  574. result = future.getResult();
  575. String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
  576. String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
  577. if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
  578. setAccount(new Account(name, type), false);
  579. accountWasSet = true;
  580. }
  581. } catch (OperationCanceledException e) {
  582. Log_OC.d(TAG, "Account creation canceled");
  583. } catch (Exception e) {
  584. Log_OC.e(TAG, "Account creation finished in exception: ", e);
  585. }
  586. } else {
  587. Log_OC.e(TAG, "Account creation callback with null bundle");
  588. }
  589. if (!accountWasSet) {
  590. moveTaskToBack(true);
  591. }
  592. }
  593. }
  594. /**
  595. * Called when the ownCloud {@link Account} associated to the Activity was just updated.
  596. *
  597. * Child classes must grant that state depending on the {@link Account} is updated.
  598. */
  599. protected void onAccountSet(boolean stateWasRecovered) {
  600. if (getAccount() != null) {
  601. mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
  602. mCapabilities = mStorageManager.getCapability(mAccount.name);
  603. } else {
  604. Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
  605. }
  606. }
  607. public FileDataStorageManager getStorageManager() {
  608. return mStorageManager;
  609. }
  610. public OnRemoteOperationListener getRemoteOperationListener() {
  611. return this;
  612. }
  613. public Handler getHandler() {
  614. return mHandler;
  615. }
  616. public FileOperationsHelper getFileOperationsHelper() {
  617. return mFileOperationsHelper;
  618. }
  619. /**
  620. *
  621. * @param operation Operation performed.
  622. * @param result Result of the removal.
  623. */
  624. @Override
  625. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  626. Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the "
  627. + "FileActivities ");
  628. mFileOperationsHelper.setOpIdWaitingFor(Long.MAX_VALUE);
  629. dismissLoadingDialog();
  630. if (!result.isSuccess() && (
  631. result.getCode() == ResultCode.UNAUTHORIZED ||
  632. (result.isException() && result.getException() instanceof AuthenticatorException)
  633. )) {
  634. requestCredentialsUpdate(this);
  635. if (result.getCode() == ResultCode.UNAUTHORIZED) {
  636. Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  637. operation, getResources()),
  638. Toast.LENGTH_LONG);
  639. t.show();
  640. }
  641. } else if (!result.isSuccess() && ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED.equals(result.getCode())) {
  642. showUntrustedCertDialog(result);
  643. } else if (operation == null ||
  644. operation instanceof CreateShareWithShareeOperation ||
  645. operation instanceof UnshareOperation ||
  646. operation instanceof SynchronizeFolderOperation ||
  647. operation instanceof UpdateShareViaLinkOperation ||
  648. operation instanceof UpdateSharePermissionsOperation
  649. ) {
  650. if (result.isSuccess()) {
  651. updateFileFromDB();
  652. } else if (result.getCode() != ResultCode.CANCELLED) {
  653. Toast t = Toast.makeText(this,
  654. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  655. Toast.LENGTH_LONG);
  656. t.show();
  657. }
  658. } else if (operation instanceof SynchronizeFileOperation) {
  659. onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result);
  660. } else if (operation instanceof GetSharesForFileOperation) {
  661. if (result.isSuccess() || result.getCode() == ResultCode.SHARE_NOT_FOUND) {
  662. updateFileFromDB();
  663. } else {
  664. Toast t = Toast.makeText(this,
  665. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  666. Toast.LENGTH_LONG);
  667. t.show();
  668. }
  669. }
  670. }
  671. /**
  672. * Invalidates the credentials stored for the current OC account and requests new credentials to the user,
  673. * navigating to {@link AuthenticatorActivity}
  674. *
  675. * Equivalent to call requestCredentialsUpdate(context, null);
  676. *
  677. * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
  678. * to make the method accessible to {@link android.content.BroadcastReceiver}s.
  679. */
  680. protected void requestCredentialsUpdate(Context context) {
  681. requestCredentialsUpdate(context, null);
  682. }
  683. /**
  684. * Invalidates the credentials stored for the given OC account and requests new credentials to the user,
  685. * navigating to {@link AuthenticatorActivity}
  686. *
  687. * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
  688. * to make the method accessible to {@link android.content.BroadcastReceiver}s.
  689. * @param account Stored OC account to request credentials update for. If null, current account will
  690. * be used.
  691. */
  692. protected void requestCredentialsUpdate(Context context, Account account) {
  693. try {
  694. /// step 1 - invalidate credentials of current account
  695. if (account == null) {
  696. account = getAccount();
  697. }
  698. OwnCloudClient client;
  699. OwnCloudAccount ocAccount =
  700. new OwnCloudAccount(account, context);
  701. client = (OwnCloudClientManagerFactory.getDefaultSingleton().
  702. removeClientFor(ocAccount));
  703. if (client != null) {
  704. OwnCloudCredentials cred = client.getCredentials();
  705. if (cred != null) {
  706. AccountManager am = AccountManager.get(context);
  707. if (cred.authTokenExpires()) {
  708. am.invalidateAuthToken(
  709. account.type,
  710. cred.getAuthToken()
  711. );
  712. } else {
  713. am.clearPassword(account);
  714. }
  715. }
  716. }
  717. /// step 2 - request credentials to user
  718. Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
  719. updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
  720. updateAccountCredentials.putExtra(
  721. AuthenticatorActivity.EXTRA_ACTION,
  722. AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
  723. updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
  724. startActivityForResult(updateAccountCredentials, REQUEST_CODE__UPDATE_CREDENTIALS);
  725. } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
  726. Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
  727. }
  728. }
  729. /**
  730. * Show untrusted cert dialog
  731. */
  732. public void showUntrustedCertDialog(RemoteOperationResult result) {
  733. // Show a dialog with the certificate info
  734. FragmentManager fm = getSupportFragmentManager();
  735. SslUntrustedCertDialog dialog = (SslUntrustedCertDialog) fm.findFragmentByTag(DIALOG_UNTRUSTED_CERT);
  736. if(dialog == null) {
  737. dialog = SslUntrustedCertDialog.newInstanceForFullSslError(
  738. (CertificateCombinedException) result.getException());
  739. FragmentTransaction ft = fm.beginTransaction();
  740. dialog.show(ft, DIALOG_UNTRUSTED_CERT);
  741. }
  742. }
  743. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
  744. RemoteOperationResult result) {
  745. OCFile syncedFile = operation.getLocalFile();
  746. if (!result.isSuccess()) {
  747. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  748. Intent i = new Intent(this, ConflictsResolveActivity.class);
  749. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
  750. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
  751. startActivity(i);
  752. }
  753. } else {
  754. if (!operation.transferWasRequested()) {
  755. Toast msg = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  756. operation, getResources()), Toast.LENGTH_LONG);
  757. msg.show();
  758. }
  759. invalidateOptionsMenu();
  760. }
  761. }
  762. protected void updateFileFromDB(){
  763. OCFile file = getFile();
  764. if (file != null) {
  765. file = getStorageManager().getFileByPath(file.getRemotePath());
  766. setFile(file);
  767. }
  768. }
  769. /**
  770. * Show loading dialog
  771. */
  772. public void showLoadingDialog(String message) {
  773. // grant that only one waiting dialog is shown
  774. dismissLoadingDialog();
  775. // Construct dialog
  776. LoadingDialog loading = new LoadingDialog(message);
  777. FragmentManager fm = getSupportFragmentManager();
  778. FragmentTransaction ft = fm.beginTransaction();
  779. loading.show(ft, DIALOG_WAIT_TAG);
  780. }
  781. /**
  782. * Dismiss loading dialog
  783. */
  784. public void dismissLoadingDialog() {
  785. Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
  786. if (frag != null) {
  787. LoadingDialog loading = (LoadingDialog) frag;
  788. loading.dismiss();
  789. }
  790. }
  791. private void doOnResumeAndBound() {
  792. mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler);
  793. long waitingForOpId = mFileOperationsHelper.getOpIdWaitingFor();
  794. if (waitingForOpId <= Integer.MAX_VALUE) {
  795. boolean wait = mOperationsServiceBinder.dispatchResultIfFinished((int)waitingForOpId,
  796. this);
  797. if (!wait ) {
  798. dismissLoadingDialog();
  799. }
  800. }
  801. }
  802. /**
  803. * Implements callback methods for service binding. Passed as a parameter to {
  804. */
  805. private class OperationsServiceConnection implements ServiceConnection {
  806. @Override
  807. public void onServiceConnected(ComponentName component, IBinder service) {
  808. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  809. Log_OC.d(TAG, "Operations service connected");
  810. mOperationsServiceBinder = (OperationsServiceBinder) service;
  811. /*if (!mOperationsServiceBinder.isPerformingBlockingOperation()) {
  812. dismissLoadingDialog();
  813. }*/
  814. if (mResumed) {
  815. doOnResumeAndBound();
  816. }
  817. } else {
  818. return;
  819. }
  820. }
  821. @Override
  822. public void onServiceDisconnected(ComponentName component) {
  823. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  824. Log_OC.d(TAG, "Operations service disconnected");
  825. mOperationsServiceBinder = null;
  826. // TODO whatever could be waiting for the service is unbound
  827. }
  828. }
  829. }
  830. @Override
  831. public FileDownloaderBinder getFileDownloaderBinder() {
  832. return mDownloaderBinder;
  833. }
  834. @Override
  835. public FileUploaderBinder getFileUploaderBinder() {
  836. return mUploaderBinder;
  837. }
  838. public void restart(){
  839. Intent i = new Intent(this, FileDisplayActivity.class);
  840. i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  841. i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
  842. startActivity(i);
  843. }
  844. public void allFilesOption(){
  845. restart();
  846. }
  847. protected OCFile getCurrentDir() {
  848. OCFile file = getFile();
  849. if (file != null) {
  850. if (file.isFolder()) {
  851. return file;
  852. } else if (getStorageManager() != null) {
  853. String parentPath = file.getParentRemotePath();
  854. return getStorageManager().getFileByPath(parentPath);
  855. }
  856. }
  857. return null;
  858. }
  859. /* OnSslUntrustedCertListener methods */
  860. @Override
  861. public void onSavedCertificate() {
  862. // Nothing to do in this context
  863. }
  864. @Override
  865. public void onFailedSavingCertificate() {
  866. ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
  867. R.string.ssl_validator_not_saved, new String[]{}, 0, R.string.common_ok, -1, -1
  868. );
  869. dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED);
  870. }
  871. @Override
  872. public void onCancelCertificate() {
  873. // nothing to do
  874. }
  875. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  876. @Override
  877. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  878. // TODO re-enable when "Accounts" is available in Navigation Drawer
  879. // if (mShowAccounts && position > 0){
  880. // position = position - 1;
  881. // }
  882. switch (position){
  883. // TODO re-enable when "Accounts" is available in Navigation Drawer
  884. // case 0: // Accounts
  885. // mShowAccounts = !mShowAccounts;
  886. // mNavigationDrawerAdapter.setShowAccounts(mShowAccounts);
  887. // mNavigationDrawerAdapter.notifyDataSetChanged();
  888. // break;
  889. case 0: // All Files
  890. allFilesOption();
  891. break;
  892. // TODO Enable when "On Device" is recovered ?
  893. // case 2:
  894. // MainApp.showOnlyFilesOnDevice(true);
  895. // break;
  896. case 1: // Uploads
  897. Intent uploadListIntent = new Intent(getApplicationContext(),
  898. UploadListActivity.class);
  899. uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  900. startActivity(uploadListIntent);
  901. break;
  902. case 2: // Settings
  903. Intent settingsIntent = new Intent(getApplicationContext(),
  904. Preferences.class);
  905. startActivity(settingsIntent);
  906. break;
  907. case 3: // Logs
  908. Intent loggerIntent = new Intent(getApplicationContext(),
  909. LogHistoryActivity.class);
  910. startActivity(loggerIntent);
  911. break;
  912. }
  913. mDrawerLayout.closeDrawers();
  914. }
  915. }
  916. }