<service android:name=".services.observer.FileObserverService"/>
<activity
- android:name=".ui.activity.CopyToClipboardActivity"
- android:label="@string/copy_link"
- android:icon="@drawable/copy_link" />
+ android:name=".ui.activity.MoveActivity"
+ android:label="@string/app_name"/>
</application>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_color"
+ android:orientation="vertical" >
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/fragment_container" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/move_files_btn_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/common_cancel" />
+
+ <Button
+ android:id="@+id/move_files_btn_choose"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/move_choose_button_text" />
+
+ </LinearLayout>
+
+ </LinearLayout>
\ No newline at end of file
<string name="auth_redirect_non_secure_connection_title">Secure connection is redirected through a non secure route.</string>
<string name="actionbar_move">Move</string>
+ <string name="file_list_empty_moving">Nothing in here. You can add a folder!</string>
+ <string name="move_choose_button_text">Choose</string>
</resources>
--- /dev/null
+package com.owncloud.android.ui.activity;
+
+import java.io.IOException;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.SynchronizeFolderOperation;
+import com.owncloud.android.services.observer.FileObserverService;
+import com.owncloud.android.syncadapter.FileSyncAdapter;
+import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.fragment.MoveFileListFragment;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.Log_OC;
+
+public class MoveActivity extends HookActivity implements FileFragment.ContainerActivity,
+ OnNavigationListener, OnClickListener{
+
+ private ArrayAdapter<String> mDirectories;
+
+ private SyncBroadcastReceiver mSyncBroadcastReceiver;
+
+ public static final int DIALOG_SHORT_WAIT = 0;
+
+ public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
+
+ private static final String TAG = MoveActivity.class.getSimpleName();
+
+ private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
+
+ private boolean mSyncInProgress = false;
+
+ private Button mCancelBtn;
+ private Button mChooseBtn;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreate() start");
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
+ super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
+
+ /// grant that FileObserverService is watching favourite files
+ if (savedInstanceState == null) {
+ Intent initObserversIntent = FileObserverService.makeInitIntent(this);
+ startService(initObserversIntent);
+ }
+
+ /// USER INTERFACE
+
+ // Inflate and set the layout view
+ setContentView(R.layout.files_move);
+ if (savedInstanceState == null) {
+ createMinFragments();
+ }
+
+ initControls();
+
+ // Action bar setup
+ mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+ getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
+ setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/); // always AFTER setContentView(...) ; to work around bug in its implementation
+
+ setBackgroundText();
+
+ Log_OC.d(TAG, "onCreate() end");
+
+ }
+
+ private void createMinFragments() {
+ MoveFileListFragment listOfFiles = new MoveFileListFragment();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+ transaction.commit();
+ }
+
+ /**
+ * Show a text message on screen view for notifying user if content is
+ * loading or folder is empty
+ */
+ private void setBackgroundText() {
+ MoveFileListFragment MoveFileListFragment = getListOfFilesFragment();
+ if (MoveFileListFragment != null) {
+ int message = R.string.file_list_loading;
+ if (!mSyncInProgress) {
+ // In case folder list is empty
+ message = R.string.file_list_empty_moving;
+ }
+ MoveFileListFragment.setMessageForEmptyList(getString(message));
+ } else {
+ Log.e(TAG, "MoveFileListFragment is null");
+ }
+ }
+
+ private MoveFileListFragment getListOfFilesFragment() {
+ Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(MoveActivity.TAG_LIST_OF_FILES);
+ if (listOfFiles != null) {
+ return (MoveFileListFragment)listOfFiles;
+ }
+ Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
+ return null;
+ }
+
+ // Custom array adapter to override text colors
+ private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
+
+ public CustomArrayAdapter(MoveActivity ctx, int view) {
+ super(ctx, view);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = super.getView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+
+ fixRoot((TextView) v );
+ return v;
+ }
+
+ public View getDropDownView(int position, View convertView,
+ ViewGroup parent) {
+ View v = super.getDropDownView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+
+ fixRoot((TextView) v );
+ return v;
+ }
+
+ private void fixRoot(TextView v) {
+ if (v.getText().equals(OCFile.PATH_SEPARATOR)) {
+ v.setText(R.string.default_display_name_for_root_folder);
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Updates action bar and second fragment, if in dual pane mode.
+ */
+ @Override
+ public void onBrowsedDownTo(OCFile directory) {
+ pushDirname(directory);
+
+ // Sync Folder
+ startSyncFolderOperation(directory);
+
+ }
+
+ /**
+ * Shows the information of the {@link OCFile} received as a
+ * parameter in the second fragment.
+ *
+ * @param file {@link OCFile} whose details will be shown
+ */
+ @Override
+ public void showDetails(OCFile file) {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
+
+ }
+
+ /**
+ * Pushes a directory to the drop down list
+ * @param directory to push
+ * @throws IllegalArgumentException If the {@link OCFile#isFolder()} returns false.
+ */
+ public void pushDirname(OCFile directory) {
+ if(!directory.isFolder()){
+ throw new IllegalArgumentException("Only directories may be pushed!");
+ }
+ mDirectories.insert(directory.getFileName(), 0);
+ setFile(directory);
+ }
+
+ public void startSyncFolderOperation(OCFile folder) {
+ long currentSyncTime = System.currentTimeMillis();
+
+ mSyncInProgress = true;
+
+ // perform folder synchronization
+ RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
+ currentSyncTime,
+ false,
+ getFileOperationsHelper().isSharedSupported(),
+ getStorageManager(),
+ getAccount(),
+ getApplicationContext()
+ );
+ synchFolderOp.execute(getAccount(), this, null, null);
+
+ setSupportProgressBarIndeterminateVisibility(true);
+
+ setBackgroundText();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log_OC.e(TAG, "onResume() start");
+
+ // refresh list of files
+ refreshListOfFilesFragment();
+
+ // Listen for sync messages
+ IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
+ syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
+ syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
+ mSyncBroadcastReceiver = new SyncBroadcastReceiver();
+ registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+
+ Log_OC.d(TAG, "onResume() end");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getSherlock().getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+ menu.findItem(R.id.action_upload).setVisible(false);
+ menu.findItem(R.id.action_settings).setVisible(false);
+ menu.findItem(R.id.action_sync_account).setVisible(false);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean retval = true;
+ switch (item.getItemId()) {
+ case R.id.action_create_dir: {
+ CreateFolderDialogFragment dialog =
+ CreateFolderDialogFragment.newInstance(getCurrentDir());
+ dialog.show(getSupportFragmentManager(), "createdirdialog");
+ break;
+ }
+ case android.R.id.home: {
+ OCFile currentDir = getCurrentDir();
+ if(currentDir != null && currentDir.getParentId() != 0) {
+ onBackPressed();
+ }
+ break;
+ }
+ default:
+ retval = super.onOptionsItemSelected(item);
+ }
+ return retval;
+ }
+
+ private OCFile getCurrentDir() {
+ OCFile file = getFile();
+ if (file != null) {
+ if (file.isFolder()) {
+ return file;
+ } else if (getStorageManager() != null) {
+ String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+ return getStorageManager().getFileByPath(parentPath);
+ }
+ }
+ return null;
+ }
+
+ protected void refreshListOfFilesFragment() {
+ MoveFileListFragment fileListFragment = getListOfFilesFragment();
+ if (fileListFragment != null) {
+ fileListFragment.listDirectory();
+ }
+ }
+
+ private class SyncBroadcastReceiver extends BroadcastReceiver {
+
+ /**
+ * {@link BroadcastReceiver} to enable syncing feedback in UI
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ String event = intent.getAction();
+ Log_OC.d(TAG, "Received broadcast " + event);
+ String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
+ String synchFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
+ RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncAdapter.EXTRA_RESULT);
+ boolean sameAccount = (getAccount() != null && accountName.equals(getAccount().name) && getStorageManager() != null);
+
+ if (sameAccount) {
+
+ if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) {
+ mSyncInProgress = true;
+
+ } else {
+ OCFile currentFile = (getFile() == null) ? null : getStorageManager().getFileByPath(getFile().getRemotePath());
+ OCFile currentDir = (getCurrentDir() == null) ? null : getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
+
+ if (currentDir == null) {
+ // current folder was removed from the server
+ Toast.makeText( MoveActivity.this,
+ String.format(getString(R.string.sync_current_folder_was_removed), mDirectories.getItem(0)),
+ Toast.LENGTH_LONG)
+ .show();
+ browseToRoot();
+
+ } else {
+ if (currentFile == null && !getFile().isFolder()) {
+ // currently selected file was removed in the server, and now we know it
+ currentFile = currentDir;
+ }
+
+ if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
+ MoveFileListFragment fileListFragment = getListOfFilesFragment();
+ if (fileListFragment != null) {
+ fileListFragment.listDirectory(currentDir);
+ }
+ }
+ setFile(currentFile);
+ }
+
+ mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
+
+ if (SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
+ equals(event) &&
+ /// TODO refactor and make common
+ synchResult != null && !synchResult.isSuccess() &&
+ (synchResult.getCode() == ResultCode.UNAUTHORIZED ||
+ synchResult.isIdPRedirection() ||
+ (synchResult.isException() && synchResult.getException()
+ instanceof AuthenticatorException))) {
+
+ OwnCloudClient client = null;
+ try {
+ OwnCloudAccount ocAccount =
+ new OwnCloudAccount(getAccount(), context);
+ client = (OwnCloudClientManagerFactory.getDefaultSingleton().
+ removeClientFor(ocAccount));
+ // TODO get rid of these exceptions
+ } catch (AccountNotFoundException e) {
+ e.printStackTrace();
+ } catch (AuthenticatorException e) {
+ e.printStackTrace();
+ } catch (OperationCanceledException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (client != null) {
+ OwnCloudCredentials cred = client.getCredentials();
+ if (cred != null) {
+ AccountManager am = AccountManager.get(context);
+ if (cred.authTokenExpires()) {
+ am.invalidateAuthToken(
+ getAccount().type,
+ cred.getAuthToken()
+ );
+ } else {
+ am.clearPassword(getAccount());
+ }
+ }
+ }
+
+ requestCredentialsUpdate();
+
+ }
+ }
+ removeStickyBroadcast(intent);
+ Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
+ setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/);
+
+ setBackgroundText();
+
+ }
+
+ } catch (RuntimeException e) {
+ // avoid app crashes after changing the serial id of RemoteOperationResult
+ // in owncloud library with broadcast notifications pending to process
+ removeStickyBroadcast(intent);
+ }
+ }
+ }
+
+ public void browseToRoot() {
+ MoveFileListFragment listOfFiles = getListOfFilesFragment();
+ if (listOfFiles != null) { // should never be null, indeed
+ while (mDirectories.getCount() > 1) {
+ popDirname();
+ }
+ OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
+ listOfFiles.listDirectory(root);
+ setFile(listOfFiles.getCurrentFile());
+ startSyncFolderOperation(root);
+ }
+ }
+
+ /**
+ * Pops a directory name from the drop down list
+ * @return True, unless the stack is empty
+ */
+ public boolean popDirname() {
+ mDirectories.remove(mDirectories.getItem(0));
+ return !mDirectories.isEmpty();
+ }
+
+ private void setNavigationListWithFolder(OCFile file) {
+ mDirectories.clear();
+ OCFile fileIt = file;
+ String parentPath;
+ while(fileIt != null && fileIt.getFileName() != OCFile.ROOT_PATH) {
+ if (fileIt.isFolder()) {
+ mDirectories.add(fileIt.getFileName());
+ }
+ // get parent from path
+ parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
+ fileIt = getStorageManager().getFileByPath(parentPath);
+ }
+ mDirectories.add(OCFile.PATH_SEPARATOR);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ if (itemPosition != 0) {
+ String targetPath = "";
+ for (int i=itemPosition; i < mDirectories.getCount() - 1; i++) {
+ targetPath = mDirectories.getItem(i) + OCFile.PATH_SEPARATOR + targetPath;
+ }
+ targetPath = OCFile.PATH_SEPARATOR + targetPath;
+ OCFile targetFolder = getStorageManager().getFileByPath(targetPath);
+ if (targetFolder != null) {
+ browseTo(targetFolder);
+ }
+
+ // the next operation triggers a new call to this method, but it's necessary to
+ // ensure that the name exposed in the action bar is the current directory when the
+ // user selected it in the navigation list
+ if (getSupportActionBar().getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST && itemPosition != 0)
+ getSupportActionBar().setSelectedNavigationItem(0);
+ }
+ return true;
+ }
+
+ public void browseTo(OCFile folder) {
+ if (folder == null || !folder.isFolder()) {
+ throw new IllegalArgumentException("Trying to browse to invalid folder " + folder);
+ }
+ MoveFileListFragment listOfFiles = getListOfFilesFragment();
+ if (listOfFiles != null) {
+ setNavigationListWithFolder(folder);
+ listOfFiles.listDirectory(folder);
+ setFile(listOfFiles.getCurrentFile());
+ startSyncFolderOperation(folder);
+ } else {
+ Log_OC.e(TAG, "Unexpected null when accessing list fragment");
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ MoveFileListFragment listOfFiles = getListOfFilesFragment();
+ if (listOfFiles != null) { // should never be null, indeed
+ if (mDirectories.getCount() <= 1) {
+ finish();
+ return;
+ }
+ int levelsUp = listOfFiles.onBrowseUp();
+ for (int i=0; i < levelsUp && mDirectories.getCount() > 1 ; i++) {
+ popDirname();
+ }
+ }
+ if (listOfFiles != null) { // should never be null, indeed
+ setFile(listOfFiles.getCurrentFile());
+ }
+ }
+
+ private void updateNavigationElementsInActionBar(OCFile chosenFile) {
+ ActionBar actionBar = getSupportActionBar();
+ if (chosenFile == null) {
+ // only list of files - set for browsing through folders
+ OCFile currentDir = getCurrentDir();
+ boolean noRoot = (currentDir != null && currentDir.getParentId() != 0);
+ actionBar.setDisplayHomeAsUpEnabled(noRoot);
+ actionBar.setDisplayShowTitleEnabled(!noRoot);
+ if (!noRoot) {
+ actionBar.setTitle(getString(R.string.default_display_name_for_root_folder));
+ }
+ actionBar.setNavigationMode(!noRoot ? ActionBar.NAVIGATION_MODE_STANDARD : ActionBar.NAVIGATION_MODE_LIST);
+ actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
+
+ } else {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setTitle(chosenFile.getFileName());
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ }
+ }
+
+ /**
+ * Called when the ownCloud {@link Account} associated to the Activity was just updated.
+ */
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
+ if (getAccount() != null) {
+ /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
+ OCFile file = getFile();
+ // get parent from path
+ String parentPath = "";
+ if (file != null) {
+ if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
+ // upload in progress - right now, files are not inserted in the local cache until the upload is successful
+ // get parent from path
+ parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+ if (getStorageManager().getFileByPath(parentPath) == null)
+ file = null; // not able to know the directory where the file is uploading
+ } else {
+ file = getStorageManager().getFileByPath(file.getRemotePath()); // currentDir = null if not in the current Account
+ }
+ }
+ if (file == null) {
+ // fall back to root folder
+ file = getStorageManager().getFileByPath(OCFile.ROOT_PATH); // never returns null
+ }
+ setFile(file);
+ setNavigationListWithFolder(file);
+
+ if (!stateWasRecovered) {
+ Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+ if (file.isFolder()) {
+ startSyncFolderOperation(file);
+ }
+ } else {
+ updateNavigationElementsInActionBar(file.isFolder() ? null : file);
+ }
+ }
+ }
+
+ /**
+ * Set controllers
+ */
+ private void initControls(){
+ mCancelBtn = (Button) findViewById(R.id.move_files_btn_cancel);
+ mCancelBtn.setOnClickListener(this);
+ mChooseBtn = (Button) findViewById(R.id.move_files_btn_choose);
+ mChooseBtn.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mCancelBtn) {
+ finish();
+ }
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package com.owncloud.android.ui.adapter;
+
+import java.util.Vector;
+
+import android.accounts.Account;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.activity.ComponentsGetter;
+import com.owncloud.android.utils.DisplayUtils;
+
+
+/**
+ * This Adapter populates a ListView with all the folders in an ownCloud instance.
+ */
+public class FolderListListAdapter extends BaseAdapter implements ListAdapter {
+ private final static String PERMISSION_SHARED_WITH_ME = "S";
+
+ private Context mContext;
+ private OCFile mFile = null;
+ private Vector<OCFile> mFolders = null;
+
+ private FileDataStorageManager mStorageManager;
+ private Account mAccount;
+ private ComponentsGetter mTransferServiceGetter;
+
+ public FolderListListAdapter(Context context, ComponentsGetter transferServiceGetter) {
+ mContext = context;
+ mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+ mTransferServiceGetter = transferServiceGetter;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ return mFolders != null ? mFolders.size() : 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (mFolders == null || mFolders.size() <= position)
+ return null;
+ return mFolders.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (mFolders == null || mFolders.size() <= position)
+ return 0;
+ return mFolders.get(position).getFileId();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @SuppressLint("InflateParams")
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ LayoutInflater inflator = (LayoutInflater) mContext
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflator.inflate(R.layout.list_item, null);
+ }
+
+ if (mFolders != null && mFolders.size() > position) {
+ OCFile file = mFolders.get(position);
+ TextView fileName = (TextView) view.findViewById(R.id.Filename);
+ String name = file.getFileName();
+
+ fileName.setText(name);
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
+ ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);
+ ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);
+ sharedWithMeIconV.setVisibility(View.GONE);
+
+ ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);
+ localStateView.bringToFront();
+ FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();
+ FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();
+
+ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
+ localStateView.setImageResource(R.drawable.downloading_file_indicator);
+ localStateView.setVisibility(View.VISIBLE);
+ } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
+ localStateView.setImageResource(R.drawable.uploading_file_indicator);
+ localStateView.setVisibility(View.VISIBLE);
+ } else if (file.isDown()) {
+ localStateView.setImageResource(R.drawable.local_file_indicator);
+ localStateView.setVisibility(View.VISIBLE);
+ } else {
+ localStateView.setVisibility(View.INVISIBLE);
+ }
+
+ TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
+ TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
+ ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
+
+
+ fileSizeV.setVisibility(View.INVISIBLE);
+ lastModV.setVisibility(View.VISIBLE);
+ lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));
+ checkBoxV.setVisibility(View.GONE);
+ view.findViewById(R.id.imageView3).setVisibility(View.GONE);
+
+ if (checkIfFileIsSharedWithMe(file)) {
+ fileIcon.setImageResource(R.drawable.shared_with_me_folder);
+ sharedWithMeIconV.setVisibility(View.VISIBLE);
+ } else {
+ fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype(), file.getFileName()));
+ }
+
+ // If folder is sharedByLink, icon folder must be changed to
+ // folder-public one
+ if (file.isShareByLink()) {
+ fileIcon.setImageResource(R.drawable.folder_public);
+ }
+
+ if (file.isShareByLink()) {
+ sharedIconV.setVisibility(View.VISIBLE);
+ } else {
+ sharedIconV.setVisibility(View.GONE);
+ }
+ }
+
+ return view;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (mFolders == null || mFolders.isEmpty());
+ }
+
+ /**
+ * Change the adapted directory for a new one
+ * @param directory New file to adapt. Can be NULL, meaning "no content to adapt".
+ * @param updatedStorageManager Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)
+ */
+ public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) {
+ mFile = directory;
+ if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
+ mStorageManager = updatedStorageManager;
+ mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
+ }
+ if (mStorageManager != null) {
+ // Only take into account the folders for after being listed
+ mFolders = getFolders(mStorageManager.getFolderContent(mFile));
+ } else {
+ mFolders = null;
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Filter for getting only the folders
+ * @param files
+ * @return Vector<OCFile>
+ */
+ public Vector<OCFile> getFolders(Vector<OCFile> files) {
+ Vector<OCFile> ret = new Vector<OCFile>();
+ OCFile current = null;
+ for (int i=0; i<files.size(); i++) {
+ current = files.get(i);
+ if (current.isFolder()) {
+ ret.add(current);
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Check if parent folder does not include 'S' permission and if file/folder
+ * is shared with me
+ *
+ * @param file: OCFile
+ * @return boolean: True if it is shared with me and false if it is not
+ */
+ private boolean checkIfFileIsSharedWithMe(OCFile file) {
+ return (mFile.getPermissions() != null && !mFile.getPermissions().contains(PERMISSION_SHARED_WITH_ME)
+ && file.getPermissions() != null && file.getPermissions().contains(PERMISSION_SHARED_WITH_ME));
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package com.owncloud.android.ui.fragment;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.activity.MoveActivity;
+import com.owncloud.android.ui.adapter.FolderListListAdapter;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ * A Fragment that lists all folders in a given path.
+ *
+ * TODO refactorize to get rid of direct dependency on MoveActivity
+ *
+ */
+public class MoveFileListFragment extends ExtendedListFragment {
+
+ private static final String TAG = MoveFileListFragment.class.getSimpleName();
+
+ private static final String MY_PACKAGE = MoveFileListFragment.class.getPackage() != null ? MoveFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
+ private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
+
+ private static final String KEY_INDEXES = "INDEXES";
+ private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
+ private static final String KEY_TOPS = "TOPS";
+ private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
+ private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
+
+ private FileFragment.ContainerActivity mContainerActivity;
+
+ private OCFile mFile = null;
+ private FolderListListAdapter mAdapter;
+
+ // Save the state of the scroll in browsing
+ private ArrayList<Integer> mIndexes;
+ private ArrayList<Integer> mFirstPositions;
+ private ArrayList<Integer> mTops;
+
+ private int mHeightCell = 0;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ Log_OC.e(TAG, "onAttach");
+ try {
+ mContainerActivity = (FileFragment.ContainerActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " +
+ FileFragment.ContainerActivity.class.getSimpleName());
+ }
+ }
+
+
+ @Override
+ public void onDetach() {
+ mContainerActivity = null;
+ super.onDetach();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log_OC.e(TAG, "onActivityCreated() start");
+
+ mAdapter = new FolderListListAdapter(getSherlockActivity(), mContainerActivity);
+
+ if (savedInstanceState != null) {
+ mFile = savedInstanceState.getParcelable(EXTRA_FILE);
+ mIndexes = savedInstanceState.getIntegerArrayList(KEY_INDEXES);
+ mFirstPositions = savedInstanceState.getIntegerArrayList(KEY_FIRST_POSITIONS);
+ mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
+ mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
+ setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
+
+ } else {
+ mIndexes = new ArrayList<Integer>();
+ mFirstPositions = new ArrayList<Integer>();
+ mTops = new ArrayList<Integer>();
+ mHeightCell = 0;
+
+ }
+
+ mAdapter = new FolderListListAdapter(getSherlockActivity(), mContainerActivity);
+
+ setListAdapter(mAdapter);
+
+ registerForContextMenu(getListView());
+ getListView().setOnCreateContextMenuListener(this);
+ }
+
+ /**
+ * Saves the current listed folder.
+ */
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(EXTRA_FILE, mFile);
+ outState.putIntegerArrayList(KEY_INDEXES, mIndexes);
+ outState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
+ outState.putIntegerArrayList(KEY_TOPS, mTops);
+ outState.putInt(KEY_HEIGHT_CELL, mHeightCell);
+ outState.putString(KEY_EMPTY_LIST_MESSAGE, getEmptyViewText());
+ }
+
+ /**
+ * Call this, when the user presses the up button.
+ *
+ * Tries to move up the current folder one level. If the parent folder was removed from the database,
+ * it continues browsing up until finding an existing folders.
+ *
+ * return Count of folder levels browsed up.
+ */
+ public int onBrowseUp() {
+ OCFile parentDir = null;
+ int moveCount = 0;
+
+ if(mFile != null){
+ FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
+
+ String parentPath = null;
+ if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
+ parentPath = new File(mFile.getRemotePath()).getParent();
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+ parentDir = storageManager.getFileByPath(parentPath);
+ moveCount++;
+ } else {
+ parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null; keep the path in root folder
+ }
+ while (parentDir == null) {
+ parentPath = new File(parentPath).getParent();
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+ parentDir = storageManager.getFileByPath(parentPath);
+ moveCount++;
+ } // exit is granted because storageManager.getFileByPath("/") never returns null
+ mFile = parentDir;
+ }
+
+ if (mFile != null) {
+ listDirectory(mFile);
+
+ ((MoveActivity)mContainerActivity).startSyncFolderOperation(mFile);
+
+ // restore index and top position
+ restoreIndexAndTopPosition();
+
+ } // else - should never happen now
+
+ return moveCount;
+ }
+
+ /*
+ * Restore index and position
+ */
+ private void restoreIndexAndTopPosition() {
+ if (mIndexes.size() > 0) {
+ // needs to be checked; not every browse-up had a browse-down before
+
+ int index = mIndexes.remove(mIndexes.size() - 1);
+
+ int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
+
+ int top = mTops.remove(mTops.size() - 1);
+
+ mList.setSelectionFromTop(firstPosition, top);
+
+ // Move the scroll if the selection is not visible
+ int indexPosition = mHeightCell*index;
+ int height = mList.getHeight();
+
+ if (indexPosition > height) {
+ if (android.os.Build.VERSION.SDK_INT >= 11)
+ {
+ mList.smoothScrollToPosition(index);
+ }
+ else if (android.os.Build.VERSION.SDK_INT >= 8)
+ {
+ mList.setSelectionFromTop(index, 0);
+ }
+
+ }
+ }
+ }
+
+ /*
+ * Save index and top position
+ */
+ private void saveIndexAndTopPosition(int index) {
+
+ mIndexes.add(index);
+
+ int firstPosition = mList.getFirstVisiblePosition();
+ mFirstPositions.add(firstPosition);
+
+ View view = mList.getChildAt(0);
+ int top = (view == null) ? 0 : view.getTop() ;
+
+ mTops.add(top);
+
+ // Save the height of a cell
+ mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+ OCFile file = (OCFile) mAdapter.getItem(position);
+ if (file != null) {
+ if (file.isFolder()) {
+ // update state and view of this fragment
+ listDirectory(file);
+ // then, notify parent activity to let it update its state and view, and other fragments
+ mContainerActivity.onBrowsedDownTo(file);
+ // save index and top position
+ saveIndexAndTopPosition(position);
+
+ }
+
+ } else {
+ Log_OC.d(TAG, "Null object in ListAdapter!!");
+ }
+
+ }
+
+ /**
+ * Use this to query the {@link OCFile} that is currently
+ * being displayed by this fragment
+ * @return The currently viewed OCFile
+ */
+ public OCFile getCurrentFile(){
+ return mFile;
+ }
+
+ /**
+ * Calls {@link MoveFileListFragment#listDirectory(OCFile)} with a null parameter
+ */
+ public void listDirectory(){
+ listDirectory(null);
+ }
+
+ /**
+ * Lists the given directory on the view. When the input parameter is null,
+ * it will either refresh the last known directory. list the root
+ * if there never was a directory.
+ *
+ * @param directory File to be listed
+ */
+ public void listDirectory(OCFile directory) {
+ FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
+ if (storageManager != null) {
+
+ // Check input parameters for null
+ if(directory == null){
+ if(mFile != null){
+ directory = mFile;
+ } else {
+ directory = storageManager.getFileByPath("/");
+ if (directory == null) return; // no files, wait for sync
+ }
+ }
+
+
+ // If that's not a directory -> List its parent
+ if(!directory.isFolder()){
+ Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+ directory = storageManager.getFileById(directory.getParentId());
+ }
+
+ mAdapter.swapDirectory(directory, storageManager);
+ if (mFile == null || !mFile.equals(directory)) {
+ mList.setSelectionFromTop(0, 0);
+ }
+ mFile = directory;
+ }
+ }
+
+
+ @Override
+ public void onRefresh() {
+ super.onRefresh();
+
+ if (mFile != null) {
+ // Refresh mFile
+ mFile = mContainerActivity.getStorageManager().getFileById(mFile.getFileId());
+
+ listDirectory(mFile);
+
+ ((MoveActivity)mContainerActivity).startSyncFolderOperation(mFile);
+ }
+ }
+}
import java.util.ArrayList;
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuInflater;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.FileMenuFilter;
import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.activity.MoveActivity;
import com.owncloud.android.ui.adapter.FileListListAdapter;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
}
return true;
}
+ case R.id.action_move: {
+ Intent i = new Intent(getActivity(), MoveActivity.class);
+ startActivity(i);
+
+ return true;
+ }
default:
return super.onContextItemSelected(item);
}