Merge pull request #327 from zmatsuo/uploadUrlFile

Add upload URL file feature
This commit is contained in:
Andy Scherzinger 2016-12-27 14:03:56 +01:00 committed by GitHub
commit 0c94de645b
9 changed files with 576 additions and 207 deletions

View file

@ -84,20 +84,14 @@
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="@style/Theme.ownCloud.NoActionBar">
<activity android:name=".ui.activity.LocalDirectorySelectorActivity" />
<activity android:name=".ui.activity.StorageMigrationActivity" />
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Nextcloud Android client application
Copyright (C) 2016 zmatsuo
Copyright (C) 2016 Nextcloud.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
License as published by the Free Software Foundation; either
version 3 of the License, or any later version.
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 AFFERO GENERAL PUBLIC LICENSE for more details.
You should have received a copy of the GNU Affero General Public
License along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="clip_horizontal"
android:orientation="vertical"
android:padding="@dimen/standard_padding">
<TextView
android:id="@+id/label_file_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/upload_file_dialog_filename"
tools:text="@string/upload_file_dialog_filename"/>
<EditText
android:id="@+id/user_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textNoSuggestions|textCapSentences"/>
<TextView
android:id="@+id/label_file_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/upload_file_dialog_filetype"
tools:text="@string/upload_file_dialog_filetype"/>
<Spinner
android:id="@+id/file_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View file

@ -61,7 +61,6 @@
<string name="sync_string_files">Files</string>
<string name="setup_btn_connect">Connect</string>
<string name="uploader_btn_upload_text">Upload</string>
<string name="uploader_btn_uploadTextSnippet_text">Create text file</string>
<string name="uploader_top_message">Choose upload folder</string>
<string name="uploader_wrn_no_account_title">No account found</string>
<string name="uploader_wrn_no_account_text">There are no %1$s accounts on your device. Please set up an account first.</string>
@ -540,6 +539,12 @@
<item quantity="one">%d selected</item>
<item quantity="other">%d selected</item>
</plurals>
<string name="upload_file_dialog_title">Input upload filename and filetype</string>
<string name="upload_file_dialog_filename">Filename</string>
<string name="upload_file_dialog_filetype">Filetype</string>
<string name="upload_file_dialog_filetype_snippet_text">Snippet text file(.txt)</string>
<string name="upload_file_dialog_filetype_internet_shortcut">Internet shortcut file(%s)</string>
<string name="upload_file_dialog_filetype_googlemap_shortcut">Google Maps shortcut file(%s)</string>
<string name="storage_description_default">Default</string>
<string name="storage_description_sd_no">SD card %1$d</string>

View file

