Tooting of text toots
This commit is contained in:
parent
dfbc1fd2e2
commit
b3a99e0764
|
@ -32,8 +32,10 @@ dependencies {
|
|||
implementation 'me.grishka.litex:recyclerview:1.2.1'
|
||||
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0'
|
||||
implementation 'me.grishka.litex:browser:1.4.0'
|
||||
implementation 'me.grishka.appkit:appkit:1.1'
|
||||
implementation 'me.grishka.appkit:appkit:1.1.1'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
implementation 'org.jsoup:jsoup:1.14.3'
|
||||
implementation 'com.squareup:otto:1.3.8'
|
||||
implementation 'de.psdev:async-otto:1.0.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
android:theme="@style/Theme.Mastodon"
|
||||
android:largeHeap="true">
|
||||
|
||||
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize">
|
||||
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package org.joinmastodon.android;
|
||||
|
||||
import com.squareup.otto.AsyncBus;
|
||||
|
||||
/**
|
||||
* Created by grishka on 24.08.15.
|
||||
*/
|
||||
public class E{
|
||||
private static AsyncBus bus=new AsyncBus();
|
||||
|
||||
public static void post(Object event){
|
||||
bus.post(event);
|
||||
}
|
||||
|
||||
public static void register(Object listener){
|
||||
bus.register(listener);
|
||||
}
|
||||
|
||||
public static void unregister(Object listener){
|
||||
bus.unregister(listener);
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import java.io.Reader;
|
|||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -76,6 +77,12 @@ public class MastodonAPIController{
|
|||
if(token!=null)
|
||||
builder.header("Authorization", "Bearer "+token);
|
||||
|
||||
if(req.headers!=null){
|
||||
for(Map.Entry<String, String> header:req.headers.entrySet()){
|
||||
builder.header(header.getKey(), header.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Request hreq=builder.build();
|
||||
Call call=httpClient.newCall(hreq);
|
||||
synchronized(req){
|
||||
|
|
|
@ -33,6 +33,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
|||
Call okhttpCall;
|
||||
Token token;
|
||||
boolean canceled;
|
||||
Map<String, String> headers;
|
||||
|
||||
public MastodonAPIRequest(HttpMethod method, String path, Class<T> respClass){
|
||||
this.path=path;
|
||||
|
@ -89,6 +90,12 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
|||
queryParams.put(key, value);
|
||||
}
|
||||
|
||||
protected void addHeader(String key, String value){
|
||||
if(headers==null)
|
||||
headers=new HashMap<>();
|
||||
headers.put(key, value);
|
||||
}
|
||||
|
||||
protected String getPathPrefix(){
|
||||
return "/api/v1";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package org.joinmastodon.android.api.requests.statuses;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CreateStatus extends MastodonAPIRequest<Status>{
|
||||
public CreateStatus(CreateStatus.Request req, String uuid){
|
||||
super(HttpMethod.POST, "/statuses", Status.class);
|
||||
setRequestBody(req);
|
||||
addHeader("Idempotency-Key", uuid);
|
||||
}
|
||||
|
||||
public static class Request{
|
||||
public String status;
|
||||
public List<String> mediaIds;
|
||||
public Poll poll;
|
||||
public String inReplyToId;
|
||||
public boolean sensitive;
|
||||
public String spoilerText;
|
||||
public StatusPrivacy visibility;
|
||||
public Instant scheduledAt;
|
||||
public String language;
|
||||
|
||||
public static class Poll{
|
||||
public ArrayList<String> options=new ArrayList<>();
|
||||
public int expiresIn;
|
||||
public boolean multiple;
|
||||
public boolean hideTotals;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.joinmastodon.android.events;
|
||||
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
public class StatusCreatedEvent{
|
||||
public Status status;
|
||||
|
||||
public StatusCreatedEvent(Status status){
|
||||
this.status=status;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||
|
||||
public class CreateTootFragment extends ToolbarFragment{
|
||||
|
||||
private EditText mainEditText;
|
||||
private String accountID;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
setHasOptionsMenu(true);
|
||||
accountID=getArguments().getString("account");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||
View view=inflater.inflate(R.layout.fragment_new_toot, container, false);
|
||||
mainEditText=view.findViewById(R.id.toot_text);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(){
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
|
||||
view.postDelayed(()->{
|
||||
mainEditText.requestFocus();
|
||||
imm.showSoftInput(mainEditText, 0);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
menu.add("TOOT!").setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
String text=mainEditText.getText().toString();
|
||||
CreateStatus.Request req=new CreateStatus.Request();
|
||||
req.status=text;
|
||||
String uuid=UUID.randomUUID().toString();
|
||||
new CreateStatus(req, uuid)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Status result){
|
||||
Nav.finish(CreateTootFragment.this);
|
||||
E.post(new StatusCreatedEvent(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,23 @@
|
|||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
|
||||
public class HomeTimelineFragment extends StatusListFragment{
|
||||
|
@ -17,6 +27,7 @@ public class HomeTimelineFragment extends StatusListFragment{
|
|||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
setTitle(R.string.app_name);
|
||||
setHasOptionsMenu(true);
|
||||
accountID=getArguments().getString("account");
|
||||
loadData();
|
||||
}
|
||||
|
@ -32,4 +43,34 @@ public class HomeTimelineFragment extends StatusListFragment{
|
|||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
menu.add("New toot");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), CreateTootFragment.class, args);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
E.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(){
|
||||
super.onDestroy();
|
||||
E.unregister(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatusCreated(StatusCreatedEvent ev){
|
||||
prependItems(Collections.singletonList(ev.status));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
|
|||
|
||||
public abstract class StatusListFragment extends BaseRecyclerFragment<Status>{
|
||||
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
||||
private DisplayItemsAdapter adapter;
|
||||
|
||||
public StatusListFragment(){
|
||||
super(20);
|
||||
|
@ -25,7 +26,7 @@ public abstract class StatusListFragment extends BaseRecyclerFragment<Status>{
|
|||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
return new DisplayItemsAdapter();
|
||||
return adapter=new DisplayItemsAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,6 +43,17 @@ public abstract class StatusListFragment extends BaseRecyclerFragment<Status>{
|
|||
displayItems.clear();
|
||||
}
|
||||
|
||||
protected void prependItems(List<Status> items){
|
||||
data.addAll(0, items);
|
||||
int offset=0;
|
||||
for(Status s:items){
|
||||
List<StatusDisplayItem> toAdd=StatusDisplayItem.buildItems(this, s);
|
||||
displayItems.addAll(offset, toAdd);
|
||||
offset+=toAdd.size();
|
||||
}
|
||||
adapter.notifyItemRangeInserted(0, offset);
|
||||
}
|
||||
|
||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||
|
||||
public DisplayItemsAdapter(){
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.joinmastodon.android.model;
|
|||
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
|
@ -54,8 +55,6 @@ public class Status extends BaseModel{
|
|||
public boolean bookmarked;
|
||||
public boolean pinned;
|
||||
|
||||
public transient CharSequence processedContent;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
|
@ -76,10 +75,6 @@ public class Status extends BaseModel{
|
|||
card.postprocess();
|
||||
if(reblog!=null)
|
||||
reblog.postprocess();
|
||||
|
||||
if(!TextUtils.isEmpty(content)){
|
||||
processedContent=HtmlParser.parse(content, emojis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +109,6 @@ public class Status extends BaseModel{
|
|||
", muted="+muted+
|
||||
", bookmarked="+bookmarked+
|
||||
", pinned="+pinned+
|
||||
", processedContent="+processedContent+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import org.joinmastodon.android.model.Attachment;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -49,7 +50,7 @@ public abstract class StatusDisplayItem{
|
|||
}
|
||||
items.add(new HeaderStatusDisplayItem(status, statusForContent.account, statusForContent.createdAt));
|
||||
if(!TextUtils.isEmpty(statusForContent.content))
|
||||
items.add(new TextStatusDisplayItem(status, statusForContent.processedContent, fragment));
|
||||
items.add(new TextStatusDisplayItem(status, HtmlParser.parse(statusForContent.content, statusForContent.emojis), fragment));
|
||||
for(Attachment attachment:statusForContent.mediaAttachments){
|
||||
if(attachment.type==Attachment.Type.IMAGE){
|
||||
items.add(new PhotoStatusDisplayItem(status, attachment));
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/toot_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0px"
|
||||
android:layout_weight="1"
|
||||
android:gravity="top"
|
||||
android:inputType="textMultiLine|textCapSentences"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/char_counter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue