From c685f0a9440b843a7a8950ee4b4ad78e05a87ec2 Mon Sep 17 00:00:00 2001 From: Peter Stuifzand Date: Sun, 4 Mar 2018 17:08:36 +0100 Subject: [PATCH] Cleanup overal --- .../eu/stuifzand/micropub/MainActivity.java | 179 +++++++------- .../stuifzand/micropub/OnTokenAcquired.java | 48 ++++ .../stuifzand/micropub/PostMessageTask.java | 1 - .../eu/stuifzand/micropub/PostViewModel.java | 20 ++ .../auth/VerifyAuthenticationTask.java | 13 +- .../micropub/auth/WebsigninTask.java | 20 +- .../eu/stuifzand/micropub/client/Client.java | 218 ++++++------------ .../MicropubConfigResponseCallback.java | 7 + .../micropub/client/MicropubConfigTask.java | 63 +++++ .../eu/stuifzand/micropub/client/Post.java | 13 ++ .../micropub/client/PostMediaTask.java | 67 ++++++ .../stuifzand/micropub/client/PostTask.java | 95 ++++++++ .../stuifzand/micropub/client/Response.java | 4 + .../eu/stuifzand/micropub/utils/IOUtils.java | 23 ++ app/src/main/res/layout/content_main.xml | 54 ++++- 15 files changed, 573 insertions(+), 252 deletions(-) create mode 100644 app/src/main/java/eu/stuifzand/micropub/OnTokenAcquired.java create mode 100644 app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigResponseCallback.java create mode 100644 app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigTask.java create mode 100644 app/src/main/java/eu/stuifzand/micropub/client/PostMediaTask.java create mode 100644 app/src/main/java/eu/stuifzand/micropub/client/PostTask.java create mode 100644 app/src/main/java/eu/stuifzand/micropub/utils/IOUtils.java diff --git a/app/src/main/java/eu/stuifzand/micropub/MainActivity.java b/app/src/main/java/eu/stuifzand/micropub/MainActivity.java index 57dda53..8e2e7d5 100644 --- a/app/src/main/java/eu/stuifzand/micropub/MainActivity.java +++ b/app/src/main/java/eu/stuifzand/micropub/MainActivity.java @@ -2,11 +2,7 @@ package eu.stuifzand.micropub; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.app.Application; +import android.app.Activity; import android.arch.lifecycle.ViewModelProviders; import android.content.Intent; import android.databinding.DataBindingUtil; @@ -19,26 +15,71 @@ import android.view.MenuItem; import android.view.View; import android.view.WindowManager; +import java.io.FileNotFoundException; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.io.InputStream; import eu.stuifzand.micropub.databinding.ActivityMainBinding; import eu.stuifzand.micropub.client.Client; import eu.stuifzand.micropub.client.Post; -import eu.stuifzand.micropub.client.Syndication; import okhttp3.HttpUrl; +import static eu.stuifzand.micropub.utils.IOUtils.getBytes; + public class MainActivity extends AppCompatActivity { + private static final int SELECT_FILE = 12; + private AccountManager accountManager; + + private Account selectedAccount; + private String authToken; + private Client client; + private PostViewModel postModel; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + accountManager = AccountManager.get(this); + + AccountManager am = AccountManager.get(this); + Bundle options = new Bundle(); + + postModel = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class); + client = ViewModelProviders.of(MainActivity.this).get(Client.class); + + TokenReady callback = (accountType, accountName, token) -> { + Account[] accounts = accountManager.getAccountsByType(accountType); + if (accounts.length == 0) + return; + selectedAccount = accounts[0]; + authToken = token; + + String micropubBackend = accountManager.getUserData(selectedAccount, "micropub"); + if (micropubBackend == null) return; + + client.setToken(accountType, accountName, token); + client.loadConfig(HttpUrl.parse(micropubBackend)); + + client.getResponse().observe(MainActivity.this, response -> { + Log.i("micropub", "response received " + response.isSuccess()); + if (response.isSuccess()) { + postModel.clear(); + } + }); + + client.getMediaResponse().observe(MainActivity.this, response -> { + Log.i("micropub", "media response received " + response.isSuccess()); + if (response.isSuccess()) { + postModel.setPhoto(response.getUrl()); + } + }); + }; + accountManager.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(this, callback), null); + // setContentView(R.layout.activity_main); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); - PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class); - Client client = ViewModelProviders.of(MainActivity.this).get(Client.class); - binding.setViewModel(model); + + binding.setViewModel(postModel); binding.setClient(client); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -50,9 +91,9 @@ public class MainActivity extends AppCompatActivity { if (urlOrNote != null) { HttpUrl url = HttpUrl.parse(urlOrNote); if (url != null) { - model.inReplyTo.set(urlOrNote); + postModel.inReplyTo.set(urlOrNote); } else { - model.content.set(urlOrNote); + postModel.content.set(urlOrNote); } } } @@ -65,16 +106,6 @@ public class MainActivity extends AppCompatActivity { // .setAction("Action", null).show(); // } // }); - - 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 @@ -104,76 +135,54 @@ public class MainActivity extends AppCompatActivity { public void sendPost(View view) { AccountManager am = AccountManager.get(this); Bundle options = new Bundle(); - TokenReady callback = new TokenReady() { - @Override - public void tokenReady(String accountType, String accountName, String token) { - PostViewModel model = ViewModelProviders.of(MainActivity.this).get(PostViewModel.class); - AccountManager am = AccountManager.get(MainActivity.this); - Account[] accounts = am.getAccountsByType(accountType); - - String micropubBackend = null; - for (Account account : accounts) { - if (account.name.equals(accountName)) { - micropubBackend = am.getUserData(account, "micropub"); - } - } - - if (micropubBackend != null) { - Log.i("micropub", "Sending message to " + micropubBackend); - Client client = ViewModelProviders.of(MainActivity.this).get(Client.class); - client.getResponse().observe(MainActivity.this, response -> { - Log.i("micropub", "response received " + response.isSuccess()); - if (response.isSuccess()) { - model.clear(); - } - }); - Post post = new Post(null, model.content.get(), model.category.get(), HttpUrl.parse(model.inReplyTo.get())); - List uids = new ArrayList(); - 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)); - } + TokenReady callback = (accountType, accountName, token) -> { + String micropubBackend = accountManager.getUserData(selectedAccount, "micropub"); + if (micropubBackend == null) { + Log.i("micropub", "micropub backend == null"); + return; } + Log.i("micropub", "Sending message to " + micropubBackend); + Post post = postModel.getPost(); + client.createPost(post, token, HttpUrl.parse(micropubBackend)); }; - am.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(callback), null); + accountManager.getAuthTokenByFeatures("Indieauth", "token", null, this, options, null, new OnTokenAcquired(this, callback), null); } - public class OnTokenAcquired implements AccountManagerCallback { - private final TokenReady callback; + public void galleryIntent(View view) { + Intent intent = new Intent(); + intent.setType("image/*"); + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + startActivityForResult(Intent.createChooser(intent, "Select File"), SELECT_FILE); + } - public OnTokenAcquired(TokenReady callback) { - this.callback = callback; - } - - @Override - public void run(AccountManagerFuture 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); - callback.tokenReady(bundle.getString("accountType"), bundle.getString("authAccount"), token); - } catch (OperationCanceledException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (AuthenticatorException e) { - e.printStackTrace(); + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == Activity.RESULT_OK) { + if (requestCode == SELECT_FILE) { + onSelectFromGalleryResult(data); } } } + + private void onSelectFromGalleryResult(Intent data) { + Log.i("micropub", "response received " + data.toString()); + try (InputStream input = getApplicationContext().getContentResolver().openInputStream(data.getData())) { + byte[] output = getBytes(input); + String mimeType = data.getType(); + if (mimeType == null) { + mimeType = data.resolveType(getApplicationContext().getContentResolver()); + } + + client.postMedia(output, mimeType); + } catch (FileNotFoundException e) { + Log.e("micropub", "Error while copying image", e); + } catch (IOException e) { + Log.e("micropub", "Error while copying image", e); + } + } + } diff --git a/app/src/main/java/eu/stuifzand/micropub/OnTokenAcquired.java b/app/src/main/java/eu/stuifzand/micropub/OnTokenAcquired.java new file mode 100644 index 0000000..7bc69a0 --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/OnTokenAcquired.java @@ -0,0 +1,48 @@ +package eu.stuifzand.micropub; + +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import java.io.IOException; + +public class OnTokenAcquired implements AccountManagerCallback { + private final TokenReady callback; + private Activity activity; + + public OnTokenAcquired(Activity activity, TokenReady callback) { + this.activity = activity; + this.callback = callback; + } + + @Override + public void run(AccountManagerFuture 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) { + activity.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); + callback.tokenReady(bundle.getString("accountType"), bundle.getString("authAccount"), token); + } catch (OperationCanceledException e) { + Log.e("micropub", "on token acquired", e); + } catch (IOException e) { + Log.e("micropub", "on token acquired", e); + } catch (AuthenticatorException e) { + Log.e("micropub", "on token acquired", e); + } + } +} diff --git a/app/src/main/java/eu/stuifzand/micropub/PostMessageTask.java b/app/src/main/java/eu/stuifzand/micropub/PostMessageTask.java index 0bc4dcf..fc16328 100644 --- a/app/src/main/java/eu/stuifzand/micropub/PostMessageTask.java +++ b/app/src/main/java/eu/stuifzand/micropub/PostMessageTask.java @@ -44,7 +44,6 @@ public class PostMessageTask extends AsyncTask { } RequestBody formBody = builder.build(); - micropubBackend = "http://192.168.178.21:5000/micropub"; Request request = new Request.Builder() .addHeader("Authorization", "Bearer " + accessToken) .method("POST", formBody) diff --git a/app/src/main/java/eu/stuifzand/micropub/PostViewModel.java b/app/src/main/java/eu/stuifzand/micropub/PostViewModel.java index 6cb6587..d932993 100644 --- a/app/src/main/java/eu/stuifzand/micropub/PostViewModel.java +++ b/app/src/main/java/eu/stuifzand/micropub/PostViewModel.java @@ -8,22 +8,42 @@ import android.databinding.ObservableList; import java.util.Arrays; import eu.stuifzand.micropub.client.Client; +import eu.stuifzand.micropub.client.Post; import eu.stuifzand.micropub.client.Syndication; +import okhttp3.HttpUrl; public class PostViewModel extends ViewModel { + public final ObservableField name = new ObservableField<>(); public final ObservableField content = new ObservableField<>(); public final ObservableField category = new ObservableField<>(); public final ObservableField inReplyTo = new ObservableField<>(); + public final ObservableField photo = new ObservableField<>(); public PostViewModel() { + this.name.set(""); this.content.set(""); this.category.set(""); this.inReplyTo.set(""); + this.photo.set(""); } public void clear() { + this.name.set(""); this.content.set(""); this.category.set(""); this.inReplyTo.set(""); + this.photo.set(""); + } + + public void setPhoto(String url) { + this.photo.set(url); + } + + public Post getPost() { + Post post = new Post(null, content.get(), category.get(), HttpUrl.parse(inReplyTo.get())); + if (!this.photo.get().equals("")) { + post.setPhoto(this.photo.get()); + } + return post; } } diff --git a/app/src/main/java/eu/stuifzand/micropub/auth/VerifyAuthenticationTask.java b/app/src/main/java/eu/stuifzand/micropub/auth/VerifyAuthenticationTask.java index e54d377..0c62c57 100644 --- a/app/src/main/java/eu/stuifzand/micropub/auth/VerifyAuthenticationTask.java +++ b/app/src/main/java/eu/stuifzand/micropub/auth/VerifyAuthenticationTask.java @@ -12,6 +12,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import java.io.IOException; +import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.FormBody; @@ -20,6 +21,7 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; +import okhttp3.logging.HttpLoggingInterceptor; public class VerifyAuthenticationTask extends AsyncTask { @@ -78,7 +80,16 @@ public class VerifyAuthenticationTask extends AsyncTask { public static final String ME = "eu.stuifzand.micropub.ME"; @@ -42,10 +44,11 @@ public class WebsigninTask extends AsyncTask { protected Bundle doInBackground(String... strings) { Log.i("micropub", "Starting WebsigninTask.doInBackground"); Bundle bundle = new Bundle(); - HashMap linkHeaders = new HashMap<>(); + HashMap linkHeaders = new HashMap<>(); try { - bundle.putString(ME, strings[0]); - Connection conn = Jsoup.connect(strings[0]); + String profileUrl = strings[0]; + bundle.putString(ME, profileUrl); + Connection conn = Jsoup.connect(profileUrl); Document doc = conn.get(); Connection.Response resp = conn.response(); @@ -61,9 +64,12 @@ public class WebsigninTask extends AsyncTask { String url = results.group(1); String rel = results.group(2); - Log.d("micropub", "Found url=" + url + " and rel=" + rel); - if (!rel.isEmpty() && !linkHeaders.containsKey(rel)) { - linkHeaders.put(rel, url); + HttpUrl base = HttpUrl.parse(profileUrl); + HttpUrl resolvedUrl = base.resolve(url); + + if (resolvedUrl != null && !rel.isEmpty() && !linkHeaders.containsKey(rel)) { + Log.d("micropub", "Found url=" + resolvedUrl + " and rel=" + rel); + linkHeaders.put(rel, resolvedUrl.toString()); } } } @@ -79,7 +85,7 @@ public class WebsigninTask extends AsyncTask { return bundle; } - String[] rels = new String[]{"authorization_endpoint","token_endpoint","micropub"}; + String[] rels = new String[]{"authorization_endpoint", "token_endpoint", "micropub"}; for (String rel : rels) { if (linkHeaders.containsKey(rel)) { diff --git a/app/src/main/java/eu/stuifzand/micropub/client/Client.java b/app/src/main/java/eu/stuifzand/micropub/client/Client.java index 9d97228..04cf906 100644 --- a/app/src/main/java/eu/stuifzand/micropub/client/Client.java +++ b/app/src/main/java/eu/stuifzand/micropub/client/Client.java @@ -1,111 +1,103 @@ 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.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.io.IOException; +import org.apache.http.params.HttpParams; + +import java.util.ArrayList; +import java.util.List; 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 final OkHttpClient httpClient; + private MutableLiveData response = new MutableLiveData<>(); + private MutableLiveData mediaResponse = new MutableLiveData<>(); public final ObservableArrayList syndicates = new ObservableArrayList<>(); private String accountType; private String accountName; private String token; + private HttpUrl mediaEndpoint; public Client(@NonNull Application application) { super(application); + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); + + httpClient = new OkHttpClient.Builder() + .addInterceptor(logging) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build(); } - class LoadSyndicatesTask extends AsyncTask { - - @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 OkHttpClient getClient() { + return httpClient; } - public void loadSyndicates() { - new LoadSyndicatesTask().execute(); + public LiveData getMediaResponse() { + return mediaResponse; + } + + public void loadConfig(HttpUrl micropubBackend) { + MicropubConfigResponseCallback callback = configElement -> { + // Media endpoint + JsonObject config = configElement.getAsJsonObject(); + JsonElement elem = config.get("media-endpoint"); + if (elem != null) { + setMediaEndpoint(elem.getAsString()); + } + + // Syndications. + JsonArray arr = config.getAsJsonArray("syndicate-to"); + if (arr != null) { + 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())); + } + } + }; + new MicropubConfigTask(this.getClient(), micropubBackend, token, callback, "config").execute(); + } + + public void loadSyndicates(HttpUrl micropubBackend) { + MicropubConfigResponseCallback callback = element -> { + JsonArray arr = element.getAsJsonObject().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())); + } + }; + new MicropubConfigTask(this.getClient(), micropubBackend, token, callback, "syndicate-to").execute(); } public void createPost(Post post, String accessToken, HttpUrl micropubBackend) { + List uids = new ArrayList(); + for (Syndication s : syndicates) { + if (s.checked.get()) { + uids.add(s.uid.get()); + } + } + post.setSyndicationUids(uids.toArray(new String[uids.size()])); + new PostTask(post, micropubBackend, accessToken, response).execute(); } @@ -119,81 +111,11 @@ public class Client extends AndroidViewModel { this.token = token; } - private class PostTask extends AsyncTask { + public void postMedia(byte[] output, @NonNull String mimeType) { + new PostMediaTask(mediaResponse, mediaEndpoint, token, output, mimeType).execute(); + } - private Post post; - private HttpUrl micropubBackend; - private String accessToken; - private MutableLiveData response; - - PostTask(Post post, HttpUrl micropubBackend, String accessToken, MutableLiveData 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; - } + public void setMediaEndpoint(String mediaEndpoint) { + this.mediaEndpoint = HttpUrl.parse(mediaEndpoint); } } diff --git a/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigResponseCallback.java b/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigResponseCallback.java new file mode 100644 index 0000000..ac4eb4c --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigResponseCallback.java @@ -0,0 +1,7 @@ +package eu.stuifzand.micropub.client; + +import com.google.gson.JsonElement; + +public interface MicropubConfigResponseCallback { + void handleResponse(JsonElement object); +} diff --git a/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigTask.java b/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigTask.java new file mode 100644 index 0000000..281e6c5 --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/client/MicropubConfigTask.java @@ -0,0 +1,63 @@ +package eu.stuifzand.micropub.client; + +import android.os.AsyncTask; +import android.util.Log; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +class MicropubConfigTask extends AsyncTask { + + private final OkHttpClient client; + private final HttpUrl micropubBackend; + private final String accessToken; + private final String configKey; + private MicropubConfigResponseCallback callback; + + public MicropubConfigTask(OkHttpClient client, HttpUrl micropubBackend, String accessToken, MicropubConfigResponseCallback callback, String configKey) { + this.client = client; + this.micropubBackend = micropubBackend; + this.accessToken = accessToken; + this.callback = callback; + this.configKey = configKey; + } + + @Override + protected String doInBackground(String... strings) { + HttpUrl backend = micropubBackend.newBuilder() + .setQueryParameter("q", configKey) + .build(); + + Request request = new Request.Builder() + .addHeader("Authorization", "Bearer " + accessToken) + .method("GET", null) + .url(backend) + .build(); + + Call call = client.newCall(request); + okhttp3.Response httpResponse = null; + + try { + httpResponse = call.execute(); + if (httpResponse.code() == 200) { + JsonParser parser = new JsonParser(); + JsonElement element = parser.parse(httpResponse.body().string()); + callback.handleResponse(element); + } + } catch (IOException e) { + Log.e("micropub", "Error while getting syndicate-to", e); + } finally { + if (httpResponse != null) { + httpResponse.close(); + } + } + return null; + } +} diff --git a/app/src/main/java/eu/stuifzand/micropub/client/Post.java b/app/src/main/java/eu/stuifzand/micropub/client/Post.java index b3700bd..f2d5915 100644 --- a/app/src/main/java/eu/stuifzand/micropub/client/Post.java +++ b/app/src/main/java/eu/stuifzand/micropub/client/Post.java @@ -8,6 +8,7 @@ public class Post { private HttpUrl inReplyTo; private String[] categories; private String[] syndicationUids; + private String photo; public Post(String content) { this.content = content; @@ -66,4 +67,16 @@ public class Post { public String[] getSyndicationUids() { return syndicationUids; } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public String getPhoto() { + return photo; + } + + public boolean hasPhoto() { + return this.photo != null; + } } diff --git a/app/src/main/java/eu/stuifzand/micropub/client/PostMediaTask.java b/app/src/main/java/eu/stuifzand/micropub/client/PostMediaTask.java new file mode 100644 index 0000000..ad6a30b --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/client/PostMediaTask.java @@ -0,0 +1,67 @@ +package eu.stuifzand.micropub.client; + +import android.arch.lifecycle.MutableLiveData; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.logging.HttpLoggingInterceptor; + +class PostMediaTask extends AsyncTask { + private MutableLiveData mediaResponse; + private HttpUrl mediaEndpoint; + private String accessToken; + private byte[] output; + private String mimeType; + + PostMediaTask(MutableLiveData mediaResponse, HttpUrl mediaEndpoint, String accessToken, byte[] output, String mimeType) { + this.mediaResponse = mediaResponse; + this.mediaEndpoint = mediaEndpoint; + this.accessToken = accessToken; + this.output = output; + this.mimeType = mimeType; + } + + @Override + protected Void doInBackground(String... strings) { + Log.i("micropub", "output size: " + output.length); + RequestBody formBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("file", "photo.jpg", RequestBody.create(MediaType.parse(mimeType), output)) + .build(); + + Request request = new Request.Builder() + .url(mediaEndpoint) + .addHeader("Authorization", "Bearer " + accessToken) + .method("POST", formBody) + .build(); + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); + OkHttpClient client = new OkHttpClient.Builder() + .addInterceptor(logging) + .build(); + Call call = client.newCall(request); + try { + okhttp3.Response httpResponse = call.execute(); + if (httpResponse.code() == 201) { + Log.i("micropub", "response received"); + String location = httpResponse.header("Location"); + mediaResponse.postValue(Response.successful(location)); + } else { + Log.i("micropub", httpResponse.body().string()); + } + } catch (IOException e) { + Log.e("micropub", "Error while sending image", e); + } + return null; + } +} diff --git a/app/src/main/java/eu/stuifzand/micropub/client/PostTask.java b/app/src/main/java/eu/stuifzand/micropub/client/PostTask.java new file mode 100644 index 0000000..63c1214 --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/client/PostTask.java @@ -0,0 +1,95 @@ +package eu.stuifzand.micropub.client; + +import android.arch.lifecycle.MutableLiveData; +import android.os.AsyncTask; + +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; + +class PostTask extends AsyncTask { + + private Post post; + private HttpUrl micropubBackend; + private String accessToken; + private MutableLiveData response; + + PostTask(Post post, HttpUrl micropubBackend, String accessToken, MutableLiveData 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()); + } + + if (post.hasPhoto()) { + builder.add("photo", post.getPhoto()); + } + + 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()); + } + + } catch (IOException e) { + response.postValue(Response.failed()); + } finally { + if (httpResponse != null) { + httpResponse.close(); + } + } + return null; + } +} diff --git a/app/src/main/java/eu/stuifzand/micropub/client/Response.java b/app/src/main/java/eu/stuifzand/micropub/client/Response.java index 18aa3eb..c20eef1 100644 --- a/app/src/main/java/eu/stuifzand/micropub/client/Response.java +++ b/app/src/main/java/eu/stuifzand/micropub/client/Response.java @@ -24,4 +24,8 @@ public class Response { public boolean isSuccess() { return success; } + + public String getUrl() { + return url; + } } diff --git a/app/src/main/java/eu/stuifzand/micropub/utils/IOUtils.java b/app/src/main/java/eu/stuifzand/micropub/utils/IOUtils.java new file mode 100644 index 0000000..9d94bfb --- /dev/null +++ b/app/src/main/java/eu/stuifzand/micropub/utils/IOUtils.java @@ -0,0 +1,23 @@ +package eu.stuifzand.micropub.utils; + +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class IOUtils { + public static byte[] getBytes(InputStream inputStream) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) { + byte[] buffer = new byte[16 * 4096]; + int read; + while ((read = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, read); + } + return outputStream.toByteArray(); + } catch (IOException e) { + Log.e("micropub", "Error while copying image", e); + } + return null; + } +} diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 30fede4..d2cb150 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -26,15 +26,15 @@ + android:textSize="16sp" /> @@ -144,14 +144,48 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" - android:layout_marginRight="8dp" + android:layout_marginTop="8dp" android:onClick="sendPost" android:text="@string/post" app:layout_constraintEnd_toEndOf="@id/editCategoryLayout" - app:layout_constraintTop_toBottomOf="@id/listLayout" - tools:layout_editor_absoluteY="445dp" /> + app:layout_constraintTop_toBottomOf="@+id/editPhotoLayout" /> + +