@ -35,6 +35,8 @@ public abstract class PreferenceManager {
private static final String AUTO_PREF__LAST_UPLOAD_PATH = "last_upload_path";
private static final String AUTO_PREF__SORT_ORDER = "sort_order";
private static final String AUTO_PREF__SORT_ASCENDING = "sort_ascending";
private static final String AUTO_PREF__UPLOAD_FILE_EXTENSION_MAP_URL = "prefs_upload_file_extension_map_url";
private static final String AUTO_PREF__UPLOAD_FILE_EXTENSION_URL = "prefs_upload_file_extension_url";
private static final String AUTO_PREF__UPLOADER_BEHAVIOR = "prefs_uploader_behaviour";
private static final String PREF__INSTANT_UPLOADING = "instant_uploading";
private static final String PREF__INSTANT_VIDEO_UPLOADING = "instant_video_uploading";
@ -77,6 +79,50 @@ public abstract class PreferenceManager {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("show_hidden_files_pref", false);
}
/**
* Gets the selected file extension position the user selected to do the last upload of a url file shared from other
* app.
*
* @param context Caller {@link Context}, used to access to shared preferences manager.
* @return selectedPos the selected file extension position.
*/
public static int getUploadUrlFileExtensionUrlSelectedPos(Context context) {
return getDefaultSharedPreferences(context).getInt(AUTO_PREF__UPLOAD_FILE_EXTENSION_URL, 0);
}
/**
* Saves the selected file extension position the user selected to do the last upload of a url file shared from
* other app.
*
* @param context Caller {@link Context}, used to access to shared preferences manager.
* @param selectedPos the selected file extension position.
*/
public static void setUploadUrlFileExtensionUrlSelectedPos(Context context, int selectedPos) {
saveIntPreference(context, AUTO_PREF__UPLOAD_FILE_EXTENSION_URL, selectedPos);
}
/**
* Gets the selected map file extension position the user selected to do the last upload of a url file shared
* from other app.
*
* @param context Caller {@link Context}, used to access to shared preferences manager.
* @return selectedPos the selected file extension position.
*/
public static int getUploadMapFileExtensionUrlSelectedPos(Context context) {
return getDefaultSharedPreferences(context).getInt(AUTO_PREF__UPLOAD_FILE_EXTENSION_MAP_URL, 0);
}
/**
* Saves the selected map file extension position the user selected to do the last upload of a url file shared from
* other app.
*
* @param context Caller {@link Context}, used to access to shared preferences manager.
* @param selectedPos the selected file extension position.
*/
public static void setUploadMapFileExtensionUrlSelectedPos(Context context, int selectedPos) {
saveIntPreference(context, AUTO_PREF__UPLOAD_FILE_EXTENSION_MAP_URL, selectedPos);
}
/**
* Gets the path where the user selected to do the last upload of a file shared from other app.
*

View file

@ -1,54 +0,0 @@
/**
* Nextcloud Android client application
*
* @author Bartosz Przybylski
* Copyright (C) 2016 ownCloud Inc.
* Copyright (C) 2016 Nextcloud
* Copyright (C) 2016 Bartosz Przybylski
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* 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 AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.owncloud.android.R;
/**
* Created by Bartosz Przybylski on 07.11.2015.
*/
public class LocalDirectorySelectorActivity extends UploadFilesActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUploadBtn.setText(R.string.folder_picker_choose_button_text);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.upload_files_btn_cancel) {
setResult(RESULT_CANCELED);
finish();
} else if (v.getId() == R.id.upload_files_btn_upload) {
Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_CHOSEN_FILES, getInitialDirectory().getAbsolutePath());
setResult(RESULT_OK, resultIntent);
finish();
}
}
}

View file

