Add syndication

This commit is contained in:
Peter Stuifzand 2018-02-25 21:53:20 +01:00
parent 30c45574a6
commit 8a9ac868f1
27 changed files with 481 additions and 287 deletions

View File

@ -23,6 +23,9 @@ android {
dataBinding { dataBinding {
enabled = true enabled = true
} }
dexOptions {
javaMaxHeapSize "2048M"
}
} }
dependencies { dependencies {
@ -38,12 +41,11 @@ dependencies {
implementation "android.arch.lifecycle:extensions:1.1.0" implementation "android.arch.lifecycle:extensions:1.1.0"
annotationProcessor "android.arch.lifecycle:compiler:1.1.0" annotationProcessor "android.arch.lifecycle:compiler:1.1.0"
// Java8 support for Lifecycles // Java8 support for Lifecycles
implementation "android.arch.lifecycle:common-java8:1.1.0" implementation "android.arch.lifecycle:common-java8:1.1.0"
compile 'org.jsoup:jsoup:1.10.1' implementation 'org.jsoup:jsoup:1.10.1'
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.2' implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test:runner:1.0.1'

View File

@ -42,12 +42,12 @@
<data android:mimeType="image/*"/> <data android:mimeType="image/*"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".eu.stuifzand.micropub.auth.WebSigninActivity" /> <activity android:name=".auth.WebSigninActivity" />
<activity android:name=".eu.stuifzand.micropub.auth.AuthenticationActivity" /> <activity android:name=".auth.AuthenticationActivity" />
<activity android:name=".eu.stuifzand.micropub.auth.AccountsActivity" /> <activity android:name=".eu.stuifzand.micropub.auth.AccountsActivity" />
<service <service
android:name=".eu.stuifzand.micropub.auth.AuthenticatorService" android:name=".auth.AuthenticatorService"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>

View File

@ -1,50 +0,0 @@
package eu.stuifzand.micropub;
import android.os.AsyncTask;
import android.view.View;
import android.widget.Toast;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class GetPageTask extends AsyncTask<String, Void, String> {
protected View view;
public GetPageTask(View view)
{
this.view = view;
}
@Override
protected String doInBackground(String... strings) {
Request request = new Request.Builder()
.url(strings[0])
.build();
OkHttpClient client = new OkHttpClient();
String msg;
Call call = client.newCall(request);
Response response = null;
try {
response = call.execute();
msg = Integer.toString(response.code());
return msg;
}
catch (IOException e) {
return e.getMessage();
}
finally {
if (response != null) {
response.close();
}
}
}
protected void onPostExecute(String message) {
String url = "https://peterstuifzand.nl/";
Toast.makeText(this.view.getContext(), message, 1).show();
}
}

View File

@ -0,0 +1,58 @@
package eu.stuifzand.micropub;
import android.content.Context;
import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.databinding.ObservableArrayList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import eu.stuifzand.micropub.client.Syndication;
import eu.stuifzand.micropub.databinding.ListItemBinding;
public class ListAdapter extends BaseAdapter {
private ObservableArrayList<Syndication> list;
private LayoutInflater inflater;
public ListAdapter(ObservableArrayList<Syndication> l) {
list = l;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null) {
inflater = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
ListItemBinding binding = DataBindingUtil.inflate(inflater, R.layout.list_item, parent, false);
binding.setInfo(list.get(position));
return binding.getRoot();
}
@BindingAdapter("app:list")
public static void bindList(ListView view, ObservableArrayList<Syndication> list) {
assert list != null;
ListAdapter adapter = new ListAdapter(list);
view.setAdapter(adapter);
}
}

View File

@ -6,26 +6,27 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture; import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException; import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException; import android.accounts.OperationCanceledException;
import android.app.Activity; import android.app.Application;
import android.arch.lifecycle.ViewModelProviders; import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent; import android.content.Intent;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Toast;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import eu.stuifzand.micropub.databinding.ActivityMainBinding; import eu.stuifzand.micropub.databinding.ActivityMainBinding;
import eu.stuifzand.micropub.eu.stuifzand.micropub.client.Client; import eu.stuifzand.micropub.client.Client;
import eu.stuifzand.micropub.eu.stuifzand.micropub.client.Post; import eu.stuifzand.micropub.client.Post;
import eu.stuifzand.micropub.client.Syndication;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@ -36,7 +37,9 @@ public class MainActivity extends AppCompatActivity {
// setContentView(R.layout.activity_main); // setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class); PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class);
Client client = ViewModelProviders.of(MainActivity.this).get(Client.class);
binding.setViewModel(model); binding.setViewModel(model);
binding.setClient(client);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
@ -63,6 +66,15 @@ public class MainActivity extends AppCompatActivity {
// } // }
// }); // });
TokenReady callback = (accountType, accountName, token) -> {
Log.i("micropub", "TokenReady called " + accountType + " " + accountName + " " + token);
client.setToken(accountType, accountName, token);
client.loadSyndicates();
};
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
am.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(callback), null);
} }
@Override @Override
@ -84,8 +96,6 @@ public class MainActivity extends AppCompatActivity {
return true; return true;
} }
if (id == R.id.action_send) { if (id == R.id.action_send) {
// EditText mEdit = (EditText)findViewById(R.id.editContent);
// new PostMessageTask(this.findViewById(R.id.editContent)).execute("http://192.168.178.21:5000/micropub", mEdit.getText().toString());
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@ -94,36 +104,13 @@ public class MainActivity extends AppCompatActivity {
public void sendPost(View view) { public void sendPost(View view) {
AccountManager am = AccountManager.get(this); AccountManager am = AccountManager.get(this);
Bundle options = new Bundle(); Bundle options = new Bundle();
am.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(true), null); TokenReady callback = new TokenReady() {
}
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
private boolean sendMessage;
public OnTokenAcquired(boolean sendMessage) {
this.sendMessage = sendMessage;
}
@Override @Override
public void run(AccountManagerFuture<Bundle> result) { public void tokenReady(String accountType, String accountName, String token) {
// Get the result of the operation from the AccountManagerFuture.
try {
Bundle bundle = result.getResult();
Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (launch != null) {
startActivityForResult(launch, 0);
return;
}
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
if (sendMessage) {
PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class); PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class);
AccountManager am = AccountManager.get(MainActivity.this); AccountManager am = AccountManager.get(MainActivity.this);
Account[] accounts = am.getAccountsByType(bundle.getString("accountType")); Account[] accounts = am.getAccountsByType(accountType);
String accountName = bundle.getString("authAccount");
String micropubBackend = null; String micropubBackend = null;
for (Account account : accounts) { for (Account account : accounts) {
@ -134,18 +121,52 @@ public class MainActivity extends AppCompatActivity {
if (micropubBackend != null) { if (micropubBackend != null) {
Log.i("micropub", "Sending message to " + micropubBackend); Log.i("micropub", "Sending message to " + micropubBackend);
Client client = new Client(getApplication()); Client client = ViewModelProviders.of(MainActivity.this).get(Client.class);
client.getResponse().observe(MainActivity.this, response -> { client.getResponse().observe(MainActivity.this, response -> {
Log.i("micropub", "response received " + response.isSuccess()); Log.i("micropub", "response received " + response.isSuccess());
if (response.isSuccess()) { if (response.isSuccess()) {
model.clear(); model.clear();
} }
}); });
client.createPost(new Post(null, model.content.get(), model.category.get(), HttpUrl.parse(model.inReplyTo.get())), token, HttpUrl.parse(micropubBackend)); Post post = new Post(null, model.content.get(), model.category.get(), HttpUrl.parse(model.inReplyTo.get()));
List<String> uids = new ArrayList<String>();
for (Syndication s : client.syndicates) {
if (s.checked.get()) {
uids.add(s.uid.get());
} }
} }
post.setSyndicationUids(uids.toArray(new String[uids.size()]));
client.createPost(post, token, HttpUrl.parse(micropubBackend));
}
}
};
am.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(callback), null);
}
public class OnTokenAcquired implements AccountManagerCallback<Bundle> {
private final TokenReady callback;
public OnTokenAcquired(TokenReady callback) {
this.callback = callback;
}
@Override
public void run(AccountManagerFuture<Bundle> result) {
// Get the result of the operation from the AccountManagerFuture.
try {
Bundle bundle = result.getResult();
Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (launch != null) {
MainActivity.this.startActivityForResult(launch, 0);
return;
}
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.d("micropub", "GetTokenForAccount Bundle is " + token); Log.d("micropub", "GetTokenForAccount Bundle is " + token);
callback.tokenReady(bundle.getString("accountType"), bundle.getString("authAccount"), token);
} catch (OperationCanceledException e) { } catch (OperationCanceledException e) {
e.printStackTrace(); e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
@ -155,23 +176,4 @@ public class MainActivity extends AppCompatActivity {
} }
} }
} }
public void startSignin(View view) {
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
am.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(false), null);
}
private void showMessage(final String msg) {
if (TextUtils.isEmpty(msg))
return;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
} }

View File

@ -57,7 +57,6 @@ public class PostMessageTask extends AsyncTask<String, Void, String> {
.addInterceptor(logging) .addInterceptor(logging)
.build(); .build();
String msg; String msg;
Call call = client.newCall(request); Call call = client.newCall(request);
Response response = null; Response response = null;

View File

@ -1,7 +1,14 @@
package eu.stuifzand.micropub; package eu.stuifzand.micropub;
import android.arch.lifecycle.ViewModel; import android.arch.lifecycle.ViewModel;
import android.databinding.ObservableArrayList;
import android.databinding.ObservableField; import android.databinding.ObservableField;
import android.databinding.ObservableList;
import java.util.Arrays;
import eu.stuifzand.micropub.client.Client;
import eu.stuifzand.micropub.client.Syndication;
public class PostViewModel extends ViewModel { public class PostViewModel extends ViewModel {
public final ObservableField<String> content = new ObservableField<>(); public final ObservableField<String> content = new ObservableField<>();

View File

@ -0,0 +1,5 @@
package eu.stuifzand.micropub;
public interface TokenReady {
void tokenReady(String type, String name, String token);
}

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity; import android.accounts.AccountAuthenticatorActivity;
@ -8,7 +8,6 @@ import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import android.webkit.WebResourceRequest; import android.webkit.WebResourceRequest;
import android.webkit.WebSettings; import android.webkit.WebSettings;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.accounts.AbstractAccountAuthenticator; import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account; import android.accounts.Account;
@ -13,8 +13,6 @@ import android.text.TextUtils;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import org.w3c.dom.Text;
import java.io.IOException; import java.io.IOException;
import okhttp3.Call; import okhttp3.Call;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;

View File

@ -1,8 +1,7 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager; import android.accounts.AccountManager;

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.auth; package eu.stuifzand.micropub.auth;
import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager; import android.accounts.AccountManager;

View File

@ -0,0 +1,199 @@
package eu.stuifzand.micropub.client;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.ObservableArrayList;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import eu.stuifzand.micropub.MainActivity;
import eu.stuifzand.micropub.TokenReady;
import eu.stuifzand.micropub.auth.VerifyAuthenticationTask;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
public class Client extends AndroidViewModel {
private MutableLiveData<Response> response = new MutableLiveData<>();
public final ObservableArrayList<Syndication> syndicates = new ObservableArrayList<>();
private String accountType;
private String accountName;
private String token;
public Client(@NonNull Application application) {
super(application);
}
class LoadSyndicatesTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... strings) {
AccountManager am = AccountManager.get(Client.this.getApplication());
Account[] accounts = am.getAccountsByType(accountType);
String micropubBackend = null;
for (Account account : accounts) {
if (account.name.equals(accountName)) {
micropubBackend = am.getUserData(account, "micropub");
break;
}
}
if (micropubBackend != null) {
HttpUrl backend = HttpUrl.parse(micropubBackend);
backend = backend.newBuilder()
.setQueryParameter("q", "syndicate-to")
.build();
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + token)
.method("GET", null)
.url(backend)
.build();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
Call call = client.newCall(request);
okhttp3.Response httpResponse = null;
try {
httpResponse = call.execute();
if (httpResponse.code() == 200) {
JsonParser parser = new JsonParser();
JsonObject element = parser.parse(httpResponse.body().string()).getAsJsonObject();
JsonArray arr = element.getAsJsonArray("syndicate-to");
syndicates.clear();
for (int i = 0; i < arr.size(); i++) {
JsonObject syn = arr.get(i).getAsJsonObject();
syndicates.add(new Syndication(syn.get("uid").getAsString(), syn.get("name").getAsString()));
}
}
} catch (IOException e) {
} finally {
if (httpResponse != null) {
httpResponse.close();
}
}
}
return null;
}
}
public void loadSyndicates() {
new LoadSyndicatesTask().execute();
}
public void createPost(Post post, String accessToken, HttpUrl micropubBackend) {
new PostTask(post, micropubBackend, accessToken, response).execute();
}
public LiveData<Response> getResponse() {
return response;
}
public void setToken(String accountType, String accountName, String token) {
this.accountType = accountType;
this.accountName = accountName;
this.token = token;
}
private class PostTask extends AsyncTask<String, Void, Void> {
private Post post;
private HttpUrl micropubBackend;
private String accessToken;
private MutableLiveData<Response> response;
PostTask(Post post, HttpUrl micropubBackend, String accessToken, MutableLiveData<Response> response) {
this.post = post;
this.micropubBackend = micropubBackend;
this.accessToken = accessToken;
this.response = response;
}
@Override
protected Void doInBackground(String... strings) {
FormBody.Builder builder = new FormBody.Builder();
builder.add("h", "entry")
.add("content", post.getContent());
for (String cat : post.getCategories()) {
builder.add("category[]", cat);
}
for (String uid : post.getSyndicationUids()) {
builder.add("mp-syndicate-to[]", uid);
}
if (post.hasInReplyTo()) {
builder.add("in-reply-to", post.getInReplyTo());
}
if (post.hasName()) {
builder.add("name", post.getName());
}
RequestBody formBody = builder.build();
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + accessToken)
.method("POST", formBody)
.url(micropubBackend)
.build();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
Call call = client.newCall(request);
okhttp3.Response httpResponse = null;
try {
httpResponse = call.execute();
if (httpResponse.code() == 201) {
String location = httpResponse.header("Location");
response.postValue(Response.successful(location));
} else {
response.postValue(Response.failed());
}
for (Syndication s : Client.this.syndicates) {
s.checked.set(false);
}
} catch (IOException e) {
response.postValue(Response.failed());
} finally {
if (httpResponse != null) {
httpResponse.close();
}
}
return null;
}
}
}

