FileActivity.java 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  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. // Uploads
  339. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[2],
  340. R.drawable.ic_uploads));
  341. // Settings
  342. mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], mDrawerContentDescriptions[1],
  343. R.drawable.ic_settings));
  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.isException() && result.getException() instanceof AuthenticatorException)
  625. )) {
  626. requestCredentialsUpdate(this);
  627. if (result.getCode() == ResultCode.UNAUTHORIZED) {
  628. dismissLoadingDialog();
  629. Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  630. operation, getResources()),
  631. Toast.LENGTH_LONG);
  632. t.show();
  633. }
  634. } else if (operation == null ||
  635. operation instanceof CreateShareWithShareeOperation ||
  636. operation instanceof UnshareOperation ||
  637. operation instanceof SynchronizeFolderOperation ||
  638. operation instanceof UpdateShareViaLinkOperation ||
  639. operation instanceof UpdateSharePermissionsOperation
  640. ) {
  641. if (result.isSuccess()) {
  642. updateFileFromDB();
  643. } else if (result.getCode() != ResultCode.CANCELLED) {
  644. Toast t = Toast.makeText(this,
  645. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  646. Toast.LENGTH_LONG);
  647. t.show();
  648. }
  649. } else if (operation instanceof CreateShareViaLinkOperation) {
  650. onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
  651. } else if (operation instanceof SynchronizeFileOperation) {
  652. onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result);
  653. } else if (operation instanceof GetSharesForFileOperation) {
  654. if (result.isSuccess() || result.getCode() == ResultCode.SHARE_NOT_FOUND) {
  655. updateFileFromDB();
  656. } else {
  657. Toast t = Toast.makeText(this,
  658. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  659. Toast.LENGTH_LONG);
  660. t.show();
  661. }
  662. }
  663. }
  664. /**
  665. * Invalidates the credentials stored for the current OC account and requests new credentials to the user,
  666. * navigating to {@link AuthenticatorActivity}
  667. *
  668. * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter
  669. * to make the method accessible to {@link android.content.BroadcastReceiver}s.
  670. */
  671. protected void requestCredentialsUpdate(Context context) {
  672. try {
  673. /// step 1 - invalidate credentials of current account
  674. OwnCloudClient client;
  675. OwnCloudAccount ocAccount =
  676. new OwnCloudAccount(getAccount(), context);
  677. client = (OwnCloudClientManagerFactory.getDefaultSingleton().
  678. removeClientFor(ocAccount));
  679. if (client != null) {
  680. OwnCloudCredentials cred = client.getCredentials();
  681. if (cred != null) {
  682. AccountManager am = AccountManager.get(context);
  683. if (cred.authTokenExpires()) {
  684. am.invalidateAuthToken(
  685. getAccount().type,
  686. cred.getAuthToken()
  687. );
  688. } else {
  689. am.clearPassword(getAccount());
  690. }
  691. }
  692. }
  693. /// step 2 - request credentials to user
  694. Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
  695. updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
  696. updateAccountCredentials.putExtra(
  697. AuthenticatorActivity.EXTRA_ACTION,
  698. AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
  699. updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
  700. startActivity(updateAccountCredentials);
  701. } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
  702. Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
  703. }
  704. }
  705. private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
  706. RemoteOperationResult result) {
  707. if (result.isSuccess()) {
  708. updateFileFromDB();
  709. Intent sendIntent = operation.getSendIntentWithSubject(this);
  710. if (sendIntent != null) {
  711. startActivity(sendIntent);
  712. }
  713. } else {
  714. // Detect Failure (403) --> needs Password
  715. if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
  716. String password = operation.getPassword();
  717. if ((password == null || password.length() == 0) &&
  718. getCapabilities().getFilesSharingPublicEnabled().isUnknown())
  719. {
  720. // Was tried without password, but not sure that it's optional. Try with password.
  721. // Try with password before giving up.
  722. // See also ShareFileFragment#OnShareViaLinkListener
  723. SharePasswordDialogFragment dialog =
  724. SharePasswordDialogFragment.newInstance(new OCFile(operation.getPath()), true);
  725. dialog.show(getSupportFragmentManager(), DIALOG_SHARE_PASSWORD);
  726. } else {
  727. Toast t = Toast.makeText(this,
  728. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  729. Toast.LENGTH_LONG);
  730. t.show();
  731. }
  732. } else {
  733. Toast t = Toast.makeText(this,
  734. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  735. Toast.LENGTH_LONG);
  736. t.show();
  737. }
  738. }
  739. }
  740. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
  741. RemoteOperationResult result) {
  742. OCFile syncedFile = operation.getLocalFile();
  743. if (!result.isSuccess()) {
  744. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  745. Intent i = new Intent(this, ConflictsResolveActivity.class);
  746. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
  747. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
  748. startActivity(i);
  749. }
  750. } else {
  751. if (!operation.transferWasRequested()) {
  752. Toast msg = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
  753. operation, getResources()), Toast.LENGTH_LONG);
  754. msg.show();
  755. }
  756. invalidateOptionsMenu();
  757. }
  758. }
  759. protected void updateFileFromDB(){
  760. OCFile file = getFile();
  761. if (file != null) {
  762. file = getStorageManager().getFileByPath(file.getRemotePath());
  763. setFile(file);
  764. }
  765. }
  766. /**
  767. * Show loading dialog
  768. */
  769. public void showLoadingDialog(String message) {
  770. // grant that only one waiting dialog is shown
  771. dismissLoadingDialog();
  772. // Construct dialog
  773. LoadingDialog loading = new LoadingDialog(message);
  774. FragmentManager fm = getSupportFragmentManager();
  775. FragmentTransaction ft = fm.beginTransaction();
  776. loading.show(ft, DIALOG_WAIT_TAG);
  777. }
  778. /**
  779. * Dismiss loading dialog
  780. */
  781. public void dismissLoadingDialog() {
  782. Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
  783. if (frag != null) {
  784. LoadingDialog loading = (LoadingDialog) frag;
  785. loading.dismiss();
  786. }
  787. }
  788. private void doOnResumeAndBound() {
  789. mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler);
  790. long waitingForOpId = mFileOperationsHelper.getOpIdWaitingFor();
  791. if (waitingForOpId <= Integer.MAX_VALUE) {
  792. boolean wait = mOperationsServiceBinder.dispatchResultIfFinished((int)waitingForOpId,
  793. this);
  794. if (!wait ) {
  795. dismissLoadingDialog();
  796. }
  797. }
  798. }
  799. /**
  800. * Implements callback methods for service binding. Passed as a parameter to {
  801. */
  802. private class OperationsServiceConnection implements ServiceConnection {
  803. @Override
  804. public void onServiceConnected(ComponentName component, IBinder service) {
  805. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  806. Log_OC.d(TAG, "Operations service connected");
  807. mOperationsServiceBinder = (OperationsServiceBinder) service;
  808. /*if (!mOperationsServiceBinder.isPerformingBlockingOperation()) {
  809. dismissLoadingDialog();
  810. }*/
  811. if (mResumed) {
  812. doOnResumeAndBound();
  813. }
  814. } else {
  815. return;
  816. }
  817. }
  818. @Override
  819. public void onServiceDisconnected(ComponentName component) {
  820. if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
  821. Log_OC.d(TAG, "Operations service disconnected");
  822. mOperationsServiceBinder = null;
  823. // TODO whatever could be waiting for the service is unbound
  824. }
  825. }
  826. }
  827. @Override
  828. public FileDownloaderBinder getFileDownloaderBinder() {
  829. return mDownloaderBinder;
  830. }
  831. @Override
  832. public FileUploaderBinder getFileUploaderBinder() {
  833. return mUploaderBinder;
  834. }
  835. public void restart(){
  836. Intent i = new Intent(this, FileDisplayActivity.class);
  837. i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  838. startActivity(i);
  839. }
  840. // TODO re-enable when "Accounts" is available in Navigation Drawer
  841. // public void closeDrawer() {
  842. // mDrawerLayout.closeDrawers();
  843. // }
  844. public void allFilesOption(){
  845. restart();
  846. }
  847. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  848. @Override
  849. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  850. // TODO re-enable when "Accounts" is available in Navigation Drawer
  851. // if (mShowAccounts && position > 0){
  852. // position = position - 1;
  853. // }
  854. switch (position){
  855. // TODO re-enable when "Accounts" is available in Navigation Drawer
  856. // case 0: // Accounts
  857. // mShowAccounts = !mShowAccounts;
  858. // mNavigationDrawerAdapter.setShowAccounts(mShowAccounts);
  859. // mNavigationDrawerAdapter.notifyDataSetChanged();
  860. // break;
  861. case 0: // All Files
  862. allFilesOption();
  863. mDrawerLayout.closeDrawers();
  864. break;
  865. // TODO Enable when "On Device" is recovered ?
  866. // case 2:
  867. // MainApp.showOnlyFilesOnDevice(true);
  868. // mDrawerLayout.closeDrawers();
  869. // break;
  870. case 1: // Uploads
  871. Intent uploadListIntent = new Intent(getApplicationContext(),
  872. UploadListActivity.class);
  873. uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  874. startActivity(uploadListIntent);
  875. mDrawerLayout.closeDrawers();
  876. break;
  877. case 2: // Settings
  878. Intent settingsIntent = new Intent(getApplicationContext(),
  879. Preferences.class);
  880. startActivity(settingsIntent);
  881. mDrawerLayout.closeDrawers();
  882. break;
  883. case 3: // Logs
  884. Intent loggerIntent = new Intent(getApplicationContext(),
  885. LogHistoryActivity.class);
  886. startActivity(loggerIntent);
  887. mDrawerLayout.closeDrawers();
  888. break;
  889. }
  890. }
  891. }
  892. }