1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package com
.owncloud
.android
.ui
.activity
;
20 import java
.io
.IOException
;
22 import android
.accounts
.Account
;
23 import android
.accounts
.AccountManager
;
24 import android
.accounts
.AuthenticatorException
;
25 import android
.accounts
.OperationCanceledException
;
26 import android
.content
.BroadcastReceiver
;
27 import android
.content
.Context
;
28 import android
.content
.Intent
;
29 import android
.content
.IntentFilter
;
30 import android
.os
.Bundle
;
31 import android
.support
.v4
.app
.Fragment
;
32 import android
.support
.v4
.app
.FragmentTransaction
;
33 import android
.support
.v4
.widget
.SwipeRefreshLayout
;
34 import android
.util
.Log
;
35 import android
.view
.View
;
36 import android
.view
.View
.OnClickListener
;
37 import android
.widget
.Button
;
38 import android
.widget
.Toast
;
40 import com
.actionbarsherlock
.app
.ActionBar
;
41 import com
.actionbarsherlock
.view
.Menu
;
42 import com
.actionbarsherlock
.view
.MenuInflater
;
43 import com
.actionbarsherlock
.view
.MenuItem
;
44 import com
.actionbarsherlock
.view
.Window
;
45 import com
.owncloud
.android
.R
;
46 import com
.owncloud
.android
.datamodel
.OCFile
;
47 import com
.owncloud
.android
.lib
.common
.OwnCloudAccount
;
48 import com
.owncloud
.android
.lib
.common
.OwnCloudClient
;
49 import com
.owncloud
.android
.lib
.common
.OwnCloudClientManagerFactory
;
50 import com
.owncloud
.android
.lib
.common
.OwnCloudCredentials
;
51 import com
.owncloud
.android
.lib
.common
.accounts
.AccountUtils
.AccountNotFoundException
;
52 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperation
;
53 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
;
54 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
;
55 import com
.owncloud
.android
.operations
.SynchronizeFolderOperation
;
56 import com
.owncloud
.android
.syncadapter
.FileSyncAdapter
;
57 import com
.owncloud
.android
.ui
.dialog
.CreateFolderDialogFragment
;
58 import com
.owncloud
.android
.ui
.fragment
.FileFragment
;
59 import com
.owncloud
.android
.ui
.fragment
.OCFileListFragment
;
60 import com
.owncloud
.android
.utils
.DisplayUtils
;
61 import com
.owncloud
.android
.utils
.Log_OC
;
63 public class MoveActivity
extends HookActivity
implements FileFragment
.ContainerActivity
,
64 OnClickListener
, SwipeRefreshLayout
.OnRefreshListener
{
66 private SyncBroadcastReceiver mSyncBroadcastReceiver
;
68 private static final String TAG
= MoveActivity
.class.getSimpleName();
70 private static final String TAG_LIST_OF_FOLDERS
= "LIST_OF_FOLDERS";
72 private boolean mSyncInProgress
= false
;
74 private Button mCancelBtn
;
75 private Button mChooseBtn
;
79 protected void onCreate(Bundle savedInstanceState
) {
80 Log_OC
.d(TAG
, "onCreate() start");
81 requestWindowFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
);
83 super.onCreate(savedInstanceState
);
85 setContentView(R
.layout
.files_move
);
87 if (savedInstanceState
== null
) {
91 // sets callback listeners for UI elements
95 ActionBar actionBar
= getSupportActionBar();
96 actionBar
.setDisplayShowTitleEnabled(true
);
97 actionBar
.setNavigationMode(ActionBar
.NAVIGATION_MODE_STANDARD
);
98 setSupportProgressBarIndeterminateVisibility(mSyncInProgress
);
99 // always AFTER setContentView(...) ; to work around bug in its implementation
101 // sets message for empty list of folders
104 Log_OC
.d(TAG
, "onCreate() end");
109 protected void onStart() {
111 getSupportActionBar().setIcon(DisplayUtils
.getSeasonalIconId());
115 protected void onDestroy() {
120 * Called when the ownCloud {@link Account} associated to the Activity was just updated.
123 protected void onAccountSet(boolean stateWasRecovered
) {
124 super.onAccountSet(stateWasRecovered
);
125 if (getAccount() != null
) {
129 OCFile folder
= getFile();
130 if (folder
== null
|| !folder
.isFolder()) {
131 // fall back to root folder
132 setFile(getStorageManager().getFileByPath(OCFile
.ROOT_PATH
));
136 if (!stateWasRecovered
) {
137 OCFileListFragment listOfFolders
= getListOfFilesFragment();
138 listOfFolders
.listDirectory(folder
);
140 startSyncFolderOperation(folder
);
143 updateNavigationElementsInActionBar();
147 private void createFragments() {
148 OCFileListFragment listOfFiles
= new OCFileListFragment();
149 Bundle args
= new Bundle();
150 args
.putBoolean(OCFileListFragment
.ARG_JUST_FOLDERS
, true
);
151 args
.putBoolean(OCFileListFragment
.ARG_ALLOW_CONTEXTUAL_ACTIONS
, false
);
152 listOfFiles
.setArguments(args
);
153 FragmentTransaction transaction
= getSupportFragmentManager().beginTransaction();
154 transaction
.add(R
.id
.fragment_container
, listOfFiles
, TAG_LIST_OF_FOLDERS
);
155 transaction
.commit();
159 * Show a text message on screen view for notifying user if content is
160 * loading or folder is empty
162 private void setBackgroundText() {
163 OCFileListFragment listFragment
= getListOfFilesFragment();
164 if (listFragment
!= null
) {
165 int message
= R
.string
.file_list_loading
;
166 if (!mSyncInProgress
) {
167 // In case folder list is empty
168 message
= R
.string
.file_list_empty_moving
;
170 listFragment
.setMessageForEmptyList(getString(message
));
172 Log
.e(TAG
, "OCFileListFragment is null");
176 private OCFileListFragment
getListOfFilesFragment() {
177 Fragment listOfFiles
= getSupportFragmentManager().findFragmentByTag(MoveActivity
.TAG_LIST_OF_FOLDERS
);
178 if (listOfFiles
!= null
) {
179 return (OCFileListFragment
)listOfFiles
;
181 Log_OC
.wtf(TAG
, "Access to unexisting list of files fragment!!");
189 * Updates action bar and second fragment, if in dual pane mode.
192 public void onBrowsedDownTo(OCFile directory
) {
194 updateNavigationElementsInActionBar();
196 startSyncFolderOperation(directory
);
201 public void startSyncFolderOperation(OCFile folder
) {
202 long currentSyncTime
= System
.currentTimeMillis();
204 mSyncInProgress
= true
;
206 // perform folder synchronization
207 RemoteOperation synchFolderOp
= new SynchronizeFolderOperation( folder
,
210 getFileOperationsHelper().isSharedSupported(),
213 getApplicationContext()
215 synchFolderOp
.execute(getAccount(), this, null
, null
);
217 setSupportProgressBarIndeterminateVisibility(true
);
223 protected void onResume() {
225 Log_OC
.e(TAG
, "onResume() start");
227 // refresh list of files
228 refreshListOfFilesFragment();
230 // Listen for sync messages
231 IntentFilter syncIntentFilter
= new IntentFilter(FileSyncAdapter
.EVENT_FULL_SYNC_START
);
232 syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_END
);
233 syncIntentFilter
.addAction(FileSyncAdapter
.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED
);
234 syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
);
235 syncIntentFilter
.addAction(SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
);
236 mSyncBroadcastReceiver
= new SyncBroadcastReceiver();
237 registerReceiver(mSyncBroadcastReceiver
, syncIntentFilter
);
239 Log_OC
.d(TAG
, "onResume() end");
243 protected void onPause() {
244 Log_OC
.e(TAG
, "onPause() start");
245 if (mSyncBroadcastReceiver
!= null
) {
246 unregisterReceiver(mSyncBroadcastReceiver
);
247 //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
248 mSyncBroadcastReceiver
= null
;
251 Log_OC
.d(TAG
, "onPause() end");
256 public boolean onCreateOptionsMenu(Menu menu
) {
257 MenuInflater inflater
= getSherlock().getMenuInflater();
258 inflater
.inflate(R
.menu
.main_menu
, menu
);
259 menu
.findItem(R
.id
.action_upload
).setVisible(false
);
260 menu
.findItem(R
.id
.action_settings
).setVisible(false
);
261 menu
.findItem(R
.id
.action_sync_account
).setVisible(false
);
266 public boolean onOptionsItemSelected(MenuItem item
) {
267 boolean retval
= true
;
268 switch (item
.getItemId()) {
269 case R
.id
.action_create_dir
: {
270 CreateFolderDialogFragment dialog
=
271 CreateFolderDialogFragment
.newInstance(getCurrentFolder());
273 getSupportFragmentManager(),
274 CreateFolderDialogFragment
.CREATE_FOLDER_FRAGMENT
278 case android
.R
.id
.home
: {
279 OCFile currentDir
= getCurrentFolder();
280 if(currentDir
!= null
&& currentDir
.getParentId() != 0) {
286 retval
= super.onOptionsItemSelected(item
);
291 private OCFile
getCurrentFolder() {
292 OCFile file
= getFile();
294 if (file
.isFolder()) {
296 } else if (getStorageManager() != null
) {
297 String parentPath
= file
.getRemotePath().substring(0, file
.getRemotePath().lastIndexOf(file
.getFileName()));
298 return getStorageManager().getFileByPath(parentPath
);
304 protected void refreshListOfFilesFragment() {
305 OCFileListFragment fileListFragment
= getListOfFilesFragment();
306 if (fileListFragment
!= null
) {
307 fileListFragment
.listDirectory();
311 public void browseToRoot() {
312 OCFileListFragment listOfFiles
= getListOfFilesFragment();
313 if (listOfFiles
!= null
) { // should never be null, indeed
314 OCFile root
= getStorageManager().getFileByPath(OCFile
.ROOT_PATH
);
315 listOfFiles
.listDirectory(root
);
316 setFile(listOfFiles
.getCurrentFile());
317 updateNavigationElementsInActionBar();
318 startSyncFolderOperation(root
);
323 public void onBackPressed() {
324 OCFileListFragment listOfFiles
= getListOfFilesFragment();
325 if (listOfFiles
!= null
) { // should never be null, indeed
326 int levelsUp
= listOfFiles
.onBrowseUp();
331 setFile(listOfFiles
.getCurrentFile());
332 updateNavigationElementsInActionBar();
336 private void updateNavigationElementsInActionBar() {
337 ActionBar actionBar
= getSupportActionBar();
338 OCFile currentDir
= getCurrentFolder();
339 boolean atRoot
= (currentDir
== null
|| currentDir
.getParentId() == 0);
340 actionBar
.setDisplayHomeAsUpEnabled(!atRoot
);
341 actionBar
.setHomeButtonEnabled(!atRoot
);
344 ?
getString(R
.string
.default_display_name_for_root_folder
)
345 : currentDir
.getFileName()
350 * Set per-view controllers
352 private void initControls(){
353 mCancelBtn
= (Button
) findViewById(R
.id
.move_files_btn_cancel
);
354 mCancelBtn
.setOnClickListener(this);
355 mChooseBtn
= (Button
) findViewById(R
.id
.move_files_btn_choose
);
356 mChooseBtn
.setOnClickListener(this);
360 public void onClick(View v
) {
361 if (v
== mCancelBtn
) {
363 } else if (v
== mChooseBtn
) {
364 // TODO request to move, OR save selected folder as a result and let request for caller
365 Toast
.makeText( MoveActivity
.this,
366 "TODO: MOVE IMPLEMENTATION",
374 private class SyncBroadcastReceiver
extends BroadcastReceiver
{
377 * {@link BroadcastReceiver} to enable syncing feedback in UI
380 public void onReceive(Context context
, Intent intent
) {
382 String event
= intent
.getAction();
383 Log_OC
.d(TAG
, "Received broadcast " + event
);
384 String accountName
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_ACCOUNT_NAME
);
385 String synchFolderRemotePath
= intent
.getStringExtra(FileSyncAdapter
.EXTRA_FOLDER_PATH
);
386 RemoteOperationResult synchResult
= (RemoteOperationResult
)intent
.getSerializableExtra(FileSyncAdapter
.EXTRA_RESULT
);
387 boolean sameAccount
= (getAccount() != null
&& accountName
.equals(getAccount().name
) && getStorageManager() != null
);
391 if (FileSyncAdapter
.EVENT_FULL_SYNC_START
.equals(event
)) {
392 mSyncInProgress
= true
;
395 OCFile currentFile
= (getFile() == null
) ? null
: getStorageManager().getFileByPath(getFile().getRemotePath());
396 OCFile currentDir
= (getCurrentFolder() == null
) ? null
: getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
398 if (currentDir
== null
) {
399 // current folder was removed from the server
400 Toast
.makeText( MoveActivity
.this,
401 String
.format(getString(R
.string
.sync_current_folder_was_removed
), "PLACEHOLDER"),
407 if (currentFile
== null
&& !getFile().isFolder()) {
408 // currently selected file was removed in the server, and now we know it
409 currentFile
= currentDir
;
412 if (synchFolderRemotePath
!= null
&& currentDir
.getRemotePath().equals(synchFolderRemotePath
)) {
413 OCFileListFragment fileListFragment
= getListOfFilesFragment();
414 if (fileListFragment
!= null
) {
415 fileListFragment
.listDirectory(currentDir
);
418 setFile(currentFile
);
421 mSyncInProgress
= (!FileSyncAdapter
.EVENT_FULL_SYNC_END
.equals(event
) && !SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_SHARES_SYNCED
.equals(event
));
423 if (SynchronizeFolderOperation
.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED
.
425 /// TODO refactor and make common
426 synchResult
!= null
&& !synchResult
.isSuccess() &&
427 (synchResult
.getCode() == ResultCode
.UNAUTHORIZED
||
428 synchResult
.isIdPRedirection() ||
429 (synchResult
.isException() && synchResult
.getException()
430 instanceof AuthenticatorException
))) {
432 OwnCloudClient client
= null
;
434 OwnCloudAccount ocAccount
=
435 new OwnCloudAccount(getAccount(), context
);
436 client
= (OwnCloudClientManagerFactory
.getDefaultSingleton().
437 removeClientFor(ocAccount
));
438 // TODO get rid of these exceptions
439 } catch (AccountNotFoundException e
) {
441 } catch (AuthenticatorException e
) {
443 } catch (OperationCanceledException e
) {
445 } catch (IOException e
) {
449 if (client
!= null
) {
450 OwnCloudCredentials cred
= client
.getCredentials();
452 AccountManager am
= AccountManager
.get(context
);
453 if (cred
.authTokenExpires()) {
454 am
.invalidateAuthToken(
459 am
.clearPassword(getAccount());
464 requestCredentialsUpdate();
468 removeStickyBroadcast(intent
);
469 Log_OC
.d(TAG
, "Setting progress visibility to " + mSyncInProgress
);
470 setSupportProgressBarIndeterminateVisibility(mSyncInProgress
/*|| mRefreshSharesInProgress*/);
476 } catch (RuntimeException e
) {
477 // avoid app crashes after changing the serial id of RemoteOperationResult
478 // in owncloud library with broadcast notifications pending to process
479 removeStickyBroadcast(intent
);
487 * Shows the information of the {@link OCFile} received as a
488 * parameter in the second fragment.
490 * @param file {@link OCFile} whose details will be shown
493 public void showDetails(OCFile file
) {
501 public void onTransferStateChanged(OCFile file
, boolean downloading
, boolean uploading
) {
507 public void onRefresh() {
508 OCFileListFragment listOfFiles
= getListOfFilesFragment();
509 if (listOfFiles
!= null
) {
510 OCFile folder
= listOfFiles
.getCurrentFile();
511 if (folder
!= null
) {
512 /*mFile = mContainerActivity.getStorageManager().getFileById(mFile.getFileId());
513 listDirectory(mFile);*/
514 startSyncFolderOperation(folder
);