View File

@ -1,36 +1,40 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.client; package eu.stuifzand.micropub.client;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class Post { public class Post {
private String name; private String name;
private String content; private String content;
private String[] categories;
private HttpUrl inReplyTo; private HttpUrl inReplyTo;
private String[] categories;
private String[] syndicationUids;
public Post(String content) { public Post(String content) {
this.content = content; this.content = content;
this.categories = new String[]{}; this.categories = new String[]{};
this.syndicationUids = new String[]{};
} }
public Post(String name, String content) { public Post(String name, String content) {
this.name = name; this.name = name;
this.content = content; this.content = content;
this.categories = new String[]{}; this.categories = new String[]{};
this.syndicationUids = new String[]{};
} }
public Post(String name, String content, String categories) { public Post(String name, String content, String categories) {
this.name = name; this.name = name;
this.content = content; this.content = content;
this.categories = categories.split("\\s+"); this.categories = categories.split("\\s+");
this.syndicationUids = new String[]{};
} }
public Post(String name, String content, String categories, HttpUrl inReplyTo) { public Post(String name, String content, String categories, HttpUrl inReplyTo) {
this(name, content, categories); this(name, content, categories);
this.inReplyTo = inReplyTo; this.inReplyTo = inReplyTo;
this.syndicationUids = new String[]{};
} }
public String getContent() { public String getContent() {
return content; return content;
} }
@ -38,6 +42,7 @@ public class Post {
public boolean hasName() { public boolean hasName() {
return this.name != null && !name.equals(""); return this.name != null && !name.equals("");
} }
public String getName() { public String getName() {
return this.name; return this.name;
} }
@ -53,4 +58,12 @@ public class Post {
public String[] getCategories() { public String[] getCategories() {
return categories; return categories;
} }
public void setSyndicationUids(String[] syndicationUids) {
this.syndicationUids = syndicationUids;
}
public String[] getSyndicationUids() {
return syndicationUids;
}
} }

View File

@ -1,4 +1,4 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.client; package eu.stuifzand.micropub.client;
public class Response { public class Response {
private boolean success; private boolean success;

View File

@ -0,0 +1,18 @@
package eu.stuifzand.micropub.client;
import android.databinding.Observable;
import android.databinding.ObservableBoolean;
import android.databinding.ObservableField;
import android.util.Log;
public class Syndication {
public ObservableBoolean checked = new ObservableBoolean();
public ObservableField<String> uid = new ObservableField<>();
public ObservableField<String> name = new ObservableField<>();
public Syndication(String uid, String name) {
this.checked.set(false);
this.uid.set(uid);
this.name.set(name);
}
}

View File

@ -1,106 +0,0 @@
package eu.stuifzand.micropub.eu.stuifzand.micropub.client;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
public class Client extends AndroidViewModel {
private MutableLiveData<Response> response = new MutableLiveData<>();
public Client(@NonNull Application application) {
super(application);
}
public void createPost(Post post, String accessToken, HttpUrl micropubBackend) {
new PostTask(post, micropubBackend, accessToken, response).execute();
}
public LiveData<Response> getResponse() {
return response;
}
private static class PostTask extends AsyncTask<String, Void, Void> {
private Post post;
private HttpUrl micropubBackend;
private String accessToken;
private MutableLiveData<Response> response;
PostTask(Post post, HttpUrl micropubBackend, String accessToken, MutableLiveData<Response> response) {
this.post = post;
this.micropubBackend = micropubBackend;
this.accessToken = accessToken;
this.response = response;
}
@Override
protected Void doInBackground(String... strings) {
FormBody.Builder builder = new FormBody.Builder();
builder.add("h", "entry")
.add("content", post.getContent());
for (String cat : post.getCategories()) {
builder.add("category[]", cat);
}
if (post.hasInReplyTo()) {
builder.add("in-reply-to", post.getInReplyTo());
}
if (post.hasName()) {
builder.add("name", post.getName());
}
RequestBody formBody = builder.build();
micropubBackend = HttpUrl.parse("http://192.168.178.21:5000/micropub");
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + accessToken)
.method("POST", formBody)
.url(micropubBackend)
.build();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
Call call = client.newCall(request);
okhttp3.Response httpResponse = null;
try {
httpResponse = call.execute();
if (httpResponse.code() == 201) {
String location = httpResponse.header("Location");
response.postValue(Response.successful(location));
} else {
response.postValue(Response.failed());
}
} catch (IOException e) {
response.postValue(Response.failed());
} finally {
if (httpResponse != null) {
httpResponse.close();
}
}
return null;
}
}
}

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="eu.stuifzand.micropub.eu.stuifzand.micropub.auth.AuthenticatedActivity"> tools:context="eu.stuifzand.micropub.auth.AuthenticatedActivity">
<android.support.design.widget.AppBarLayout <android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -4,7 +4,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="eu.stuifzand.micropub.eu.stuifzand.micropub.auth.WebSigninActivity"> tools:context="eu.stuifzand.micropub.auth.WebSigninActivity">
<WebView <WebView
android:id="@+id/webview" android:id="@+id/webview"

View File

@ -7,6 +7,10 @@
<variable <variable
name="viewModel" name="viewModel"
type="eu.stuifzand.micropub.PostViewModel" /> type="eu.stuifzand.micropub.PostViewModel" />
<variable
name="client"
type="eu.stuifzand.micropub.client.Client" />
</data> </data>
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout
@ -32,7 +36,9 @@
layout="@layout/content_main" layout="@layout/content_main"
android:layout_height="607dp" android:layout_height="607dp"
app:viewModel="@{viewModel}" app:viewModel="@{viewModel}"
tools:layout_editor_absoluteY="-4dp" /> app:client="@{client}"
tools:layout_editor_absoluteY="-4dp"
android:layout_width="match_parent" />
<android.support.design.widget.FloatingActionButton <android.support.design.widget.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"

View File

@ -5,7 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="eu.stuifzand.micropub.eu.stuifzand.micropub.auth.AuthenticatedActivity" tools:context="eu.stuifzand.micropub.auth.AuthenticatedActivity"
tools:showIn="@layout/activity_authenticated"> tools:showIn="@layout/activity_authenticated">
<TextView <TextView

View File

@ -8,17 +8,20 @@
<variable <variable
name="viewModel" name="viewModel"
type="eu.stuifzand.micropub.PostViewModel" /> type="eu.stuifzand.micropub.PostViewModel" />
<variable
name="client"
type="eu.stuifzand.micropub.client.Client" />
</data> </data>
<ScrollView <ScrollView
tools:context="eu.stuifzand.micropub.MainActivity"
tools:showIn="@layout/activity_main"
android:layout_marginTop="56dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="56dp"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
> tools:context="eu.stuifzand.micropub.MainActivity"
tools:showIn="@layout/activity_main">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -32,6 +35,21 @@
android:scrollbars="vertical" android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:id="@+id/labelCounter"
android:layout_width="83dp"
android:layout_height="23dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:gravity="end"
android:text="@{String.valueOf(viewModel.content.length)}"
android:textAlignment="textEnd"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="@id/editInReplyTo"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<EditText <EditText
android:id="@+id/editInReplyTo" android:id="@+id/editInReplyTo"
android:layout_width="0dp" android:layout_width="0dp"
@ -50,22 +68,27 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/labelCounter" /> app:layout_constraintTop_toBottomOf="@id/labelCounter" />
<TextView <EditText
android:id="@+id/labelCounter" android:id="@+id/content"
android:layout_width="83dp" android:layout_width="0dp"
android:layout_height="23dp" android:layout_height="230dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginTop="16dp" android:layout_marginStart="8dp"
android:text="@{String.valueOf(viewModel.content.length)}" android:ems="10"
android:textAlignment="textEnd" android:gravity="top"
app:layout_constraintBottom_toTopOf="parent" android:hint="Content"
app:layout_constraintEnd_toEndOf="@+id/editInReplyTo" android:imeOptions="actionNext"
app:layout_constraintTop_toTopOf="parent" android:inputType="textMultiLine"
app:layout_constraintVertical_bias="0.0" android:nextFocusForward="@id/editCategory"
android:layout_marginRight="8dp" android:padding="3dp"
android:gravity="end" /> android:singleLine="false"
android:text="@={viewModel.content}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/editInReplyTo"
tools:layout_editor_absoluteY="101dp" />
<EditText <EditText
android:id="@+id/editCategory" android:id="@+id/editCategory"
@ -83,7 +106,26 @@
android:text="@={viewModel.category}" android:text="@={viewModel.category}"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/content" /> app:layout_constraintTop_toBottomOf="@id/content" />
<LinearLayout
android:id="@+id/listLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/editCategory">
<ListView
android:id="@+id/listSyndication"
android:layout_width="match_parent"
android:layout_height="100dp"
app:list="@{client.syndicates}" />
</LinearLayout>
<Button <Button
android:id="@+id/btnPost" android:id="@+id/btnPost"
@ -91,34 +133,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:onClick="sendPost" android:onClick="sendPost"
android:text="Post" android:text="Post"
app:layout_constraintEnd_toEndOf="@+id/editCategory" app:layout_constraintEnd_toEndOf="@id/editCategory"
app:layout_constraintTop_toBottomOf="@+id/editCategory" /> app:layout_constraintTop_toBottomOf="@id/listLayout"
tools:layout_editor_absoluteY="445dp" />
<EditText
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="230dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:ems="10"
android:gravity="top"
android:hint="Content"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:nextFocusForward="@id/editCategory"
android:padding="3dp"
android:singleLine="false"
android:text="@={viewModel.content}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editInReplyTo" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</layout> </layout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="info"
type="eu.stuifzand.micropub.client.Syndication" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/enabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={info.checked}"
android:text="@{info.name}"
/>
</LinearLayout>
</layout>