Display account

This commit is contained in:
Thomas 2020-06-27 17:58:13 +02:00
parent f2b4e86df5
commit fc654fe561
31 changed files with 1950 additions and 6 deletions

View File

@ -38,7 +38,6 @@ allprojects {
}
dependencies {
implementation "androidx.multidex:multidex:2.0.1"
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'

View File

@ -46,6 +46,14 @@
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".ShowAccountActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:theme="@style/AppThemeNoActionBar"
android:windowSoftInputMode="stateAlwaysHidden" />
<receiver
android:name=".services.PeertubeUploadReceiver"
android:exported="false">

View File

@ -0,0 +1,489 @@
package app.fedilab.fedilabtube;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.bumptech.glide.Glide;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.tabs.TabLayout;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.fedilabtube.asynctasks.PostActionAsyncTask;
import app.fedilab.fedilabtube.asynctasks.RetrieveFeedsAsyncTask;
import app.fedilab.fedilabtube.asynctasks.RetrieveRelationshipAsyncTask;
import app.fedilab.fedilabtube.client.APIResponse;
import app.fedilab.fedilabtube.client.PeertubeAPI;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.InstanceNodeInfo;
import app.fedilab.fedilabtube.client.entities.Relationship;
import app.fedilab.fedilabtube.client.entities.Status;
import app.fedilab.fedilabtube.client.entities.StatusDrawerParams;
import app.fedilab.fedilabtube.drawer.StatusListAdapter;
import app.fedilab.fedilabtube.fragment.DisplayAccountsFragment;
import app.fedilab.fedilabtube.fragment.DisplayStatusFragment;
import app.fedilab.fedilabtube.helper.Helper;
import app.fedilab.fedilabtube.interfaces.OnPostActionInterface;
import app.fedilab.fedilabtube.interfaces.OnRetrieveFeedsAccountInterface;
import app.fedilab.fedilabtube.interfaces.OnRetrieveFeedsInterface;
import app.fedilab.fedilabtube.interfaces.OnRetrieveRelationshipInterface;
import app.fedilab.fedilabtube.sqlite.AccountDAO;
import app.fedilab.fedilabtube.sqlite.Sqlite;
import es.dmoral.toasty.Toasty;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import static app.fedilab.fedilabtube.helper.Helper.getLiveInstance;
public class ShowAccountActivity extends AppCompatActivity implements OnPostActionInterface, OnRetrieveFeedsAccountInterface, OnRetrieveRelationshipInterface, OnRetrieveFeedsInterface {
private List<Status> statuses;
private StatusListAdapter statusListAdapter;
private ImageButton account_follow;
private ViewPager mPager;
private TabLayout tabLayout;
private TextView account_note;
private String userId;
private Relationship relationship;
private ImageButton header_edit_profile;
private List<Status> pins;
private int maxScrollSize;
private boolean avatarShown = true;
private ImageView account_pp;
private TextView account_dn;
private TextView account_un;
private Account account;
private String accountId;
private boolean ischannel;
private AsyncTask<Void, Void, Void> retrieveRelationship;
private action doAction;
private PeertubeAPI.StatusAction doActionAccount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
setContentView(R.layout.activity_show_account);
setTitle("");
pins = new ArrayList<>();
Bundle b = getIntent().getExtras();
account_follow = findViewById(R.id.account_follow);
header_edit_profile = findViewById(R.id.header_edit_profile);
account_follow.setEnabled(false);
account_pp = findViewById(R.id.account_pp);
account_dn = findViewById(R.id.account_dn);
account_un = findViewById(R.id.account_un);
account_pp.setBackgroundResource(R.drawable.account_pp_border);
if (b != null) {
account = b.getParcelable("account");
if (account == null) {
accountId = b.getString("accountId");
} else {
accountId = account.getId();
}
ischannel = b.getBoolean("ischannel", false);
userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
} else {
Toasty.error(ShowAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
}
statuses = new ArrayList<>();
StatusDrawerParams statusDrawerParams = new StatusDrawerParams();
statusDrawerParams.setType(RetrieveFeedsAsyncTask.Type.USER);
statusDrawerParams.setTargetedId(accountId);
statusDrawerParams.setStatuses(statuses);
statusListAdapter = new StatusListAdapter(statusDrawerParams);
tabLayout = findViewById(R.id.account_tabLayout);
account_note = findViewById(R.id.account_note);
/*header_edit_profile.setOnClickListener(v -> {
Intent intent = new Intent(ShowAccountActivity.this, EditProfileActivity.class);
startActivity(intent);
});*/
ImageView action_back = findViewById(R.id.action_back);
action_back.setOnClickListener(v -> finish());
if (account != null) {
ManageAccount();
}
}
private void ManageAccount() {
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
String accountIdRelation = account.getAcct();
retrieveRelationship = new RetrieveRelationshipAsyncTask(ShowAccountActivity.this, accountIdRelation, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
String urlHeader = account.getHeader();
if (urlHeader != null && urlHeader.startsWith("/")) {
urlHeader = "https://"+Helper.getLiveInstance(ShowAccountActivity.this) + account.getHeader();
}
if (urlHeader != null && !urlHeader.contains("missing.png")) {
ImageView banner_pp = findViewById(R.id.banner_pp);
Glide.with(banner_pp.getContext())
.load(urlHeader)
.into(banner_pp);
}
//Peertube account watched by a Mastodon account
//Bot account
TextView actionbar_title = findViewById(R.id.show_account_title);
if (account.getAcct() != null)
actionbar_title.setText(account.getAcct());
ImageView pp_actionBar = findViewById(R.id.pp_actionBar);
if (account.getAvatar() != null) {
Helper.loadGiF(ShowAccountActivity.this, account, pp_actionBar);
}
final AppBarLayout appBar = findViewById(R.id.appBar);
maxScrollSize = appBar.getTotalScrollRange();
//Timed muted account
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, null);
final SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
final Account authenticatedAccount = new AccountDAO(ShowAccountActivity.this, db).getUniqAccount(userId, instance);
appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
LinearLayout toolbarContent = findViewById(R.id.toolbar_content);
if (toolbarContent != null) {
if (Math.abs(verticalOffset) - appBar.getTotalScrollRange() == 0) {
if (toolbarContent.getVisibility() == View.GONE)
toolbarContent.setVisibility(View.VISIBLE);
} else {
if (toolbarContent.getVisibility() == View.VISIBLE)
toolbarContent.setVisibility(View.GONE);
}
}
if (maxScrollSize == 0)
maxScrollSize = appBarLayout.getTotalScrollRange();
int percentage = (Math.abs(verticalOffset)) * 100 / maxScrollSize;
if (percentage >= 40 && avatarShown) {
avatarShown = false;
account_pp.animate()
.scaleY(0).scaleX(0)
.setDuration(400)
.start();
}
if (percentage <= 40 && !avatarShown) {
avatarShown = true;
account_pp.animate()
.scaleY(1).scaleX(1)
.start();
}
});
mPager = findViewById(R.id.account_viewpager);
if (!ischannel) {
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.videos)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.channels)));
mPager.setOffscreenPageLimit(2);
} else {
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.videos)));
mPager.setOffscreenPageLimit(1);
}
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
TabLayout.Tab tab = tabLayout.getTabAt(position);
if (tab != null)
tab.select();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
Fragment fragment = null;
if (mPager.getAdapter() != null)
fragment = (Fragment) mPager.getAdapter().instantiateItem(mPager, tab.getPosition());
switch (tab.getPosition()) {
case 0:
if (fragment != null) {
DisplayStatusFragment displayStatusFragment = ((DisplayStatusFragment) fragment);
displayStatusFragment.scrollToTop();
}
break;
case 1:
if (fragment != null) {
DisplayAccountsFragment displayAccountsFragment = ((DisplayAccountsFragment) fragment);
displayAccountsFragment.scrollToTop();
}
break;
}
}
});
account_dn.setText(account.getDisplay_name());
if (!ischannel || account.getAcct().split("-").length < 4) {
account_un.setText(String.format("@%s", account.getAcct()));
} else {
account_un.setVisibility(View.GONE);
}
SpannableString spannableString;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spannableString = new SpannableString(Html.fromHtml(account.getNote(), FROM_HTML_MODE_LEGACY));
else
spannableString = new SpannableString(Html.fromHtml(account.getNote()));
account.setNoteSpan(spannableString);
account_note.setText(account.getNoteSpan(), TextView.BufferType.SPANNABLE);
account_note.setMovementMethod(LinkMovementMethod.getInstance());
Helper.loadGiF(ShowAccountActivity.this, account, account_pp);
//Follow button
String target = account.getAcct();
String finalTarget = target;
account_follow.setOnClickListener(v -> {
if (doAction == action.NOTHING) {
Toasty.info(ShowAccountActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show();
} else if (doAction == action.FOLLOW) {
account_follow.setEnabled(false);
new PostActionAsyncTask(ShowAccountActivity.this, PeertubeAPI.StatusAction.FOLLOW, finalTarget, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else if (doAction == action.UNFOLLOW) {
boolean confirm_unfollow = sharedpreferences.getBoolean(Helper.SET_UNFOLLOW_VALIDATION, true);
if (confirm_unfollow) {
AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(ShowAccountActivity.this);
unfollowConfirm.setTitle(getString(R.string.unfollow_confirm));
unfollowConfirm.setMessage(account.getAcct());
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
unfollowConfirm.setPositiveButton(R.string.yes, (dialog, which) -> {
account_follow.setEnabled(false);
new PostActionAsyncTask(ShowAccountActivity.this, PeertubeAPI.StatusAction.UNFOLLOW, finalTarget, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
dialog.dismiss();
});
unfollowConfirm.show();
} else {
account_follow.setEnabled(false);
new PostActionAsyncTask(ShowAccountActivity.this, PeertubeAPI.StatusAction.UNFOLLOW, finalTarget, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
TextView account_date = findViewById(R.id.account_date);
account_date.setText(Helper.shortDateToString(account.getCreated_at()));
account_date.setVisibility(View.VISIBLE);
}
@Override
public void onRetrieveFeedsAccount(List<Status> statuses) {
if (statuses != null) {
this.statuses.addAll(statuses);
statusListAdapter.notifyDataSetChanged();
}
}
@Override
public void onRetrieveFeeds(APIResponse apiResponse) {
if (apiResponse.getError() != null) {
Toasty.error(ShowAccountActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
return;
}
}
@Override
public void onRetrieveRelationship(Relationship relationship, Error error) {
if (error != null) {
Toasty.error(ShowAccountActivity.this, error.getError(), Toast.LENGTH_LONG).show();
return;
}
this.relationship = relationship;
manageButtonVisibility();
//The authenticated account is followed by the account
if (relationship != null && relationship.isFollowed_by() && !accountId.equals(userId)) {
TextView account_followed_by = findViewById(R.id.account_followed_by);
account_followed_by.setVisibility(View.VISIBLE);
}
invalidateOptionsMenu();
}
//Manages the visibility of the button
private void manageButtonVisibility() {
if (relationship == null)
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int[][] states = new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled}, // disabled
new int[]{-android.R.attr.state_checked}, // unchecked
new int[]{android.R.attr.state_pressed} // pressed
};
int[] colors = new int[]{
ContextCompat.getColor(ShowAccountActivity.this, R.color.colorAccent),
ContextCompat.getColor(ShowAccountActivity.this, R.color.colorAccent),
ContextCompat.getColor(ShowAccountActivity.this, R.color.colorAccent),
ContextCompat.getColor(ShowAccountActivity.this, R.color.colorAccent)
};
account_follow.setBackgroundTintList(new ColorStateList(states, colors));
}
account_follow.setEnabled(true);
if (relationship.isFollowing()) {
account_follow.setImageResource(R.drawable.ic_user_minus);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
account_follow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ShowAccountActivity.this, R.color.red_1)));
}
doAction = action.UNFOLLOW;
account_follow.setContentDescription(getString(R.string.action_unfollow));
account_follow.setVisibility(View.VISIBLE);
} else if (!relationship.isFollowing()) {
account_follow.setImageResource(R.drawable.ic_user_plus);
doAction = action.FOLLOW;
account_follow.setVisibility(View.VISIBLE);
account_follow.setContentDescription(getString(R.string.action_follow));
} else {
account_follow.setVisibility(View.GONE);
doAction = action.NOTHING;
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onStop() {
super.onStop();
if (retrieveRelationship != null && !retrieveRelationship.isCancelled()) {
retrieveRelationship.cancel(true);
}
}
@Override
public void onPostAction(int statusCode, PeertubeAPI.StatusAction statusAction, String targetedId, Error error) {
if (error != null) {
Toasty.error(ShowAccountActivity.this, error.getError(), Toast.LENGTH_LONG).show();
return;
}
String target = account.getAcct();
//IF action is unfollow or mute, sends an intent to remove statuses
if (statusAction == PeertubeAPI.StatusAction.UNFOLLOW ) {
Bundle b = new Bundle();
b.putString("receive_action", targetedId);
Intent intentBC = new Intent(Helper.RECEIVE_ACTION);
intentBC.putExtras(b);
}
retrieveRelationship = new RetrieveRelationshipAsyncTask(ShowAccountActivity.this, target, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public enum action {
FOLLOW,
UNFOLLOW,
NOTHING
}
/**
* Pager adapter for the 4 fragments
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NotNull
@Override
public Fragment getItem(int position) {
Bundle bundle = new Bundle();
if (position == 0) {
DisplayStatusFragment displayStatusFragment = new DisplayStatusFragment();
bundle = new Bundle();
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.USER);
bundle.putString("targetedid", account.getAcct());
bundle.putBoolean("showReply", false);
bundle.putBoolean("ischannel", ischannel);
displayStatusFragment.setArguments(bundle);
return displayStatusFragment;
}
DisplayAccountsFragment displayAccountsFragment = new DisplayAccountsFragment();
bundle.putString("targetedid", account.getId());
bundle.putString("instance", getLiveInstance(ShowAccountActivity.this));
bundle.putString("name", account.getAcct());
displayAccountsFragment.setArguments(bundle);
return displayAccountsFragment;
}
@Override
public int getCount() {
if (ischannel)
return 1;
else
return 2;
}
}
}

View File

@ -0,0 +1,43 @@
package app.fedilab.fedilabtube.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import app.fedilab.fedilabtube.client.PeertubeAPI;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.interfaces.OnRetrieveAccountInterface;
public class RetrieveAccountAsyncTask extends AsyncTask<Void, Void, Void> {
private String targetedId;
private Account account;
private OnRetrieveAccountInterface listener;
private Error error;
private WeakReference<Context> contextReference;
public RetrieveAccountAsyncTask(Context context, String targetedId, OnRetrieveAccountInterface onRetrieveAccountInterface) {
this.contextReference = new WeakReference<>(context);
this.targetedId = targetedId;
this.listener = onRetrieveAccountInterface;
}
@Override
protected Void doInBackground(Void... params) {
PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get());
account = peertubeAPI.getAccount(targetedId);
error = peertubeAPI.getError();
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveAccount(account, error);
}
}

View File

@ -0,0 +1,41 @@
package app.fedilab.fedilabtube.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import app.fedilab.fedilabtube.client.APIResponse;
import app.fedilab.fedilabtube.client.PeertubeAPI;
import app.fedilab.fedilabtube.interfaces.OnRetrieveAccountsInterface;
public class RetrieveAccountsAsyncTask extends AsyncTask<Void, Void, Void> {
private APIResponse apiResponse;
private OnRetrieveAccountsInterface listener;
private WeakReference<Context> contextReference;
private String name;
public RetrieveAccountsAsyncTask(Context context, String name, OnRetrieveAccountsInterface onRetrieveAccountsInterface) {
this.contextReference = new WeakReference<>(context);
this.name = name;
this.listener = onRetrieveAccountsInterface;
}
@Override
protected Void doInBackground(Void... params) {
PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get());
apiResponse = peertubeAPI.getPeertubeChannel(name);
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveAccounts(apiResponse);
}
}

View File

@ -0,0 +1,45 @@
package app.fedilab.fedilabtube.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
import app.fedilab.fedilabtube.client.PeertubeAPI;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.Relationship;
import app.fedilab.fedilabtube.interfaces.OnRetrieveRelationshipInterface;
public class RetrieveRelationshipAsyncTask extends AsyncTask<Void, Void, Void> {
private String accountId;
private Relationship relationship;
private OnRetrieveRelationshipInterface listener;
private Error error;
private WeakReference<Context> contextReference;
public RetrieveRelationshipAsyncTask(Context context, String accountId, OnRetrieveRelationshipInterface onRetrieveRelationshipInterface) {
this.contextReference = new WeakReference<>(context);
this.listener = onRetrieveRelationshipInterface;
this.accountId = accountId;
}
@Override
protected Void doInBackground(Void... params) {
PeertubeAPI api = new PeertubeAPI(this.contextReference.get());
relationship = new Relationship();
relationship.setFollowing(api.isFollowing(accountId));
error = api.getError();
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveRelationship(relationship, error);
}
}

View File

@ -420,7 +420,7 @@ public class PeertubeAPI {
account.setDisplay_name(accountObject.get("name").toString());
account.setHost(accountObject.get("host").toString());
account.setSocial("PEERTUBE");
account.setInstance(accountObject.getString("host"));
if (accountObject.has("createdAt"))
account.setCreated_at(Helper.mstStringToDate(accountObject.get("createdAt").toString()));
else

View File

@ -0,0 +1,96 @@
package app.fedilab.fedilabtube.client.entities;
public class Relationship {
private String id;
private boolean following;
private boolean followed_by;
private boolean blocking;
private boolean muting;
private boolean requested;
private boolean muting_notifications;
private boolean endorsed;
private boolean showing_reblogs;
private boolean blocked_by;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isFollowing() {
return following;
}
public void setFollowing(boolean following) {
this.following = following;
}
public boolean isFollowed_by() {
return followed_by;
}
public void setFollowed_by(boolean followed_by) {
this.followed_by = followed_by;
}
public boolean isBlocking() {
return blocking;
}
public void setBlocking(boolean blocking) {
this.blocking = blocking;
}
public boolean isMuting() {
return muting;
}
public void setMuting(boolean muting) {
this.muting = muting;
}
public boolean isRequested() {
return requested;
}
public void setRequested(boolean requested) {
this.requested = requested;
}
public boolean isMuting_notifications() {
return muting_notifications;
}
public void setMuting_notifications(boolean muting_notifications) {
this.muting_notifications = muting_notifications;
}
public boolean isEndorsed() {
return endorsed;
}
public void setEndorsed(boolean endorsed) {
this.endorsed = endorsed;
}
public boolean isShowing_reblogs() {
return showing_reblogs;
}
public void setShowing_reblogs(boolean showing_reblogs) {
this.showing_reblogs = showing_reblogs;
}
public boolean isBlocked_by() {
return blocked_by;
}
public void setBlocked_by(boolean blocked_by) {
this.blocked_by = blocked_by;
}
}

View File

@ -0,0 +1,199 @@
package app.fedilab.fedilabtube.drawer;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
import app.fedilab.fedilabtube.R;
import app.fedilab.fedilabtube.ShowAccountActivity;
import app.fedilab.fedilabtube.client.PeertubeAPI;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.helper.Helper;
import app.fedilab.fedilabtube.interfaces.OnPostActionInterface;
import es.dmoral.toasty.Toasty;
public class AccountsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnPostActionInterface {
private List<Account> accounts;
private Context context;
private AccountsListAdapter accountsListAdapter;
private String targetedId;
public AccountsListAdapter(String targetedId, List<Account> accounts) {
this.accounts = accounts;
this.accountsListAdapter = this;
this.targetedId = targetedId;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
return new ViewHolder(layoutInflater.inflate(R.layout.drawer_account, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
final AccountsListAdapter.ViewHolder holder = (AccountsListAdapter.ViewHolder) viewHolder;
final Account account = accounts.get(position);
PeertubeAPI.StatusAction doAction = null;
holder.account_follow.hide();
if (account.getDisplay_name() != null && !account.getDisplay_name().trim().equals(""))
holder.account_dn.setText(account.getDisplay_name());
else
holder.account_dn.setText(account.getUsername().replace("@", ""));
holder.account_un.setText(String.format("@%s", account.getUsername()));
holder.account_ac.setText(account.getAcct());
if (account.getUsername().equals(account.getAcct()))
holder.account_ac.setVisibility(View.GONE);
else
holder.account_ac.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
holder.account_ds.setText(Html.fromHtml(account.getNote(), Html.FROM_HTML_MODE_LEGACY));
else
holder.account_ds.setText(Html.fromHtml(account.getNote()));
holder.account_ds.setAutoLinkMask(Linkify.WEB_URLS);
holder.account_sc.setText(Helper.withSuffix(account.getStatuses_count()));
holder.account_fgc.setText(Helper.withSuffix(account.getFollowing_count()));
holder.account_frc.setText(Helper.withSuffix(account.getFollowers_count()));
//Profile picture
Helper.loadGiF(context, account, holder.account_pp);
if (account.isMakingAction()) {
holder.account_follow.setEnabled(false);
} else {
holder.account_follow.setEnabled(true);
}
//Follow button
PeertubeAPI.StatusAction finalDoAction = doAction;
holder.account_follow.setOnClickListener(v -> {
});
Intent intent = new Intent(context, ShowAccountActivity.class);
Bundle b = new Bundle();
b.putBoolean("peertubeaccount", true);
b.putBoolean("ischannel", true);
b.putString("targetedid", account.getAcct());
b.putParcelable("account", account);
intent.putExtras(b);
context.startActivity(intent);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return accounts.size();
}
private Account getItemAt(int position) {
if (accounts.size() > position)
return accounts.get(position);
else
return null;
}
@Override
public void onPostAction(int statusCode, PeertubeAPI.StatusAction statusAction, String targetedId, Error error) {
if (error != null) {
Toasty.error(context, error.getError(), Toast.LENGTH_LONG).show();
return;
}
/*
if (statusAction == PeertubeAPI.StatusAction.FOLLOW) {
if (action == RetrieveAccountsAsyncTask.Type.CHANNELS) {
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
new InstancesDAO(context, db).insertInstance(accounts.get(0).getAcct().split("@")[1], accounts.get(0).getAcct().split("@")[0], "PEERTUBE_CHANNEL");
} else {
for (Account account : accounts) {
if (account.getId().equals(targetedId)) {
account.setFollowType(Account.followAction.FOLLOW);
account.setMakingAction(false);
}
}
accountsListAdapter.notifyDataSetChanged();
}
}
if (statusAction == PeertubeAPI.StatusAction.UNFOLLOW) {
for (Account account : accounts) {
if (account.getId().equals(targetedId)) {
account.setFollowType(Account.followAction.NOT_FOLLOW);
account.setMakingAction(false);
}
}
accountsListAdapter.notifyDataSetChanged();
}*/
}
private void notifyAccountChanged(Account account) {
for (int i = 0; i < accountsListAdapter.getItemCount(); i++) {
//noinspection ConstantConditions
if (accountsListAdapter.getItemAt(i) != null && accountsListAdapter.getItemAt(i).getId().equals(account.getId())) {
try {
accountsListAdapter.notifyItemChanged(i);
} catch (Exception ignored) {
}
}
}
}
private static class ViewHolder extends RecyclerView.ViewHolder {
ImageView account_pp;
TextView account_ac;
TextView account_dn;
TextView account_un;
TextView account_ds;
TextView account_sc;
TextView account_fgc;
TextView account_frc;
LinearLayout account_info;
FloatingActionButton account_follow;
LinearLayout account_container;
ViewHolder(View itemView) {
super(itemView);
account_pp = itemView.findViewById(R.id.account_pp);
account_dn = itemView.findViewById(R.id.account_dn);
account_ac = itemView.findViewById(R.id.account_ac);
account_un = itemView.findViewById(R.id.account_un);
account_ds = itemView.findViewById(R.id.account_ds);
account_sc = itemView.findViewById(R.id.account_sc);
account_fgc = itemView.findViewById(R.id.account_fgc);
account_frc = itemView.findViewById(R.id.account_frc);
account_follow = itemView.findViewById(R.id.account_follow);
account_info = itemView.findViewById(R.id.account_info);
account_container = itemView.findViewById(R.id.account_container);
}
}
}

View File

@ -3,6 +3,7 @@ package app.fedilab.fedilabtube.drawer;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -20,6 +21,7 @@ import java.util.List;
import app.fedilab.fedilabtube.PeertubeActivity;
import app.fedilab.fedilabtube.R;
import app.fedilab.fedilabtube.ShowAccountActivity;
import app.fedilab.fedilabtube.client.APIResponse;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Peertube;
@ -32,6 +34,7 @@ public class PeertubeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private List<Peertube> peertubes;
private Context context;
private boolean ownVideos;
public PeertubeAdapter(List<Peertube> peertubes) {
this.peertubes = peertubes;
@ -56,13 +59,18 @@ public class PeertubeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
peertube.setInstance(Helper.getLiveInstance(context));
Account account = peertube.getAccount();
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, "");
ownVideos = peertube.getAccount().getInstance().compareTo(Helper.getLiveInstance(context)) == 0 && peertube.getAccount().getId().compareTo(userId) == 0;
holder.peertube_account_name.setText(account.getAcct());
holder.peertube_title.setText(peertube.getName());
holder.peertube_duration.setText(context.getString(R.string.duration_video, Helper.secondsToString(peertube.getDuration())));
holder.peertube_date.setText(String.format(" - %s", Helper.dateDiff(context, peertube.getCreated_at())));
holder.peertube_views.setText(context.getString(R.string.number_view_video, Helper.withSuffix(peertube.getView())));
Glide.with(holder.peertube_video_image.getContext())
.load("https://" + peertube.getInstance() + peertube.getThumbnailPath())
.into(holder.peertube_video_image);
@ -72,6 +80,15 @@ public class PeertubeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
Helper.loadGiF(context, account, holder.peertube_profile);
if (!ownVideos) {
holder.peertube_profile.setOnClickListener(v -> {
Intent intent = new Intent(context, ShowAccountActivity.class);
Bundle b = new Bundle();
b.putParcelable("account", peertube.getAccount());
intent.putExtras(b);
context.startActivity(intent);
});
}
if (peertube.getHeaderType() != null && peertube.getHeaderTypeValue() != null) {
String type = peertube.getHeaderType();

View File

@ -0,0 +1,181 @@
package app.fedilab.fedilabtube.fragment;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.fedilabtube.R;
import app.fedilab.fedilabtube.asynctasks.RetrieveAccountsAsyncTask;
import app.fedilab.fedilabtube.client.APIResponse;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.drawer.AccountsListAdapter;
import app.fedilab.fedilabtube.interfaces.OnRetrieveAccountsInterface;
import es.dmoral.toasty.Toasty;
public class DisplayAccountsFragment extends Fragment implements OnRetrieveAccountsInterface {
private boolean flag_loading;
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private AccountsListAdapter accountsListAdapter;
private String max_id;
private List<Account> accounts;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private String targetedId, name;
private boolean swiped;
private RecyclerView lv_accounts;
private View rootView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_accounts, container, false);
context = getContext();
Bundle bundle = this.getArguments();
accounts = new ArrayList<>();
if (bundle != null) {
if (bundle.containsKey("tag"))
targetedId = bundle.getString("tag", null);
else
targetedId = bundle.getString("targetedid", null);
name = bundle.getString("name", null);
}
max_id = null;
firstLoad = true;
flag_loading = true;
swiped = false;
swipeRefreshLayout = rootView.findViewById(R.id.swipeContainer);
lv_accounts = rootView.findViewById(R.id.lv_accounts);
lv_accounts.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
mainLoader = rootView.findViewById(R.id.loader);
nextElementLoader = rootView.findViewById(R.id.loading_next_accounts);
textviewNoAction = rootView.findViewById(R.id.no_action);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
accountsListAdapter = new AccountsListAdapter(targetedId, this.accounts);
lv_accounts.setAdapter(accountsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(context);
lv_accounts.setLayoutManager(mLayoutManager);
lv_accounts.addOnScrollListener(new RecyclerView.OnScrollListener() {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (firstVisibleItem + visibleItemCount == totalItemCount) {
if (!flag_loading) {
flag_loading = true;
asyncTask = new RetrieveAccountsAsyncTask(context, name, DisplayAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
}
});
swipeRefreshLayout.setOnRefreshListener(this::pullToRefresh);
asyncTask = new RetrieveAccountsAsyncTask(context, name, DisplayAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
rootView = null;
}
@Override
public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
public void onDestroy() {
super.onDestroy();
if (asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
asyncTask.cancel(true);
}
public void scrollToTop() {
if (lv_accounts != null)
lv_accounts.setAdapter(accountsListAdapter);
}
@Override
public void onRetrieveAccounts(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if (apiResponse.getError() != null) {
Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
swiped = false;
flag_loading = false;
return;
}
flag_loading = (apiResponse.getMax_id() == null);
List<Account> accounts = apiResponse.getAccounts();
if (!swiped && firstLoad && (accounts == null || accounts.size() == 0))
textviewNoAction.setVisibility(View.VISIBLE);
else
textviewNoAction.setVisibility(View.GONE);
max_id = apiResponse.getMax_id();
if (swiped) {
accountsListAdapter = new AccountsListAdapter(targetedId, this.accounts);
lv_accounts.setAdapter(accountsListAdapter);
swiped = false;
}
if (accounts != null && accounts.size() > 0) {
int currentPosition = this.accounts.size();
this.accounts.addAll(accounts);
accountsListAdapter.notifyItemRangeChanged(currentPosition, accounts.size());
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
}
public void pullToRefresh() {
max_id = null;
accounts = new ArrayList<>();
firstLoad = true;
flag_loading = true;
swiped = true;
swipeRefreshLayout.setRefreshing(true);
asyncTask = new RetrieveAccountsAsyncTask(context, name, DisplayAccountsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}

View File

@ -35,13 +35,11 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;
@ -118,6 +116,8 @@ public class Helper {
public static final String SET_CUSTOM_TABS = "set_custom_tabs";
public static final String SET_DISPLAY_CONFIRM = "set_display_confirm";
public static final String INTENT_ADD_UPLOADED_MEDIA = "intent_add_uploaded_media";
public static final String RECEIVE_ACTION = "receive_action";
public static final String SET_UNFOLLOW_VALIDATION = "set_unfollow_validation";
//List of available academies
public static String[] academies = {
@ -308,6 +308,17 @@ public class Helper {
}
}
/**
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
*
* @param date Date
* @return String
*/
public static String shortDateToString(Date date) {
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
return df.format(date);
}
public static String dateDiffFull(Date dateToot) {
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault());

View File

@ -0,0 +1,9 @@
package app.fedilab.fedilabtube.interfaces;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Error;
public interface OnRetrieveAccountInterface {
void onRetrieveAccount(Account account, Error error);
}

View File

@ -0,0 +1,8 @@
package app.fedilab.fedilabtube.interfaces;
import app.fedilab.fedilabtube.client.APIResponse;
public interface OnRetrieveAccountsInterface {
void onRetrieveAccounts(APIResponse apiResponse);
}

View File

@ -0,0 +1,10 @@
package app.fedilab.fedilabtube.interfaces;
import java.util.List;
import app.fedilab.fedilabtube.client.entities.Status;
public interface OnRetrieveFeedsAccountInterface {
void onRetrieveFeedsAccount(List<Status> statuses);
}

View File

@ -0,0 +1,10 @@
package app.fedilab.fedilabtube.interfaces;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.Relationship;
public interface OnRetrieveRelationshipInterface {
void onRetrieveRelationship(Relationship relationship, Error error);
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@null" />
<stroke
android:width="2dp"
android:color="@android:color/transparent" />
<corners android:radius="4dp" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>

View File

@ -0,0 +1,13 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1dip"
android:color="?colorAccent" />
<corners android:radius="2dp" />
<padding
android:bottom="1dip"
android:left="4dip"
android:right="4dip"
android:top="1dip" />
</shape>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

View File

@ -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="#fff"
android:pathData="m9.6992,12c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4c2.21,0 4,1.79 4,4s-1.79,4 -4,4zM22.3012,10v2.3752h-6.6017v-2.3752zM9.6992,14c2.67,0 8,1.34 8,4v2h-16v-2c0,-2.66 5.33,-4 8,-4z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>

View File

@ -0,0 +1,434 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ShowAccountActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/top_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/banner_pp"
android:layout_width="match_parent"
android:layout_height="@dimen/layout_height_header"
android:contentDescription="@string/profile_banner"
android:scaleType="centerCrop"
android:src="@drawable/default_banner"
app:layout_collapseMode="parallax"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/account_pp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@drawable/account_pp_border"
android:contentDescription="@string/profile_picture"
android:padding="2dp"
app:layout_constraintBottom_toBottomOf="@id/banner_pp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner_pp"
app:layout_scrollFlags="scroll" />
<ImageButton
android:id="@+id/account_follow"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:contentDescription="@string/make_an_action"
android:scaleType="fitCenter"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@id/account_pp"
app:layout_constraintTop_toBottomOf="@id/banner_pp" />
<ImageButton
android:id="@+id/header_edit_profile"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:contentDescription="@string/edit_profile"
android:scaleType="fitCenter"
android:src="@drawable/ic_baseline_edit_24"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@id/account_pp"
app:layout_constraintTop_toBottomOf="@id/banner_pp" />
<View
android:layout_width="match_parent"
android:layout_height="50dp"
android:alpha="0.4"
android:background="@android:color/black"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/action_back"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentStart="true"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:contentDescription="@string/go_back"
android:src="@drawable/ic_baseline_arrow_back_24"
android:tint="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/main_header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/top_banner"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="8dp"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<LinearLayout
android:id="@+id/names_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/account_dn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?colorAccent"
android:textSize="18sp" />
<TextView
android:id="@+id/account_un"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textSize="16sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/account_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center"
android:padding="10dp"
android:textIsSelectable="true" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<TextView
android:id="@+id/account_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:layout_marginEnd="3dp"
android:background="@drawable/colored_border"
android:singleLine="true"
android:textColor="?colorAccent"
android:visibility="gone" />
<TextView
android:id="@+id/account_followed_by"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:layout_marginEnd="3dp"
android:background="@drawable/colored_border"
android:singleLine="true"
android:text="@string/followed_by"
android:textColor="?colorAccent"
android:visibility="gone" />
</LinearLayout>
</HorizontalScrollView>
<!-- Fields container -->
<LinearLayout
android:id="@+id/fields_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<!-- Fields 1 to 4 -->
<LinearLayout
android:id="@+id/field1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/label1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:minHeight="20dp"
android:padding="5dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/value1BG"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/value1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/field2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/label2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:minHeight="20dp"
android:padding="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/value2BG"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/value2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/field3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/label3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:minHeight="20dp"
android:padding="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/value3BG"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/value3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/field4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/label4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:minHeight="20dp"
android:padding="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/value4BG"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/value4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- End Fields container -->
</LinearLayout>
</RelativeLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:theme="@style/ThemeOverlay.AppCompat.Light">
<LinearLayout
android:id="@+id/toolbar_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animationCache="true"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="@+id/pp_actionBar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp"
android:contentDescription="@string/profile_picture"
android:gravity="center_vertical" />
<TextView
android:id="@+id/show_account_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:singleLine="true"
android:textSize="14sp" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/account_tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabSelectedTextColor="?colorAccent" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/account_viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,158 @@
<?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:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/account_pp"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/profile_picture" />
<LinearLayout
android:id="@+id/account_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/account_dn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="18sp" />
<TextView
android:id="@+id/account_un"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/account_ac"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:singleLine="true"
android:textSize="16sp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/account_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/videos" />
<TextView
android:id="@+id/account_sc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/following" />
<TextView
android:id="@+id/account_fgc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/followers" />
<TextView
android:id="@+id/account_frc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/account_ds"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:autoLink="web" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/account_follow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:contentDescription="@string/action_follow"
android:gravity="center"
android:scaleType="fitXY"
android:visibility="gone"
app:backgroundTint="?colorAccent"
app:fabSize="mini"
/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin">
<!-- Listview Accounts -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lv_accounts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@null"
android:scrollbars="none" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<RelativeLayout
android:id="@+id/no_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<TextView
android:id="@+id/no_action_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:padding="10dp"
android:text="@string/no_channels"
android:textSize="25sp"
android:textStyle="italic|bold"
android:typeface="serif" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<!-- Loader for next accounts -->
<RelativeLayout
android:id="@+id/loading_next_accounts"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center_horizontal"
android:gravity="bottom|center_horizontal"
android:visibility="gone">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

View File

@ -7,4 +7,5 @@
<color name="positive_thumbs">#2b90d9</color>
<color name="negative_thumbs">#F44336</color>
<color name="red_1">#F44336</color>
</resources>

View File

@ -4,4 +4,5 @@
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">5dp</dimen>
<dimen name="drawer_padding">2dp</dimen>
<dimen name="layout_height_header">180dp</dimen>
</resources>

View File

@ -97,4 +97,20 @@
<string name="change_instance">Changer d\'instance</string>
<string name="account">Compte</string>
<string name="instance_choice">Choisissez une instance</string>
<string name="toast_error_loading_account">Une erreur sest produite pendant le chargement du compte!</string>
<string name="profile_banner">Bannière du profil</string>
<string name="make_an_action">Faire une action</string>
<string name="go_back">Retour</string>
<string name="open_menu">Ouvrir le menu</string>
<string name="display_more">Afficher plus</string>
<string name="edit_profile">Éditer le profil</string>
<string name="followed_by">Vous suit</string>
<string name="no_channels">Aucune chaîne !</string>
<string name="following">Suit</string>
<string name="followers">Abonné·e·s</string>
<string name="nothing_to_do">Aucune action ne peut être réalisée</string>
<string name="unfollow_confirm">Voulez-vous vous désabonner de ce compte ?</string>
<string name="action_unfollow">Se désabonner</string>
<string name="action_follow">Suivre</string>
</resources>

View File

@ -9,4 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Base application theme. -->
<style name="AppThemeNoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>