FileActivity.java 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. /**
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * Copyright (C) 2011 Bartek Przybylski
  6. * Copyright (C) 2015 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. // Logs
  342. if (BuildConfig.DEBUG) {
  343. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2],
  344. mDrawerContentDescriptions[2],R.drawable.ic_log));
  345. }
  346. // setting the nav drawer list adapter
  347. mNavigationDrawerAdapter = new NavigationDrawerListAdapter(getApplicationContext(), this,
  348. mDrawerItems);
  349. mDrawerList.setAdapter(mNavigationDrawerAdapter);
  350. mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.string.drawer_open,R.string.drawer_close) {
  351. /** Called when a drawer has settled in a completely closed state. */
  352. public void onDrawerClosed(View view) {
  353. super.onDrawerClosed(view);
  354. updateActionBarTitleAndHomeButton(null);
  355. invalidateOptionsMenu();
  356. }
  357. /** Called when a drawer has settled in a completely open state. */
  358. public void onDrawerOpened(View drawerView) {
  359. super.onDrawerOpened(drawerView);
  360. getSupportActionBar().setTitle(R.string.app_name);
  361. mDrawerToggle.setDrawerIndicatorEnabled(true);
  362. invalidateOptionsMenu();
  363. }
  364. };
  365. // Set the list's click listener
  366. mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
  367. // Set the drawer toggle as the DrawerListener
  368. mDrawerLayout.setDrawerListener(mDrawerToggle);
  369. mDrawerToggle.setDrawerIndicatorEnabled(false);
  370. }
  371. /**
  372. * sets the given account name in the drawer in case the drawer is available. The account name
  373. * is shortened beginning from the @-sign in the username.
  374. *
  375. * @param navigationDrawerLayout the drawer layout to be used
  376. * @param account the account to be set in the drawer
  377. */
  378. protected void setUsernameInDrawer(RelativeLayout navigationDrawerLayout, Account account) {
  379. if (navigationDrawerLayout != null && account != null) {
  380. TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
  381. int lastAtPos = account.name.lastIndexOf("@");
  382. username.setText(account.name.substring(0, lastAtPos));
  383. }
  384. }
  385. /**
  386. * Updates title bar and home buttons (state and icon).
  387. *
  388. * Assumes that navigation drawer is NOT visible.
  389. */
  390. protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
  391. String title = getString(R.string.default_display_name_for_root_folder); // default
  392. boolean inRoot;
  393. /// choose the appropiate title
  394. if (chosenFile == null) {
  395. chosenFile = mFile; // if no file is passed, current file decides
  396. }
  397. inRoot = (
  398. chosenFile == null ||
  399. (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID)
  400. );
  401. if (!inRoot) {
  402. title = chosenFile.getFileName();
  403. }
  404. /// set the chosen title
  405. ActionBar actionBar = getSupportActionBar();
  406. actionBar.setTitle(title);
  407. /// also as content description
  408. View actionBarTitleView = getWindow().getDecorView().findViewById(
  409. getResources().getIdentifier("action_bar_title", "id", "android")
  410. );
  411. if (actionBarTitleView != null) { // it's null in Android 2.x
  412. actionBarTitleView.setContentDescription(title);
  413. }
  414. /// set home button properties
  415. mDrawerToggle.setDrawerIndicatorEnabled(inRoot);
  416. actionBar.setDisplayHomeAsUpEnabled(true);
  417. actionBar.setDisplayShowTitleEnabled(true);
  418. }
  419. /**
  420. * Sets and validates the ownCloud {@link Account} associated to the Activity.
  421. *
  422. * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
  423. *
  424. * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
  425. *
  426. * @param account New {@link Account} to set.
  427. * @param savedAccount When 'true', account was retrieved from a saved instance state.
  428. */
  429. protected void setAccount(Account account, boolean savedAccount) {
  430. Account oldAccount = mAccount;
  431. boolean validAccount =
  432. (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(),
  433. account.name));
  434. if (validAccount) {
  435. mAccount = account;
  436. mAccountWasSet = true;
  437. mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
  438. } else {
  439. swapToDefaultAccount();
  440. }
  441. }
  442. /**
  443. * Tries to swap the current ownCloud {@link Account} for other valid and existing.
  444. *
  445. * If no valid ownCloud {@link Account} exists, the the user is requested
  446. * to create a new ownCloud {@link Account}.
  447. *
  448. * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
  449. */
  450. private void swapToDefaultAccount() {
  451. // default to the most recently used account
  452. Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
  453. if (newAccount == null) {
  454. /// no account available: force account creation
  455. createFirstAccount();
  456. mRedirectingToSetupAccount = true;
  457. mAccountWasSet = false;
  458. mAccountWasRestored = false;
  459. } else {
  460. mAccountWasSet = true;
  461. mAccountWasRestored = (newAccount.equals(mAccount));
  462. mAccount = newAccount;
  463. }
  464. }
  465. /**
  466. * Launches the account creation activity. To use when no ownCloud account is available
  467. */
  468. private void createFirstAccount() {
  469. AccountManager am = AccountManager.get(getApplicationContext());
  470. am.addAccount(MainApp.getAccountType(),
  471. null,
  472. null,
  473. null,
  474. this,
  475. new AccountCreationCallback(),
  476. null);
  477. }
  478. /**
  479. * {@inheritDoc}
  480. */
  481. @Override
  482. protected void onSaveInstanceState(Bundle outState) {
  483. super.onSaveInstanceState(outState);
  484. outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
  485. outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification);
  486. outState.putLong(KEY_WAITING_FOR_OP_ID, mFileOperationsHelper.getOpIdWaitingFor());
  487. if(getSupportActionBar() != null && getSupportActionBar().getTitle() != null) {
  488. // Null check in case the actionbar is used in ActionBar.NAVIGATION_MODE_LIST
  489. // since it doesn't have a title then
  490. outState.putString(KEY_ACTION_BAR_TITLE, getSupportActionBar().getTitle().toString());
  491. }
  492. }
  493. /**
  494. * Getter for the main {@link OCFile} handled by the activity.
  495. *
  496. * @return Main {@link OCFile} handled by the activity.
  497. */
  498. public OCFile getFile() {
  499. return mFile;
  500. }
  501. /**
  502. * Setter for the main {@link OCFile} handled by the activity.
  503. *
  504. * @param file Main {@link OCFile} to be handled by the activity.
  505. */
  506. public void setFile(OCFile file) {
  507. mFile = file;
  508. }
  509. /**
  510. * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity
  511. * is located.
  512. *
  513. * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity
  514. * is located.
  515. */
  516. public Account getAccount() {
  517. return mAccount;
  518. }
  519. protected void setAccount(Account account) {
  520. mAccount = account;
  521. }
  522. /**
  523. * Getter for the capabilities of the server where the current OC account lives.
  524. *
  525. * @return Capabilities of the server where the current OC account lives. Null if the account is not
  526. * set yet.
  527. */
  528. public OCCapability getCapabilities() {
  529. return mCapabilities;
  530. }
  531. /**
  532. * @return Value of mFromNotification: True if the Activity is launched by a notification
  533. */
  534. public boolean fromNotification() {
  535. return mFromNotification;
  536. }
  537. /**
  538. * @return 'True' when the Activity is finishing to enforce the setup of a new account.
  539. */
  540. protected boolean isRedirectingToSetupAccount() {
  541. return mRedirectingToSetupAccount;
  542. }
  543. public OperationsServiceBinder getOperationsServiceBinder() {
  544. return mOperationsServiceBinder;
  545. }
  546. protected ServiceConnection newTransferenceServiceConnection() {
  547. return null;
  548. }
  549. /**
  550. * Helper class handling a callback from the {@link AccountManager} after the creation of
  551. * a new ownCloud {@link Account} finished, successfully or not.
  552. *
  553. * At this moment, only called after the creation of the first account.
  554. */
  555. public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
  556. @Override
  557. public void run(AccountManagerFuture<Bundle> future) {
  558. FileActivity.this.mRedirectingToSetupAccount = false;
  559. boolean accountWasSet = false;
  560. if (future != null) {
  561. try {
  562. Bundle result;
  563. result = future.getResult();
  564. String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
  565. String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
  566. if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
  567. setAccount(new Account(name, type), false);
  568. accountWasSet = true;
  569. }
  570. } catch (OperationCanceledException e) {
  571. Log_OC.d(TAG, "Account creation canceled");
  572. } catch (Exception e) {
  573. Log_OC.e(TAG, "Account creation finished in exception: ", e);
  574. }
  575. } else {
  576. Log_OC.e(TAG, "Account creation callback with null bundle");
  577. }
  578. if (!accountWasSet) {
  579. moveTaskToBack(true);
  580. }
  581. }
  582. }
  583. /**
  584. * Called when the ownCloud {@link Account} associated to the Activity was just updated.
  585. *
  586. * Child classes must grant that state depending on the {@link Account} is updated.
  587. */
  588. protected void onAccountSet(boolean stateWasRecovered) {
  589. if (getAccount() != null) {
  590. mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
  591. mCapabilities = mStorageManager.getCapability(mAccount.name);
  592. } else {
  593. Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
  594. }
  595. }
  596. public FileDataStorageManager getStorageManager() {
  597. return mStorageManager;
  598. }
  599. public OnRemoteOperationListener getRemoteOperationListener() {
  600. return this;
  601. }
  602. public Handler getHandler() {
  603. return mHandler;
  604. }
  605. public FileOperationsHelper getFileOperationsHelper() {
  606. return mFileOperationsHelper;
  607. }
  608. /**
  609. *
  610. * @param operation Removal operation performed.
  611. * @param result Result of the removal.
  612. */
  613. @Override
  614. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  615. Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the "
  616. + "FileActivities ");
  617. mFileOperationsHelper.setOpIdWaitingFor(Long.MAX_VALUE);
  618. dismissLoadingDialog();
  619. if (!result.isSuccess() && (
  620. result.getCode() == ResultCode.UNAUTHORIZED ||
  621. result.isIdPRedirection() ||
  622. (result.isException() && result.getException() instanceof AuthenticatorException)
  623. )) {
  624. requestCredentialsUpdate(this);
  625. if (result.getCode() == ResultCode.UNAUTHORIZED) {
  626. dismissLoadingDialog();
  627. Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  628. operation, getResources()),
  629. Toast.LENGTH_LONG);
  630. t.show();
  631. }
  632. } else if (operation == null ||
  633. operation instanceof CreateShareWithShareeOperation ||
  634. operation instanceof UnshareOperation ||
  635. operation instanceof SynchronizeFolderOperation ||
  636. operation instanceof UpdateShareViaLinkOperation ||
  637. operation instanceof UpdateSharePermissionsOperation
  638. ) {
  639. if (result.isSuccess()) {
  640. updateFileFromDB();
  641. } else if (result.getCode() != ResultCode.CANCELLED) {
  642. Toast t = Toast.makeText(this,
  643. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  644. Toast.LENGTH_LONG);
  645. t.show();
  646. }
  647. } else if (operation instanceof CreateShareViaLinkOperation) {
  648. onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
  649. } else if (operation instanceof SynchronizeFileOperation) {
  650. onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result);
  651. } else if (operation instanceof GetSharesForFileOperation) {
  652. if (result.isSuccess() || result.getCode() == ResultCode.SHARE_NOT_FOUND) {
  653. updateFileFromDB();
  654. } else {
  655. Toast t = Toast.makeText(this,
  656. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  657. Toast.LENGTH_LONG);
  658. t.show();
  659. }
  660. }
  661. }
  662. /**
  663. * Invalidates the credentials stored for the current OC account and requests new credentials to the user,
  664. * navigating to {@link AuthenticatorActivity}
  665. *
  666. * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
  667. * to make the method accessible to {@link android.content.BroadcastReceiver}s.
  668. */
  669. protected void requestCredentialsUpdate(Context context) {
  670. try {
  671. /// step 1 - invalidate credentials of current account
  672. OwnCloudClient client;
  673. OwnCloudAccount ocAccount =
  674. new OwnCloudAccount(getAccount(), context);
  675. client = (OwnCloudClientManagerFactory.getDefaultSingleton().
  676. removeClientFor(ocAccount));
  677. if (client != null) {
  678. OwnCloudCredentials cred = client.getCredentials();
  679. if (cred != null) {
  680. AccountManager am = AccountManager.get(context);
  681. if (cred.authTokenExpires()) {
  682. am.invalidateAuthToken(
  683. getAccount().type,
  684. cred.getAuthToken()
  685. );
  686. } else {
  687. am.clearPassword(getAccount());
  688. }
  689. }
  690. }
  691. /// step 2 - request credentials to user
  692. Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
  693. updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
  694. updateAccountCredentials.putExtra(
  695. AuthenticatorActivity.EXTRA_ACTION,
  696. AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
  697. updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
  698. startActivity(updateAccountCredentials);
  699. } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
  700. Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
  701. }
  702. }
  703. private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
  704. RemoteOperationResult result) {
  705. if (result.isSuccess()) {
  706. updateFileFromDB();
  707. Intent sendIntent = operation.getSendIntentWithSubject(this);
  708. if (sendIntent != null) {
  709. startActivity(sendIntent);
  710. }
  711. } else {
  712. // Detect Failure (403) --> needs Password
  713. if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
  714. String password = operation.getPassword();
  715. if ((password == null || password.length() == 0) &&
  716. getCapabilities().getFilesSharingPublicEnabled().isUnknown())
  717. {
  718. // Was tried without password, but not sure that it's optional. Try with password.
  719. // Try with password before giving up.
  720. // See also ShareFileFragment#OnShareViaLinkListener
  721. SharePasswordDialogFragment dialog =
  722. SharePasswordDialogFragment.newInstance(new OCFile(operation.getPath()), true);
  723. dialog.show(getSupportFragmentManager(), DIALOG_SHARE_PASSWORD);
  724. } else {
  725. Toast t = Toast.makeText(this,
  726. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  727. Toast.LENGTH_LONG);
  728. t.show();
  729. }
  730. } else {
  731. Toast t = Toast.makeText(this,
  732. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  733. Toast.LENGTH_LONG);
  734. t.show();
  735. }
  736. }
  737. }
  738. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
  739. RemoteOperationResult result) {
  740. OCFile syncedFile = operation.getLocalFile();
  741. if (!result.isSuccess()) {
  742. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  743. Intent i = new Intent(this, ConflictsResolveActivity.class);
  744. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
  745. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
  746. startActivity(i);
  747. }
  748. } else {
  749. if (!operation.transferWasRequested()) {
  750. Toast msg = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  751. operation, getResources()), Toast.LENGTH_LONG);
  752. msg.show();
  753. }
  754. invalidateOptionsMenu();
  755. }
  756. }
  757. protected void updateFileFromDB(){
  758. OCFile file = getFile();
  759. if (file != null) {
  760. file = getStorageManager().getFileByPath(file.getRemotePath());
  761. setFile(file);
  762. }
  763. }
  764. /**
  765. * Show loading dialog
  766. */
  767. public void showLoadingDialog(String message) {
  768. // grant that only one waiting dialog is shown
  769. dismissLoadingDialog();
  770. // Construct dialog
  771. LoadingDialog loading = new LoadingDialog(message);
  772. FragmentManager fm = getSupportFragmentManager();
  773. FragmentTransaction ft = fm.beginTransaction();
  774. loading.show(ft, DIALOG_WAIT_TAG);
  775. }
  776. /**
  777. * Dismiss loading dialog
  778. */
  779. public void dismissLoadingDialog() {
  780. Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
  781. if (frag != null) {
  782. LoadingDialog loading = (LoadingDialog) frag;
  783. loading.dismiss();
  784. }
  785. }
  786. private void doOnResumeAndBound() {
  787. mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler);
  788. long waitingForOpId = mFileOperationsHelper.getOpIdWaitingFor();
  789. if (waitingForOpId <= Integer.MAX_VALUE) {
  790. boolean wait = mOperationsServiceBinder.dispatchResultIfFinished((int)waitingForOpId,
  791. this);
  792. if (!wait ) {
  793. dismissLoadingDialog();
  794. }
  795. }
  796. }
  797. /**
  798. * Implements callback methods for service binding. Passed as a parameter to {
  799. */
  800. private class OperationsServiceConnection implements ServiceConnection {
  801. @Override
  802. public void onServiceConnected(ComponentName component, IBinder service) {
  803. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  804. Log_OC.d(TAG, "Operations service connected");
  805. mOperationsServiceBinder = (OperationsServiceBinder) service;
  806. /*if (!mOperationsServiceBinder.isPerformingBlockingOperation()) {
  807. dismissLoadingDialog();
  808. }*/
  809. if (mResumed) {
  810. doOnResumeAndBound();
  811. }
  812. } else {
  813. return;
  814. }
  815. }
  816. @Override
  817. public void onServiceDisconnected(ComponentName component) {
  818. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  819. Log_OC.d(TAG, "Operations service disconnected");
  820. mOperationsServiceBinder = null;
  821. // TODO whatever could be waiting for the service is unbound
  822. }
  823. }
  824. }
  825. @Override
  826. public FileDownloaderBinder getFileDownloaderBinder() {
  827. return mDownloaderBinder;
  828. }
  829. @Override
  830. public FileUploaderBinder getFileUploaderBinder() {
  831. return mUploaderBinder;
  832. }
  833. public void restart(){
  834. Intent i = new Intent(this, FileDisplayActivity.class);
  835. i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  836. startActivity(i);
  837. }
  838. // TODO re-enable when "Accounts" is available in Navigation Drawer
  839. // public void closeDrawer() {
  840. // mDrawerLayout.closeDrawers();
  841. // }
  842. public void allFilesOption(){
  843. restart();
  844. }
  845. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  846. @Override
  847. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  848. // TODO re-enable when "Accounts" is available in Navigation Drawer
  849. // if (mShowAccounts && position > 0){
  850. // position = position - 1;
  851. // }
  852. switch (position){
  853. // TODO re-enable when "Accounts" is available in Navigation Drawer
  854. // case 0: // Accounts
  855. // mShowAccounts = !mShowAccounts;
  856. // mNavigationDrawerAdapter.setShowAccounts(mShowAccounts);
  857. // mNavigationDrawerAdapter.notifyDataSetChanged();
  858. // break;
  859. case 0: // All Files
  860. allFilesOption();
  861. mDrawerLayout.closeDrawers();
  862. break;
  863. // TODO Enable when "On Device" is recovered ?
  864. // case 2:
  865. // MainApp.showOnlyFilesOnDevice(true);
  866. // mDrawerLayout.closeDrawers();
  867. // break;
  868. case 1: // Settings
  869. Intent settingsIntent = new Intent(getApplicationContext(),
  870. Preferences.class);
  871. startActivity(settingsIntent);
  872. mDrawerLayout.closeDrawers();
  873. break;
  874. case 2: // Logs
  875. Intent loggerIntent = new Intent(getApplicationContext(),
  876. LogHistoryActivity.class);
  877. startActivity(loggerIntent);
  878. mDrawerLayout.closeDrawers();
  879. break;
  880. }
  881. }
  882. }
  883. }