Add support for OPML files import/export - Fix #239
This commit is contained in:
parent
0fa4e1172e
commit
bf2049ff50
7 changed files with 490 additions and 25 deletions
|
@ -58,6 +58,7 @@ repositories {
|
|||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url 'http://guardian.github.com/maven/repo-releases' } //needed for com.gu:option:1.3 in Android-DirectoryChooser
|
||||
maven { url "http://dl.bintray.com/lukaville/maven" } //Needed for com.nbsp:library:1.02 in Material File Picker
|
||||
|
||||
}
|
||||
|
||||
|
@ -106,4 +107,5 @@ dependencies {
|
|||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
compile project(':MaterialShowcaseView:library')
|
||||
compile 'com.nbsp:library:1.02'
|
||||
}
|
||||
|
|
|
@ -76,6 +76,15 @@
|
|||
<data android:pathPattern=".*\\atom.xml" />
|
||||
<data android:pathPattern=".*\\rss.xml" />
|
||||
<data android:pathPattern=".*\\.rss" />
|
||||
|
||||
<data android:scheme="http" android:host="*"
|
||||
android:pathPattern=".*\\.opml" />
|
||||
<data android:scheme="https" android:host="*"
|
||||
android:pathPattern=".*\\.opml" />
|
||||
<data android:scheme="content" android:host="*"
|
||||
android:pathPattern=".*\\.opml" />
|
||||
<data android:scheme="file" android:host="*"
|
||||
android:pathPattern=".*\\.opml" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
|
|
|
@ -2,31 +2,57 @@ package de.luhmer.owncloudnewsreader;
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nbsp.materialfilepicker.ui.FilePickerActivity;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
import butterknife.OnClick;
|
||||
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
|
||||
import de.luhmer.owncloudnewsreader.database.model.Folder;
|
||||
import de.luhmer.owncloudnewsreader.helper.AsyncTaskHelper;
|
||||
import de.luhmer.owncloudnewsreader.helper.FileUtils;
|
||||
import de.luhmer.owncloudnewsreader.helper.OpmlXmlParser;
|
||||
import de.luhmer.owncloudnewsreader.helper.ThemeChooser;
|
||||
import de.luhmer.owncloudnewsreader.helper.URLConnectionReader;
|
||||
import de.luhmer.owncloudnewsreader.model.Tuple;
|
||||
import de.luhmer.owncloudnewsreader.reader.HttpJsonRequest;
|
||||
import de.luhmer.owncloudnewsreader.reader.owncloud.API;
|
||||
import de.luhmer.owncloudnewsreader.reader.owncloud.apiv2.APIv2;
|
||||
|
@ -75,21 +101,6 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, folderNames);
|
||||
mFolderView.setAdapter(spinnerArrayAdapter);
|
||||
|
||||
mAddFeedButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
//Hide keyboard
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(mFeedUrlView.getWindowToken(), 0);
|
||||
|
||||
|
||||
attemptAddNewFeed();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
|
||||
|
@ -101,6 +112,10 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
url = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
}
|
||||
|
||||
if(url.endsWith(".opml")) {
|
||||
AsyncTaskHelper.StartAsyncTask(new ImportOpmlSubscriptionsTask(url, NewFeedActivity.this));
|
||||
}
|
||||
|
||||
//String scheme = intent.getScheme();
|
||||
//ContentResolver resolver = getContentResolver();
|
||||
|
||||
|
@ -110,6 +125,179 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_addFeed)
|
||||
public void btnAddFeedClick() {
|
||||
//Hide keyboard
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(mFeedUrlView.getWindowToken(), 0);
|
||||
|
||||
|
||||
attemptAddNewFeed();
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_import_opml)
|
||||
public void importOpml() {
|
||||
Intent intentFilePicker = new Intent(this, FilePickerActivity.class);
|
||||
startActivityForResult(intentFilePicker, 1);
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_export_opml)
|
||||
public void exportOpml() {
|
||||
String xml = OpmlXmlParser.GenerateOPML(this);
|
||||
|
||||
String path = FileUtils.getPath(this) + "/../subscriptions.opml";
|
||||
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(new File(path));
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fos);
|
||||
outputStreamWriter.write(xml);
|
||||
outputStreamWriter.close();
|
||||
fos.close();
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage("Successfully exported to: " + path)
|
||||
.setTitle("OPML Export")
|
||||
.setNeutralButton("Ok", null)
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.e("Exception", "File write failed: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == 1 && resultCode == RESULT_OK) {
|
||||
String filePath = data.getStringExtra(FilePickerActivity.RESULT_FILE_PATH);
|
||||
|
||||
AsyncTaskHelper.StartAsyncTask(new ImportOpmlSubscriptionsTask(filePath, NewFeedActivity.this));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImportOpmlSubscriptionsTask extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
private final String mUrlToFile;
|
||||
private HashMap<String, String> extractedUrls;
|
||||
private ProgressDialog pd;
|
||||
private Context mContext;
|
||||
|
||||
ImportOpmlSubscriptionsTask(String urlToFile, Context context) {
|
||||
this.mUrlToFile = urlToFile;
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
pd = new ProgressDialog(mContext);
|
||||
pd.setTitle("Parsing OMPL...");
|
||||
pd.setMessage("Please wait.");
|
||||
pd.setCancelable(false);
|
||||
pd.setIndeterminate(true);
|
||||
pd.show();
|
||||
|
||||
super.onPreExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
String opmlContent;
|
||||
try {
|
||||
if(mUrlToFile.startsWith("http")) {//http[s]
|
||||
opmlContent = URLConnectionReader.getText(mUrlToFile.toString());
|
||||
} else {
|
||||
opmlContent = getStringFromFile(mUrlToFile);
|
||||
}
|
||||
|
||||
InputStream is = new ByteArrayInputStream(opmlContent.getBytes());
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||
parser.setInput(is, null);
|
||||
parser.nextTag();
|
||||
extractedUrls = OpmlXmlParser.ReadFeed(parser);
|
||||
|
||||
publishProgress();
|
||||
|
||||
API api = new APIv2(HttpJsonRequest.getInstance().getRootUrl());
|
||||
|
||||
HashMap<String, Long> existingFolders = new HashMap<>();
|
||||
InputStream isFolder = HttpJsonRequest.getInstance().PerformJsonRequest(api.getFolderUrl());
|
||||
String folderJSON = convertStreamToString(isFolder);
|
||||
JSONArray jArrFolder = new JSONObject(folderJSON).getJSONArray("folders");
|
||||
for(int i = 0; i < jArrFolder.length(); i++) {
|
||||
JSONObject folder = ((JSONObject) jArrFolder.get(i));
|
||||
long folderId = folder.getLong("id");
|
||||
String folderName = folder.getString("name");
|
||||
|
||||
existingFolders.put(folderName, folderId);
|
||||
}
|
||||
|
||||
|
||||
for(String feedUrl : extractedUrls.keySet()) {
|
||||
long folderId = 0; //id of the parent folder, 0 for root
|
||||
String folderName = extractedUrls.get(feedUrl);
|
||||
if(folderName != null) { //Get Folder ID (create folder if not exists)
|
||||
if(existingFolders.containsKey(folderName)) { //Check if folder exists
|
||||
folderId = existingFolders.get(folderName);
|
||||
} else { //If not, create a new one on the server
|
||||
Tuple<Integer, String> status = HttpJsonRequest.getInstance().performCreateFolderRequest(api.getFolderUrl(), folderName);
|
||||
if (status.key == 200 || status.key == 409) { //200 = Ok, 409 = If the folder exists already
|
||||
JSONObject jObj = new JSONObject(status.value).getJSONArray("folders").getJSONObject(0);
|
||||
folderId = jObj.getLong("id");
|
||||
existingFolders.put(folderName, folderId); //Add folder to list of existing folder in order to prevent that the method tries to create it multiple times
|
||||
} else {
|
||||
throw new Exception("Failed to create folder on server!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int status = HttpJsonRequest.getInstance().performCreateFeedRequest(api.getFeedUrl(), feedUrl, folderId);
|
||||
if(status == 200 || status == 409) {
|
||||
|
||||
} else {
|
||||
throw new Exception("Failed to create feed on server!");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Void... values) {
|
||||
String text = "Extracted the following feeds:\n";
|
||||
for (String url : extractedUrls.keySet()) {
|
||||
text += "\n" + url;
|
||||
}
|
||||
pd.setMessage(text);
|
||||
|
||||
super.onProgressUpdate(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (pd != null) {
|
||||
pd.dismiss();
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
Toast.makeText(mContext, "Failed to parse OPML file", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(mContext, "Successfully imported OPML!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to sign in or register the account specified by the login form.
|
||||
|
@ -195,8 +383,6 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents an asynchronous login/registration task used to authenticate
|
||||
* the user.
|
||||
|
@ -215,20 +401,14 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
API api = new APIv2(HttpJsonRequest.getInstance().getRootUrl());
|
||||
|
||||
try {
|
||||
int status = HttpJsonRequest.getInstance().performCreateFeedRequest(api.getFeedUrl(),
|
||||
mUrlToFeed, mFolderId);
|
||||
|
||||
int status = HttpJsonRequest.getInstance().performCreateFeedRequest(api.getFeedUrl(), mUrlToFeed, mFolderId);
|
||||
if(status == 200) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.d("NewFeedActivity", "Status: " + status);
|
||||
} catch(Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -271,6 +451,29 @@ public class NewFeedActivity extends AppCompatActivity {
|
|||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@NonNull public static String convertStreamToString(InputStream is) throws Exception {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getStringFromFile (String filePath) throws Exception {
|
||||
File fl = new File(filePath);
|
||||
FileInputStream fin = new FileInputStream(fl);
|
||||
String ret = convertStreamToString(fin);
|
||||
//Make sure you close all streams.
|
||||
fin.close();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
package de.luhmer.owncloudnewsreader.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Xml;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
|
||||
import de.luhmer.owncloudnewsreader.database.model.Feed;
|
||||
import de.luhmer.owncloudnewsreader.database.model.Folder;
|
||||
|
||||
/**
|
||||
* Created by David on 14.01.2016.
|
||||
*/
|
||||
public class OpmlXmlParser {
|
||||
|
||||
//Create XML
|
||||
public static String GenerateOPML(Context context) {
|
||||
XmlSerializer serializer = Xml.newSerializer();
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
serializer.setOutput(writer);
|
||||
serializer.startDocument("UTF-8", true);
|
||||
serializer.startTag("", "opml");
|
||||
serializer.attribute("", "version", "2.0");
|
||||
|
||||
serializer.startTag("", "head");
|
||||
serializer.startTag("", "title");
|
||||
serializer.text("Subscriptions");
|
||||
serializer.endTag("", "title");
|
||||
serializer.endTag("", "head");
|
||||
|
||||
serializer.startTag("", "body");
|
||||
|
||||
|
||||
DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(context);
|
||||
List<Folder> folderList = dbConn.getListOfFolders();
|
||||
List<Feed> feedList = dbConn.getListOfFeeds();
|
||||
|
||||
//Process all feeds in folders
|
||||
for(Folder folder : folderList) {
|
||||
serializer.startTag("", "outline");
|
||||
serializer.attribute("", "title", folder.getLabel());
|
||||
serializer.attribute("", "text", folder.getLabel());
|
||||
|
||||
for(Feed feed : folder.getFeedList()) {
|
||||
feedList.remove(feed);//Remove feed from feedlist (So only feeds without folders will remain)
|
||||
GenerateXMLForFeed(serializer, feed);
|
||||
}
|
||||
serializer.endTag("", "outline");
|
||||
}
|
||||
|
||||
//All feeds without folder
|
||||
for(Feed feed : feedList) {
|
||||
GenerateXMLForFeed(serializer, feed);
|
||||
}
|
||||
|
||||
serializer.endTag("", "body");
|
||||
serializer.endTag("", "opml");
|
||||
serializer.endDocument();
|
||||
return writer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateXMLForFeed(XmlSerializer serializer, Feed feed) throws IOException {
|
||||
serializer.startTag("", "outline");
|
||||
serializer.attribute("", "title", feed.getFeedTitle());
|
||||
serializer.attribute("", "text", feed.getFeedTitle());
|
||||
serializer.attribute("", "type", "rss");
|
||||
serializer.attribute("", "xmlUrl", feed.getLink());
|
||||
//serializer.attribute("", "htmlUrl", key);
|
||||
serializer.endTag("", "outline");
|
||||
}
|
||||
|
||||
|
||||
//Parse XML
|
||||
|
||||
// We don't use namespaces
|
||||
private static final String ns = null;
|
||||
public static HashMap<String, String> ReadFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
HashMap<String, String> extractedUrls = new HashMap<>();
|
||||
|
||||
parser.require(XmlPullParser.START_TAG, ns, "opml");
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
String name = parser.getName();
|
||||
// Starts by looking for the entry tag
|
||||
if (name.equals("body")) {
|
||||
extractedUrls.putAll(readFolder(parser));
|
||||
} else {
|
||||
Skip(parser);
|
||||
}
|
||||
}
|
||||
return extractedUrls;
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
public Entry(String folderName, String feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
this.folderName = folderName;
|
||||
}
|
||||
|
||||
public String folderName;
|
||||
public String feedUrl;
|
||||
}
|
||||
|
||||
|
||||
private static HashMap<String, String> readFolder(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
HashMap<String, String> extractedUrls = new HashMap<>();
|
||||
|
||||
String name;
|
||||
String folderName = null;
|
||||
|
||||
parser.require(XmlPullParser.START_TAG, ns, "body");
|
||||
|
||||
while(parser.next() >= 0) { //Loop over all
|
||||
if(parser.getEventType() == XmlPullParser.END_TAG) { //If read endtag and folder Name is != null
|
||||
if(folderName == null) { //If end tag is read and we aren't exiting a folder --> exit!
|
||||
break;
|
||||
}
|
||||
folderName = null;
|
||||
}
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
name = parser.getName();
|
||||
if (name.equals("outline")) {
|
||||
Entry entry = ReadOutline(parser);
|
||||
if (entry.folderName != null) {
|
||||
folderName = entry.folderName;
|
||||
} else {
|
||||
entry.folderName = folderName;
|
||||
extractedUrls.put(entry.feedUrl, entry.folderName);
|
||||
parser.next(); //Read closing tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extractedUrls;
|
||||
}
|
||||
|
||||
// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
|
||||
// to their respective "read" methods for processing. Otherwise, skips the tag.
|
||||
private static Entry ReadOutline(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
//parser.require(XmlPullParser.START_TAG, ns, "outline");
|
||||
|
||||
String link = parser.getAttributeValue(null, "xmlUrl");
|
||||
String title = null;
|
||||
if(link == null) { //Parse folder title if no feedUrl is available
|
||||
title = parser.getAttributeValue(null, "title");
|
||||
}
|
||||
|
||||
return new Entry(title, link);
|
||||
}
|
||||
|
||||
private static void Skip(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int depth = 1;
|
||||
while (depth != 0) {
|
||||
switch (parser.next()) {
|
||||
case XmlPullParser.END_TAG:
|
||||
depth--;
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package de.luhmer.owncloudnewsreader.helper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* Created by David on 13.01.2016.
|
||||
*/
|
||||
public class URLConnectionReader {
|
||||
public static String getText(String url) throws Exception {
|
||||
URL website = new URL(url);
|
||||
URLConnection connection = website.openConnection();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
connection.getInputStream()));
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
String inputLine;
|
||||
|
||||
while ((inputLine = in.readLine()) != null)
|
||||
response.append(inputLine);
|
||||
|
||||
in.close();
|
||||
|
||||
return response.toString();
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ import javax.net.ssl.SSLContext;
|
|||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import de.luhmer.owncloudnewsreader.SettingsActivity;
|
||||
import de.luhmer.owncloudnewsreader.model.Tuple;
|
||||
import de.luhmer.owncloudnewsreader.ssl.MemorizingTrustManager;
|
||||
import de.luhmer.owncloudnewsreader.ssl.TLSSocketFactory;
|
||||
|
||||
|
@ -231,4 +232,15 @@ public class HttpJsonRequest {
|
|||
|
||||
return response.code();
|
||||
}
|
||||
|
||||
public Tuple<Integer, String> performCreateFolderRequest(HttpUrl url, String folderName) throws Exception {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(RequestBody.create(JSON, new JSONObject().put("name", folderName).toString()))
|
||||
.build();
|
||||
|
||||
Response response = client.newCall(request).execute();
|
||||
String body = response.body().string();
|
||||
return new Tuple<>(response.code(), body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,32 @@
|
|||
android:text="@string/action_add_feed"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
style="?android:textAppearanceSmall"
|
||||
android:id="@+id/btn_import_opml"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Import OPML"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="?android:textAppearanceSmall"
|
||||
android:id="@+id/btn_export_opml"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Export OPML"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
Loading…
Reference in a new issue