Merge branch 'main'
# Conflicts: # mastodon/build.gradle # mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java # mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/discover/LocalTimelineFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/SignupFragment.java # mastodon/src/main/java/org/joinmastodon/android/model/Status.java # mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java # mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/PhotoStatusDisplayItem.java # mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java # mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java # mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java # mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java # mastodon/src/main/res/layout/display_item_footer.xml # mastodon/src/main/res/layout/fragment_profile.xml # mastodon/src/main/res/layout/recycler_fragment_with_fab.xml # mastodon/src/main/res/values/strings.xml
This commit is contained in:
commit
ec0f6edc41
|
@ -16,4 +16,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=true
|
android.enableJetifier=false
|
|
@ -41,6 +41,11 @@ public class GlobalUserPreferences{
|
||||||
public static boolean disableAltTextReminder;
|
public static boolean disableAltTextReminder;
|
||||||
public static boolean showAltIndicator;
|
public static boolean showAltIndicator;
|
||||||
public static boolean showNoAltIndicator;
|
public static boolean showNoAltIndicator;
|
||||||
|
public static boolean enablePreReleases;
|
||||||
|
public static boolean prefixRepliesWithRe;
|
||||||
|
public static boolean bottomEncoding;
|
||||||
|
public static boolean collapseLongPosts;
|
||||||
|
public static boolean spectatorMode;
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
@ -88,9 +93,13 @@ public class GlobalUserPreferences{
|
||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
||||||
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
||||||
showAltIndicator =prefs.getBoolean("showAltIndicator", true);
|
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
||||||
showNoAltIndicator =prefs.getBoolean("showNoAltIndicator", true);
|
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
||||||
enablePreReleases =prefs.getBoolean("enablePreReleases", false);
|
enablePreReleases=prefs.getBoolean("enablePreReleases", false);
|
||||||
|
prefixRepliesWithRe=prefs.getBoolean("prefixRepliesWithRe", false);
|
||||||
|
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
||||||
|
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
||||||
|
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
|
@ -136,7 +145,11 @@ public class GlobalUserPreferences{
|
||||||
.putBoolean("showAltIndicator", showAltIndicator)
|
.putBoolean("showAltIndicator", showAltIndicator)
|
||||||
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
||||||
.putBoolean("enablePreReleases", enablePreReleases)
|
.putBoolean("enablePreReleases", enablePreReleases)
|
||||||
|
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
||||||
|
.putBoolean("collapseLongPosts", collapseLongPosts)
|
||||||
|
.putBoolean("spectatorMode", spectatorMode)
|
||||||
.putString("publishButtonText", publishButtonText)
|
.putString("publishButtonText", publishButtonText)
|
||||||
|
.putBoolean("bottomEncoding", bottomEncoding)
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putString("color", color.name())
|
.putString("color", color.name())
|
||||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||||
|
|
|
@ -155,6 +155,7 @@ public class MainActivity extends FragmentStackActivity{
|
||||||
);
|
);
|
||||||
Bundle currentArgs = currentFragment.getArguments();
|
Bundle currentArgs = currentFragment.getArguments();
|
||||||
if (this.fragmentContainers.size() == 1
|
if (this.fragmentContainers.size() == 1
|
||||||
|
&& currentArgs != null
|
||||||
&& currentArgs.getBoolean("_can_go_back", false)
|
&& currentArgs.getBoolean("_can_go_back", false)
|
||||||
&& currentArgs.containsKey("account")) {
|
&& currentArgs.containsKey("account")) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
|
|
|
@ -4,6 +4,10 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
|
||||||
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
||||||
|
public SetAccountFollowed(String id, boolean followed, boolean showReblogs){
|
||||||
|
this(id, followed, showReblogs, false);
|
||||||
|
}
|
||||||
|
|
||||||
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
||||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
||||||
if(followed)
|
if(followed)
|
||||||
|
|
|
@ -15,12 +15,15 @@ import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
|
@ -61,6 +64,7 @@ public class AccountTimelineFragment extends StatusListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(getActivity()==null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.ACCOUNT)).collect(Collectors.toList());
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -59,6 +59,8 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
|
@ -79,9 +81,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
||||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||||
protected Rect tmpRect=new Rect();
|
protected Rect tmpRect=new Rect();
|
||||||
|
protected ImageButton fab;
|
||||||
|
|
||||||
public BaseStatusListFragment(){
|
public BaseStatusListFragment(){
|
||||||
super(20);
|
super(20);
|
||||||
|
if (withComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,6 +103,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
return adapter=new DisplayItemsAdapter();
|
return adapter=new DisplayItemsAdapter();
|
||||||
|
@ -353,6 +363,13 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
list.setItemAnimator(new BetterItemAnimator());
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
|
||||||
|
if (withComposeButton()) {
|
||||||
|
fab = view.findViewById(R.id.fab);
|
||||||
|
fab.setVisibility(View.VISIBLE);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(this::onFabLongClick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -518,7 +535,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
status.spoilerRevealed=!status.spoilerRevealed;
|
status.spoilerRevealed=!status.spoilerRevealed;
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(status.spoilerText)){
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class);
|
TextStatusDisplayItem.Holder text = findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class);
|
||||||
if(text!=null){
|
if(text!=null){
|
||||||
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition());
|
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition());
|
||||||
}
|
}
|
||||||
|
@ -527,6 +544,23 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
updateImagesSpoilerState(status, holder.getItemID());
|
updateImagesSpoilerState(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onEnableExpandable(TextStatusDisplayItem.Holder holder, boolean expandable) {
|
||||||
|
if (holder.getItem().status.textExpandable != expandable && list != null) {
|
||||||
|
holder.getItem().status.textExpandable = expandable;
|
||||||
|
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
||||||
|
if (header != null) header.rebind();
|
||||||
|
holder.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onToggleExpanded(Status status, String itemID) {
|
||||||
|
status.textExpanded = !status.textExpanded;
|
||||||
|
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||||
|
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
||||||
|
if (text != null) text.rebind();
|
||||||
|
if (header != null) header.rebind();
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateImagesSpoilerState(Status status, String itemID){
|
protected void updateImagesSpoilerState(Status status, String itemID){
|
||||||
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
||||||
for(ImageStatusDisplayItem.Holder photo:(List<ImageStatusDisplayItem.Holder>)findAllHoldersOfType(itemID, ImageStatusDisplayItem.Holder.class)){
|
for(ImageStatusDisplayItem.Holder photo:(List<ImageStatusDisplayItem.Holder>)findAllHoldersOfType(itemID, ImageStatusDisplayItem.Holder.class)){
|
||||||
|
@ -544,14 +578,13 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
|
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
||||||
|
|
||||||
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warningItem){
|
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
||||||
int i = warningItem.getAbsoluteAdapterPosition();
|
int startPos = warning.getAbsoluteAdapterPosition();
|
||||||
for(StatusDisplayItem item:warningItem.filteredItems){
|
displayItems.remove(startPos);
|
||||||
i++;
|
displayItems.addAll(startPos, warning.filteredItems);
|
||||||
displayItems.add(i, item);
|
adapter.notifyItemRangeInserted(startPos, warning.filteredItems.size() - 1);
|
||||||
}
|
if (startPos == 0) scrollToTop();
|
||||||
displayItems.remove(warningItem.getAbsoluteAdapterPosition());
|
warning.getItem().status.filterRevealed = true;
|
||||||
adapter.notifyItemChanged(warningItem.getAbsoluteAdapterPosition());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAccountID(){
|
public String getAccountID(){
|
||||||
|
@ -669,6 +702,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
currentPhotoViewer.onPause();
|
currentPhotoViewer.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onFabClick(View v){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean onFabLongClick(View v) {
|
||||||
|
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
||||||
|
}
|
||||||
|
|
||||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public DisplayItemsAdapter(){
|
public DisplayItemsAdapter(){
|
||||||
|
|
|
@ -5,6 +5,7 @@ import static android.os.ext.SdkExtensions.getExtensionVersion;
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
||||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
|
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
|
||||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
||||||
|
import static org.joinmastodon.android.ui.utils.UiUtils.isPhotoPickerAvailable;
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ import android.text.TextWatcher;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -67,6 +69,7 @@ import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||||
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
|
@ -116,6 +119,7 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||||
import org.joinmastodon.android.utils.MastodonLanguage;
|
import org.joinmastodon.android.utils.MastodonLanguage;
|
||||||
|
import org.joinmastodon.android.utils.StatusTextEncoder;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
@ -156,11 +160,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*");
|
private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*");
|
||||||
private static final String TAG="ComposeFragment";
|
private static final String TAG="ComposeFragment";
|
||||||
|
|
||||||
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
public static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
||||||
private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
public static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
||||||
private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
public static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
||||||
|
|
||||||
@SuppressLint("NewApi") // this class actually exists on 6.0
|
@SuppressLint("NewApi") // this class actually exists on 6.0
|
||||||
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
||||||
|
@ -230,7 +234,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private boolean ignoreSelectionChanges=false;
|
private boolean ignoreSelectionChanges=false;
|
||||||
private Runnable updateUploadEtaRunnable;
|
private Runnable updateUploadEtaRunnable;
|
||||||
|
|
||||||
private String language;
|
private String language, encoding;
|
||||||
private MastodonLanguage.LanguageResolver languageResolver;
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
|
|
||||||
private int navigationBarColorBefore;
|
private int navigationBarColorBefore;
|
||||||
|
@ -735,7 +739,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerEdit.setText(replyTo.spoilerText);
|
if(GlobalUserPreferences.prefixRepliesWithRe && !replyTo.spoilerText.startsWith("re: ")){
|
||||||
|
spoilerEdit.setText("re: " + replyTo.spoilerText);
|
||||||
|
}else{
|
||||||
|
spoilerEdit.setText(replyTo.spoilerText);
|
||||||
|
}
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}
|
}
|
||||||
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||||
|
@ -872,9 +880,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLanguage(MastodonLanguage loc) {
|
private void updateLanguage(MastodonLanguage loc) {
|
||||||
language = loc.getLanguage();
|
updateLanguage(loc.getLanguage(), loc.getLanguageName(), loc.getDefaultName());
|
||||||
languageButton.setText(loc.getLanguageName());
|
}
|
||||||
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
|
|
||||||
|
private void updateLanguage(String languageTag, String languageName, String defaultName) {
|
||||||
|
language = languageTag;
|
||||||
|
languageButton.setText(languageName);
|
||||||
|
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, defaultName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@ -891,8 +903,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
|
|
||||||
Menu languageMenu = languagePopup.getMenu();
|
Menu languageMenu = languagePopup.getMenu();
|
||||||
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
||||||
MastodonLanguage l = languageResolver.from(recentLanguage);
|
if (recentLanguage.equals("bottom")) {
|
||||||
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
addBottomLanguage(languageMenu);
|
||||||
|
} else {
|
||||||
|
MastodonLanguage l = languageResolver.from(recentLanguage);
|
||||||
|
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
||||||
|
@ -901,13 +917,33 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
||||||
|
|
||||||
|
btn.setOnLongClickListener(v->{
|
||||||
|
btn.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
if (!GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
languagePopup.setOnMenuItemClickListener(i->{
|
languagePopup.setOnMenuItemClickListener(i->{
|
||||||
if (i.hasSubMenu()) return false;
|
if (i.hasSubMenu()) return false;
|
||||||
updateLanguage(allLanguages.get(i.getItemId()));
|
if (i.getItemId() == allLanguages.size()) {
|
||||||
|
updateLanguage(language, "\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48", "bottom");
|
||||||
|
encoding = "bottom";
|
||||||
|
} else {
|
||||||
|
updateLanguage(allLanguages.get(i.getItemId()));
|
||||||
|
encoding = null;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBottomLanguage(Menu menu) {
|
||||||
|
if (menu.findItem(allLanguages.size()) == null) {
|
||||||
|
menu.add(0, allLanguages.size(), Menu.NONE, "bottom (\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
return true;
|
return true;
|
||||||
|
@ -1046,6 +1082,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private void publish(boolean force){
|
private void publish(boolean force){
|
||||||
String text=mainEditText.getText().toString();
|
String text=mainEditText.getText().toString();
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
|
if ("bottom".equals(encoding)) {
|
||||||
|
text = new StatusTextEncoder(Bottom::encode).encode(text);
|
||||||
|
req.spoilerText = "bottom-encoded emoji spam";
|
||||||
|
}
|
||||||
if (localOnly &&
|
if (localOnly &&
|
||||||
GlobalUserPreferences.accountsInGlitchMode.contains(accountID) &&
|
GlobalUserPreferences.accountsInGlitchMode.contains(accountID) &&
|
||||||
!GLITCH_LOCAL_ONLY_PATTERN.matcher(text).matches()) {
|
!GLITCH_LOCAL_ONLY_PATTERN.matcher(text).matches()) {
|
||||||
|
@ -1188,6 +1228,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
||||||
newRecentLanguages.remove(language);
|
newRecentLanguages.remove(language);
|
||||||
newRecentLanguages.add(0, language);
|
newRecentLanguages.add(0, language);
|
||||||
|
if (encoding != null) {
|
||||||
|
newRecentLanguages.remove(encoding);
|
||||||
|
newRecentLanguages.add(0, encoding);
|
||||||
|
}
|
||||||
|
if ("bottom".equals(encoding) && !GlobalUserPreferences.bottomEncoding) {
|
||||||
|
GlobalUserPreferences.bottomEncoding = true;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}
|
||||||
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}
|
}
|
||||||
|
@ -1253,7 +1301,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
|
|
||||||
private void confirmDiscardDraftAndFinish(){
|
private void confirmDiscardDraftAndFinish(){
|
||||||
new M3AlertDialogBuilder(getActivity())
|
boolean attachmentsPending = attachments.stream().anyMatch(att -> att.state != AttachmentUploadState.DONE);
|
||||||
|
if (attachmentsPending) new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_unfinished_attachments)
|
||||||
|
.setMessage(R.string.sk_unfinished_attachments_message)
|
||||||
|
.setPositiveButton(R.string.edit, (d, w) -> {})
|
||||||
|
.setNegativeButton(R.string.discard, (d, w) -> Nav.finish(this))
|
||||||
|
.show();
|
||||||
|
else new M3AlertDialogBuilder(getActivity())
|
||||||
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
||||||
.setPositiveButton(R.string.save, (d, w) -> {
|
.setPositiveButton(R.string.save, (d, w) -> {
|
||||||
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
||||||
|
@ -1263,18 +1318,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if Android platform photopicker is available on the device\
|
|
||||||
* @return whether the device supports photopicker intents.
|
|
||||||
*/
|
|
||||||
private boolean isPhotoPickerAvailable() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
return true;
|
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
return getExtensionVersion(Build.VERSION_CODES.R) >= 2;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the correct intent for the device version to select media.
|
* Builds the correct intent for the device version to select media.
|
||||||
|
@ -1286,24 +1329,24 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
*/
|
*/
|
||||||
private void openFilePicker(boolean photoPicker){
|
private void openFilePicker(boolean photoPicker){
|
||||||
Intent intent;
|
Intent intent;
|
||||||
boolean usePhotoPicker = photoPicker && isPhotoPickerAvailable();
|
boolean usePhotoPicker=photoPicker && isPhotoPickerAvailable();
|
||||||
if (usePhotoPicker) {
|
if(usePhotoPicker){
|
||||||
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MAX_ATTACHMENTS-getMediaAttachmentsCount());
|
||||||
} else {
|
}else{
|
||||||
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("*/*");
|
intent.setType("*/*");
|
||||||
}
|
}
|
||||||
if (!usePhotoPicker && instance.configuration != null &&
|
if(!usePhotoPicker && instance.configuration!=null &&
|
||||||
instance.configuration.mediaAttachments != null &&
|
instance.configuration.mediaAttachments!=null &&
|
||||||
instance.configuration.mediaAttachments.supportedMimeTypes != null &&
|
instance.configuration.mediaAttachments.supportedMimeTypes!=null &&
|
||||||
!instance.configuration.mediaAttachments.supportedMimeTypes.isEmpty()) {
|
!instance.configuration.mediaAttachments.supportedMimeTypes.isEmpty()){
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES,
|
intent.putExtra(Intent.EXTRA_MIME_TYPES,
|
||||||
instance.configuration.mediaAttachments.supportedMimeTypes.toArray(
|
instance.configuration.mediaAttachments.supportedMimeTypes.toArray(
|
||||||
new String[0]));
|
new String[0]));
|
||||||
} else {
|
}else{
|
||||||
if (!usePhotoPicker) {
|
if(!usePhotoPicker){
|
||||||
// If photo picker is being used these are the default mimetypes.
|
// If photo picker is being used these are the default mimetypes.
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
|
||||||
|
|
||||||
public abstract class FabStatusListFragment extends StatusListFragment {
|
|
||||||
protected ImageButton fab;
|
|
||||||
|
|
||||||
public FabStatusListFragment() {
|
|
||||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
fab = view.findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(this::onFabClick);
|
|
||||||
fab.setOnLongClickListener(this::onFabLongClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean onFabLongClick(View v) {
|
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,12 +17,15 @@ import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
|
@ -33,11 +36,11 @@ import me.grishka.appkit.utils.V;
|
||||||
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||||
private String hashtag;
|
private String hashtag;
|
||||||
private boolean following;
|
private boolean following;
|
||||||
private ImageButton fab;
|
|
||||||
private MenuItem followButton;
|
private MenuItem followButton;
|
||||||
|
|
||||||
public HashtagTimelineFragment(){
|
@Override
|
||||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,6 +123,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -134,14 +138,12 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
protected boolean onFabLongClick(View v) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||||
fab=view.findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(this::onFabClick);
|
|
||||||
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' '));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFabClick(View v){
|
@Override
|
||||||
|
protected void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("prefilledText", '#'+hashtag+' ');
|
args.putString("prefilledText", '#'+hashtag+' ');
|
||||||
|
|
|
@ -375,7 +375,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void updateList(List<T> addItems, Map<Integer, T> items) {
|
private <T> void updateList(List<T> addItems, Map<Integer, T> items) {
|
||||||
if (addItems.size() == 0) return;
|
if (addItems.size() == 0 || getActivity() == null) return;
|
||||||
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
||||||
updateOverflowMenu();
|
updateOverflowMenu();
|
||||||
}
|
}
|
||||||
|
@ -662,7 +662,8 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
FrameLayout tabView = tabViews[viewType % getItemCount()];
|
FrameLayout tabView = tabViews[viewType % getItemCount()];
|
||||||
((ViewGroup)tabView.getParent()).removeView(tabView);
|
ViewGroup tabParent = (ViewGroup) tabView.getParent();
|
||||||
|
if (tabParent != null) tabParent.removeView(tabView);
|
||||||
tabView.setVisibility(View.VISIBLE);
|
tabView.setVisibility(View.VISIBLE);
|
||||||
return new SimpleViewHolder(tabView);
|
return new SimpleViewHolder(tabView);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,16 @@ import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTimelineFragment extends FabStatusListFragment {
|
public class HomeTimelineFragment extends StatusListFragment {
|
||||||
private HomeTabFragment parent;
|
private HomeTabFragment parent;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
private String lastSavedMarkerID;
|
private String lastSavedMarkerID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
|
|
|
@ -18,14 +18,17 @@ import org.joinmastodon.android.api.requests.lists.UpdateList;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
import org.joinmastodon.android.events.ListDeletedEvent;
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
|
@ -39,10 +42,10 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||||
private String listTitle;
|
private String listTitle;
|
||||||
@Nullable
|
@Nullable
|
||||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
private ImageButton fab;
|
|
||||||
|
|
||||||
public ListTimelineFragment() {
|
@Override
|
||||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -134,10 +137,11 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result) {
|
public void onSuccess(List<Status> result) {
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.HOME)).collect(Collectors.toList());
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -148,14 +152,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
protected void onFabClick(View v){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
fab=view.findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(this::onFabClick);
|
|
||||||
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
|
|
@ -99,7 +99,6 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
|
||||||
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline list) {
|
public void onSuccess(ListTimeline list) {
|
||||||
saveListMembership(list.id, true);
|
|
||||||
data.add(0, list);
|
data.add(0, list);
|
||||||
adapter.notifyItemRangeInserted(0, 1);
|
adapter.notifyItemRangeInserted(0, 1);
|
||||||
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
|
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
|
||||||
|
|
|
@ -48,6 +48,11 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
private String maxID;
|
private String maxID;
|
||||||
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -92,7 +97,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
};
|
};
|
||||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, Filter.FilterContext.NOTIFICATIONS);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, false, Filter.FilterContext.NOTIFICATIONS);
|
||||||
if(titleItem!=null){
|
if(titleItem!=null){
|
||||||
for(StatusDisplayItem item:items){
|
for(StatusDisplayItem item:items){
|
||||||
if(item instanceof ImageStatusDisplayItem imgItem){
|
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||||
|
@ -193,6 +198,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||||
|
if (getParentFragment() instanceof NotificationsFragment) fab.setVisibility(View.GONE);
|
||||||
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,4 +256,4 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
displayItems.subList(index, lastIndex).clear();
|
displayItems.subList(index, lastIndex).clear();
|
||||||
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,11 @@ import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -37,7 +39,6 @@ import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -62,6 +63,7 @@ import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||||
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
||||||
|
@ -70,8 +72,10 @@ import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.CoverImageView;
|
import org.joinmastodon.android.ui.views.CoverImageView;
|
||||||
|
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
@ -84,6 +88,13 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
@ -91,10 +102,17 @@ import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
|
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
|
||||||
|
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
||||||
private static final int AVATAR_RESULT=722;
|
private static final int AVATAR_RESULT=722;
|
||||||
|
@ -102,23 +120,24 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
private ImageView avatar;
|
private ImageView avatar;
|
||||||
private CoverImageView cover;
|
private CoverImageView cover;
|
||||||
private View avatarBorder;
|
private View avatarBorder, nameWrap;
|
||||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
||||||
private ProgressBarButton actionButton, notifyButton;
|
private ProgressBarButton actionButton, notifyButton;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private NestedRecyclerScrollView scrollView;
|
private NestedRecyclerScrollView scrollView;
|
||||||
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
||||||
private ProfileAboutFragment aboutFragment;
|
// private ProfileAboutFragment aboutFragment;
|
||||||
private TabLayout tabbar;
|
private TabLayout tabbar;
|
||||||
private SwipeRefreshLayout refreshLayout;
|
private SwipeRefreshLayout refreshLayout;
|
||||||
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
||||||
private float titleTransY;
|
private float titleTransY;
|
||||||
private View postsBtn, followersBtn, followingBtn;
|
private View postsBtn, followersBtn, followingBtn, profileCounters;
|
||||||
private EditText nameEdit, bioEdit;
|
private EditText nameEdit, bioEdit;
|
||||||
private ProgressBar actionProgress, notifyProgress;
|
private ProgressBar actionProgress, notifyProgress;
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
private TabLayoutMediator tabLayoutMediator;
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
private TextView followsYouView;
|
private TextView followsYouView;
|
||||||
|
private ViewGroup rolesView;
|
||||||
|
|
||||||
public FrameLayout noteWrap;
|
public FrameLayout noteWrap;
|
||||||
public EditText noteEdit;
|
public EditText noteEdit;
|
||||||
|
@ -140,6 +159,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
private boolean editModeLoading;
|
private boolean editModeLoading;
|
||||||
private boolean isScrollingUp = false;
|
private boolean isScrollingUp = false;
|
||||||
|
|
||||||
|
private static final int MAX_FIELDS=4;
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
public UsableRecyclerView list;
|
||||||
|
private List<AccountField> metadataListData=Collections.emptyList();
|
||||||
|
private MetadataAdapter adapter;
|
||||||
|
private ItemTouchHelper dragHelper=new ItemTouchHelper(new ReorderCallback());
|
||||||
|
private RecyclerView.ViewHolder draggedViewHolder;
|
||||||
|
private ListImageLoaderWrapper imgLoader;
|
||||||
|
|
||||||
public ProfileFragment(){
|
public ProfileFragment(){
|
||||||
super(R.layout.loader_fragment_overlay_toolbar);
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
}
|
}
|
||||||
|
@ -184,8 +213,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
cover=content.findViewById(R.id.cover);
|
cover=content.findViewById(R.id.cover);
|
||||||
avatarBorder=content.findViewById(R.id.avatar_border);
|
avatarBorder=content.findViewById(R.id.avatar_border);
|
||||||
name=content.findViewById(R.id.name);
|
name=content.findViewById(R.id.name);
|
||||||
|
nameWrap=content.findViewById(R.id.name_wrap);
|
||||||
username=content.findViewById(R.id.username);
|
username=content.findViewById(R.id.username);
|
||||||
bio=content.findViewById(R.id.bio);
|
bio=content.findViewById(R.id.bio);
|
||||||
|
profileCounters=content.findViewById(R.id.profile_counters);
|
||||||
followersCount=content.findViewById(R.id.followers_count);
|
followersCount=content.findViewById(R.id.followers_count);
|
||||||
followersLabel=content.findViewById(R.id.followers_label);
|
followersLabel=content.findViewById(R.id.followers_label);
|
||||||
followersBtn=content.findViewById(R.id.followers_btn);
|
followersBtn=content.findViewById(R.id.followers_btn);
|
||||||
|
@ -208,6 +239,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
notifyProgress=content.findViewById(R.id.notify_progress);
|
notifyProgress=content.findViewById(R.id.notify_progress);
|
||||||
fab=content.findViewById(R.id.fab);
|
fab=content.findViewById(R.id.fab);
|
||||||
followsYouView=content.findViewById(R.id.follows_you);
|
followsYouView=content.findViewById(R.id.follows_you);
|
||||||
|
list=content.findViewById(R.id.metadata);
|
||||||
|
rolesView=content.findViewById(R.id.roles);
|
||||||
|
|
||||||
noteEdit = content.findViewById(R.id.note_edit);
|
noteEdit = content.findViewById(R.id.note_edit);
|
||||||
noteWrap = content.findViewById(R.id.note_edit_wrap);
|
noteWrap = content.findViewById(R.id.note_edit_wrap);
|
||||||
|
@ -274,7 +307,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tabViews=new FrameLayout[5];
|
tabViews=new FrameLayout[4];
|
||||||
for(int i=0;i<tabViews.length;i++){
|
for(int i=0;i<tabViews.length;i++){
|
||||||
FrameLayout tabView=new FrameLayout(getActivity());
|
FrameLayout tabView=new FrameLayout(getActivity());
|
||||||
tabView.setId(switch(i){
|
tabView.setId(switch(i){
|
||||||
|
@ -291,7 +324,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
}
|
}
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setOffscreenPageLimit(5);
|
pager.setOffscreenPageLimit(4);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new ProfilePagerAdapter());
|
pager.setAdapter(new ProfilePagerAdapter());
|
||||||
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
||||||
|
@ -352,6 +385,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
|
list.setDrawSelectorOnTop(true);
|
||||||
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
imgLoader=new ListImageLoaderWrapper(getActivity(), list, new RecyclerViewDelegate(list), null);
|
||||||
|
list.setAdapter(adapter=new MetadataAdapter());
|
||||||
|
list.setClipToPadding(false);
|
||||||
|
|
||||||
return sizeWrapper;
|
return sizeWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,8 +467,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
||||||
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
||||||
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
||||||
aboutFragment=new ProfileAboutFragment();
|
// aboutFragment=new ProfileAboutFragment();
|
||||||
aboutFragment.setFields(fields);
|
setFields(fields);
|
||||||
}
|
}
|
||||||
pager.getAdapter().notifyDataSetChanged();
|
pager.getAdapter().notifyDataSetChanged();
|
||||||
super.dataLoaded();
|
super.dataLoaded();
|
||||||
|
@ -520,6 +561,19 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
name.setText(ssb);
|
name.setText(ssb);
|
||||||
setTitle(ssb);
|
setTitle(ssb);
|
||||||
|
|
||||||
|
if (account.roles != null && !account.roles.isEmpty()) {
|
||||||
|
rolesView.setVisibility(View.VISIBLE);
|
||||||
|
rolesView.removeAllViews();
|
||||||
|
name.setPadding(0, 0, V.dp(12), 0);
|
||||||
|
for (Account.Role role : account.roles) {
|
||||||
|
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
||||||
|
roleText.setText(role.name);
|
||||||
|
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||||
|
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||||
|
rolesView.addView(roleText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
|
||||||
|
|
||||||
|
@ -603,9 +657,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
fields.add(field);
|
fields.add(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(aboutFragment!=null){
|
setFields(fields);
|
||||||
aboutFragment.setFields(fields);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToolbar(){
|
private void updateToolbar(){
|
||||||
|
@ -809,8 +861,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
coverGradient.setTopOffset(scrollY);
|
coverGradient.setTopOffset(scrollY);
|
||||||
cover.invalidate();
|
cover.invalidate();
|
||||||
titleTransY=getToolbar().getHeight();
|
titleTransY=getToolbar().getHeight();
|
||||||
if(scrollY>name.getTop()-topBarsH){
|
if(scrollY>nameWrap.getTop()-topBarsH){
|
||||||
titleTransY=Math.max(0f, titleTransY-(scrollY-(name.getTop()-topBarsH)));
|
titleTransY=Math.max(0f, titleTransY-(scrollY-(nameWrap.getTop()-topBarsH)));
|
||||||
}
|
}
|
||||||
if(toolbarTitleView!=null){
|
if(toolbarTitleView!=null){
|
||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
|
@ -827,7 +879,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
case 1 -> postsWithRepliesFragment;
|
case 1 -> postsWithRepliesFragment;
|
||||||
case 2 -> pinnedPostsFragment;
|
case 2 -> pinnedPostsFragment;
|
||||||
case 3 -> mediaFragment;
|
case 3 -> mediaFragment;
|
||||||
case 4 -> aboutFragment;
|
// case 4 -> aboutFragment;
|
||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -892,16 +944,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
pager.setUserInputEnabled(false);
|
pager.setUserInputEnabled(false);
|
||||||
actionButton.setText(R.string.done);
|
actionButton.setText(R.string.done);
|
||||||
pager.setCurrentItem(4);
|
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
for(int i=0;i<tabViews.length-1;i++){
|
|
||||||
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, .3f));
|
|
||||||
tabbar.getTabAt(i).view.setEnabled(false);
|
|
||||||
}
|
|
||||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
||||||
avatar.setForeground(overlay);
|
avatar.setForeground(overlay);
|
||||||
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
||||||
|
|
||||||
|
nameWrap.setVisibility(View.GONE);
|
||||||
nameEdit.setVisibility(View.VISIBLE);
|
nameEdit.setVisibility(View.VISIBLE);
|
||||||
nameEdit.setText(account.displayName);
|
nameEdit.setText(account.displayName);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
|
@ -913,10 +961,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
bioEdit.setText(account.source.note);
|
bioEdit.setText(account.source.note);
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
||||||
|
profileCounters.setVisibility(View.GONE);
|
||||||
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, .3f));
|
pager.setVisibility(View.GONE);
|
||||||
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, .3f));
|
tabbar.setVisibility(View.GONE);
|
||||||
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, .3f));
|
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
|
@ -924,7 +971,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
aboutFragment.enterEditMode(account.source.fields);
|
// aboutFragment.enterEditMode(account.source.fields);
|
||||||
|
|
||||||
|
V.setVisibilityAnimated(fab, View.GONE);
|
||||||
|
metadataListData=account.source.fields;
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
dragHelper.attachToRecyclerView(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exitEditMode(){
|
private void exitEditMode(){
|
||||||
|
@ -935,16 +987,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
actionButton.setText(R.string.edit_profile);
|
actionButton.setText(R.string.edit_profile);
|
||||||
for(int i=0;i<tabViews.length-1;i++){
|
|
||||||
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, 1f));
|
|
||||||
}
|
|
||||||
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
||||||
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
||||||
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, 1f));
|
profileCounters.setVisibility(View.VISIBLE);
|
||||||
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, 1f));
|
pager.setVisibility(View.VISIBLE);
|
||||||
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, 1f));
|
tabbar.setVisibility(View.VISIBLE);
|
||||||
|
V.setVisibilityAnimated(nameWrap, View.VISIBLE);
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
|
@ -953,20 +1003,21 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public void onAnimationEnd(Animator animation){
|
||||||
for(int i=0;i<tabViews.length-1;i++){
|
|
||||||
tabbar.getTabAt(i).view.setEnabled(true);
|
|
||||||
}
|
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
nameEdit.setVisibility(View.GONE);
|
nameEdit.setVisibility(View.GONE);
|
||||||
bioEdit.setVisibility(View.GONE);
|
bioEdit.setVisibility(View.GONE);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
lp.addRule(RelativeLayout.BELOW, R.id.name);
|
lp.addRule(RelativeLayout.BELOW, R.id.name_wrap);
|
||||||
username.getParent().requestLayout();
|
username.getParent().requestLayout();
|
||||||
avatar.setForeground(null);
|
avatar.setForeground(null);
|
||||||
|
scrollToTop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
|
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
|
||||||
|
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
|
||||||
|
V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,7 +1025,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
if(!isInEditMode)
|
if(!isInEditMode)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
setActionProgressVisible(true);
|
setActionProgressVisible(true);
|
||||||
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, aboutFragment.getFields())
|
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, metadataListData)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
|
@ -1148,4 +1199,227 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
public void setFields(ArrayList<AccountField> fields){
|
||||||
|
metadataListData=fields;
|
||||||
|
if (isInEditMode) {
|
||||||
|
isInEditMode=false;
|
||||||
|
dragHelper.attachToRecyclerView(null);
|
||||||
|
}
|
||||||
|
if (adapter != null) adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MetadataAdapter extends UsableRecyclerView.Adapter<BaseViewHolder> implements ImageLoaderRecyclerAdapter {
|
||||||
|
public MetadataAdapter(){
|
||||||
|
super(imgLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return switch(viewType){
|
||||||
|
case 0 -> new AboutViewHolder();
|
||||||
|
case 1 -> new EditableAboutViewHolder();
|
||||||
|
case 2 -> new AddRowViewHolder();
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+viewType);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(BaseViewHolder holder, int position){
|
||||||
|
if(position<metadataListData.size()){
|
||||||
|
holder.bind(metadataListData.get(position));
|
||||||
|
}else{
|
||||||
|
holder.bind(null);
|
||||||
|
}
|
||||||
|
super.onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount(){
|
||||||
|
if(isInEditMode){
|
||||||
|
int size=metadataListData.size();
|
||||||
|
if(size<MAX_FIELDS)
|
||||||
|
size++;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
return metadataListData.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position){
|
||||||
|
if(isInEditMode){
|
||||||
|
return position==metadataListData.size() ? 2 : 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getImageCountForItem(int position){
|
||||||
|
return isInEditMode || metadataListData.get(position).emojiRequests==null
|
||||||
|
? 0 : metadataListData.get(position).emojiRequests.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
|
return metadataListData.get(position).emojiRequests.get(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class BaseViewHolder extends BindableViewHolder<AccountField> {
|
||||||
|
public BaseViewHolder(int layout){
|
||||||
|
super(getActivity(), layout, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
|
||||||
|
private TextView title;
|
||||||
|
private LinkedTextView value;
|
||||||
|
|
||||||
|
public AboutViewHolder(){
|
||||||
|
super(R.layout.item_profile_about);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
value=findViewById(R.id.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item){
|
||||||
|
title.setText(item.parsedName);
|
||||||
|
value.setText(item.parsedValue);
|
||||||
|
if(item.verifiedAt!=null){
|
||||||
|
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
|
||||||
|
value.setTextColor(textColor);
|
||||||
|
value.setLinkTextColor(textColor);
|
||||||
|
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_24_regular, getActivity().getTheme()).mutate();
|
||||||
|
check.setTint(textColor);
|
||||||
|
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
|
||||||
|
}else{
|
||||||
|
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||||
|
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
|
||||||
|
value.setCompoundDrawables(null, null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(int index, Drawable image){
|
||||||
|
CustomEmojiSpan span=index>=item.nameEmojis.length ? item.valueEmojis[index-item.nameEmojis.length] : item.nameEmojis[index];
|
||||||
|
span.setDrawable(image);
|
||||||
|
title.invalidate();
|
||||||
|
value.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearImage(int index){
|
||||||
|
setImage(index, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EditableAboutViewHolder extends BaseViewHolder {
|
||||||
|
private EditText title;
|
||||||
|
private EditText value;
|
||||||
|
|
||||||
|
public EditableAboutViewHolder(){
|
||||||
|
super(R.layout.item_profile_about_editable);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
value=findViewById(R.id.value);
|
||||||
|
findViewById(R.id.dragger_thingy).setOnLongClickListener(v->{
|
||||||
|
dragHelper.startDrag(this);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
title.addTextChangedListener(new SimpleTextWatcher(e->item.name=e.toString()));
|
||||||
|
value.addTextChangedListener(new SimpleTextWatcher(e->item.value=e.toString()));
|
||||||
|
findViewById(R.id.remove_row_btn).setOnClickListener(this::onRemoveRowClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item){
|
||||||
|
title.setText(item.name);
|
||||||
|
value.setText(item.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRemoveRowClick(View v){
|
||||||
|
int pos=getAbsoluteAdapterPosition();
|
||||||
|
metadataListData.remove(pos);
|
||||||
|
adapter.notifyItemRemoved(pos);
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
BaseViewHolder vh=(BaseViewHolder) list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
vh.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AddRowViewHolder extends BaseViewHolder implements UsableRecyclerView.Clickable{
|
||||||
|
public AddRowViewHolder(){
|
||||||
|
super(R.layout.item_profile_about_add_row);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(){
|
||||||
|
metadataListData.add(new AccountField());
|
||||||
|
if(metadataListData.size()==MAX_FIELDS){ // replace this row with new row
|
||||||
|
adapter.notifyItemChanged(metadataListData.size()-1);
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemInserted(metadataListData.size()-1);
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReorderCallback extends ItemTouchHelper.SimpleCallback{
|
||||||
|
public ReorderCallback(){
|
||||||
|
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target){
|
||||||
|
if(target instanceof AddRowViewHolder)
|
||||||
|
return false;
|
||||||
|
int fromPosition=viewHolder.getAbsoluteAdapterPosition();
|
||||||
|
int toPosition=target.getAbsoluteAdapterPosition();
|
||||||
|
if (fromPosition<toPosition) {
|
||||||
|
for (int i=fromPosition;i<toPosition;i++) {
|
||||||
|
Collections.swap(metadataListData, i, i+1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i=fromPosition;i>toPosition;i--) {
|
||||||
|
Collections.swap(metadataListData, i, i-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyItemMoved(fromPosition, toPosition);
|
||||||
|
((BindableViewHolder)viewHolder).rebind();
|
||||||
|
((BindableViewHolder)target).rebind();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||||
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
|
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||||
|
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||||
|
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
|
draggedViewHolder=viewHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder){
|
||||||
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
viewHolder.itemView.animate().translationZ(0).setDuration(100).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
|
draggedViewHolder=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLongPressDragEnabled(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
private ImageButton fab;
|
|
||||||
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
||||||
|
|
||||||
public ScheduledStatusListFragment() {
|
@Override
|
||||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,14 +57,24 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
protected void onFabClick(View v) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
fab=view.findViewById(R.id.fab);
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
fab.setOnClickListener(v -> Nav.go(getActivity(), ComposeFragment.class, args));
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, args));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onFabLongClick(View v) {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
|
return UiUtils.pickAccountForCompose(getActivity(), accountID, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,13 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||||
|
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
|
@ -66,7 +68,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
@ -222,15 +223,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
items.add(new SwitchItem(R.string.sk_settings_prefix_reply_cw_with_re, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.prefixRepliesWithRe, i->{
|
||||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
GlobalUserPreferences.prefixRepliesWithRe=i.checked;
|
||||||
// GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
// }));
|
}));
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
|
||||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
|
||||||
// GlobalUserPreferences.save();
|
|
||||||
// needAppRestart=true;
|
|
||||||
// }));
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_timelines));
|
items.add(new HeaderItem(R.string.sk_timelines));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||||
|
@ -264,6 +260,19 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
|
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
|
||||||
GlobalUserPreferences.showNoAltIndicator=i.checked;
|
GlobalUserPreferences.showNoAltIndicator=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_collapse_long_posts, R.drawable.ic_fluent_chevron_down_24_filled, GlobalUserPreferences.collapseLongPosts, i->{
|
||||||
|
GlobalUserPreferences.collapseLongPosts=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_spectator_mode, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
||||||
|
GlobalUserPreferences.spectatorMode=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
|
GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -360,7 +369,20 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
}));
|
}));
|
||||||
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||||
|
|
||||||
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
if(BuildConfig.DEBUG){
|
||||||
|
items.add(new RedHeaderItem("Debug options"));
|
||||||
|
items.add(new TextItem("Test e-mail confirmation flow", ()->{
|
||||||
|
AccountSession sess=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
sess.activated=false;
|
||||||
|
sess.activationInfo=new AccountActivationInfo("test@email", System.currentTimeMillis());
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putBoolean("debug", true);
|
||||||
|
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePublishText(Button btn) {
|
private void updatePublishText(Button btn) {
|
||||||
|
@ -645,7 +667,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
this.text=getString(text);
|
this.text=getString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderItem(String text) {
|
public HeaderItem(String text){
|
||||||
this.text=text;
|
this.text=text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,6 +785,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
this.secondaryText = secondaryText;
|
this.secondaryText = secondaryText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextItem(String text, Runnable onClick){
|
||||||
|
this.text=text;
|
||||||
|
this.onClick=onClick;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 4;
|
return 4;
|
||||||
|
@ -775,6 +802,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
super(text);
|
super(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RedHeaderItem(String text){
|
||||||
|
super(text);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 5;
|
return 5;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.os.Bundle;
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
|
@ -31,7 +32,9 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
||||||
protected EventListener eventListener=new EventListener();
|
protected EventListener eventListener=new EventListener();
|
||||||
|
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, true, null, Filter.FilterContext.HOME);
|
boolean addFooter = !GlobalUserPreferences.spectatorMode ||
|
||||||
|
(this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id));
|
||||||
|
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, Filter.FilterContext.HOME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,7 +26,7 @@ import java.util.stream.Collectors;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class ThreadFragment extends StatusListFragment{
|
public class ThreadFragment extends StatusListFragment{
|
||||||
private Status mainStatus;
|
protected Status mainStatus;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
|
|
@ -129,7 +129,8 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
// list.setPadding(0, V.dp(16), 0, V.dp(16));
|
// list.setPadding(0, V.dp(16), 0, V.dp(16));
|
||||||
list.setClipToPadding(false);
|
list.setClipToPadding(false);
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 72, 16));
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1,
|
||||||
|
Math.round(16f + 56f * getResources().getConfiguration().fontScale), 16));
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,13 @@ import android.view.View;
|
||||||
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
import org.joinmastodon.android.fragments.IsOnTop;
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
import org.joinmastodon.android.fragments.FabStatusListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
@ -17,10 +16,16 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class FederatedTimelineFragment extends FabStatusListFragment {
|
public class FederatedTimelineFragment extends StatusListFragment {
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.FEDERATED_TIMELINE);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.FEDERATED_TIMELINE);
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count)
|
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count)
|
||||||
|
@ -30,7 +35,8 @@ public class FederatedTimelineFragment extends FabStatusListFragment {
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||||
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|
|
@ -3,9 +3,7 @@ package org.joinmastodon.android.fragments.discover;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
import org.joinmastodon.android.fragments.FabStatusListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
@ -17,10 +15,16 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class LocalTimelineFragment extends FabStatusListFragment {
|
public class LocalTimelineFragment extends StatusListFragment {
|
||||||
// private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
||||||
|
@ -30,7 +34,8 @@ public class LocalTimelineFragment extends FabStatusListFragment {
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||||
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
@ -39,6 +44,6 @@ public class LocalTimelineFragment extends FabStatusListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
// bannerHelper.maybeAddBanner(contentWrap);
|
bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,30 +193,24 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||||
mgr.removeAccount(accountID);
|
mgr.removeAccount(accountID);
|
||||||
mgr.addAccount(mgr.getInstanceInfo(session.domain), session.token, result, session.app, null);
|
mgr.addAccount(mgr.getInstanceInfo(session.domain), session.token, result, session.app, null);
|
||||||
String newID=mgr.getLastActiveAccountID();
|
String newID=mgr.getLastActiveAccountID();
|
||||||
Bundle args=new Bundle();
|
accountID=newID;
|
||||||
args.putString("account", newID);
|
if((session.self.avatar!=null || session.self.displayName!=null) && !getArguments().getBoolean("debug")){
|
||||||
if(session.self.avatar!=null || session.self.displayName!=null){
|
new UpdateAccountCredentials(session.self.displayName, "", (File)null, null, Collections.emptyList())
|
||||||
File avaFile=session.self.avatar!=null ? new File(session.self.avatar) : null;
|
|
||||||
new UpdateAccountCredentials(session.self.displayName, "", avaFile, null, Collections.emptyList())
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
if(avaFile!=null)
|
|
||||||
avaFile.delete();
|
|
||||||
mgr.updateAccountInfo(newID, result);
|
mgr.updateAccountInfo(newID, result);
|
||||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
if(avaFile!=null)
|
proceed();
|
||||||
avaFile.delete();
|
|
||||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(newID);
|
.exec(newID);
|
||||||
}else{
|
}else{
|
||||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
proceed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,4 +243,11 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
resendBtn.removeCallbacks(resendTimer);
|
resendBtn.removeCallbacks(resendTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void proceed(){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
// Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||||
|
Nav.goClearingStack(getActivity(), OnboardingProfileSetupFragment.class, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
private ItemsAdapter itemsAdapter;
|
private ItemsAdapter itemsAdapter;
|
||||||
private ElevationOnScrollListener onScrollListener;
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
|
||||||
|
private static final int SIGNUP_REQUEST=722;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -139,7 +141,16 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
protected void onButtonClick(){
|
protected void onButtonClick(){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putParcelable("instance", Parcels.wrap(instance));
|
args.putParcelable("instance", Parcels.wrap(instance));
|
||||||
Nav.go(getActivity(), SignupFragment.class, args);
|
Nav.goForResult(getActivity(), SignupFragment.class, args, SIGNUP_REQUEST, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
||||||
|
super.onFragmentResult(reqCode, success, result);
|
||||||
|
if(reqCode==SIGNUP_REQUEST && !success){
|
||||||
|
setResult(false, null);
|
||||||
|
Nav.finish(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -92,7 +92,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||||
protected boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
|
protected boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
|
||||||
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
|
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
|
||||||
return true;
|
return true;
|
||||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
currentSearchQuery=searchEdit.getText().toString().toLowerCase().trim();
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
searchEdit.removeCallbacks(searchDebouncer);
|
searchEdit.removeCallbacks(searchDebouncer);
|
||||||
Instance instance=instancesCache.get(normalizeInstanceDomain(currentSearchQuery));
|
Instance instance=instancesCache.get(normalizeInstanceDomain(currentSearchQuery));
|
||||||
|
@ -106,7 +106,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onSearchChangedDebounced(){
|
protected void onSearchChangedDebounced(){
|
||||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
currentSearchQuery=searchEdit.getText().toString().toLowerCase().trim();
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
loadInstanceInfo(currentSearchQuery, false);
|
loadInstanceInfo(currentSearchQuery, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -527,6 +527,15 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!searchQueryMode){
|
||||||
|
// Prevent search view automatically getting focused when the user returns to this fragment
|
||||||
|
focusThing.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
|
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
|
||||||
public InstancesAdapter(){
|
public InstancesAdapter(){
|
||||||
super(imgLoader);
|
super(imgLoader);
|
||||||
|
|
|
@ -0,0 +1,350 @@
|
||||||
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
|
import org.joinmastodon.android.fragments.HomeFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
|
import org.joinmastodon.android.model.FollowSuggestion;
|
||||||
|
import org.joinmastodon.android.model.ParsedAccount;
|
||||||
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
|
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.FragmentRootLinearLayout;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<ParsedAccount>{
|
||||||
|
private String accountID;
|
||||||
|
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||||
|
private GetAccountRelationships relationshipsRequest;
|
||||||
|
private View buttonBar;
|
||||||
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
private int numRunningFollowRequests=0;
|
||||||
|
|
||||||
|
public OnboardingFollowSuggestionsFragment(){
|
||||||
|
super(R.layout.fragment_onboarding_follow_suggestions, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setRetainInstance(true);
|
||||||
|
setTitle(R.string.popular_on_mastodon);
|
||||||
|
accountID=getArguments().getString("account");
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
|
|
||||||
|
view.findViewById(R.id.btn_next).setOnClickListener(UiUtils.rateLimitedClickListener(this::onFollowAllClick));
|
||||||
|
view.findViewById(R.id.btn_skip).setOnClickListener(UiUtils.rateLimitedClickListener(v->proceed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
|
if(onScrollListener!=null){
|
||||||
|
onScrollListener.setViews(buttonBar, getToolbar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
new GetFollowSuggestions(40)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<FollowSuggestion> result){
|
||||||
|
onDataLoaded(result.stream().map(fs->new ParsedAccount(fs.account, accountID)).collect(Collectors.toList()), false);
|
||||||
|
loadRelationships();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRelationships(){
|
||||||
|
relationships=Collections.emptyMap();
|
||||||
|
relationshipsRequest=new GetAccountRelationships(data.stream().map(fs->fs.account.id).collect(Collectors.toList()));
|
||||||
|
relationshipsRequest.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Relationship> result){
|
||||||
|
relationshipsRequest=null;
|
||||||
|
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
||||||
|
if(list==null)
|
||||||
|
return;
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder instanceof SuggestionViewHolder svh)
|
||||||
|
svh.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
relationshipsRequest=null;
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
|
if(Build.VERSION.SDK_INT>=27){
|
||||||
|
int inset=insets.getSystemWindowInsetBottom();
|
||||||
|
buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0);
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0));
|
||||||
|
}else{
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
|
return new SuggestionsAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFollowAllClick(View v){
|
||||||
|
if(!loaded || relationships.isEmpty())
|
||||||
|
return;
|
||||||
|
if(data.isEmpty()){
|
||||||
|
proceed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<String> accountIdsToFollow=new ArrayList<>();
|
||||||
|
for(ParsedAccount acc:data){
|
||||||
|
Relationship rel=relationships.get(acc.account.id);
|
||||||
|
if(rel==null)
|
||||||
|
continue;
|
||||||
|
if(rel.canFollow())
|
||||||
|
accountIdsToFollow.add(acc.account.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProgressDialog progress=new ProgressDialog(getActivity());
|
||||||
|
progress.setIndeterminate(false);
|
||||||
|
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||||
|
progress.setMax(accountIdsToFollow.size());
|
||||||
|
progress.setCancelable(false);
|
||||||
|
progress.setMessage(getString(R.string.sending_follows));
|
||||||
|
progress.show();
|
||||||
|
|
||||||
|
for(int i=0;i<Math.min(accountIdsToFollow.size(), 5);i++){ // Send up to 5 requests in parallel
|
||||||
|
followNextAccount(accountIdsToFollow, progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void followNextAccount(ArrayList<String> accountIdsToFollow, ProgressDialog progress){
|
||||||
|
if(accountIdsToFollow.isEmpty()){
|
||||||
|
if(numRunningFollowRequests==0){
|
||||||
|
progress.dismiss();
|
||||||
|
proceed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
numRunningFollowRequests++;
|
||||||
|
String id=accountIdsToFollow.remove(0);
|
||||||
|
new SetAccountFollowed(id, true, true)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Relationship result){
|
||||||
|
numRunningFollowRequests--;
|
||||||
|
progress.setProgress(progress.getMax()-accountIdsToFollow.size()-numRunningFollowRequests);
|
||||||
|
followNextAccount(accountIdsToFollow, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
numRunningFollowRequests--;
|
||||||
|
progress.setProgress(progress.getMax()-accountIdsToFollow.size()-numRunningFollowRequests);
|
||||||
|
followNextAccount(accountIdsToFollow, progress);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void proceed(){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), HomeFragment.class, args);
|
||||||
|
getActivity().getWindow().getDecorView().postDelayed(()->Nav.finish(this), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canGoBack(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToolbarNavigationClick(){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SuggestionsAdapter extends UsableRecyclerView.Adapter<SuggestionViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
|
public SuggestionsAdapter(){
|
||||||
|
super(imgLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public SuggestionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new SuggestionViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount(){
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(SuggestionViewHolder holder, int position){
|
||||||
|
holder.bind(data.get(position));
|
||||||
|
super.onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getImageCountForItem(int position){
|
||||||
|
return data.get(position).emojiHelper.getImageCount()+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
|
ParsedAccount account=data.get(position);
|
||||||
|
if(image==0)
|
||||||
|
return account.avatarRequest;
|
||||||
|
return account.emojiHelper.getImageRequest(image-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SuggestionViewHolder extends BindableViewHolder<ParsedAccount> implements ImageLoaderViewHolder, UsableRecyclerView.Clickable{
|
||||||
|
private final TextView name, username, bio;
|
||||||
|
private final ImageView avatar;
|
||||||
|
private final ProgressBarButton actionButton;
|
||||||
|
private final ProgressBar actionProgress;
|
||||||
|
private final View actionWrap;
|
||||||
|
|
||||||
|
private Relationship relationship;
|
||||||
|
|
||||||
|
public SuggestionViewHolder(){
|
||||||
|
super(getActivity(), R.layout.item_user_row_m3, list);
|
||||||
|
name=findViewById(R.id.name);
|
||||||
|
username=findViewById(R.id.username);
|
||||||
|
bio=findViewById(R.id.bio);
|
||||||
|
avatar=findViewById(R.id.avatar);
|
||||||
|
actionButton=findViewById(R.id.action_btn);
|
||||||
|
actionProgress=findViewById(R.id.action_progress);
|
||||||
|
actionWrap=findViewById(R.id.action_btn_wrap);
|
||||||
|
|
||||||
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(10));
|
||||||
|
avatar.setClipToOutline(true);
|
||||||
|
actionButton.setOnClickListener(UiUtils.rateLimitedClickListener(this::onActionButtonClick));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(ParsedAccount item){
|
||||||
|
name.setText(item.parsedName);
|
||||||
|
username.setText(item.account.getDisplayUsername());
|
||||||
|
if(TextUtils.isEmpty(item.parsedBio)){
|
||||||
|
bio.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
bio.setVisibility(View.VISIBLE);
|
||||||
|
bio.setText(item.parsedBio);
|
||||||
|
}
|
||||||
|
|
||||||
|
relationship=relationships.get(item.account.id);
|
||||||
|
if(relationship==null){
|
||||||
|
actionWrap.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
actionWrap.setVisibility(View.VISIBLE);
|
||||||
|
UiUtils.setRelationshipToActionButtonM3(relationship, actionButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(int index, Drawable image){
|
||||||
|
if(index==0){
|
||||||
|
avatar.setImageDrawable(image);
|
||||||
|
}else{
|
||||||
|
item.emojiHelper.setImageDrawable(index-1, image);
|
||||||
|
name.invalidate();
|
||||||
|
bio.invalidate();
|
||||||
|
}
|
||||||
|
if(image instanceof Animatable a && !a.isRunning())
|
||||||
|
a.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearImage(int index){
|
||||||
|
setImage(index, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
||||||
|
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onActionButtonClick(View v){
|
||||||
|
itemView.setHasTransientState(true);
|
||||||
|
UiUtils.performAccountAction(getActivity(), item.account, accountID, relationship, actionButton, this::setActionProgressVisible, rel->{
|
||||||
|
itemView.setHasTransientState(false);
|
||||||
|
relationships.put(item.account.id, rel);
|
||||||
|
rebind();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setActionProgressVisible(boolean visible){
|
||||||
|
actionButton.setTextVisible(!visible);
|
||||||
|
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
actionButton.setClickable(!visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.fragments.HomeFragment;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.AccountField;
|
||||||
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
|
|
||||||
|
public class OnboardingProfileSetupFragment extends ToolbarFragment implements ReorderableLinearLayout.OnDragListener{
|
||||||
|
private Button btn;
|
||||||
|
private View buttonBar;
|
||||||
|
private String accountID;
|
||||||
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
private ScrollView scroller;
|
||||||
|
private EditText nameEdit, bioEdit;
|
||||||
|
private ImageView avaImage, coverImage;
|
||||||
|
private Button addRow;
|
||||||
|
private ReorderableLinearLayout profileFieldsLayout;
|
||||||
|
private Uri avatarUri, coverUri;
|
||||||
|
|
||||||
|
private static final int AVATAR_RESULT=348;
|
||||||
|
private static final int COVER_RESULT=183;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setRetainInstance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground));
|
||||||
|
accountID=getArguments().getString("account");
|
||||||
|
setTitle(R.string.profile_setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
|
View view=inflater.inflate(R.layout.fragment_onboarding_profile_setup, container, false);
|
||||||
|
|
||||||
|
scroller=view.findViewById(R.id.scroller);
|
||||||
|
nameEdit=view.findViewById(R.id.display_name);
|
||||||
|
bioEdit=view.findViewById(R.id.bio);
|
||||||
|
avaImage=view.findViewById(R.id.avatar);
|
||||||
|
coverImage=view.findViewById(R.id.header);
|
||||||
|
addRow=view.findViewById(R.id.add_row);
|
||||||
|
profileFieldsLayout=view.findViewById(R.id.profile_fields);
|
||||||
|
|
||||||
|
btn=view.findViewById(R.id.btn_next);
|
||||||
|
btn.setOnClickListener(v->onButtonClick());
|
||||||
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
|
|
||||||
|
avaImage.setOutlineProvider(OutlineProviders.roundedRect(24));
|
||||||
|
avaImage.setClipToOutline(true);
|
||||||
|
|
||||||
|
Account account=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
|
if(savedInstanceState==null){
|
||||||
|
nameEdit.setText(account.displayName);
|
||||||
|
makeFieldsRow();
|
||||||
|
}else{
|
||||||
|
ArrayList<String> fieldTitles=savedInstanceState.getStringArrayList("fieldTitles");
|
||||||
|
ArrayList<String> fieldValues=savedInstanceState.getStringArrayList("fieldValues");
|
||||||
|
for(int i=0;i<fieldTitles.size();i++){
|
||||||
|
View row=makeFieldsRow();
|
||||||
|
EditText title=row.findViewById(R.id.title);
|
||||||
|
EditText content=row.findViewById(R.id.content);
|
||||||
|
title.setText(fieldTitles.get(i));
|
||||||
|
content.setText(fieldValues.get(i));
|
||||||
|
}
|
||||||
|
if(fieldTitles.size()==4)
|
||||||
|
addRow.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRow.setOnClickListener(v->{
|
||||||
|
makeFieldsRow();
|
||||||
|
if(profileFieldsLayout.getChildCount()==4){
|
||||||
|
addRow.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
profileFieldsLayout.setDragListener(this);
|
||||||
|
avaImage.setOnClickListener(v->startActivityForResult(UiUtils.getMediaPickerIntent(new String[]{"image/*"}, 1), AVATAR_RESULT));
|
||||||
|
coverImage.setOnClickListener(v->startActivityForResult(UiUtils.getMediaPickerIntent(new String[]{"image/*"}, 1), COVER_RESULT));
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
scroller.setOnScrollChangeListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
|
if(onScrollListener!=null){
|
||||||
|
onScrollListener.setViews(buttonBar, getToolbar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onButtonClick(){
|
||||||
|
ArrayList<AccountField> fields=new ArrayList<>();
|
||||||
|
for(int i=0;i<profileFieldsLayout.getChildCount();i++){
|
||||||
|
View row=profileFieldsLayout.getChildAt(i);
|
||||||
|
EditText title=row.findViewById(R.id.title);
|
||||||
|
EditText content=row.findViewById(R.id.content);
|
||||||
|
AccountField fld=new AccountField();
|
||||||
|
fld.name=title.getText().toString();
|
||||||
|
fld.value=content.getText().toString();
|
||||||
|
fields.add(fld);
|
||||||
|
}
|
||||||
|
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), avatarUri, coverUri, fields)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Account result){
|
||||||
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, result);
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), OnboardingFollowSuggestionsFragment.class, args);
|
||||||
|
getActivity().getWindow().getDecorView().postDelayed(()->Nav.finish(OnboardingProfileSetupFragment.this), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.wrapProgress(getActivity(), R.string.saving, true)
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
|
if(Build.VERSION.SDK_INT>=27){
|
||||||
|
int inset=insets.getSystemWindowInsetBottom();
|
||||||
|
buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0);
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0));
|
||||||
|
}else{
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View makeFieldsRow(){
|
||||||
|
View view=LayoutInflater.from(getActivity()).inflate(R.layout.onboarding_profile_field, profileFieldsLayout, false);
|
||||||
|
profileFieldsLayout.addView(view);
|
||||||
|
view.findViewById(R.id.dragger_thingy).setOnLongClickListener(v->{
|
||||||
|
profileFieldsLayout.startDragging(view);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwapItems(int oldIndex, int newIndex){}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState){
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
ArrayList<String> fieldTitles=new ArrayList<>(), fieldValues=new ArrayList<>();
|
||||||
|
for(int i=0;i<profileFieldsLayout.getChildCount();i++){
|
||||||
|
View row=profileFieldsLayout.getChildAt(i);
|
||||||
|
EditText title=row.findViewById(R.id.title);
|
||||||
|
EditText content=row.findViewById(R.id.content);
|
||||||
|
fieldTitles.add(title.getText().toString());
|
||||||
|
fieldValues.add(content.getText().toString());
|
||||||
|
}
|
||||||
|
outState.putStringArrayList("fieldTitles", fieldTitles);
|
||||||
|
outState.putStringArrayList("fieldValues", fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||||
|
if(resultCode!=Activity.RESULT_OK)
|
||||||
|
return;
|
||||||
|
ImageView img;
|
||||||
|
Uri uri=data.getData();
|
||||||
|
int size;
|
||||||
|
if(requestCode==AVATAR_RESULT){
|
||||||
|
img=avaImage;
|
||||||
|
avatarUri=uri;
|
||||||
|
size=V.dp(100);
|
||||||
|
}else{
|
||||||
|
img=coverImage;
|
||||||
|
coverUri=uri;
|
||||||
|
size=V.dp(1000);
|
||||||
|
}
|
||||||
|
img.setForeground(null);
|
||||||
|
ViewImageLoader.load(img, null, new UrlImageLoaderRequest(uri, size, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canGoBack(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToolbarNavigationClick(){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.text.style.TypefaceSpan;
|
||||||
|
import android.text.style.URLSpan;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -16,11 +20,9 @@ import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
|
||||||
import org.joinmastodon.android.api.MastodonDetailedErrorResponse;
|
import org.joinmastodon.android.api.MastodonDetailedErrorResponse;
|
||||||
import org.joinmastodon.android.api.requests.accounts.RegisterAccount;
|
import org.joinmastodon.android.api.requests.accounts.RegisterAccount;
|
||||||
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
||||||
|
@ -31,18 +33,22 @@ import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
import org.joinmastodon.android.ui.text.LinkSpan;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.FloatingHintEditTextLayout;
|
import org.joinmastodon.android.ui.views.FloatingHintEditTextLayout;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.NodeVisitor;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -51,12 +57,10 @@ import me.grishka.appkit.api.APIRequest;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.ToolbarFragment;
|
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;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
|
|
||||||
public class SignupFragment extends ToolbarFragment{
|
public class SignupFragment extends ToolbarFragment{
|
||||||
private static final int AVATAR_RESULT=198;
|
|
||||||
private static final String TAG="SignupFragment";
|
private static final String TAG="SignupFragment";
|
||||||
|
|
||||||
private Instance instance;
|
private Instance instance;
|
||||||
|
@ -73,6 +77,7 @@ public class SignupFragment extends ToolbarFragment{
|
||||||
private boolean submitAfterGettingToken;
|
private boolean submitAfterGettingToken;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
private HashSet<EditText> errorFields=new HashSet<>();
|
private HashSet<EditText> errorFields=new HashSet<>();
|
||||||
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
@ -145,19 +150,22 @@ public class SignupFragment extends ToolbarFragment{
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.findViewById(R.id.scroller).setOnScrollChangeListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
@Override
|
||||||
protected void onUpdateToolbar(){
|
protected void onUpdateToolbar(){
|
||||||
// super.onUpdateToolbar();
|
super.onUpdateToolbar();
|
||||||
getToolbar().setBackground(null);
|
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
|
||||||
getToolbar().setElevation(0);
|
getToolbar().setElevation(0);
|
||||||
|
if(onScrollListener!=null){
|
||||||
|
onScrollListener.setViews(buttonBar, getToolbar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onButtonClick(){
|
private void onButtonClick(){
|
||||||
if(!password.getText().toString().equals(passwordConfirm.getText().toString())){
|
if(!password.getText().toString().equals(passwordConfirm.getText().toString())){
|
||||||
passwordConfirm.setError(getString(R.string.signup_passwords_dont_match));
|
passwordConfirmWrap.setErrorState(getString(R.string.signup_passwords_dont_match));
|
||||||
passwordConfirmWrap.setErrorState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showProgressDialog();
|
showProgressDialog();
|
||||||
|
@ -212,8 +220,22 @@ public class SignupFragment extends ToolbarFragment{
|
||||||
anyFieldsSkipped=true;
|
anyFieldsSkipped=true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
field.setError(fieldErrors.get(fieldName).stream().map(err->err.description).collect(Collectors.joining("\n")));
|
List<MastodonDetailedErrorResponse.FieldError> errors=Objects.requireNonNull(fieldErrors.get(fieldName));
|
||||||
getFieldWrapByName(fieldName).setErrorState();
|
if(errors.size()==1){
|
||||||
|
getFieldWrapByName(fieldName).setErrorState(getErrorDescription(errors.get(0), fieldName));
|
||||||
|
}else{
|
||||||
|
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||||
|
boolean firstErr=true;
|
||||||
|
for(MastodonDetailedErrorResponse.FieldError err:errors){
|
||||||
|
if(firstErr){
|
||||||
|
firstErr=false;
|
||||||
|
}else{
|
||||||
|
ssb.append('\n');
|
||||||
|
}
|
||||||
|
ssb.append(getErrorDescription(err, fieldName));
|
||||||
|
}
|
||||||
|
getFieldWrapByName(fieldName).setErrorState(getErrorDescription(errors.get(0), fieldName));
|
||||||
|
}
|
||||||
errorFields.add(field);
|
errorFields.add(field);
|
||||||
if(first){
|
if(first){
|
||||||
first=false;
|
first=false;
|
||||||
|
@ -231,6 +253,40 @@ public class SignupFragment extends ToolbarFragment{
|
||||||
.exec(instance.uri, apiToken);
|
.exec(instance.uri, apiToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CharSequence getErrorDescription(MastodonDetailedErrorResponse.FieldError error, String fieldName){
|
||||||
|
return switch(fieldName){
|
||||||
|
case "email" -> switch(error.error){
|
||||||
|
case "ERR_BLOCKED" -> {
|
||||||
|
String emailAddr=email.getText().toString();
|
||||||
|
String s=getResources().getString(R.string.signup_email_domain_blocked, TextUtils.htmlEncode(instance.uri), TextUtils.htmlEncode(emailAddr.substring(emailAddr.lastIndexOf('@')+1)));
|
||||||
|
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||||
|
Jsoup.parseBodyFragment(s).body().traverse(new NodeVisitor(){
|
||||||
|
private int spanStart;
|
||||||
|
@Override
|
||||||
|
public void head(Node node, int depth){
|
||||||
|
if(node instanceof TextNode tn){
|
||||||
|
ssb.append(tn.text());
|
||||||
|
}else if(node instanceof Element){
|
||||||
|
spanStart=ssb.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tail(Node node, int depth){
|
||||||
|
if(node instanceof Element){
|
||||||
|
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
yield ssb;
|
||||||
|
}
|
||||||
|
default -> error.description;
|
||||||
|
};
|
||||||
|
default -> error.description;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private EditText getFieldByName(String name){
|
private EditText getFieldByName(String name){
|
||||||
return switch(name){
|
return switch(name){
|
||||||
case "email" -> email;
|
case "email" -> email;
|
||||||
|
@ -323,6 +379,11 @@ public class SignupFragment extends ToolbarFragment{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onGoBackLinkClick(LinkSpan span){
|
||||||
|
setResult(false, null);
|
||||||
|
Nav.finish(this);
|
||||||
|
}
|
||||||
|
|
||||||
private class ErrorClearingListener implements TextWatcher{
|
private class ErrorClearingListener implements TextWatcher{
|
||||||
public final EditText editText;
|
public final EditText editText;
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,14 @@ public class Account extends BaseModel{
|
||||||
*/
|
*/
|
||||||
public Instant muteExpiresAt;
|
public Instant muteExpiresAt;
|
||||||
|
|
||||||
|
public List<Role> roles;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class Role {
|
||||||
|
public String name;
|
||||||
|
/** #rrggbb */
|
||||||
|
public String color;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class Filter extends BaseModel{
|
||||||
public String title;
|
public String title;
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public String phrase;
|
public String phrase;
|
||||||
|
public String title;
|
||||||
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
|
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
|
||||||
public Instant expiresAt;
|
public Instant expiresAt;
|
||||||
public boolean irreversible;
|
public boolean irreversible;
|
||||||
|
@ -52,6 +53,7 @@ public class Filter extends BaseModel{
|
||||||
else
|
else
|
||||||
pattern=Pattern.compile(Pattern.quote(phrase), Pattern.CASE_INSENSITIVE);
|
pattern=Pattern.compile(Pattern.quote(phrase), Pattern.CASE_INSENSITIVE);
|
||||||
}
|
}
|
||||||
|
if (title == null) title = phrase;
|
||||||
return pattern.matcher(text).find();
|
return pattern.matcher(text).find();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +82,9 @@ public class Filter extends BaseModel{
|
||||||
@SerializedName("public")
|
@SerializedName("public")
|
||||||
PUBLIC,
|
PUBLIC,
|
||||||
@SerializedName("thread")
|
@SerializedName("thread")
|
||||||
THREAD
|
THREAD,
|
||||||
|
@SerializedName("account")
|
||||||
|
ACCOUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FilterAction{
|
public enum FilterAction{
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class Instance extends BaseModel{
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public String version;
|
public String version;
|
||||||
/**
|
/**
|
||||||
* Primary langauges of the website and its staff.
|
* Primary languages of the website and its staff.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
// @RequiredField
|
||||||
public List<String> languages;
|
public List<String> languages;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class ParsedAccount{
|
||||||
|
public Account account;
|
||||||
|
public CharSequence parsedName, parsedBio;
|
||||||
|
public CustomEmojiHelper emojiHelper;
|
||||||
|
public ImageLoaderRequest avatarRequest;
|
||||||
|
|
||||||
|
public ParsedAccount(Account account, String accountID){
|
||||||
|
this.account=account;
|
||||||
|
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
|
||||||
|
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
|
|
||||||
|
emojiHelper=new CustomEmojiHelper();
|
||||||
|
SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName);
|
||||||
|
ssb.append(parsedBio);
|
||||||
|
emojiHelper.setText(ssb);
|
||||||
|
|
||||||
|
avatarRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(40), V.dp(40));
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,10 @@ public class Relationship extends BaseModel{
|
||||||
public boolean blockedBy;
|
public boolean blockedBy;
|
||||||
public String note;
|
public String note;
|
||||||
|
|
||||||
|
public boolean canFollow(){
|
||||||
|
return !(following || blocking || blockedBy || domainBlocking);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "Relationship{"+
|
return "Relationship{"+
|
||||||
|
|
|
@ -62,19 +62,13 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
||||||
}
|
}
|
||||||
|
|
||||||
public Status toStatus() {
|
public Status toStatus() {
|
||||||
Status s = new Status();
|
Status s = Status.ofFake(id, params.text, scheduledAt);
|
||||||
s.id = id;
|
|
||||||
s.mediaAttachments = mediaAttachments;
|
s.mediaAttachments = mediaAttachments;
|
||||||
s.createdAt = scheduledAt;
|
|
||||||
s.inReplyToId = params.inReplyToId > 0 ? "" + params.inReplyToId : null;
|
s.inReplyToId = params.inReplyToId > 0 ? "" + params.inReplyToId : null;
|
||||||
s.content = s.text = params.text;
|
|
||||||
s.spoilerText = params.spoilerText;
|
s.spoilerText = params.spoilerText;
|
||||||
s.visibility = params.visibility;
|
s.visibility = params.visibility;
|
||||||
s.language = params.language;
|
s.language = params.language;
|
||||||
s.sensitive = params.sensitive;
|
s.sensitive = params.sensitive;
|
||||||
s.mentions = List.of();
|
|
||||||
s.tags = List.of();
|
|
||||||
s.emojis = List.of();
|
|
||||||
if (params.poll != null) s.poll = params.poll.toPoll();
|
if (params.poll != null) s.poll = params.poll.toPoll();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,9 +58,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||||
public boolean bookmarked;
|
public boolean bookmarked;
|
||||||
public boolean pinned;
|
public boolean pinned;
|
||||||
|
|
||||||
public boolean filterRevealed;
|
public transient boolean filterRevealed;
|
||||||
|
|
||||||
public transient boolean spoilerRevealed;
|
public transient boolean spoilerRevealed;
|
||||||
|
public transient boolean textExpanded, textExpandable;
|
||||||
public transient boolean hasGapAfter;
|
public transient boolean hasGapAfter;
|
||||||
private transient String strippedText;
|
private transient String strippedText;
|
||||||
|
|
||||||
|
@ -160,6 +160,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||||
s.mentions = List.of();
|
s.mentions = List.of();
|
||||||
s.tags = List.of();
|
s.tags = List.of();
|
||||||
s.emojis = List.of();
|
s.emojis = List.of();
|
||||||
|
s.filtered = List.of();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
|
@ -140,7 +142,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView name, username, timestamp, extraText, separator;
|
private final TextView name, username, timestamp, extraText, separator;
|
||||||
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator, botIcon;
|
private final View collapseBtn;
|
||||||
|
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator;
|
||||||
private final PopupMenu optionsMenu;
|
private final PopupMenu optionsMenu;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private APIRequest<?> currentRelationshipRequest;
|
private APIRequest<?> currentRelationshipRequest;
|
||||||
|
@ -163,6 +166,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
visibility=findViewById(R.id.visibility);
|
visibility=findViewById(R.id.visibility);
|
||||||
deleteNotification=findViewById(R.id.delete_notification);
|
deleteNotification=findViewById(R.id.delete_notification);
|
||||||
unreadIndicator=findViewById(R.id.unread_indicator);
|
unreadIndicator=findViewById(R.id.unread_indicator);
|
||||||
|
collapseBtn=findViewById(R.id.collapse_btn);
|
||||||
|
collapseBtnIcon=findViewById(R.id.collapse_btn_icon);
|
||||||
botIcon=findViewById(R.id.bot_icon);
|
botIcon=findViewById(R.id.bot_icon);
|
||||||
extraText=findViewById(R.id.extra_text);
|
extraText=findViewById(R.id.extra_text);
|
||||||
avatar.setOnClickListener(this::onAvaClick);
|
avatar.setOnClickListener(this::onAvaClick);
|
||||||
|
@ -175,6 +180,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
fragment.removeNotification(item.notification);
|
fragment.removeNotification(item.notification);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
collapseBtn.setOnClickListener(l -> item.parentFragment.onToggleExpanded(item.status, getItemID()));
|
||||||
|
|
||||||
optionsMenu=new PopupMenu(activity, more);
|
optionsMenu=new PopupMenu(activity, more);
|
||||||
|
|
||||||
|
@ -326,7 +332,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
||||||
timestamp.setText(item.scheduledStatus.scheduledAt.atZone(ZoneId.systemDefault()).format(formatter));
|
timestamp.setText(item.scheduledStatus.scheduledAt.atZone(ZoneId.systemDefault()).format(formatter));
|
||||||
}
|
}
|
||||||
else if ((item.status==null || item.status.editedAt==null) && item.createdAt != null)
|
else if ((!item.inset || item.status==null || item.status.editedAt==null) && item.createdAt != null)
|
||||||
timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt));
|
timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt));
|
||||||
else if (item.status != null && item.status.editedAt != null)
|
else if (item.status != null && item.status.editedAt != null)
|
||||||
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
||||||
|
@ -400,6 +406,16 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
more.setContentDescription(desc);
|
more.setContentDescription(desc);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) more.setTooltipText(desc);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) more.setTooltipText(desc);
|
||||||
|
|
||||||
|
if (item.status == null || !item.status.textExpandable) {
|
||||||
|
collapseBtn.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
String collapseText = item.parentFragment.getString(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
||||||
|
collapseBtn.setVisibility(item.status.textExpandable ? View.VISIBLE : View.GONE);
|
||||||
|
collapseBtn.setContentDescription(collapseText);
|
||||||
|
if (GlobalUserPreferences.reduceMotion) collapseBtnIcon.setScaleY(item.status.textExpanded ? -1 : 1);
|
||||||
|
else collapseBtnIcon.animate().scaleY(item.status.textExpanded ? -1 : 1).start();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) collapseBtn.setTooltipText(collapseText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
@ -19,6 +28,7 @@ import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||||
import androidx.annotation.LayoutRes;
|
import androidx.annotation.LayoutRes;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
|
|
||||||
public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
public final int index;
|
public final int index;
|
||||||
|
@ -56,11 +66,35 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
||||||
private boolean didClear;
|
private boolean didClear;
|
||||||
|
|
||||||
|
private AnimatorSet currentAnim;
|
||||||
|
private final FrameLayout altTextWrapper;
|
||||||
|
private final TextView altTextButton;
|
||||||
|
private final ImageView noAltTextButton;
|
||||||
|
private final View altTextScroller;
|
||||||
|
private final ImageButton altTextClose;
|
||||||
|
private final TextView altText, noAltText;
|
||||||
|
|
||||||
|
private View altOrNoAltButton;
|
||||||
|
private boolean altTextShown;
|
||||||
|
|
||||||
public Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
public Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
||||||
super(activity, layout, parent);
|
super(activity, layout, parent);
|
||||||
photo=findViewById(R.id.photo);
|
photo=findViewById(R.id.photo);
|
||||||
photo.setOnClickListener(this::onViewClick);
|
photo.setOnClickListener(this::onViewClick);
|
||||||
this.layout=(ImageAttachmentFrameLayout)itemView;
|
this.layout=(ImageAttachmentFrameLayout)itemView;
|
||||||
|
|
||||||
|
altTextWrapper=findViewById(R.id.alt_text_wrapper);
|
||||||
|
altTextButton=findViewById(R.id.alt_button);
|
||||||
|
noAltTextButton=findViewById(R.id.no_alt_button);
|
||||||
|
altTextScroller=findViewById(R.id.alt_text_scroller);
|
||||||
|
altTextClose=findViewById(R.id.alt_text_close);
|
||||||
|
altText=findViewById(R.id.alt_text);
|
||||||
|
noAltText=findViewById(R.id.no_alt_text);
|
||||||
|
|
||||||
|
altTextButton.setOnClickListener(this::onShowHideClick);
|
||||||
|
noAltTextButton.setOnClickListener(this::onShowHideClick);
|
||||||
|
altTextClose.setOnClickListener(this::onShowHideClick);
|
||||||
|
// altTextScroller.setNestedScrollingEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,6 +107,111 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
||||||
photo.setImageDrawable(crossfadeDrawable);
|
photo.setImageDrawable(crossfadeDrawable);
|
||||||
photo.setContentDescription(TextUtils.isEmpty(item.attachment.description) ? item.parentFragment.getString(R.string.media_no_description) : item.attachment.description);
|
photo.setContentDescription(TextUtils.isEmpty(item.attachment.description) ? item.parentFragment.getString(R.string.media_no_description) : item.attachment.description);
|
||||||
didClear=false;
|
didClear=false;
|
||||||
|
|
||||||
|
if (currentAnim != null) currentAnim.cancel();
|
||||||
|
|
||||||
|
boolean altTextMissing = TextUtils.isEmpty(item.attachment.description);
|
||||||
|
altOrNoAltButton = altTextMissing ? noAltTextButton : altTextButton;
|
||||||
|
altTextShown=false;
|
||||||
|
|
||||||
|
altTextScroller.setVisibility(View.GONE);
|
||||||
|
altTextClose.setVisibility(View.GONE);
|
||||||
|
altTextButton.setVisibility(View.VISIBLE);
|
||||||
|
noAltTextButton.setVisibility(View.VISIBLE);
|
||||||
|
altTextButton.setAlpha(1f);
|
||||||
|
noAltTextButton.setAlpha(1f);
|
||||||
|
altTextWrapper.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (altTextMissing){
|
||||||
|
if (GlobalUserPreferences.showNoAltIndicator) {
|
||||||
|
noAltTextButton.setVisibility(View.VISIBLE);
|
||||||
|
noAltText.setVisibility(View.VISIBLE);
|
||||||
|
altTextWrapper.setBackgroundResource(R.drawable.bg_image_no_alt_overlay);
|
||||||
|
altTextButton.setVisibility(View.GONE);
|
||||||
|
altText.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
altTextWrapper.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (GlobalUserPreferences.showAltIndicator) {
|
||||||
|
noAltTextButton.setVisibility(View.GONE);
|
||||||
|
noAltText.setVisibility(View.GONE);
|
||||||
|
altTextWrapper.setBackgroundResource(R.drawable.bg_image_alt_overlay);
|
||||||
|
altTextButton.setVisibility(View.VISIBLE);
|
||||||
|
altTextButton.setText(R.string.sk_alt_button);
|
||||||
|
altText.setVisibility(View.VISIBLE);
|
||||||
|
altText.setText(item.attachment.description);
|
||||||
|
altText.setPadding(0, 0, 0, 0);
|
||||||
|
} else {
|
||||||
|
altTextWrapper.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onShowHideClick(View v){
|
||||||
|
boolean show=v.getId()==R.id.alt_button || v.getId()==R.id.no_alt_button;
|
||||||
|
|
||||||
|
if(altTextShown==show)
|
||||||
|
return;
|
||||||
|
if(currentAnim!=null)
|
||||||
|
currentAnim.cancel();
|
||||||
|
|
||||||
|
altTextShown=show;
|
||||||
|
if(show){
|
||||||
|
altTextScroller.setVisibility(View.VISIBLE);
|
||||||
|
altTextClose.setVisibility(View.VISIBLE);
|
||||||
|
}else{
|
||||||
|
altOrNoAltButton.setVisibility(View.VISIBLE);
|
||||||
|
// Hide these views temporarily so FrameLayout measures correctly
|
||||||
|
altTextScroller.setVisibility(View.GONE);
|
||||||
|
altTextClose.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the current size...
|
||||||
|
int prevLeft=altTextWrapper.getLeft();
|
||||||
|
int prevRight=altTextWrapper.getRight();
|
||||||
|
int prevTop=altTextWrapper.getTop();
|
||||||
|
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
|
@Override
|
||||||
|
public boolean onPreDraw(){
|
||||||
|
altTextWrapper.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
|
||||||
|
// ...and this is after the layout pass, right now the FrameLayout has its final size, but we animate that change
|
||||||
|
if(!show){
|
||||||
|
// Show these views again so they're visible for the duration of the animation.
|
||||||
|
// No one would notice they were missing during measure/layout.
|
||||||
|
altTextScroller.setVisibility(View.VISIBLE);
|
||||||
|
altTextClose.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
AnimatorSet set=new AnimatorSet();
|
||||||
|
set.playTogether(
|
||||||
|
ObjectAnimator.ofInt(altTextWrapper, "left", prevLeft, altTextWrapper.getLeft()),
|
||||||
|
ObjectAnimator.ofInt(altTextWrapper, "right", prevRight, altTextWrapper.getRight()),
|
||||||
|
ObjectAnimator.ofInt(altTextWrapper, "top", prevTop, altTextWrapper.getTop()),
|
||||||
|
ObjectAnimator.ofFloat(altOrNoAltButton, View.ALPHA, show ? 1f : 0f, show ? 0f : 1f),
|
||||||
|
ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f),
|
||||||
|
ObjectAnimator.ofFloat(altTextClose, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f)
|
||||||
|
);
|
||||||
|
set.setDuration(300);
|
||||||
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation){
|
||||||
|
if(show){
|
||||||
|
altOrNoAltButton.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
altTextScroller.setVisibility(View.GONE);
|
||||||
|
altTextClose.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
currentAnim=null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
set.start();
|
||||||
|
currentAnim=set;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,150 +37,9 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
return Type.PHOTO;
|
return Type.PHOTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends ImageStatusDisplayItem.Holder<PhotoStatusDisplayItem>{
|
public static class Holder extends ImageStatusDisplayItem.Holder<PhotoStatusDisplayItem> {
|
||||||
private final FrameLayout altTextWrapper;
|
public Holder(Activity activity, ViewGroup parent) {
|
||||||
private final TextView altTextButton;
|
|
||||||
private final ImageView noAltTextButton;
|
|
||||||
private final View altTextScroller;
|
|
||||||
private final ImageButton altTextClose;
|
|
||||||
private final TextView altText, noAltText;
|
|
||||||
|
|
||||||
private View altOrNoAltButton;
|
|
||||||
private boolean altTextShown;
|
|
||||||
private AnimatorSet currentAnim;
|
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
|
||||||
super(activity, R.layout.display_item_photo, parent);
|
super(activity, R.layout.display_item_photo, parent);
|
||||||
altTextWrapper=findViewById(R.id.alt_text_wrapper);
|
|
||||||
altTextButton=findViewById(R.id.alt_button);
|
|
||||||
noAltTextButton=findViewById(R.id.no_alt_button);
|
|
||||||
altTextScroller=findViewById(R.id.alt_text_scroller);
|
|
||||||
altTextClose=findViewById(R.id.alt_text_close);
|
|
||||||
altText=findViewById(R.id.alt_text);
|
|
||||||
noAltText=findViewById(R.id.no_alt_text);
|
|
||||||
|
|
||||||
altTextButton.setOnClickListener(this::onShowHideClick);
|
|
||||||
noAltTextButton.setOnClickListener(this::onShowHideClick);
|
|
||||||
altTextClose.setOnClickListener(this::onShowHideClick);
|
|
||||||
// altTextScroller.setNestedScrollingEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(ImageStatusDisplayItem item){
|
|
||||||
super.onBind(item);
|
|
||||||
boolean altTextMissing = TextUtils.isEmpty(item.attachment.description);
|
|
||||||
altOrNoAltButton = altTextMissing ? noAltTextButton : altTextButton;
|
|
||||||
altTextShown=false;
|
|
||||||
if(currentAnim!=null)
|
|
||||||
currentAnim.cancel();
|
|
||||||
|
|
||||||
altTextScroller.setVisibility(View.GONE);
|
|
||||||
altTextClose.setVisibility(View.GONE);
|
|
||||||
altTextButton.setVisibility(View.VISIBLE);
|
|
||||||
noAltTextButton.setVisibility(View.VISIBLE);
|
|
||||||
altTextButton.setAlpha(1f);
|
|
||||||
noAltTextButton.setAlpha(1f);
|
|
||||||
altTextWrapper.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
if (altTextMissing){
|
|
||||||
if (GlobalUserPreferences.showNoAltIndicator) {
|
|
||||||
noAltTextButton.setVisibility(View.VISIBLE);
|
|
||||||
noAltText.setVisibility(View.VISIBLE);
|
|
||||||
altTextWrapper.setBackgroundResource(R.drawable.bg_image_no_alt_overlay);
|
|
||||||
altTextButton.setVisibility(View.GONE);
|
|
||||||
altText.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
altTextWrapper.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (GlobalUserPreferences.showAltIndicator) {
|
|
||||||
noAltTextButton.setVisibility(View.GONE);
|
|
||||||
noAltText.setVisibility(View.GONE);
|
|
||||||
altTextWrapper.setBackgroundResource(R.drawable.bg_image_alt_overlay);
|
|
||||||
altTextButton.setVisibility(View.VISIBLE);
|
|
||||||
altTextButton.setText(R.string.sk_alt_button);
|
|
||||||
altText.setVisibility(View.VISIBLE);
|
|
||||||
altText.setText(item.attachment.description);
|
|
||||||
altText.setPadding(0, 0, 0, 0);
|
|
||||||
} else {
|
|
||||||
altTextWrapper.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!item.status.filterRevealed){
|
|
||||||
this.itemView.setVisibility(View.GONE);
|
|
||||||
ViewGroup.LayoutParams params = this.itemView.getLayoutParams();
|
|
||||||
params.height = 0;
|
|
||||||
params.width = 0;
|
|
||||||
this.itemView.setLayoutParams(params);
|
|
||||||
// item.parentFragment.notifyItemsChanged(this.getAbsoluteAdapterPosition());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onShowHideClick(View v){
|
|
||||||
boolean show=v.getId()==R.id.alt_button || v.getId()==R.id.no_alt_button;
|
|
||||||
|
|
||||||
if(altTextShown==show)
|
|
||||||
return;
|
|
||||||
if(currentAnim!=null)
|
|
||||||
currentAnim.cancel();
|
|
||||||
|
|
||||||
altTextShown=show;
|
|
||||||
if(show){
|
|
||||||
altTextScroller.setVisibility(View.VISIBLE);
|
|
||||||
altTextClose.setVisibility(View.VISIBLE);
|
|
||||||
}else{
|
|
||||||
altOrNoAltButton.setVisibility(View.VISIBLE);
|
|
||||||
// Hide these views temporarily so FrameLayout measures correctly
|
|
||||||
altTextScroller.setVisibility(View.GONE);
|
|
||||||
altTextClose.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the current size...
|
|
||||||
int prevLeft=altTextWrapper.getLeft();
|
|
||||||
int prevRight=altTextWrapper.getRight();
|
|
||||||
int prevTop=altTextWrapper.getTop();
|
|
||||||
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
|
||||||
@Override
|
|
||||||
public boolean onPreDraw(){
|
|
||||||
altTextWrapper.getViewTreeObserver().removeOnPreDrawListener(this);
|
|
||||||
|
|
||||||
// ...and this is after the layout pass, right now the FrameLayout has its final size, but we animate that change
|
|
||||||
if(!show){
|
|
||||||
// Show these views again so they're visible for the duration of the animation.
|
|
||||||
// No one would notice they were missing during measure/layout.
|
|
||||||
altTextScroller.setVisibility(View.VISIBLE);
|
|
||||||
altTextClose.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
|
||||||
set.playTogether(
|
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "left", prevLeft, altTextWrapper.getLeft()),
|
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "right", prevRight, altTextWrapper.getRight()),
|
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "top", prevTop, altTextWrapper.getTop()),
|
|
||||||
ObjectAnimator.ofFloat(altOrNoAltButton, View.ALPHA, show ? 1f : 0f, show ? 0f : 1f),
|
|
||||||
ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f),
|
|
||||||
ObjectAnimator.ofFloat(altTextClose, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f)
|
|
||||||
);
|
|
||||||
set.setDuration(300);
|
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation){
|
|
||||||
if(show){
|
|
||||||
altOrNoAltButton.setVisibility(View.GONE);
|
|
||||||
}else{
|
|
||||||
altTextScroller.setVisibility(View.GONE);
|
|
||||||
altTextClose.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
currentAnim=null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
set.start();
|
|
||||||
currentAnim=set;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,34 +81,40 @@ public abstract class StatusDisplayItem{
|
||||||
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
|
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
|
||||||
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
|
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
|
||||||
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
||||||
case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent);
|
|
||||||
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
|
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
|
||||||
|
case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, Filter.FilterContext filterContext){
|
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification){
|
||||||
|
return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, false, Filter.FilterContext.HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, Filter.FilterContext filterContext){
|
||||||
return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, false, filterContext);
|
return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, false, filterContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate, Filter.FilterContext filterContext){
|
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate){
|
||||||
|
return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, disableTranslate, Filter.FilterContext.HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate, Filter.FilterContext filterContext){
|
||||||
String parentID=parentObject.getID();
|
String parentID=parentObject.getID();
|
||||||
ArrayList<StatusDisplayItem> items=new ArrayList<>();
|
ArrayList<StatusDisplayItem> items=new ArrayList<>();
|
||||||
|
|
||||||
ArrayList<StatusDisplayItem> filtered=new ArrayList<>();
|
|
||||||
|
|
||||||
Status statusForContent=status.getContentStatus();
|
Status statusForContent=status.getContentStatus();
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
|
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
|
||||||
|
|
||||||
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(filterContext)).collect(Collectors.toList());
|
List<Filter> filters = AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream()
|
||||||
|
.filter(f -> f.context.contains(filterContext)).collect(Collectors.toList());
|
||||||
StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters);
|
StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters);
|
||||||
|
|
||||||
if(!statusForContent.filterRevealed){
|
if(!statusForContent.filterRevealed){
|
||||||
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
|
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(status.reblog!=null){
|
if(status.reblog!=null){
|
||||||
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
|
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
|
||||||
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
|
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
|
||||||
|
@ -189,9 +195,10 @@ public abstract class StatusDisplayItem{
|
||||||
item.index=i++;
|
item.index=i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!statusForContent.filterRevealed){
|
if (!statusForContent.filterRevealed) {
|
||||||
filtered.add(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items));
|
return new ArrayList<>(List.of(
|
||||||
return filtered;
|
new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
|
|
@ -3,14 +3,17 @@ package org.joinmastodon.android.ui.displayitems;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.LayerDrawable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||||
|
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
||||||
|
@ -19,12 +22,14 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.model.TranslatedStatus;
|
import org.joinmastodon.android.model.TranslatedStatus;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
|
import org.joinmastodon.android.utils.StatusTextEncoder;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
@ -44,6 +49,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
public boolean translated = false;
|
public boolean translated = false;
|
||||||
public TranslatedStatus translation = null;
|
public TranslatedStatus translation = null;
|
||||||
private AccountSession session;
|
private AccountSession session;
|
||||||
|
public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48");
|
||||||
|
|
||||||
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status, boolean disableTranslate){
|
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status, boolean disableTranslate){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
|
@ -81,10 +87,14 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final LinkedTextView text;
|
private final LinkedTextView text;
|
||||||
private final LinearLayout spoilerHeader;
|
private final LinearLayout spoilerHeader;
|
||||||
private final TextView spoilerTitle, spoilerTitleInline, translateInfo;
|
private final TextView spoilerTitle, spoilerTitleInline, translateInfo, readMore;
|
||||||
private final View spoilerOverlay, borderTop, borderBottom, textWrap, translateWrap, translateProgress;
|
private final View spoilerOverlay, borderTop, borderBottom, textWrap, translateWrap, translateProgress, spaceBelowText;
|
||||||
private final Drawable backgroundColor, borderColor;
|
private final int backgroundColor, borderColor;
|
||||||
private final Button translateButton;
|
private final Button translateButton;
|
||||||
|
private final ScrollView textScrollView;
|
||||||
|
|
||||||
|
private final float textMaxHeight, textCollapsedHeight;
|
||||||
|
private final LinearLayout.LayoutParams collapseParams, wrapParams;
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_text, parent);
|
super(activity, R.layout.display_item_text, parent);
|
||||||
|
@ -101,14 +111,16 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
translateInfo=findViewById(R.id.translate_info);
|
translateInfo=findViewById(R.id.translate_info);
|
||||||
translateProgress=findViewById(R.id.translate_progress);
|
translateProgress=findViewById(R.id.translate_progress);
|
||||||
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
||||||
|
backgroundColor=UiUtils.getThemeColor(activity, R.attr.colorBackgroundLight);
|
||||||
TypedValue outValue=new TypedValue();
|
borderColor=UiUtils.getThemeColor(activity, R.attr.colorPollVoted);
|
||||||
activity.getTheme().resolveAttribute(R.attr.colorBackgroundLight, outValue, true);
|
textScrollView=findViewById(R.id.text_scroll_view);
|
||||||
backgroundColor=activity.getDrawable(outValue.resourceId);
|
readMore=findViewById(R.id.read_more);
|
||||||
// activity.getTheme().resolveAttribute(R.attr.colorBackgroundLightest, outValue, true);
|
spaceBelowText=findViewById(R.id.space_below_text);
|
||||||
// backgroundColorInset=activity.getDrawable(outValue.resourceId);
|
textMaxHeight=activity.getResources().getDimension(R.dimen.text_max_height);
|
||||||
activity.getTheme().resolveAttribute(R.attr.colorPollVoted, outValue, true);
|
textCollapsedHeight=activity.getResources().getDimension(R.dimen.text_collapsed_height);
|
||||||
borderColor=activity.getDrawable(outValue.resourceId);
|
collapseParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) textCollapsedHeight);
|
||||||
|
wrapParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
readMore.setOnClickListener(v -> item.parentFragment.onToggleExpanded(item.status, getItemID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -117,6 +129,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
|
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
|
||||||
: item.text);
|
: item.text);
|
||||||
text.setTextIsSelectable(item.textSelectable);
|
text.setTextIsSelectable(item.textSelectable);
|
||||||
|
if (item.textSelectable) {
|
||||||
|
textScrollView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
|
}
|
||||||
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
||||||
text.setInvalidateOnEveryFrame(false);
|
text.setInvalidateOnEveryFrame(false);
|
||||||
spoilerTitleInline.setBackground(item.inset ? null : backgroundColor);
|
spoilerTitleInline.setBackground(item.inset ? null : backgroundColor);
|
||||||
|
@ -149,18 +164,30 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
||||||
instanceInfo.v2.configuration.translation.enabled;
|
instanceInfo.v2.configuration.translation.enabled;
|
||||||
|
|
||||||
translateWrap.setVisibility(translateEnabled &&
|
boolean isBottomText = BOTTOM_TEXT_PATTERN.matcher(item.status.getStrippedText()).find();
|
||||||
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
boolean translateVisible = (isBottomText || (
|
||||||
item.status.language != null &&
|
translateEnabled &&
|
||||||
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))
|
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||||
? View.VISIBLE : View.GONE);
|
item.status.language != null &&
|
||||||
|
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))))
|
||||||
|
&& (!GlobalUserPreferences.translateButtonOpenedOnly || item.textSelectable);
|
||||||
|
translateWrap.setVisibility(translateVisible ? View.VISIBLE : View.GONE);
|
||||||
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
|
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
|
||||||
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : "");
|
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, isBottomText ? "bottom-java" : item.translation.provider) : "");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
translateButton.setOnClickListener(v->{
|
translateButton.setOnClickListener(v->{
|
||||||
if (item.translation == null) {
|
if (item.translation == null) {
|
||||||
|
if (isBottomText) {
|
||||||
|
try {
|
||||||
|
item.translation = new TranslatedStatus();
|
||||||
|
item.translation.content = new StatusTextEncoder(Bottom::decode).decode(item.status.getStrippedText(), BOTTOM_TEXT_PATTERN);
|
||||||
|
item.translated = true;
|
||||||
|
} catch (TranslationError err) {
|
||||||
|
item.translation = null;
|
||||||
|
Toast.makeText(itemView.getContext(), err.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
rebind();
|
||||||
|
return;
|
||||||
|
}
|
||||||
translateProgress.setVisibility(View.VISIBLE);
|
translateProgress.setVisibility(View.VISIBLE);
|
||||||
translateButton.setClickable(false);
|
translateButton.setClickable(false);
|
||||||
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
|
@ -190,6 +217,25 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
||||||
|
spaceBelowText.setVisibility(translateVisible ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (!GlobalUserPreferences.collapseLongPosts) {
|
||||||
|
textScrollView.setLayoutParams(wrapParams);
|
||||||
|
readMore.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GlobalUserPreferences.collapseLongPosts) text.post(() -> {
|
||||||
|
boolean tooBig = text.getMeasuredHeight() > textMaxHeight;
|
||||||
|
boolean inTimeline = !item.textSelectable;
|
||||||
|
boolean hasSpoiler = !TextUtils.isEmpty(item.status.spoilerText);
|
||||||
|
boolean expandable = inTimeline && tooBig && !hasSpoiler;
|
||||||
|
item.parentFragment.onEnableExpandable(this, expandable);
|
||||||
|
});
|
||||||
|
|
||||||
|
readMore.setVisibility(item.status.textExpandable && !item.status.textExpanded ? View.VISIBLE : View.GONE);
|
||||||
|
textScrollView.setLayoutParams(item.status.textExpandable && !item.status.textExpanded ? collapseParams : wrapParams);
|
||||||
|
if (item.status.textExpandable && !translateVisible) spaceBelowText.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -602,7 +602,7 @@ public class TabLayout extends HorizontalScrollView {
|
||||||
* <p>If the tab indicator color is not {@code Color.TRANSPARENT}, the indicator will be wrapped
|
* <p>If the tab indicator color is not {@code Color.TRANSPARENT}, the indicator will be wrapped
|
||||||
* and tinted right before it is drawn by {@link SlidingTabIndicator#draw(Canvas)}. If you'd like
|
* and tinted right before it is drawn by {@link SlidingTabIndicator#draw(Canvas)}. If you'd like
|
||||||
* the inherent color or the tinted color of a custom drawable to be used, make sure this color is
|
* the inherent color or the tinted color of a custom drawable to be used, make sure this color is
|
||||||
* set to {@code Color.TRANSPARENT} to avoid your color/tint being overriden.
|
* set to {@code Color.TRANSPARENT} to avoid your color/tint being overridden.
|
||||||
*
|
*
|
||||||
* @param color color to use for the indicator
|
* @param color color to use for the indicator
|
||||||
* @attr ref com.google.android.material.R.styleable#TabLayout_tabIndicatorColor
|
* @attr ref com.google.android.material.R.styleable#TabLayout_tabIndicatorColor
|
||||||
|
|
|
@ -18,6 +18,10 @@ public class LinkSpan extends CharacterStyle {
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
|
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID){
|
||||||
|
this(link, listener, type, accountID, null);
|
||||||
|
}
|
||||||
|
|
||||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, String text){
|
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, String text){
|
||||||
this.listener=listener;
|
this.listener=listener;
|
||||||
this.link=link;
|
this.link=link;
|
||||||
|
@ -40,6 +44,7 @@ public class LinkSpan extends CharacterStyle {
|
||||||
case URL -> UiUtils.openURL(context, accountID, link);
|
case URL -> UiUtils.openURL(context, accountID, link);
|
||||||
case MENTION -> UiUtils.openProfileByID(context, accountID, link);
|
case MENTION -> UiUtils.openProfileByID(context, accountID, link);
|
||||||
case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link, null);
|
case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link, null);
|
||||||
|
case CUSTOM -> listener.onLinkClick(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +78,7 @@ public class LinkSpan extends CharacterStyle {
|
||||||
public enum Type{
|
public enum Type{
|
||||||
URL,
|
URL,
|
||||||
MENTION,
|
MENTION,
|
||||||
HASHTAG
|
HASHTAG,
|
||||||
|
CUSTOM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.os.ext.SdkExtensions;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
@ -658,8 +661,40 @@ public class UiUtils{
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void performAccountAction(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback){
|
public static void setRelationshipToActionButtonM3(Relationship relationship, Button button){
|
||||||
|
boolean secondaryStyle;
|
||||||
if(relationship.blocking){
|
if(relationship.blocking){
|
||||||
|
button.setText(R.string.button_blocked);
|
||||||
|
secondaryStyle=true;
|
||||||
|
}else if(relationship.blockedBy){
|
||||||
|
button.setText(R.string.button_follow);
|
||||||
|
secondaryStyle=false;
|
||||||
|
}else if(relationship.requested){
|
||||||
|
button.setText(R.string.button_follow_pending);
|
||||||
|
secondaryStyle=true;
|
||||||
|
}else if(!relationship.following){
|
||||||
|
button.setText(relationship.followedBy ? R.string.follow_back : R.string.button_follow);
|
||||||
|
secondaryStyle=false;
|
||||||
|
}else{
|
||||||
|
button.setText(R.string.button_following);
|
||||||
|
secondaryStyle=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setEnabled(!relationship.blockedBy);
|
||||||
|
int styleRes=secondaryStyle ? R.style.Widget_Mastodon_M3_Button_Tonal : R.style.Widget_Mastodon_M3_Button_Filled;
|
||||||
|
TypedArray ta=button.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
||||||
|
button.setBackground(ta.getDrawable(0));
|
||||||
|
ta.recycle();
|
||||||
|
ta=button.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.textColor});
|
||||||
|
if(relationship.blocking)
|
||||||
|
button.setTextColor(button.getResources().getColorStateList(R.color.error_600));
|
||||||
|
else
|
||||||
|
button.setTextColor(ta.getColorStateList(0));
|
||||||
|
ta.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void performAccountAction(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback) {
|
||||||
|
if (relationship.blocking) {
|
||||||
confirmToggleBlockUser(activity, accountID, account, true, resultCallback);
|
confirmToggleBlockUser(activity, accountID, account, true, resultCallback);
|
||||||
}else if(relationship.muting){
|
}else if(relationship.muting){
|
||||||
confirmToggleMuteUser(activity, accountID, account, true, resultCallback);
|
confirmToggleMuteUser(activity, accountID, account, true, resultCallback);
|
||||||
|
@ -1163,11 +1198,62 @@ public class UiUtils{
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int alphaBlendColors(int color1, int color2, float alpha){
|
/**
|
||||||
float alpha0=1f-alpha;
|
* Check to see if Android platform photopicker is available on the device\
|
||||||
int r=Math.round(((color1 >> 16) & 0xFF)*alpha0+((color2 >> 16) & 0xFF)*alpha);
|
*
|
||||||
int g=Math.round(((color1 >> 8) & 0xFF)*alpha0+((color2 >> 8) & 0xFF)*alpha);
|
* @return whether the device supports photopicker intents.
|
||||||
int b=Math.round((color1 & 0xFF)*alpha0+(color2 & 0xFF)*alpha);
|
*/
|
||||||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
@SuppressLint("NewApi")
|
||||||
|
public static boolean isPhotoPickerAvailable(){
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU){
|
||||||
|
return true;
|
||||||
|
}else if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R){
|
||||||
|
return SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R)>=2;
|
||||||
|
}else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
public static Intent getMediaPickerIntent(String[] mimeTypes, int maxCount){
|
||||||
|
Intent intent;
|
||||||
|
if(isPhotoPickerAvailable()){
|
||||||
|
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
|
if(maxCount>1)
|
||||||
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxCount);
|
||||||
|
}else{
|
||||||
|
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
}
|
||||||
|
if(mimeTypes.length>1){
|
||||||
|
intent.setType("*/*");
|
||||||
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
|
||||||
|
}else if(mimeTypes.length==1){
|
||||||
|
intent.setType(mimeTypes[0]);
|
||||||
|
}else{
|
||||||
|
intent.setType("*/*");
|
||||||
|
}
|
||||||
|
if(maxCount>1)
|
||||||
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a View.OnClickListener to filter multiple clicks in succession.
|
||||||
|
* Useful for buttons that perform some action that changes their state asynchronously.
|
||||||
|
* @param l
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static View.OnClickListener rateLimitedClickListener(View.OnClickListener l){
|
||||||
|
return new View.OnClickListener(){
|
||||||
|
private long lastClickTime;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v){
|
||||||
|
if(SystemClock.uptimeMillis()-lastClickTime>500L){
|
||||||
|
lastClickTime=SystemClock.uptimeMillis();
|
||||||
|
l.onClick(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.text.Editable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
@ -47,6 +48,7 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
||||||
private RectF tmpRect=new RectF();
|
private RectF tmpRect=new RectF();
|
||||||
private ColorStateList labelColors, origHintColors;
|
private ColorStateList labelColors, origHintColors;
|
||||||
private boolean errorState;
|
private boolean errorState;
|
||||||
|
private TextView errorView;
|
||||||
|
|
||||||
public FloatingHintEditTextLayout(Context context){
|
public FloatingHintEditTextLayout(Context context){
|
||||||
this(context, null);
|
this(context, null);
|
||||||
|
@ -95,12 +97,22 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
||||||
label.setAlpha(0f);
|
label.setAlpha(0f);
|
||||||
|
|
||||||
edit.addTextChangedListener(new SimpleTextWatcher(this::onTextChanged));
|
edit.addTextChangedListener(new SimpleTextWatcher(this::onTextChanged));
|
||||||
|
|
||||||
|
errorView=new LinkedTextView(getContext());
|
||||||
|
errorView.setTextAppearance(R.style.m3_body_small);
|
||||||
|
errorView.setTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3OnSurfaceVariant));
|
||||||
|
errorView.setLinkTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3Primary));
|
||||||
|
errorView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
errorView.setPadding(V.dp(16), V.dp(4), V.dp(16), 0);
|
||||||
|
errorView.setVisibility(View.GONE);
|
||||||
|
addView(errorView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onTextChanged(Editable text){
|
private void onTextChanged(Editable text){
|
||||||
if(errorState){
|
if(errorState){
|
||||||
|
errorView.setVisibility(View.GONE);
|
||||||
errorState=false;
|
errorState=false;
|
||||||
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field));
|
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field, getContext().getTheme()));
|
||||||
refreshDrawableState();
|
refreshDrawableState();
|
||||||
}
|
}
|
||||||
boolean newHintVisible=text.length()==0;
|
boolean newHintVisible=text.length()==0;
|
||||||
|
@ -211,12 +223,34 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
||||||
label.setTextColor(color.getColorForState(getDrawableState(), 0xff00ff00));
|
label.setTextColor(color.getColorForState(getDrawableState(), 0xff00ff00));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setErrorState(){
|
public void setErrorState(CharSequence error){
|
||||||
if(errorState)
|
if(errorState)
|
||||||
return;
|
return;
|
||||||
errorState=true;
|
errorState=true;
|
||||||
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field_error, getContext().getTheme()));
|
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field_error, getContext().getTheme()));
|
||||||
label.setTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3Error));
|
label.setTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3Error));
|
||||||
|
errorView.setVisibility(VISIBLE);
|
||||||
|
errorView.setText(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||||
|
if(errorView.getVisibility()!=GONE){
|
||||||
|
int width=MeasureSpec.getSize(widthMeasureSpec)-getPaddingLeft()-getPaddingRight();
|
||||||
|
LayoutParams editLP=(LayoutParams) edit.getLayoutParams();
|
||||||
|
width-=editLP.leftMargin+editLP.rightMargin;
|
||||||
|
errorView.measure(width | MeasureSpec.EXACTLY, MeasureSpec.UNSPECIFIED);
|
||||||
|
LayoutParams lp=(LayoutParams) errorView.getLayoutParams();
|
||||||
|
lp.width=width;
|
||||||
|
lp.height=errorView.getMeasuredHeight();
|
||||||
|
lp.gravity=Gravity.LEFT | Gravity.BOTTOM;
|
||||||
|
lp.leftMargin=editLP.leftMargin;
|
||||||
|
editLP.bottomMargin=errorView.getMeasuredHeight();
|
||||||
|
}else{
|
||||||
|
LayoutParams editLP=(LayoutParams) edit.getLayoutParams();
|
||||||
|
editLP.bottomMargin=0;
|
||||||
|
}
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PaddedForegroundDrawable extends Drawable{
|
private class PaddedForegroundDrawable extends Drawable{
|
||||||
|
@ -313,8 +347,8 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
||||||
@Override
|
@Override
|
||||||
protected void onBoundsChange(@NonNull Rect bounds){
|
protected void onBoundsChange(@NonNull Rect bounds){
|
||||||
super.onBoundsChange(bounds);
|
super.onBoundsChange(bounds);
|
||||||
LayoutParams lp=(LayoutParams) edit.getLayoutParams();
|
int offset=V.dp(12);
|
||||||
wrapped.setBounds(bounds.left+lp.leftMargin-V.dp(12), bounds.top, bounds.right-lp.rightMargin+V.dp(12), bounds.bottom);
|
wrapped.setBounds(edit.getLeft()-offset, edit.getTop()-offset, edit.getRight()+offset, edit.getBottom()+offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
|
public class UntouchableScrollView extends ScrollView {
|
||||||
|
public UntouchableScrollView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UntouchableScrollView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UntouchableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UntouchableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
super.onTouchEvent(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ public class ElevationOnScrollListener extends RecyclerView.OnScrollListener imp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||||
handleScroll(v.getContext(), scrollY==0);
|
handleScroll(v.getContext(), scrollY<=0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleScroll(Context context, boolean newAtTop){
|
private void handleScroll(Context context, boolean newAtTop){
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.joinmastodon.android.utils;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.MatchResult;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
// not a good class
|
||||||
|
public class StatusTextEncoder {
|
||||||
|
private final Function<String, String> fn;
|
||||||
|
|
||||||
|
// see ComposeFragment.HIGHLIGHT_PATTERN
|
||||||
|
private final static Pattern EXCLUDE_PATTERN = Pattern.compile("\\s*(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))\\s*");
|
||||||
|
|
||||||
|
public StatusTextEncoder(Function<String, String> fn) {
|
||||||
|
this.fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettiest method award winner 2023 [citation needed]
|
||||||
|
public String encode(String content) {
|
||||||
|
StringBuilder encodedString = new StringBuilder();
|
||||||
|
// matches mentions and hashtags
|
||||||
|
Matcher m = EXCLUDE_PATTERN.matcher(content);
|
||||||
|
int previousEnd = 0;
|
||||||
|
while (m.find()) {
|
||||||
|
MatchResult res = m.toMatchResult();
|
||||||
|
// everything before the match - do encode
|
||||||
|
encodedString.append(fn.apply(content.substring(previousEnd, res.start())));
|
||||||
|
previousEnd = res.end();
|
||||||
|
// the match - do not encode
|
||||||
|
encodedString.append(res.group());
|
||||||
|
}
|
||||||
|
// everything after the last match - do encode
|
||||||
|
encodedString.append(fn.apply(content.substring(previousEnd)));
|
||||||
|
return encodedString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettiest almost-exact replica of a pretty function
|
||||||
|
public String decode(String content, Pattern regex) {
|
||||||
|
Matcher m = regex.matcher(content);
|
||||||
|
StringBuilder decodedString = new StringBuilder();
|
||||||
|
int previousEnd = 0;
|
||||||
|
while (m.find()) {
|
||||||
|
MatchResult res = m.toMatchResult();
|
||||||
|
// everything before the match - do not decode
|
||||||
|
decodedString.append(content.substring(previousEnd, res.start()));
|
||||||
|
previousEnd = res.end();
|
||||||
|
// the match - do decode
|
||||||
|
decodedString.append(fn.apply(res.group()));
|
||||||
|
}
|
||||||
|
decodedString.append(content.substring(previousEnd));
|
||||||
|
return decodedString.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3OnSecondaryContainer" android:state_enabled="true"/>
|
||||||
|
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
|
||||||
|
</selector>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_enabled="true">
|
||||||
|
<ripple android:color="@color/m3_on_secondary_container_overlay">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="?colorM3SecondaryContainer"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="?colorM3DisabledBackground"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="28dp"/>
|
||||||
|
<solid android:color="?colorM3Surface"/>
|
||||||
|
</shape>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:dither="true"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="4sp" />
|
||||||
|
<solid android:color="?colorBackgroundLight" />
|
||||||
|
<stroke android:width="2dp" android:color="#00ff00" />
|
||||||
|
</shape>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11,19V13H5V11H11V5H13V11H19V13H13V19Z"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M29.45,6V9H9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39H39Q39,39 39,39Q39,39 39,39V18.6H42V39Q42,40.2 41.1,41.1Q40.2,42 39,42H9Q7.8,42 6.9,41.1Q6,40.2 6,39V9Q6,7.8 6.9,6.9Q7.8,6 9,6ZM38,6V10.05H42.05V13.05H38V17.1H35V13.05H30.95V10.05H35V6ZM12,33.9H36L28.8,24.3L22.45,32.65L17.75,26.45ZM9,9V14.55V18.6V39Q9,39 9,39Q9,39 9,39Q9,39 9,39Q9,39 9,39V9Q9,9 9,9Q9,9 9,9Z"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M4,15V13H20V15ZM4,11V9H20V11Z"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||||
|
<path android:pathData="M15.794 7.733c0.286 0.3 0.274 0.774-0.026 1.06l-5.25 5.001c-0.29 0.276-0.745 0.276-1.035 0l-5.25-5c-0.3-0.287-0.312-0.761-0.026-1.061 0.286-0.3 0.76-0.312 1.06-0.026l4.734 4.509 4.733-4.51c0.3-0.285 0.774-0.273 1.06 0.027z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M4.293 8.293c0.39-0.39 1.024-0.39 1.414 0L12 14.586l6.293-6.293c0.39-0.39 1.024-0.39 1.414 0 0.39 0.39 0.39 1.024 0 1.414l-7 7c-0.39 0.39-1.024 0.39-1.414 0l-7-7c-0.39-0.39-0.39-1.024 0-1.414z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M7.75 12c0 0.966-0.784 1.75-1.75 1.75S4.25 12.966 4.25 12 5.034 10.25 6 10.25 7.75 11.034 7.75 12zm6 0c0 0.966-0.784 1.75-1.75 1.75s-1.75-0.784-1.75-1.75 0.784-1.75 1.75-1.75 1.75 0.784 1.75 1.75zM18 13.75c0.966 0 1.75-0.784 1.75-1.75s-0.784-1.75-1.75-1.75-1.75 0.784-1.75 1.75 0.784 1.75 1.75 1.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This is hidden from screenreaders because that same alt text is set as content description on the ImageView -->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/alt_text_wrapper"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|bottom"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:importantForAccessibility="noHideDescendants"
|
||||||
|
android:background="@drawable/bg_image_alt_overlay">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/no_alt_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/ic_fluent_important_20_filled"
|
||||||
|
android:tint="?colorGray25" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/alt_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_label_large"
|
||||||
|
android:textColor="?colorGray25"
|
||||||
|
android:gravity="center"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:paddingVertical="1dp"
|
||||||
|
android:text="@string/sk_alt_button"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/alt_text_close"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="end|top"
|
||||||
|
android:src="@drawable/ic_baseline_close_24"
|
||||||
|
android:tint="#FFF"
|
||||||
|
android:background="?android:actionBarItemBackground"/>
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.NestableScrollView
|
||||||
|
android:id="@+id/alt_text_scroller"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="40dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/alt_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:textAppearance="@style/m3_body_medium"
|
||||||
|
android:textColor="?colorGray25"
|
||||||
|
tools:text="Alt text goes here"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_alt_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:textAppearance="@style/m3_label_large"
|
||||||
|
android:textColor="?colorGray25"
|
||||||
|
android:text="@string/sk_no_alt_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.NestableScrollView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:contentDescription="@string/new_post"
|
||||||
|
style="@style/Widget.Mastodon.Button.Compose" />
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/warning_wrap"
|
||||||
|
android:background="@drawable/bg_timeline_gap">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:text="@string/sk_filtered"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/reveal_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:text="@string/tap_to_reveal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textColor="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -4,8 +4,7 @@
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:paddingLeft="20dp"
|
android:paddingHorizontal="16dp">
|
||||||
android:paddingRight="20dp">
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/reply_btn"
|
android:id="@+id/reply_btn"
|
||||||
|
@ -15,10 +14,11 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reply"
|
android:id="@+id/reply"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center_vertical"
|
||||||
android:drawableStart="@drawable/ic_fluent_chat_multiple_24_selector_text"
|
android:drawableStart="@drawable/ic_fluent_chat_multiple_24_selector_text"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
android:drawableTint="?android:textColorSecondary"
|
android:drawableTint="?android:textColorSecondary"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textAppearance="@style/m3_label_large"
|
android:textAppearance="@style/m3_label_large"
|
||||||
|
@ -38,10 +38,11 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/boost"
|
android:id="@+id/boost"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center_vertical"
|
||||||
android:drawableStart="@drawable/ic_boost"
|
android:drawableStart="@drawable/ic_boost"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
android:drawableTint="@color/boost_icon"
|
android:drawableTint="@color/boost_icon"
|
||||||
android:textColor="@color/boost_icon"
|
android:textColor="@color/boost_icon"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
@ -62,10 +63,11 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/favorite"
|
android:id="@+id/favorite"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center_vertical"
|
||||||
android:drawableStart="@drawable/ic_fluent_star_24_selector"
|
android:drawableStart="@drawable/ic_fluent_star_24_selector"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
android:drawableTint="@color/favorite_icon"
|
android:drawableTint="@color/favorite_icon"
|
||||||
android:textColor="@color/favorite_icon"
|
android:textColor="@color/favorite_icon"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
@ -86,10 +88,10 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/bookmark"
|
android:id="@+id/bookmark"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center_vertical"
|
||||||
android:drawableStart="@drawable/ic_fluent_bookmark_24_selector"
|
android:drawableStart="@drawable/ic_fluent_bookmark_24_selector"
|
||||||
android:drawablePadding="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
android:drawableTint="@color/bookmark_icon"
|
android:drawableTint="@color/bookmark_icon"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textAppearance="@style/m3_label_large" />
|
android:textAppearance="@style/m3_label_large" />
|
||||||
|
@ -103,14 +105,14 @@
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/share_btn"
|
android:id="@+id/share_btn"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:minWidth="56dp">
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/share"
|
android:id="@+id/share"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center_vertical"
|
||||||
android:src="@drawable/ic_fluent_share_24_regular"
|
android:src="@drawable/ic_fluent_share_24_regular"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
android:tint="?android:textColorSecondary"
|
android:tint="?android:textColorSecondary"
|
||||||
android:gravity="center_vertical"/>
|
android:gravity="center_vertical"/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -25,4 +25,6 @@
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
android:background="@drawable/ic_gif"/>
|
android:background="@drawable/ic_gif"/>
|
||||||
|
|
||||||
|
<include layout="@layout/alt_badge" />
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
|
@ -3,7 +3,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="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="11dp"
|
android:paddingTop="13dp"
|
||||||
android:paddingEnd="4dp"
|
android:paddingEnd="4dp"
|
||||||
android:paddingStart="16dp">
|
android:paddingStart="16dp">
|
||||||
|
|
||||||
|
@ -41,11 +41,35 @@
|
||||||
android:src="@drawable/ic_visibility"
|
android:src="@drawable/ic_visibility"
|
||||||
android:tint="?android:textColorSecondary" />
|
android:tint="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/collapse_btn"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_toStartOf="@id/visibility"
|
||||||
|
android:background="?android:actionBarItemBackground"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:importantForAccessibility="noHideDescendants">
|
||||||
|
|
||||||
|
<!-- wrapping this button so the flip animation doesn't flip the background and the tooltip
|
||||||
|
isn't displaced by the -1 scale -->
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/collapse_btn_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/ic_fluent_chevron_down_20_filled"
|
||||||
|
android:tint="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/unread_indicator"
|
android:id="@+id/unread_indicator"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:layout_toStartOf="@id/visibility"
|
android:layout_toStartOf="@id/collapse_btn"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:tint="?android:colorAccent"
|
android:tint="?android:colorAccent"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
|
@ -58,14 +82,15 @@
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginTop="5dp" />
|
android:layout_marginTop="3dp" />
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
||||||
android:id="@+id/name_wrap"
|
android:id="@+id/name_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginTop="7dp"
|
android:layout_marginTop="3dp"
|
||||||
|
android:paddingTop="4sp"
|
||||||
android:layout_toStartOf="@id/unread_indicator"
|
android:layout_toStartOf="@id/unread_indicator"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
android:minHeight="24sp">
|
android:minHeight="24sp">
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
@ -11,81 +10,6 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:scaleType="centerCrop"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
<!-- This is hidden from screenreaders because that same alt text is set as content description on the ImageView -->
|
<include layout="@layout/alt_badge" />
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/alt_text_wrapper"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start|bottom"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:importantForAccessibility="noHideDescendants"
|
|
||||||
android:background="@drawable/bg_image_alt_overlay">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/no_alt_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:src="@drawable/ic_fluent_important_20_filled"
|
|
||||||
android:tint="?colorGray25" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/alt_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_label_large"
|
|
||||||
android:textColor="?colorGray25"
|
|
||||||
android:gravity="center"
|
|
||||||
android:includeFontPadding="false"
|
|
||||||
android:paddingHorizontal="5dp"
|
|
||||||
android:paddingVertical="1dp"
|
|
||||||
android:text="@string/sk_alt_button"/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/alt_text_close"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_gravity="end|top"
|
|
||||||
android:src="@drawable/ic_baseline_close_24"
|
|
||||||
android:tint="#FFF"
|
|
||||||
android:background="?android:actionBarItemBackground"/>
|
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.NestableScrollView
|
|
||||||
android:id="@+id/alt_text_scroller"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="40dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/alt_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:textAppearance="@style/m3_body_medium"
|
|
||||||
android:textColor="?colorGray25"
|
|
||||||
tools:text="Alt text goes here"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/no_alt_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginVertical="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginStart="14dp"
|
|
||||||
android:textAppearance="@style/m3_label_large"
|
|
||||||
android:textColor="?colorGray25"
|
|
||||||
android:text="@string/sk_no_alt_text"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.NestableScrollView>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
|
@ -44,19 +44,48 @@
|
||||||
android:background="?attr/colorPollVoted"/>
|
android:background="?attr/colorPollVoted"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
<org.joinmastodon.android.ui.views.UntouchableScrollView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text_scroll_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:textSize="16sp"
|
android:requiresFadingEdge="vertical"
|
||||||
android:textAppearance="@style/m3_body_large"/>
|
android:scrollbars="none"
|
||||||
|
android:fadingEdgeLength="36dp">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textAppearance="@style/m3_body_large"/>
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.UntouchableScrollView>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/space_below_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/read_more"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_text_button"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:textAppearance="@style/m3_label_medium"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:text="@string/sk_expand"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:importantForAccessibility="no"/>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||||
android:id="@+id/translate_wrap"
|
android:id="@+id/translate_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
|
@ -68,8 +97,7 @@
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||||
android:id="@+id/translate_btn"
|
android:id="@+id/translate_btn"
|
||||||
style="?secondaryButtonStyle"
|
android:background="@drawable/bg_text_button"
|
||||||
android:background="?android:selectableItemBackground"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -88,13 +116,16 @@
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/translate_info"
|
android:id="@+id/translate_info"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginVertical="4dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textAlignment="textEnd"
|
android:textAlignment="textEnd"
|
||||||
tools:text="Translated using TranslateEngine" />
|
tools:text="Translated using TranslateEngine" />
|
||||||
|
@ -129,4 +160,4 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -18,4 +18,6 @@
|
||||||
android:elevation="3dp"
|
android:elevation="3dp"
|
||||||
android:background="@drawable/play_button"/>
|
android:background="@drawable/play_button"/>
|
||||||
|
|
||||||
|
<include layout="@layout/alt_badge" />
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
</org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<me.grishka.appkit.views.FragmentRootLinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/appkit_loader_root"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:background="?android:colorBackground">
|
||||||
|
|
||||||
|
<include layout="@layout/appkit_toolbar"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/appkit_loader_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<include layout="@layout/loading"
|
||||||
|
android:id="@+id/loading"/>
|
||||||
|
|
||||||
|
<ViewStub android:layout="?errorViewLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/error"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/content_stub"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:background="@drawable/bg_onboarding_panel">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_skip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Tonal"
|
||||||
|
android:text="@string/skip"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_next"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||||
|
android:text="@string/follow_all"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</me.grishka.appkit.views.FragmentRootLinearLayout>
|
|
@ -0,0 +1,164 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scroller"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_marginStart="56dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:text="@string/profile_setup_subtitle"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/header"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="144dp"
|
||||||
|
android:foreground="@drawable/ic_add_photo_alternate_48px"
|
||||||
|
android:foregroundGravity="center"
|
||||||
|
android:foregroundTint="?colorM3OnSecondaryContainer"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="?colorM3SecondaryContainer"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="104dp"
|
||||||
|
android:layout_height="104dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="-44dp"
|
||||||
|
android:background="@drawable/bg_onboarding_avatar">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="96dp"
|
||||||
|
android:layout_height="96dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:foreground="@drawable/ic_add_photo_alternate_48px"
|
||||||
|
android:foregroundGravity="center"
|
||||||
|
android:foregroundTint="?colorM3OnSecondaryContainer"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="?colorM3SecondaryContainer"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
|
android:id="@+id/display_name_wrap"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/display_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="@null"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:inputType="textPersonName|textCapWords"
|
||||||
|
android:autofillHints="name"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:hint="@string/display_name"/>
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
|
android:id="@+id/bio_wrap"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_marginTop="-8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/bio"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="@null"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
|
android:hint="@string/profile_bio"/>
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.ReorderableLinearLayout
|
||||||
|
android:id="@+id/profile_fields"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:drawableEnd="@drawable/ic_add_24px"
|
||||||
|
android:drawableTint="?colorM3OnSurface"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:stateListAnimator="@null"
|
||||||
|
android:text="@string/profile_add_row"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:textAppearance="@style/m3_body_small"
|
||||||
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
|
android:text="@string/profile_setup_explanation"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:background="@drawable/bg_onboarding_panel"
|
||||||
|
android:id="@+id/button_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_next"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:minWidth="145dp"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||||
|
android:text="@string/next" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
android:id="@+id/scroller"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
@ -20,7 +21,8 @@
|
||||||
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
android:id="@+id/display_name_wrap"
|
android:id="@+id/display_name_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="80dp"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
app:labelTextColor="@color/m3_outlined_text_field_label"
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
android:foreground="@drawable/bg_m3_outlined_text_field">
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
@ -78,12 +80,14 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/domain"
|
android:id="@+id/domain"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_gravity="right|center_vertical"
|
android:layout_gravity="right|top"
|
||||||
android:layout_marginRight="20dp"
|
android:layout_marginRight="20dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="16dp"
|
android:paddingRight="16dp"
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:gravity="center_vertical"
|
||||||
tools:text="\@mastodon.social"/>
|
tools:text="\@mastodon.social"/>
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
||||||
|
@ -91,8 +95,9 @@
|
||||||
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
android:id="@+id/email_wrap"
|
android:id="@+id/email_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
app:labelTextColor="@color/m3_outlined_text_field_label"
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
android:foreground="@drawable/bg_m3_outlined_text_field">
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
@ -125,8 +130,9 @@
|
||||||
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
android:id="@+id/password_wrap"
|
android:id="@+id/password_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
app:labelTextColor="@color/m3_outlined_text_field_label"
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
android:foreground="@drawable/bg_m3_outlined_text_field">
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
@ -160,8 +166,9 @@
|
||||||
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||||
android:id="@+id/password_confirm_wrap"
|
android:id="@+id/password_confirm_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
app:labelTextColor="@color/m3_outlined_text_field_label"
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
android:foreground="@drawable/bg_m3_outlined_text_field">
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
@ -188,6 +195,9 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="56dp"
|
android:layout_marginStart="56dp"
|
||||||
android:layout_marginEnd="20dp"
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginTop="-8dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
android:textAppearance="@style/m3_body_small"
|
android:textAppearance="@style/m3_body_small"
|
||||||
android:textColor="?colorM3OnSurfaceVariant"
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
android:text="@string/password_note"/>
|
android:text="@string/password_note"/>
|
||||||
|
@ -198,6 +208,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
app:labelTextColor="@color/m3_outlined_text_field_label"
|
app:labelTextColor="@color/m3_outlined_text_field_label"
|
||||||
android:foreground="@drawable/bg_m3_outlined_text_field">
|
android:foreground="@drawable/bg_m3_outlined_text_field">
|
||||||
|
|
||||||
|
@ -223,6 +234,9 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="56dp"
|
android:layout_marginStart="56dp"
|
||||||
android:layout_marginEnd="20dp"
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginTop="-8dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
android:textAppearance="@style/m3_body_small"
|
android:textAppearance="@style/m3_body_small"
|
||||||
android:textColor="?colorM3OnSurfaceVariant"
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
android:text="@string/signup_reason_note"/>
|
android:text="@string/signup_reason_note"/>
|
||||||
|
@ -235,7 +249,8 @@
|
||||||
android:id="@+id/button_bar"
|
android:id="@+id/button_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/bg_onboarding_panel">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_next"
|
android:id="@+id/btn_next"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<me.grishka.appkit.views.RecursiveSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<me.grishka.appkit.views.RecursiveSwipeRefreshLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/refresh_layout"
|
android:id="@+id/refresh_layout"
|
||||||
|
@ -22,7 +23,6 @@
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="15dp"
|
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.CoverImageView
|
<org.joinmastodon.android.ui.views.CoverImageView
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
android:layout_height="112dp"
|
android:layout_height="112dp"
|
||||||
android:layout_below="@id/cover"
|
android:layout_below="@id/cover"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginTop="-40dp"
|
android:layout_marginTop="-56dp"
|
||||||
android:layout_marginStart="14dp"
|
android:layout_marginStart="14dp"
|
||||||
android:outlineProvider="@null"
|
android:outlineProvider="@null"
|
||||||
android:background="@drawable/profile_ava_bg">
|
android:background="@drawable/profile_ava_bg">
|
||||||
|
@ -74,110 +74,18 @@
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/profile_counters"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/cover"
|
|
||||||
android:layout_toEndOf="@id/avatar_border"
|
|
||||||
android:gravity="end">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/posts_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="4dp">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/posts_count"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_large"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="123" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/posts_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_small"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="middle"
|
|
||||||
tools:text="posts" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/following_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_horizontal">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/following_count"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_large"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="123"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/following_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_small"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="middle"
|
|
||||||
tools:text="following"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/followers_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_horizontal">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/followers_count"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_large"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="123"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/followers_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/m3_title_small"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="middle"
|
|
||||||
tools:text="followers"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/profile_action_btn_wrap"
|
android:id="@+id/profile_action_btn_wrap"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/profile_counters"
|
android:layout_below="@id/cover"
|
||||||
android:layout_alignParentEnd="true">
|
android:layout_alignParentEnd="true">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingVertical="16dp"
|
android:paddingVertical="16dp"
|
||||||
android:paddingLeft="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingRight="4dp"
|
android:paddingEnd="4dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||||
|
@ -203,8 +111,8 @@
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingVertical="16dp"
|
android:paddingVertical="16dp"
|
||||||
android:paddingRight="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingLeft="4dp"
|
android:paddingStart="4dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||||
|
@ -227,26 +135,39 @@
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||||
android:id="@+id/name"
|
android:id="@+id/name_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/avatar_border"
|
android:layout_below="@id/avatar_border"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="17dp"
|
android:layout_marginTop="12dp">
|
||||||
android:layout_marginBottom="0.4dp"
|
|
||||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
<TextView
|
||||||
android:textAlignment="viewStart"
|
android:id="@+id/name"
|
||||||
android:textAppearance="@style/m3_headline_small"
|
android:layout_width="wrap_content"
|
||||||
tools:text="Eugen" />
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/m3_headline_small"
|
||||||
|
tools:text="Eugen" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/roles"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/username"
|
android:id="@+id/username"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_below="@id/name"
|
android:layout_below="@id/name_wrap"
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
android:paddingBottom="8dp"
|
android:paddingBottom="8dp"
|
||||||
android:textAppearance="@style/m3_title_medium"
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
@ -312,9 +233,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/avatar_border"
|
android:layout_below="@id/avatar_border"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:background="@drawable/edit_text_border"
|
android:background="@drawable/edit_text_border"
|
||||||
|
@ -323,28 +243,152 @@
|
||||||
android:elevation="0dp"
|
android:elevation="0dp"
|
||||||
tools:text="Eugen" />
|
tools:text="Eugen" />
|
||||||
|
|
||||||
<EditText
|
<FrameLayout
|
||||||
android:id="@+id/bio_edit"
|
android:id="@+id/bio_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/username"
|
android:layout_below="@id/username"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginHorizontal="16dp">
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:textAppearance="@style/m3_body_large"
|
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||||
android:textSize="16sp"
|
android:id="@+id/bio"
|
||||||
android:background="@drawable/edit_text_border"
|
android:layout_width="match_parent"
|
||||||
android:inputType="textMultiLine|textCapSentences"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:textAppearance="@style/m3_body_large"
|
||||||
android:elevation="0dp"
|
android:textSize="16sp"
|
||||||
tools:text="Founder, CEO and lead developer @Mastodon, Germany." />
|
tools:text="Founder, CEO and lead developer @Mastodon, Germany." />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/bio_edit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:background="@drawable/edit_text_border"
|
||||||
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:elevation="0dp"
|
||||||
|
tools:text="Founder, CEO and lead developer @Mastodon, Germany." />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/border_top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0.5dp"
|
||||||
|
android:layout_below="@id/bio_container"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="?attr/colorPollVoted"/>
|
||||||
|
|
||||||
|
<me.grishka.appkit.views.UsableRecyclerView
|
||||||
|
android:id="@+id/metadata"
|
||||||
|
android:background="?colorBackgroundLightest"
|
||||||
|
android:layout_below="@id/border_top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="4dp" />
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||||
|
android:id="@+id/profile_counters"
|
||||||
|
android:background="?colorBackgroundLightest"
|
||||||
|
android:layout_below="@id/metadata"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingHorizontal="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/posts_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/posts_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="123" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/posts_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4sp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
tools:text="posts" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/following_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@drawable/bg_text_button">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/following_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="123"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/following_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4sp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
tools:text="following"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/followers_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@drawable/bg_text_button">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/followers_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="123"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/followers_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4sp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
tools:text="followers"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.tabs.TabLayout
|
<org.joinmastodon.android.ui.tabs.TabLayout
|
||||||
android:id="@+id/tabbar"
|
android:id="@+id/tabbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="38dp"
|
android:layout_height="38dp"
|
||||||
android:layout_marginLeft="4dp"
|
android:paddingHorizontal="4dp"
|
||||||
android:layout_marginRight="4dp"
|
android:background="?colorBackgroundLightest"
|
||||||
app:tabPaddingStart="12dp"
|
app:tabPaddingStart="12dp"
|
||||||
app:tabPaddingEnd="12dp"
|
app:tabPaddingEnd="12dp"
|
||||||
app:tabMinWidth="0dp"
|
app:tabMinWidth="0dp"
|
||||||
|
@ -353,6 +397,14 @@
|
||||||
app:tabIndicatorColor="?android:textColorPrimary"
|
app:tabIndicatorColor="?android:textColorPrimary"
|
||||||
app:tabMode="scrollable"
|
app:tabMode="scrollable"
|
||||||
app:tabGravity="start"/>
|
app:tabGravity="start"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/border_bottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0.5dp"
|
||||||
|
android:layout_marginTop="-0.5dp"
|
||||||
|
android:background="?attr/colorPollVoted"/>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/pager"
|
android:id="@+id/pager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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="62dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingRight="16dp"
|
android:paddingRight="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
android:layout_width="46dp"
|
android:layout_width="46sp"
|
||||||
android:layout_height="46dp"
|
android:layout_height="46sp"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
tools:src="#0f0"/>
|
tools:src="#0f0"/>
|
||||||
|
@ -29,9 +29,10 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24sp"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
android:layout_toStartOf="@id/button"
|
android:layout_toStartOf="@id/button"
|
||||||
|
android:layout_marginTop="2sp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/username"
|
android:id="@+id/username"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20sp"
|
||||||
android:layout_below="@id/name"
|
android:layout_below="@id/name"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
android:layout_toStartOf="@id/button"
|
android:layout_toStartOf="@id/button"
|
||||||
|
|
|
@ -1,30 +1,34 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?colorBackgroundLight"
|
android:paddingHorizontal="16dp">
|
||||||
android:elevation="2dp"
|
|
||||||
android:outlineProvider="background"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="@style/m3_label_medium"
|
android:textAppearance="@style/m3_label_medium"
|
||||||
android:minHeight="16dp"
|
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
tools:text="Field title"/>
|
tools:text="Field title"/>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||||
android:id="@+id/value"
|
android:id="@+id/value"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="2"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
tools:text="Field value"/>
|
tools:text="Field value"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -4,9 +4,7 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?colorBackgroundLight"
|
android:layout_marginBottom="4dp">
|
||||||
android:elevation="2dp"
|
|
||||||
android:outlineProvider="background">
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/remove_row_btn"
|
android:id="@+id/remove_row_btn"
|
||||||
|
@ -26,11 +24,12 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_toStartOf="@id/dragger_thingy"
|
android:layout_toStartOf="@id/dragger_thingy"
|
||||||
android:layout_toEndOf="@id/remove_row_btn"
|
android:layout_toEndOf="@id/remove_row_btn"
|
||||||
android:layout_marginTop="16dp"
|
android:minHeight="24dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:gravity="bottom"
|
||||||
android:textAppearance="@style/m3_label_medium"
|
android:textAppearance="@style/m3_label_medium"
|
||||||
android:background="@drawable/bg_profile_field_edit_text"
|
android:background="@drawable/bg_profile_field_edit_text"
|
||||||
android:hint="@string/field_label"
|
android:hint="@string/field_label"
|
||||||
android:minHeight="16dp"
|
|
||||||
android:inputType="textCapSentences"
|
android:inputType="textCapSentences"
|
||||||
tools:text="Field title"/>
|
tools:text="Field title"/>
|
||||||
|
|
||||||
|
@ -41,7 +40,9 @@
|
||||||
android:layout_below="@id/title"
|
android:layout_below="@id/title"
|
||||||
android:layout_toStartOf="@id/dragger_thingy"
|
android:layout_toStartOf="@id/dragger_thingy"
|
||||||
android:layout_toEndOf="@id/remove_row_btn"
|
android:layout_toEndOf="@id/remove_row_btn"
|
||||||
android:layout_marginBottom="16dp"
|
android:minHeight="28dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:gravity="bottom"
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
android:background="@drawable/bg_profile_field_edit_text"
|
android:background="@drawable/bg_profile_field_edit_text"
|
||||||
android:hint="@string/field_content"
|
android:hint="@string/field_content"
|
||||||
|
@ -55,7 +56,6 @@
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignBottom="@id/value"
|
android:layout_alignBottom="@id/value"
|
||||||
android:layout_marginBottom="-16dp"
|
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
android:contentDescription="@string/reorder"
|
android:contentDescription="@string/reorder"
|
||||||
android:src="@drawable/ic_fluent_re_order_dots_vertical_24_regular"/>
|
android:src="@drawable/ic_fluent_re_order_dots_vertical_24_regular"/>
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
tools:src="#0f0"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/action_btn_wrap"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="18dp"
|
||||||
|
android:layout_marginStart="-8dp">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||||
|
android:id="@+id/action_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||||
|
tools:text="Follow"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/action_progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true"
|
||||||
|
style="?android:progressBarStyleSmall"
|
||||||
|
android:elevation="10dp"
|
||||||
|
android:outlineProvider="none"
|
||||||
|
android:indeterminateTint="?colorM3OnPrimary"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_toEndOf="@id/avatar"
|
||||||
|
android:layout_toStartOf="@id/action_btn_wrap"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
tools:text="User Name"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_toEndOf="@id/avatar"
|
||||||
|
android:layout_below="@id/name"
|
||||||
|
android:layout_toStartOf="@id/action_btn_wrap"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_title_small"
|
||||||
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
|
tools:text="\@username@server.social"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bio"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/avatar"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:textAppearance="@style/m3_body_medium"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
tools:text="Description"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:background="?colorM3Background">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/dragger_thingy"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:tint="?colorM3OnSurface"
|
||||||
|
android:src="@drawable/ic_drag_handle_24px"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_toStartOf="@id/dragger_thingy"
|
||||||
|
android:background="@null"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
|
android:textColorHint="?colorM3OnSurfaceVariant"
|
||||||
|
android:inputType="textCapSentences"
|
||||||
|
android:hint="@string/field_content"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:singleLine="true"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_toStartOf="@id/dragger_thingy"
|
||||||
|
android:layout_below="@id/content"
|
||||||
|
android:background="@null"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:textAppearance="@style/m3_body_medium"
|
||||||
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
|
android:textColorHint="?colorM3OnSurfaceVariant"
|
||||||
|
android:inputType="textCapSentences"
|
||||||
|
android:hint="@string/field_label"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:singleLine="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -20,10 +20,6 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/empty"/>
|
android:id="@+id/empty"/>
|
||||||
|
|
||||||
<ImageButton
|
<include layout="@layout/compose_fab" />
|
||||||
android:contentDescription="@string/mo_fab_compose"
|
|
||||||
android:tooltipText="@string/mo_fab_compose"
|
|
||||||
android:id="@+id/fab"
|
|
||||||
style="@style/Widget.Mastodon.Button.Compose"/>
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</me.grishka.appkit.views.RecursiveSwipeRefreshLayout>
|
</me.grishka.appkit.views.RecursiveSwipeRefreshLayout>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="text_max_height">350dp</dimen>
|
||||||
|
<dimen name="text_collapsed_height">150dp</dimen>
|
||||||
|
</resources>
|
|
@ -432,4 +432,10 @@
|
||||||
<string name="privacy_policy_explanation">TL;DR: We don\'t collect or process anything.</string>
|
<string name="privacy_policy_explanation">TL;DR: We don\'t collect or process anything.</string>
|
||||||
<!-- %s is server domain -->
|
<!-- %s is server domain -->
|
||||||
<string name="server_policy_disagree">Disagree with %s</string>
|
<string name="server_policy_disagree">Disagree with %s</string>
|
||||||
</resources>
|
<string name="profile_bio">Bio</string>
|
||||||
|
<!-- Shown in a progress dialog when you tap "follow all" -->
|
||||||
|
<string name="sending_follows">Following users…</string>
|
||||||
|
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
|
||||||
|
<string name="signup_email_domain_blocked">%1$s doesn\'t allow signups from %2$s. Try a different one or <a>pick a different server</a>.</string>
|
||||||
|
<string name="signup_username_taken">This username is taken.</string>
|
||||||
|
</resources>
|
||||||
|
|
|
@ -251,4 +251,12 @@
|
||||||
<string name="sk_new_reports">New reports</string>
|
<string name="sk_new_reports">New reports</string>
|
||||||
<string name="sk_settings_server_version">Server version: %s</string>
|
<string name="sk_settings_server_version">Server version: %s</string>
|
||||||
<string name="sk_notify_poll_results">Poll results</string>
|
<string name="sk_notify_poll_results">Poll results</string>
|
||||||
|
<string name="sk_settings_prefix_reply_cw_with_re">Prefix reply CW with “re:”</string>
|
||||||
|
<string name="sk_filtered">Filtered: %s</string>
|
||||||
|
<string name="sk_expand">Expand</string>
|
||||||
|
<string name="sk_collapse">Collapse</string>
|
||||||
|
<string name="sk_settings_collapse_long_posts">Collapse very long posts</string>
|
||||||
|
<string name="sk_unfinished_attachments">Fix attachments?</string>
|
||||||
|
<string name="sk_unfinished_attachments_message">Some attachments haven’t finished uploading.</string>
|
||||||
|
<string name="sk_spectator_mode">Spectator mode</string>
|
||||||
</resources>
|
</resources>
|
|
@ -406,6 +406,13 @@
|
||||||
<item name="android:paddingRight">24dp</item>
|
<item name="android:paddingRight">24dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.Mastodon.M3.Button.Tonal">
|
||||||
|
<item name="android:background">@drawable/bg_button_m3_tonal</item>
|
||||||
|
<item name="android:textColor">@color/button_text_m3_text</item>
|
||||||
|
<item name="android:paddingLeft">24dp</item>
|
||||||
|
<item name="android:paddingRight">24dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="alert_title">
|
<style name="alert_title">
|
||||||
<item name="android:textColor">?android:textColorPrimary</item>
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
<item name="android:textSize">24sp</item>
|
<item name="android:textSize">24sp</item>
|
||||||
|
@ -469,6 +476,18 @@
|
||||||
<item name="android:letterSpacing">0.1</item>
|
<item name="android:letterSpacing">0.1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="role_label">
|
||||||
|
<item name="android:fontFamily">sans-serif-medium</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
|
<item name="android:background">@drawable/bg_pill</item>
|
||||||
|
<item name="android:paddingStart">8dp</item>
|
||||||
|
<item name="android:paddingEnd">8dp</item>
|
||||||
|
<item name="android:paddingTop">6dp</item>
|
||||||
|
<item name="android:paddingBottom">6dp</item>
|
||||||
|
<item name="android:layout_marginEnd">8dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="m3_headline_small">
|
<style name="m3_headline_small">
|
||||||
<item name="android:textSize">24sp</item>
|
<item name="android:textSize">24sp</item>
|
||||||
<item name="android:textColor">?android:textColorPrimary</item>
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
|
|
|
@ -4,6 +4,7 @@ dependencyResolutionManagement {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Moshidon"
|
rootProject.name = "Moshidon"
|
||||||
|
|
Loading…
Reference in New Issue