FileActivity.java 39 KB

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