@ -57,6 +57,7 @@ import com.owncloud.android.services.OperationsService;
import com.owncloud.android.ui.adapter.AccountListAdapter;
import com.owncloud.android.ui.adapter.AccountListItem;
import com.owncloud.android.ui.helpers.FileOperationsHelper;
import com.owncloud.android.utils.DisplayUtils;
import java.util.ArrayList;
import java.util.HashSet;
@ -96,7 +97,7 @@ public class ManageAccountsActivity extends FileActivity
updateActionBarTitleAndHomeButtonByString(getResources().getString(R.string.prefs_manage_accounts));
Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
mOriginalAccounts = toAccountNameSet(accountList);
mOriginalAccounts = DisplayUtils.toAccountNameSet(accountList);
mOriginalCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name;
setAccount(AccountUtils.getCurrentOwnCloudAccount(this));
@ -116,20 +117,6 @@ public class ManageAccountsActivity extends FileActivity
initializeComponentGetters();
}
/**
* converts an array of accounts into a set of account names.
*
* @param accountList the account array
* @return set of account names
*/
private Set<String> toAccountNameSet(Account[] accountList) {
Set<String> actualAccounts = new HashSet<>(accountList.length);
for (Account account : accountList) {
actualAccounts.add(account.name);
}
return actualAccounts;
}
@Override
public void onBackPressed() {
Intent resultIntent = new Intent();
@ -147,7 +134,7 @@ public class ManageAccountsActivity extends FileActivity
*/
private boolean hasAccountListChanged() {
Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
Set<String> actualAccounts = toAccountNameSet(accountList);
Set<String> actualAccounts = DisplayUtils.toAccountNameSet(accountList);
return !mOriginalAccounts.equals(actualAccounts);
}

View file

@ -31,27 +31,37 @@ import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources.NotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.owncloud.android.MainApp;
@ -68,20 +78,24 @@ import com.owncloud.android.operations.CreateFolderOperation;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.adapter.AccountListAdapter;
import com.owncloud.android.ui.adapter.AccountListItem;
import com.owncloud.android.ui.adapter.UploaderAdapter;
import com.owncloud.android.ui.asynctasks.CopyAndUploadContentUrisTask;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.fragment.TaskRetainerFragment;
import com.owncloud.android.ui.helpers.UriUploader;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.FileStorageUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -93,13 +107,15 @@ import java.util.Vector;
* This can be used to upload things to an ownCloud instance.
*/
public class ReceiveExternalFilesActivity extends FileActivity
implements OnItemClickListener, android.view.View.OnClickListener,
CopyAndUploadContentUrisTask.OnCopyTmpFilesTaskListener {
implements OnItemClickListener, View.OnClickListener, CopyAndUploadContentUrisTask.OnCopyTmpFilesTaskListener {
private static final String TAG = ReceiveExternalFilesActivity.class.getSimpleName();
private static final String FTAG_ERROR_FRAGMENT = "ERROR_FRAGMENT";
public static final String TEXT_FILE_SUFFIX = ".txt";
public static final String URL_FILE_SUFFIX = ".url";
public static final String WEBLOC_FILE_SUFFIX = ".webloc";
public static final String DESKTOP_FILE_SUFFIX = ".desktop";
private AccountManager mAccountManager;
private Stack<String> mParents;
@ -112,9 +128,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
private boolean mAccountSelected;
private boolean mAccountSelectionShowing;
private final static int DIALOG_NO_ACCOUNT = 0;
private final static int DIALOG_MULTIPLE_ACCOUNT = 1;
private final static int REQUEST_CODE__SETUP_ACCOUNT = REQUEST_CODE__LAST_SHARED + 1;
private final static String KEY_PARENTS = "PARENTS";
@ -122,7 +135,11 @@ public class ReceiveExternalFilesActivity extends FileActivity
private final static String KEY_ACCOUNT_SELECTED = "ACCOUNT_SELECTED";
private final static String KEY_ACCOUNT_SELECTION_SHOWING = "ACCOUNT_SELECTION_SHOWING";
private static final String DIALOG_WAIT_COPY_FILE = "DIALOG_WAIT_COPY_FILE";
private boolean mUploadFromTmpFile = false;
private String mSubjectText;
private String mExtraText;
private final static String FILENAME_ENCODING = "UTF-8";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -171,10 +188,12 @@ public class ReceiveExternalFilesActivity extends FileActivity
Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
if (accounts.length == 0) {
Log_OC.i(TAG, "No ownCloud account is available");
showDialog(DIALOG_NO_ACCOUNT);
DialogNoAccount dialog = new DialogNoAccount();
dialog.show(getSupportFragmentManager(), null);
} else if (accounts.length > 1 && !mAccountSelected && !mAccountSelectionShowing) {
Log_OC.i(TAG, "More than one ownCloud is available");
showDialog(DIALOG_MULTIPLE_ACCOUNT);
DialogMultipleAccount dialog = new DialogMultipleAccount();
dialog.show(getSupportFragmentManager(), null);
mAccountSelectionShowing = true;
} else {
if (!savedAccount) {
@ -203,7 +222,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
Log_OC.d(TAG, "onSaveInstanceState() start");
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_PARENTS, mParents);
//outState.putParcelable(KEY_ACCOUNT, mAccount);
outState.putParcelable(KEY_FILE, mFile);
outState.putBoolean(KEY_ACCOUNT_SELECTED, mAccountSelected);
outState.putBoolean(KEY_ACCOUNT_SELECTION_SHOWING, mAccountSelectionShowing);
@ -220,25 +238,24 @@ public class ReceiveExternalFilesActivity extends FileActivity
super.onDestroy();
}
@Override
protected Dialog onCreateDialog(final int id) {
final AlertDialog.Builder builder = new Builder(this);
switch (id) {
case DIALOG_NO_ACCOUNT:
public static class DialogNoAccount extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new Builder(getActivity());
builder.setIcon(R.drawable.ic_warning);
builder.setTitle(R.string.uploader_wrn_no_account_title);
builder.setMessage(String.format(
getString(R.string.uploader_wrn_no_account_text),
getString(R.string.app_name)));
getString(R.string.uploader_wrn_no_account_text),
getString(R.string.app_name)));
builder.setCancelable(false);
builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ECLAIR_MR1) {
android.os.Build.VERSION_CODES.ECLAIR_MR1) {
// using string value since in API7 this
// constatn is not defined
// in API7 < this constatant is defined in
// constant is not defined
// in API7 < this constant is defined in
// Settings.ADD_ACCOUNT_SETTINGS
// and Settings.EXTRA_AUTHORITIES
Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
@ -247,10 +264,10 @@ public class ReceiveExternalFilesActivity extends FileActivity
} else {
// since in API7 there is no direct call for
// account setup, so we need to
// show our own AccountSetupAcricity, get
// show our own AccountSetupActivity, get
// desired results and setup
// everything for ourself
Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
// everything for ourselves
Intent intent = new Intent(getActivity().getBaseContext(), AccountAuthenticator.class);
startActivityForResult(intent, REQUEST_CODE__SETUP_ACCOUNT);
}
}
@ -258,40 +275,357 @@ public class ReceiveExternalFilesActivity extends FileActivity
builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
getActivity().finish();
}
});
return builder.create();
case DIALOG_MULTIPLE_ACCOUNT:
Account accounts[] = mAccountManager.getAccountsByType(MainApp.getAccountType());
CharSequence dialogItems[] = new CharSequence[accounts.length];
for (int i = 0; i < dialogItems.length; ++i) {
dialogItems[i] = DisplayUtils.getAccountNameDisplayText(
this, accounts[i], accounts[i].name, DisplayUtils.convertIdn(accounts[i].name, false));
}
}
}
public static class DialogMultipleAccount extends DialogFragment {
private AccountListAdapter mAccountListAdapter;
private Drawable mTintedCheck;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final ReceiveExternalFilesActivity parent = (ReceiveExternalFilesActivity) getActivity();
AlertDialog.Builder builder = new Builder(parent);
mTintedCheck = DrawableCompat.wrap(ContextCompat.getDrawable(parent,
R.drawable.ic_account_circle_white_18dp));
int tint = ContextCompat.getColor(parent, R.color.primary);
DrawableCompat.setTint(mTintedCheck, tint);
mAccountListAdapter = new AccountListAdapter(parent, getAccountListItems(parent), mTintedCheck);
builder.setTitle(R.string.common_choose_account);
builder.setItems(dialogItems, new OnClickListener() {
builder.setAdapter(mAccountListAdapter, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setAccount(mAccountManager.getAccountsByType(MainApp.getAccountType())[which]);
onAccountSet(mAccountWasRestored);
final ReceiveExternalFilesActivity parent = (ReceiveExternalFilesActivity) getActivity();
parent.setAccount(parent.mAccountManager.getAccountsByType(MainApp.getAccountType())[which]);
parent.onAccountSet(parent.mAccountWasRestored);
dialog.dismiss();
mAccountSelected = true;
mAccountSelectionShowing = false;
parent.mAccountSelected = true;
parent.mAccountSelectionShowing = false;
}
});
builder.setCancelable(true);
builder.setOnCancelListener(new OnCancelListener() {
return builder.create();
}
/**
* creates the account list items list including the add-account action in case multiaccount_support is enabled.
*
* @return list of account list items
*/
private ArrayList<AccountListItem> getAccountListItems(ReceiveExternalFilesActivity activity) {
Account[] accountList = activity.mAccountManager.getAccountsByType(MainApp.getAccountType());
ArrayList<AccountListItem> adapterAccountList = new ArrayList<>(accountList.length);
for (Account account : accountList) {
adapterAccountList.add(new AccountListItem(account));
}
return adapterAccountList;
}
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
final ReceiveExternalFilesActivity parent = (ReceiveExternalFilesActivity) getActivity();
parent.mAccountSelectionShowing = false;
parent.finish();
}
}
public static class DialogInputUploadFilename extends DialogFragment {
private static final String KEY_SUBJECT_TEXT = "SUBJECT_TEXT";
private static final String KEY_EXTRA_TEXT = "EXTRA_TEXT";
private static final int CATEGORY_URL = 1;
private static final int CATEGORY_MAPS_URL = 2;
private List<String> mFilenameBase;
private List<String> mFilenameSuffix;
private List<String> mText;
private int mFileCategory;
private Spinner mSpinner;
public static DialogInputUploadFilename newInstance(String subjectText, String extraText) {
DialogInputUploadFilename dialog = new DialogInputUploadFilename();
Bundle args = new Bundle();
args.putString(KEY_SUBJECT_TEXT, subjectText);
args.putString(KEY_EXTRA_TEXT, extraText);
dialog.setArguments(args);
return dialog;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mFilenameBase = new ArrayList<>();
mFilenameSuffix = new ArrayList<>();
mText = new ArrayList<>();
String subjectText = getArguments().getString(KEY_SUBJECT_TEXT);
String extraText = getArguments().getString(KEY_EXTRA_TEXT);
LayoutInflater layout = LayoutInflater.from(getActivity().getBaseContext());
View view = layout.inflate(R.layout.upload_file_dialog, null);
ArrayAdapter<String> adapter
= new ArrayAdapter<>(getActivity().getBaseContext(), android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
int selectPos = 0;
String filename = renameSafeFilename(subjectText);
if (filename == null) {
filename = "";
}
adapter.add(getString(R.string.upload_file_dialog_filetype_snippet_text));
mText.add(extraText);
mFilenameBase.add(filename);
mFilenameSuffix.add(TEXT_FILE_SUFFIX);
if (isIntentStartWithUrl(extraText)) {
String str = getString(R.string.upload_file_dialog_filetype_internet_shortcut);
mText.add(internetShortcutUrlText(extraText));
mFilenameBase.add(filename);
mFilenameSuffix.add(URL_FILE_SUFFIX);
adapter.add(String.format(str,URL_FILE_SUFFIX));
mText.add(internetShortcutWeblocText(extraText));
mFilenameBase.add(filename);
mFilenameSuffix.add(WEBLOC_FILE_SUFFIX);
adapter.add(String.format(str,WEBLOC_FILE_SUFFIX));
mText.add(internetShortcutDesktopText(extraText, filename));
mFilenameBase.add(filename);
mFilenameSuffix.add(DESKTOP_FILE_SUFFIX);
adapter.add(String.format(str,DESKTOP_FILE_SUFFIX));
selectPos = PreferenceManager.getUploadUrlFileExtensionUrlSelectedPos(getActivity());
mFileCategory = CATEGORY_URL;
} else if (isIntentFromGoogleMap(subjectText, extraText)) {
String str = getString(R.string.upload_file_dialog_filetype_googlemap_shortcut);
String texts[] = extraText.split("\n");
mText.add(internetShortcutUrlText(texts[2]));
mFilenameBase.add(texts[0]);
mFilenameSuffix.add(URL_FILE_SUFFIX);
adapter.add(String.format(str,URL_FILE_SUFFIX));
mText.add(internetShortcutWeblocText(texts[2]));
mFilenameBase.add(texts[0]);
mFilenameSuffix.add(WEBLOC_FILE_SUFFIX);
adapter.add(String.format(str,WEBLOC_FILE_SUFFIX));
mText.add(internetShortcutDesktopText(texts[2], texts[0]));
mFilenameBase.add(texts[0]);
mFilenameSuffix.add(DESKTOP_FILE_SUFFIX);
adapter.add(String.format(str,DESKTOP_FILE_SUFFIX));
selectPos = PreferenceManager.getUploadMapFileExtensionUrlSelectedPos(getActivity());
mFileCategory = CATEGORY_MAPS_URL;
}
final EditText userInput = (EditText) view.findViewById(R.id.user_input);
setFilename(userInput, selectPos);
userInput.requestFocus();
final Spinner spinner = (Spinner) view.findViewById(R.id.file_type);
setupSpinner(adapter, selectPos, userInput, spinner);
if (adapter.getCount() == 1) {
TextView label = (TextView) view.findViewById(R.id.label_file_type);
label.setVisibility(View.GONE);
spinner.setVisibility(View.GONE);
}
mSpinner = spinner;
Dialog filenameDialog = createFilenameDialog(view, userInput, spinner);
filenameDialog.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
return filenameDialog;
}
private void setupSpinner(ArrayAdapter<String> adapter, int selectPos, final EditText userInput, Spinner spinner) {
spinner.setAdapter(adapter);
spinner.setSelection(selectPos, false);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onCancel(DialogInterface dialog) {
mAccountSelectionShowing = false;
dialog.cancel();
finish();
public void onItemSelected(AdapterView parent, View view, int position, long id) {
Spinner spinner = (Spinner) parent;
int selectPos = spinner.getSelectedItemPosition();
setFilename(userInput, selectPos);
saveSelection(selectPos);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// nothing to do
}
});
}
@NonNull
private Dialog createFilenameDialog(View view, final EditText userInput, final Spinner spinner) {
Builder builder = new Builder(getActivity());
builder.setView(view);
builder.setTitle(R.string.upload_file_dialog_title);
builder.setPositiveButton(R.string.common_ok, new OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
int selectPos = spinner.getSelectedItemPosition();
// verify if file name has suffix
String filename = userInput.getText().toString();
String suffix = mFilenameSuffix.get(selectPos);
if (!filename.endsWith(suffix)){
filename += suffix;
}
File file = createTempFile("tmp.tmp", mText.get(selectPos));
if (file == null) {
getActivity().finish();
}
String tmpname = file.getAbsolutePath();
((ReceiveExternalFilesActivity)getActivity()).uploadFile(tmpname, filename);
}
});
builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
dialog.cancel();
}
});
return builder.create();
default:
throw new IllegalArgumentException("Unknown dialog id: " + id);
}
public void onPause() {
hideSpinnerDropDown(mSpinner);
super.onPause();
}
private void saveSelection(int selectPos) {
switch (mFileCategory) {
case CATEGORY_URL:
PreferenceManager.setUploadUrlFileExtensionUrlSelectedPos(getActivity(), selectPos);
break;
case CATEGORY_MAPS_URL:
PreferenceManager.setUploadMapFileExtensionUrlSelectedPos(getActivity(), selectPos);
break;
default:
Log_OC.d(TAG, "Simple text snippet only: no selection to be persisted");
break;
}
}
private void hideSpinnerDropDown(Spinner spinner) {
try {
Method method = Spinner.class.getDeclaredMethod("onDetachedFromWindow");
method.setAccessible(true);
method.invoke(spinner);
} catch (Exception e) {
Log_OC.e(TAG, "onDetachedFromWindow", e);
}
}
private void setFilename(EditText inputText, int selectPos)
{
String filename = mFilenameBase.get(selectPos) + mFilenameSuffix.get(selectPos);
inputText.setText(filename);
int selectionStart = 0;
int extensionStart = filename.lastIndexOf(".");
int selectionEnd = (extensionStart >= 0) ? extensionStart : filename.length();
if (selectionEnd >= 0) {
inputText.setSelection(
Math.min(selectionStart, selectionEnd),
Math.max(selectionStart, selectionEnd));
}
}
private boolean isIntentFromGoogleMap(String subjectText, String extraText) {
String texts[] = extraText.split("\n");
if (texts.length != 3)
return false;
if (texts[0].length() == 0 || !subjectText.equals(texts[0]))
return false;
return texts[2].startsWith("https://goo.gl/maps/");
}
private boolean isIntentStartWithUrl(String extraText) {
return (extraText.startsWith("http://") || extraText.startsWith("https://"));
}
@Nullable
private String renameSafeFilename(String filename) {
String safeFilename = filename;
safeFilename = safeFilename.replaceAll("[?]", "_");
safeFilename = safeFilename.replaceAll("\"", "_");
safeFilename = safeFilename.replaceAll("/", "_");
safeFilename = safeFilename.replaceAll("<", "_");
safeFilename = safeFilename.replaceAll(">", "_");
safeFilename = safeFilename.replaceAll("[*]", "_");
safeFilename = safeFilename.replaceAll("[|]", "_");
safeFilename = safeFilename.replaceAll(";", "_");
safeFilename = safeFilename.replaceAll("=", "_");
safeFilename = safeFilename.replaceAll(",", "_");
try {
int maxLength = 128;
if (safeFilename.getBytes(FILENAME_ENCODING).length > maxLength) {
safeFilename = new String(safeFilename.getBytes(FILENAME_ENCODING), 0, maxLength, FILENAME_ENCODING);
}
} catch (UnsupportedEncodingException e) {
Log_OC.e(TAG, "rename failed ", e);
return null;
}
return safeFilename;
}
private String internetShortcutUrlText(String url) {
return "[InternetShortcut]\r\n" +
"URL=" + url + "\r\n";
}
private String internetShortcutWeblocText(String url) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
"<plist version=\"1.0\">\n" +
"<dict>\n" +
"<key>URL</key>\n" +
"<string>" + url + "</string>\n" +
"</dict>\n" +
"</plist>\n";
}
private String internetShortcutDesktopText(String url, String filename) {
return "[Desktop Entry]\n" +
"Encoding=UTF-8\n" +
"Name=" + filename + "\n" +
"Type=Link\n" +
"URL=" + url + "\n" +
"Icon=text-html";
}
@Nullable
private File createTempFile(String filename, String text) {
File file = new File(((ReceiveExternalFilesActivity)getActivity()).getCacheDir(), filename);
FileWriter fw = null;
try {
fw = new FileWriter(file);
fw.write(text);
} catch (IOException e) {
Log_OC.d(TAG, "Error ", e);
return null;
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
Log_OC.d(TAG, "Error closing file writer ", e);
}
}
}
return file;
}
}
@ -344,62 +678,9 @@ public class ReceiveExternalFilesActivity extends FileActivity
mUploadPath += p + OCFile.PATH_SEPARATOR;
}
if (uploadTextSnippet()){
LayoutInflater layout = LayoutInflater.from(getBaseContext());
View view = layout.inflate(R.layout.edit_box_dialog, null);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
alertDialogBuilder.setView(view);
final EditText userInput = (EditText) view.findViewById(R.id.user_input);
userInput.setText(TEXT_FILE_SUFFIX);
alertDialogBuilder.setCancelable(false)
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
PrintWriter out;
try {
File f = File.createTempFile("nextcloud", TEXT_FILE_SUFFIX);
out = new PrintWriter(f, "UTF8");
out.println(getIntent().getStringExtra(Intent.EXTRA_TEXT));
out.close();
FileUploader.UploadRequester requester =
new FileUploader.UploadRequester();
// verify if file name has suffix
String filename = userInput.getText().toString();
if (!filename.endsWith(TEXT_FILE_SUFFIX)){
filename += TEXT_FILE_SUFFIX;
}
requester.uploadNewFile(
getBaseContext(),
getAccount(),
f.getAbsolutePath(),
mFile.getRemotePath() + filename,
FileUploader.LOCAL_BEHAVIOUR_COPY,
null,
true,
UploadFileOperation.CREATED_BY_USER
);
} catch (IOException e) {
Log_OC.w(TAG, e.getMessage());
}
finish();
}
})
.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
dialog.cancel();
}
});
alertDialogBuilder.create().show();
if (mUploadFromTmpFile){
DialogInputUploadFilename dialog = DialogInputUploadFilename.newInstance(mSubjectText, mExtraText);
dialog.show(getSupportFragmentManager(), null);
} else {
Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
uploadFiles();
@ -420,13 +701,13 @@ public class ReceiveExternalFilesActivity extends FileActivity
super.onActivityResult(requestCode, resultCode, data);
Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
if (requestCode == REQUEST_CODE__SETUP_ACCOUNT) {
dismissDialog(DIALOG_NO_ACCOUNT);
if (resultCode == RESULT_CANCELED) {
finish();
}
Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAuthTokenType());
if (accounts.length == 0) {
showDialog(DIALOG_NO_ACCOUNT);
DialogNoAccount dialog = new DialogNoAccount();
dialog.show(getSupportFragmentManager(), null);
} else {
// there is no need for checking for is there more then one
// account at this point
@ -483,10 +764,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
Button btnChooseFolder = (Button) findViewById(R.id.uploader_choose_folder);
btnChooseFolder.setOnClickListener(this);
if (uploadTextSnippet()){
btnChooseFolder.setText(R.string.uploader_btn_uploadTextSnippet_text);
}
Button btnNewFolder = (Button) findViewById(R.id.uploader_cancel);
btnNewFolder.setOnClickListener(this);
@ -534,21 +811,54 @@ public class ReceiveExternalFilesActivity extends FileActivity
}
private void prepareStreamsToUpload() {
if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
Intent intent = getIntent();
if (intent.getAction().equals(Intent.ACTION_SEND)) {
mStreamsToUpload = new ArrayList<>();
mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
} else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
mStreamsToUpload.add(intent.getParcelableExtra(Intent.EXTRA_STREAM));
} else if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
mStreamsToUpload = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
}
if (mStreamsToUpload == null || mStreamsToUpload.get(0) == null) {
mStreamsToUpload = null;
saveTextsFromIntent(intent);
}
}
private void saveTextsFromIntent(Intent intent) {
if (!intent.getType().equals("text/plain")) {
return;
}
mUploadFromTmpFile = true;
mSubjectText = intent.getStringExtra(Intent.EXTRA_SUBJECT);
if (mSubjectText == null) {
mSubjectText = intent.getStringExtra(Intent.EXTRA_TITLE);
if (mSubjectText == null) {
mSubjectText = DateFormat.format("yyyyMMdd_kkmmss", Calendar.getInstance()).toString();
}
}
mExtraText = intent.getStringExtra(Intent.EXTRA_TEXT);
}
private boolean somethingToUpload() {
return (mStreamsToUpload != null && mStreamsToUpload.size() > 0 && mStreamsToUpload.get(0) != null ||
uploadTextSnippet());
mUploadFromTmpFile);
}
private boolean uploadTextSnippet() {
return getIntent().getStringExtra(Intent.EXTRA_TEXT) != null && getIntent().getExtras().keySet().size() == 1;
public void uploadFile(String tmpname, String filename) {
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
getBaseContext(),
getAccount(),
tmpname,
mFile.getRemotePath() + filename,
FileUploader.LOCAL_BEHAVIOUR_COPY,
null,
true,
UploadFileOperation.CREATED_BY_USER
);
finish();
}
@SuppressLint("NewApi")
@ -837,3 +1147,4 @@ public class ReceiveExternalFilesActivity extends FileActivity
errorDialog.show(getSupportFragmentManager(), FTAG_ERROR_FRAGMENT);
}
}

