Better toot layouts, char counter in compose
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
@@ -9,8 +11,10 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.PhotoStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||
@@ -26,6 +30,7 @@ import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost{
|
||||
@@ -204,6 +209,26 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
currentPhotoViewer.offsetView(-dx, -dy);
|
||||
}
|
||||
});
|
||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||
private Paint paint=new Paint();
|
||||
{
|
||||
paint.setColor(0xFFD0D5DD);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(V.dp(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||
if(holder instanceof FooterStatusDisplayItem.Holder){
|
||||
float y=child.getY()+child.getHeight()-V.dp(.5f);
|
||||
c.drawLine(child.getX(), y, child.getX()+child.getWidth(), y, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected int getMainAdapterOffset(){
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Outline;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
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.ViewOutlineProvider;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.twitter.twittertext.Regex;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.text.BreakIterator;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class ComposeFragment extends ToolbarFragment{
|
||||
|
||||
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final String VALID_URL_PATTERN_STRING =
|
||||
"(" + // $1 total match
|
||||
"(" + Regex.URL_VALID_PRECEDING_CHARS + ")" + // $2 Preceding character
|
||||
"(" + // $3 URL
|
||||
"(https?://)" + // $4 Protocol (optional)
|
||||
"(" + Regex.URL_VALID_DOMAIN + ")" + // $5 Domain(s)
|
||||
"(?::(" + Regex.URL_VALID_PORT_NUMBER + "))?" + // $6 Port number (optional)
|
||||
"(/" +
|
||||
Regex.URL_VALID_PATH + "*+" +
|
||||
")?" + // $7 URL Path and anchor
|
||||
"(\\?" + Regex.URL_VALID_URL_QUERY_CHARS + "*" + // $8 Query String
|
||||
Regex.URL_VALID_URL_QUERY_ENDING_CHARS + ")?" +
|
||||
")" +
|
||||
")";
|
||||
private static final Pattern URL_PATTERN=Pattern.compile(VALID_URL_PATTERN_STRING, Pattern.CASE_INSENSITIVE);
|
||||
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
||||
|
||||
private TextView selfName, selfUsername;
|
||||
private ImageView selfAvatar;
|
||||
private Account self;
|
||||
private String instanceDomain;
|
||||
|
||||
private EditText mainEditText;
|
||||
private TextView charCounter;
|
||||
private String accountID;
|
||||
private int charCount, charLimit;
|
||||
|
||||
private MenuItem publishButton;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
setHasOptionsMenu(true);
|
||||
accountID=getArguments().getString("account");
|
||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
charLimit=session.tootCharLimit;
|
||||
if(charLimit==0)
|
||||
charLimit=500;
|
||||
self=session.self;
|
||||
instanceDomain=session.domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
||||
mainEditText=view.findViewById(R.id.toot_text);
|
||||
charCounter=view.findViewById(R.id.char_counter);
|
||||
charCounter.setText(String.valueOf(charLimit));
|
||||
|
||||
selfName=view.findViewById(R.id.name);
|
||||
selfUsername=view.findViewById(R.id.username);
|
||||
selfAvatar=view.findViewById(R.id.avatar);
|
||||
selfName.setText(self.displayName);
|
||||
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
||||
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
||||
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline){
|
||||
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), V.dp(12));
|
||||
}
|
||||
};
|
||||
selfAvatar.setOutlineProvider(roundCornersOutline);
|
||||
selfAvatar.setClipToOutline(true);
|
||||
|
||||
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);
|
||||
|
||||
mainEditText.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){
|
||||
updateCharCounter(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
publishButton=menu.add("TOOT!");
|
||||
publishButton.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
updatePublishButtonState();
|
||||
}
|
||||
|
||||
@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(ComposeFragment.this);
|
||||
E.post(new StatusCreatedEvent(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateCharCounter(CharSequence text){
|
||||
String countableText=MENTION_PATTERN.matcher(URL_PATTERN.matcher(text).replaceAll("$2xxxxxxxxxxxxxxxxxxxxxxx")).replaceAll("$1@$3");
|
||||
breakIterator.setText(countableText);
|
||||
charCount=0;
|
||||
while(breakIterator.next()!=BreakIterator.DONE){
|
||||
charCount++;
|
||||
}
|
||||
|
||||
charCounter.setText(String.valueOf(charLimit-charCount));
|
||||
updatePublishButtonState();
|
||||
}
|
||||
|
||||
private void updatePublishButtonState(){
|
||||
publishButton.setEnabled(charCount>0 && charCount<=charLimit);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -49,4 +49,9 @@ public class HomeFragment extends AppKitFragment{
|
||||
super.onHiddenChanged(hidden);
|
||||
homeTimelineFragment.onHiddenChanged(hidden);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsLightStatusBar(){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
@@ -23,6 +25,11 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
|
||||
public class HomeTimelineFragment extends StatusListFragment{
|
||||
private ImageButton fab;
|
||||
|
||||
public HomeTimelineFragment(){
|
||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
@@ -44,6 +51,13 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab=view.findViewById(R.id.fab);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
inflater.inflate(R.menu.home, menu);
|
||||
@@ -55,7 +69,7 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
args.putString("account", accountID);
|
||||
int id=item.getItemId();
|
||||
if(id==R.id.new_toot){
|
||||
Nav.go(getActivity(), CreateTootFragment.class, args);
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}else if(id==R.id.notifications){
|
||||
Nav.go(getActivity(), NotificationsFragment.class, args);
|
||||
}else if(id==R.id.my_profile){
|
||||
@@ -81,4 +95,10 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
public void onStatusCreated(StatusCreatedEvent ev){
|
||||
prependItems(Collections.singletonList(ev.status));
|
||||
}
|
||||
|
||||
private void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Build;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||
private final Status status;
|
||||
private final String accountID;
|
||||
|
||||
public FooterStatusDisplayItem(String parentID, Status status, String accountID){
|
||||
super(parentID);
|
||||
this.status=status;
|
||||
this.accountID=accountID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(){
|
||||
return Type.FOOTER;
|
||||
}
|
||||
|
||||
public static class Holder extends BindableViewHolder<FooterStatusDisplayItem>{
|
||||
private final TextView reply, boost, favorite;
|
||||
private final ImageView share;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_footer, parent);
|
||||
reply=findViewById(R.id.reply);
|
||||
boost=findViewById(R.id.boost);
|
||||
favorite=findViewById(R.id.favorite);
|
||||
share=findViewById(R.id.share);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(reply, R.color.text_secondary);
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(boost, R.color.text_secondary);
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(favorite, R.color.text_secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(FooterStatusDisplayItem item){
|
||||
bindButton(reply, item.status.repliesCount);
|
||||
bindButton(boost, item.status.reblogsCount);
|
||||
bindButton(favorite, item.status.favouritesCount);
|
||||
}
|
||||
|
||||
private void bindButton(TextView btn, int count){
|
||||
if(count>0){
|
||||
btn.setText(DecimalFormat.getIntegerInstance().format(count));
|
||||
btn.setCompoundDrawablePadding(V.dp(8));
|
||||
}else{
|
||||
btn.setText("");
|
||||
btn.setCompoundDrawablePadding(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,21 @@ package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -23,6 +26,7 @@ import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
private Account user;
|
||||
@@ -56,20 +60,34 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
}
|
||||
|
||||
public static class Holder extends BindableViewHolder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||
private final TextView name, subtitle;
|
||||
private final ImageView avatar;
|
||||
private final TextView name, username, timestamp;
|
||||
private final ImageView avatar, more;
|
||||
|
||||
private static final ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline){
|
||||
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), V.dp(12));
|
||||
}
|
||||
};
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_header, parent);
|
||||
name=findViewById(R.id.name);
|
||||
subtitle=findViewById(R.id.subtitle);
|
||||
username=findViewById(R.id.username);
|
||||
timestamp=findViewById(R.id.timestamp);
|
||||
avatar=findViewById(R.id.avatar);
|
||||
more=findViewById(R.id.more);
|
||||
avatar.setOnClickListener(this::onAvaClick);
|
||||
avatar.setOutlineProvider(roundCornersOutline);
|
||||
avatar.setClipToOutline(true);
|
||||
more.setOnClickListener(this::onMoreClick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(HeaderStatusDisplayItem item){
|
||||
name.setText(item.user.displayName);
|
||||
subtitle.setText('@'+item.user.acct);
|
||||
username.setText('@'+item.user.acct);
|
||||
timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,5 +108,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
args.putParcelable("profileAccount", Parcels.wrap(item.user));
|
||||
Nav.go(item.parentFragment.getActivity(), ProfileFragment.class, args);
|
||||
}
|
||||
|
||||
private void onMoreClick(View v){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
|
||||
@@ -27,6 +28,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_reblog_or_reply_line, parent);
|
||||
text=findViewById(R.id.text);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(text, R.color.text_secondary);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,7 @@ public abstract class StatusDisplayItem{
|
||||
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
|
||||
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
|
||||
case PHOTO -> new PhotoStatusDisplayItem.Holder(activity, parent);
|
||||
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
|
||||
default -> throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
@@ -58,6 +59,7 @@ public abstract class StatusDisplayItem{
|
||||
items.add(new PhotoStatusDisplayItem(parentID, status, attachment, fragment));
|
||||
}
|
||||
}
|
||||
items.add(new FooterStatusDisplayItem(parentID, status, accountID));
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
package org.joinmastodon.android.ui.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
|
||||
public class UiUtils{
|
||||
@@ -14,4 +23,37 @@ public class UiUtils{
|
||||
.build()
|
||||
.launchUrl(context, Uri.parse(url));
|
||||
}
|
||||
|
||||
public static String formatRelativeTimestamp(Context context, Instant instant){
|
||||
long t=instant.toEpochMilli();
|
||||
long now=System.currentTimeMillis();
|
||||
long diff=now-t;
|
||||
if(diff<60_000L){
|
||||
return context.getString(R.string.time_seconds, diff/1000L);
|
||||
}else if(diff<3600_000L){
|
||||
return context.getString(R.string.time_minutes, diff/60_000L);
|
||||
}else if(diff<3600_000L*24L){
|
||||
return context.getString(R.string.time_hours, diff/3600_000L);
|
||||
}else{
|
||||
return context.getString(R.string.time_days, diff/(3600_000L*24L));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 6.0 has a bug where start and end compound drawables don't get tinted.
|
||||
* This works around it by setting the tint colors directly to the drawables.
|
||||
* @param textView
|
||||
* @param color
|
||||
*/
|
||||
public static void fixCompoundDrawableTintOnAndroid6(TextView textView, @ColorRes int color){
|
||||
Drawable[] drawables=textView.getCompoundDrawablesRelative();
|
||||
for(int i=0;i<drawables.length;i++){
|
||||
if(drawables[i]!=null){
|
||||
Drawable tinted=drawables[i].mutate();
|
||||
tinted.setTintList(textView.getContext().getColorStateList(color));
|
||||
drawables[i]=tinted;
|
||||
}
|
||||
}
|
||||
textView.setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A LinearLayout for TextViews. First child TextView will get truncated if it doesn't fit, remaining will always wrap content.
|
||||
*/
|
||||
public class HeaderSubtitleLinearLayout extends LinearLayout{
|
||||
public HeaderSubtitleLinearLayout(Context context){
|
||||
super(context);
|
||||
}
|
||||
|
||||
public HeaderSubtitleLinearLayout(Context context, AttributeSet attrs){
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public HeaderSubtitleLinearLayout(Context context, AttributeSet attrs, int defStyleAttr){
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||
if(getChildCount()>1){
|
||||
int remainingWidth=MeasureSpec.getSize(widthMeasureSpec);
|
||||
for(int i=1;i<getChildCount();i++){
|
||||
View v=getChildAt(i);
|
||||
v.measure(MeasureSpec.getSize(widthMeasureSpec) | MeasureSpec.AT_MOST, heightMeasureSpec);
|
||||
LayoutParams lp=(LayoutParams) v.getLayoutParams();
|
||||
remainingWidth-=v.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
|
||||
}
|
||||
View first=getChildAt(0);
|
||||
if(first instanceof TextView){
|
||||
((TextView) first).setMaxWidth(remainingWidth);
|
||||
}
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user