Browse Source

Merge pull request #327 from zmatsuo/uploadUrlFile

Add upload URL file feature
Andy Scherzinger 8 năm trước cách đây
mục cha
commit
0c94de645b

+ 0 - 6
AndroidManifest.xml

@@ -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>

+ 55 - 0
res/layout/upload_file_dialog.xml

@@ -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>

+ 6 - 1
res/values/strings.xml

@@ -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>

+ 46 - 0
src/com/owncloud/android/db/PreferenceManager.java

@@ -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.
      *

+ 0 - 54
src/com/owncloud/android/ui/activity/LocalDirectorySelectorActivity.java

@@ -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();
-		}
-	}
-}

+ 3 - 16
src/com/owncloud/android/ui/activity/ManageAccountsActivity.java

@@ -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);
     }
 

+ 424 - 113
src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -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 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 onCancel(DialogInterface dialog) {
-                    mAccountSelectionShowing = false;
+                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();
-                    finish();
                 }
             });
+
             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);
     }
 }
+

+ 26 - 17
src/com/owncloud/android/ui/adapter/AccountListAdapter.java

@@ -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());
-            }
-        });
-
-        /// bind listener to remove account
-        viewHolder.removeButtonItem.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mListener.performAccountRemoval(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());
+                }
+            });
+        }
     }
 
     private void setCurrentlyActiveState(AccountViewHolderItem viewHolder, Account account) {

+ 16 - 0
src/com/owncloud/android/utils/DisplayUtils.java

@@ -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.
      *