View file

@ -53,7 +53,9 @@ public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements
super(context, -1, values);
this.mContext = context;
this.mValues = values;
this.mListener = (AccountListAdapterListener) context;
if (context instanceof AccountListAdapterListener) {
this.mListener = (AccountListAdapterListener) context;
}
this.mAccountAvatarRadiusDimension = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius);
this.mTintedCheck = tintedCheck;
}
@ -75,6 +77,11 @@ public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements
viewHolder.passwordButtonItem = (ImageView) convertView.findViewById(R.id.passwordButton);
viewHolder.removeButtonItem = (ImageView) convertView.findViewById(R.id.removeButton);
if(mListener == null) {
viewHolder.passwordButtonItem.setVisibility(View.GONE);
viewHolder.removeButtonItem.setVisibility(View.GONE);
}
convertView.setTag(viewHolder);
} else {
viewHolder = (AccountViewHolderItem) convertView.getTag();
@ -94,7 +101,7 @@ public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements
setupListeners(position, viewHolder);
} // create add account action item
else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType()) {
else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType() && mListener != null) {
return setupAddAccountListItem(parent);
}
}
@ -125,21 +132,23 @@ public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements
}
private void setupListeners(final int position, AccountViewHolderItem viewHolder) {
/// bind listener to change password
viewHolder.passwordButtonItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.changePasswordOfAccount(mValues.get(position).getAccount());
}
});
if (mListener != null) {
/// bind listener to change password
viewHolder.passwordButtonItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.changePasswordOfAccount(mValues.get(position).getAccount());
}
});
/// bind listener to remove account
viewHolder.removeButtonItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.performAccountRemoval(mValues.get(position).getAccount());
}
});
/// bind listener to remove account
viewHolder.removeButtonItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.performAccountRemoval(mValues.get(position).getAccount());
}
});
}
}
private void setCurrentlyActiveState(AccountViewHolderItem viewHolder, Account account) {

View file

@ -61,7 +61,9 @@ import java.net.IDN;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A helper class for UI/display related operations.
@ -208,6 +210,20 @@ public class DisplayUtils {
}
}
/**
* converts an array of accounts into a set of account names.
*
* @param accountList the account array
* @return set of account names
*/
public static Set<String> toAccountNameSet(Account[] accountList) {
Set<String> actualAccounts = new HashSet<>(accountList.length);
for (Account account : accountList) {
actualAccounts.add(account.name);
}
return actualAccounts;
}
/**
* calculates the relative time string based on the given modificaion timestamp.
*