forked from mirrors/Fedilab
Merge branch 'develop' into main
This commit is contained in:
commit
867d2ed492
65 changed files with 1846 additions and 938 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,3 +10,5 @@
|
|||
local.properties
|
||||
/cropper/build/
|
||||
/build/
|
||||
/app/fdroid/release/
|
||||
/app/playstore/release/
|
||||
|
|
|
@ -9,8 +9,8 @@ android {
|
|||
defaultConfig {
|
||||
minSdk 21
|
||||
targetSdk 31
|
||||
versionCode 395
|
||||
versionName "3.0.5"
|
||||
versionCode 396
|
||||
versionName "3.0.6"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
flavorDimensions "default"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<paths>
|
||||
<external-path
|
||||
name="my_images"
|
||||
path="Android/data/fr.gouv.etalab.mastodon.test/files/Pictures" />
|
||||
path="Android/data/fr.gouv.etalab.mastodon/files/Pictures" />
|
||||
|
||||
<cache-path
|
||||
name="*"
|
||||
|
|
|
@ -117,6 +117,11 @@
|
|||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:theme="@style/AppThemeBar"
|
||||
android:label="@string/search" />
|
||||
<activity
|
||||
android:name=".activities.TrendsActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/trending"
|
||||
android:theme="@style/AppThemeBar" />
|
||||
<activity
|
||||
android:name=".activities.ReorderTimelinesActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
|
|
|
@ -107,6 +107,7 @@ import app.fedilab.android.activities.ReorderTimelinesActivity;
|
|||
import app.fedilab.android.activities.ScheduledActivity;
|
||||
import app.fedilab.android.activities.SearchResultTabActivity;
|
||||
import app.fedilab.android.activities.SettingsActivity;
|
||||
import app.fedilab.android.activities.TrendsActivity;
|
||||
import app.fedilab.android.broadcastreceiver.NetworkStateReceiver;
|
||||
import app.fedilab.android.client.entities.api.Emoji;
|
||||
import app.fedilab.android.client.entities.api.EmojiInstance;
|
||||
|
@ -509,6 +510,9 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
} else if (id == R.id.nav_announcements) {
|
||||
Intent intent = new Intent(this, AnnouncementActivity.class);
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.nav_trends) {
|
||||
Intent intent = new Intent(this, TrendsActivity.class);
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.nav_cache) {
|
||||
Intent intent = new Intent(BaseMainActivity.this, CacheActivity.class);
|
||||
startActivity(intent);
|
||||
|
@ -900,11 +904,13 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
itemFilter.setTitle(show_filtered);
|
||||
}
|
||||
popup.setOnDismissListener(menu1 -> {
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + binding.viewPager.getCurrentItem());
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, binding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.refreshAllAdapters();
|
||||
}
|
||||
}
|
||||
});
|
||||
String finalShow_filtered = show_filtered;
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
|
@ -990,7 +996,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
}
|
||||
|
||||
public void refreshFragment() {
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + binding.viewPager.getCurrentItem());
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, binding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentNotificationContainer) {
|
||||
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
|
||||
fragTransaction.detach(fragment).commit();
|
||||
|
@ -999,6 +1006,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
fragTransaction2.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
|
@ -1035,7 +1043,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
*/
|
||||
private void scrollToTop() {
|
||||
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + binding.viewPager.getCurrentItem());
|
||||
if (binding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, binding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.scrollToTop();
|
||||
|
@ -1047,6 +1056,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
|
|||
fragmentNotificationContainer.scrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
|
|
@ -75,7 +75,7 @@ public class MainApplication extends MultiDexApplication {
|
|||
super.attachBaseContext(base);
|
||||
MultiDex.install(MainApplication.this);
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
|
||||
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), true);
|
||||
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false);
|
||||
if (send_crash_reports) {
|
||||
ACRA.init(this, new CoreConfigurationBuilder()
|
||||
//core configuration:
|
||||
|
|
|
@ -109,12 +109,17 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
|
|||
@Override
|
||||
public void onReceive(android.content.Context context, Intent intent) {
|
||||
String imgpath = intent.getStringExtra("imgpath");
|
||||
float focusX = intent.getFloatExtra("focusX", -2);
|
||||
float focusY = intent.getFloatExtra("focusY", -2);
|
||||
if (imgpath != null) {
|
||||
int position = 0;
|
||||
for (Status status : statusList) {
|
||||
if (status.media_attachments != null && status.media_attachments.size() > 0) {
|
||||
for (Attachment attachment : status.media_attachments) {
|
||||
if (attachment.local_path.equalsIgnoreCase(imgpath)) {
|
||||
if (focusX != -2) {
|
||||
attachment.focus = focusX + "," + focusY;
|
||||
}
|
||||
composeAdapter.notifyItemChanged(position);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class ContextActivity extends BaseActivity {
|
|||
focusedStatus = null; // or other values
|
||||
if (b != null)
|
||||
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
|
||||
if (focusedStatus == null) {
|
||||
if (focusedStatus == null && currentAccount == null || currentAccount.mastodon_account == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -33,15 +33,18 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -94,6 +97,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
private float startX;
|
||||
private float startY;
|
||||
private ActivityMediaPagerBinding binding;
|
||||
private FragmentMedia mCurrentFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -123,7 +127,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
|
||||
setTitle("");
|
||||
|
||||
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(MediaActivity.this);
|
||||
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||
binding.mediaViewpager.setAdapter(mPagerAdapter);
|
||||
binding.mediaViewpager.setSaveEnabled(false);
|
||||
binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
|
||||
|
@ -135,15 +139,14 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
binding.mediaDescription.setText(description);
|
||||
|
||||
}
|
||||
binding.mediaViewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||
binding.mediaViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
super.onPageSelected(position);
|
||||
String description = attachments.get(position).description;
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
|
@ -153,13 +156,9 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
binding.mediaDescription.setText(description);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
super.onPageScrollStateChanged(state);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setFullscreen(true);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
Point size = new Point();
|
||||
|
@ -358,18 +357,22 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
|
||||
public FragmentMedia getCurrentFragment() {
|
||||
return mCurrentFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Media Pager
|
||||
*/
|
||||
private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
ScreenSlidePagerAdapter(FragmentActivity fa) {
|
||||
super(fa);
|
||||
@SuppressWarnings("deprecation")
|
||||
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
|
||||
ScreenSlidePagerAdapter(FragmentManager fm) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@NotNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
Bundle bundle = new Bundle();
|
||||
FragmentMedia mediaSliderFragment = new FragmentMedia();
|
||||
bundle.putInt(Helper.ARG_MEDIA_POSITION, position);
|
||||
|
@ -379,7 +382,15 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
public void setPrimaryItem(@NotNull ViewGroup container, int position, @NotNull Object object) {
|
||||
if (getCurrentFragment() != object) {
|
||||
mCurrentFragment = ((FragmentMedia) object);
|
||||
}
|
||||
super.setPrimaryItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return attachments.size();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
|
@ -63,7 +62,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
@ -94,6 +93,7 @@ import app.fedilab.android.helper.Helper;
|
|||
import app.fedilab.android.helper.MastodonHelper;
|
||||
import app.fedilab.android.helper.SpannableHelper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.drawer.FieldAdapter;
|
||||
import app.fedilab.android.ui.drawer.IdentityProofsAdapter;
|
||||
import app.fedilab.android.ui.pageadapter.FedilabProfileTLPageAdapter;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
|
@ -247,29 +247,27 @@ public class ProfileActivity extends BaseActivity {
|
|||
binding.accountTabLayout.clearOnTabSelectedListeners();
|
||||
binding.accountTabLayout.removeAllTabs();
|
||||
//Tablayout for timelines/following/followers
|
||||
FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(ProfileActivity.this, account);
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab());
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab());
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab());
|
||||
FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(getSupportFragmentManager(), account);
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count))));
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count))));
|
||||
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count))));
|
||||
binding.accountViewpager.setAdapter(fedilabProfileTLPageAdapter);
|
||||
binding.accountViewpager.setOffscreenPageLimit(3);
|
||||
binding.accountViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.accountTabLayout));
|
||||
binding.accountTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
binding.accountViewpager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
new TabLayoutMediator(binding.accountTabLayout, binding.accountViewpager,
|
||||
(tab, position) -> {
|
||||
binding.accountViewpager.setCurrentItem(tab.getPosition(), true);
|
||||
switch (position) {
|
||||
case 0:
|
||||
tab.setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)));
|
||||
break;
|
||||
case 1:
|
||||
tab.setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)));
|
||||
break;
|
||||
case 2:
|
||||
tab.setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)));
|
||||
break;
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
}
|
||||
).attach();
|
||||
});
|
||||
binding.accountTabLayout.setTabTextColors(ThemeHelper.getAttColor(ProfileActivity.this, R.attr.mTextColor), ContextCompat.getColor(ProfileActivity.this, R.color.cyanea_accent_dark_reference));
|
||||
binding.accountTabLayout.setTabIconTint(ThemeHelper.getColorStateList(ProfileActivity.this));
|
||||
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
|
||||
|
@ -357,43 +355,9 @@ public class ProfileActivity extends BaseActivity {
|
|||
//Fields for profile
|
||||
List<Field> fields = account.fields;
|
||||
if (fields != null && fields.size() > 0) {
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
LinearLayout field;
|
||||
TextView labelView;
|
||||
TextView valueView;
|
||||
switch (i) {
|
||||
case 1:
|
||||
field = binding.field1;
|
||||
labelView = binding.label1;
|
||||
valueView = binding.value1;
|
||||
break;
|
||||
case 2:
|
||||
field = binding.field2;
|
||||
labelView = binding.label2;
|
||||
valueView = binding.value2;
|
||||
break;
|
||||
case 3:
|
||||
field = binding.field3;
|
||||
labelView = binding.label3;
|
||||
valueView = binding.value3;
|
||||
break;
|
||||
default:
|
||||
field = binding.field4;
|
||||
labelView = binding.label4;
|
||||
valueView = binding.value4;
|
||||
break;
|
||||
}
|
||||
|
||||
field.setVisibility(View.VISIBLE);
|
||||
if (fields.get(i).verified_at != null) {
|
||||
valueView.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(ProfileActivity.this, R.drawable.ic_baseline_verified_24), null);
|
||||
fields.get(i).value_span.setSpan(new ForegroundColorSpan(ContextCompat.getColor(ProfileActivity.this, R.color.verified_text)), 0, fields.get(i).value_span.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
valueView.setText(fields.get(i).value_span != null ? fields.get(i).value_span : fields.get(i).value, TextView.BufferType.SPANNABLE);
|
||||
valueView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
labelView.setText(fields.get(i).name);
|
||||
}
|
||||
binding.fieldsContainer.setVisibility(View.VISIBLE);
|
||||
FieldAdapter fieldAdapter = new FieldAdapter(fields);
|
||||
binding.fieldsContainer.setAdapter(fieldAdapter);
|
||||
binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this));
|
||||
}
|
||||
if (account.span_display_name == null && account.display_name == null) {
|
||||
binding.accountDn.setText(account.username);
|
||||
|
|
|
@ -24,7 +24,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.databinding.ActivityScheduledBinding;
|
||||
|
@ -56,31 +56,30 @@ public class ScheduledActivity extends BaseActivity {
|
|||
|
||||
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
|
||||
binding.title.setText(R.string.scheduled);
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab());
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab());
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab());
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_server)));
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_client)));
|
||||
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.reblog)));
|
||||
|
||||
binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(ScheduledActivity.this));
|
||||
binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(getSupportFragmentManager()));
|
||||
binding.scheduleViewpager.setOffscreenPageLimit(3);
|
||||
binding.scheduleViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.scheduleTablayout));
|
||||
binding.scheduleTablayout.setTabTextColors(ThemeHelper.getAttColor(ScheduledActivity.this, R.attr.mTextColor), ContextCompat.getColor(ScheduledActivity.this, R.color.cyanea_accent_dark_reference));
|
||||
binding.scheduleTablayout.setTabIconTint(ThemeHelper.getColorStateList(ScheduledActivity.this));
|
||||
|
||||
new TabLayoutMediator(binding.scheduleTablayout, binding.scheduleViewpager,
|
||||
(tab, position) -> {
|
||||
binding.scheduleViewpager.setCurrentItem(tab.getPosition(), true);
|
||||
switch (position) {
|
||||
case 0:
|
||||
tab.setText(getString(R.string.toots_server));
|
||||
break;
|
||||
case 1:
|
||||
tab.setText(getString(R.string.toots_client));
|
||||
break;
|
||||
case 2:
|
||||
tab.setText(getString(R.string.reblog));
|
||||
break;
|
||||
binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
binding.scheduleViewpager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
).attach();
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.os.Bundle;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -28,11 +29,12 @@ import androidx.annotation.NonNull;
|
|||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -75,35 +77,12 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
|
||||
}
|
||||
setTitle(search);
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab());
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab());
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab());
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab());
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.accounts)));
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache)));
|
||||
binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(SearchResultTabActivity.this, R.attr.mTextColor), ContextCompat.getColor(SearchResultTabActivity.this, R.color.cyanea_accent_dark_reference));
|
||||
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(SearchResultTabActivity.this));
|
||||
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this);
|
||||
binding.searchViewpager.setAdapter(mPagerAdapter);
|
||||
binding.searchViewpager.setSaveEnabled(false);
|
||||
binding.searchViewpager.setOffscreenPageLimit(3);
|
||||
new TabLayoutMediator(binding.searchTabLayout, binding.searchViewpager,
|
||||
(tab, position) -> {
|
||||
binding.searchViewpager.setCurrentItem(tab.getPosition(), true);
|
||||
switch (position) {
|
||||
case 0:
|
||||
tab.setText(getString(R.string.tags));
|
||||
break;
|
||||
case 1:
|
||||
tab.setText(getString(R.string.accounts));
|
||||
break;
|
||||
case 2:
|
||||
tab.setText(getString(R.string.toots));
|
||||
break;
|
||||
case 3:
|
||||
tab.setText(getString(R.string.action_cache));
|
||||
break;
|
||||
}
|
||||
}
|
||||
).attach();
|
||||
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
|
@ -112,12 +91,12 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentByTag("f" + binding.searchViewpager.getCurrentItem());
|
||||
Fragment fragment;
|
||||
if (binding.searchViewpager.getAdapter() != null) {
|
||||
fragment = (Fragment) binding.searchViewpager.getAdapter().instantiateItem(binding.searchViewpager, tab.getPosition());
|
||||
if (fragment instanceof FragmentMastodonAccount) {
|
||||
FragmentMastodonAccount fragmentMastodonAccount = ((FragmentMastodonAccount) fragment);
|
||||
fragmentMastodonAccount.scrollToTop();
|
||||
|
@ -129,6 +108,7 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
fragmentMastodonTag.scrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -149,7 +129,7 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
imm.hideSoftInputFromWindow(binding.searchTabLayout.getWindowToken(), 0);
|
||||
query = query.replaceAll("^#+", "");
|
||||
search = query.trim();
|
||||
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this);
|
||||
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||
binding.searchViewpager.setAdapter(mPagerAdapter);
|
||||
searchView.clearFocus();
|
||||
setTitle(search);
|
||||
|
@ -172,7 +152,24 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
searchView.setIconified(false);
|
||||
});
|
||||
|
||||
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||
binding.searchViewpager.setAdapter(mPagerAdapter);
|
||||
binding.searchViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
|
||||
if (tab != null)
|
||||
tab.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -193,16 +190,15 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
/**
|
||||
* Pager adapter for the 4 fragments
|
||||
*/
|
||||
private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
ScreenSlidePagerAdapter(FragmentActivity fa) {
|
||||
super(fa);
|
||||
@SuppressWarnings("deprecation")
|
||||
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
|
||||
ScreenSlidePagerAdapter(FragmentManager fm) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@NotNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
Bundle bundle = new Bundle();
|
||||
switch (position) {
|
||||
case 0:
|
||||
|
@ -229,7 +225,11 @@ public class SearchResultTabActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
package app.fedilab.android.activities;
|
||||
/* Copyright 2022 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
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.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.databinding.ActivityTrendsBinding;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTag;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
|
||||
|
||||
public class TrendsActivity extends BaseActivity {
|
||||
|
||||
|
||||
private ActivityTrendsBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ThemeHelper.applyThemeBar(this);
|
||||
binding = ActivityTrendsBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
|
||||
}
|
||||
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
|
||||
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
|
||||
binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(TrendsActivity.this, R.attr.mTextColor), ContextCompat.getColor(TrendsActivity.this, R.color.cyanea_accent_dark_reference));
|
||||
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(TrendsActivity.this));
|
||||
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
binding.trendsViewpager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
Fragment fragment;
|
||||
if (binding.trendsViewpager.getAdapter() != null) {
|
||||
fragment = (Fragment) binding.trendsViewpager.getAdapter().instantiateItem(binding.trendsViewpager, tab.getPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.scrollToTop();
|
||||
} else if (fragment instanceof FragmentMastodonTag) {
|
||||
FragmentMastodonTag fragmentMastodonTag = ((FragmentMastodonTag) fragment);
|
||||
fragmentMastodonTag.scrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||
binding.trendsViewpager.setAdapter(mPagerAdapter);
|
||||
binding.trendsViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
|
||||
if (tab != null)
|
||||
tab.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pager adapter for the 4 fragments
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private static 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) {
|
||||
FragmentMastodonTag fragmentMastodonTag = new FragmentMastodonTag();
|
||||
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_TAG);
|
||||
fragmentMastodonTag.setArguments(bundle);
|
||||
return fragmentMastodonTag;
|
||||
}
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
|
||||
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE);
|
||||
fragmentMastodonTimeline.setArguments(bundle);
|
||||
return fragmentMastodonTimeline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import app.fedilab.android.client.entities.api.Conversation;
|
|||
import app.fedilab.android.client.entities.api.Marker;
|
||||
import app.fedilab.android.client.entities.api.MastodonList;
|
||||
import app.fedilab.android.client.entities.api.Status;
|
||||
import app.fedilab.android.client.entities.api.Tag;
|
||||
import app.fedilab.android.client.entities.misskey.MisskeyNote;
|
||||
import app.fedilab.android.client.entities.nitter.Nitter;
|
||||
import app.fedilab.android.client.entities.peertube.PeertubeVideo;
|
||||
|
@ -52,6 +53,14 @@ public interface MastodonTimelinesService {
|
|||
@Query("limit") Integer limit
|
||||
);
|
||||
|
||||
|
||||
@GET("trends/statuses")
|
||||
Call<List<Status>> getStatusTrends(@Header("Authorization") String token);
|
||||
|
||||
|
||||
@GET("trends/tags")
|
||||
Call<List<Tag>> getTagTrends(@Header("Authorization") String token);
|
||||
|
||||
//Public Tags timelines
|
||||
@GET("timelines/tag/{hashtag}")
|
||||
Call<List<Status>> getHashTag(
|
||||
|
|
|
@ -43,7 +43,37 @@ public class Attachment implements Serializable {
|
|||
public long size;
|
||||
@SerializedName("local_path")
|
||||
public String local_path;
|
||||
@SerializedName("meta")
|
||||
public Meta meta;
|
||||
|
||||
public String peertubeHost = null;
|
||||
public String peertubeId = null;
|
||||
public String focus = null;
|
||||
|
||||
public static class Meta implements Serializable {
|
||||
@SerializedName("focus")
|
||||
public Focus focus;
|
||||
@SerializedName("original")
|
||||
public MediaData original;
|
||||
@SerializedName("small")
|
||||
public MediaData small;
|
||||
}
|
||||
|
||||
public static class Focus implements Serializable {
|
||||
@SerializedName("x")
|
||||
public float x;
|
||||
@SerializedName("y")
|
||||
public float y;
|
||||
}
|
||||
|
||||
public static class MediaData implements Serializable {
|
||||
@SerializedName("width")
|
||||
public int width;
|
||||
@SerializedName("height")
|
||||
public int height;
|
||||
@SerializedName("size")
|
||||
public String size;
|
||||
@SerializedName("aspect")
|
||||
public float aspect;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ public class QuickLoad {
|
|||
QuickLoad localQuickLoad = getSavedValue(account, Timeline.TimeLineEnum.LOCAL, null);
|
||||
QuickLoad publicQuickLoad = getSavedValue(account, Timeline.TimeLineEnum.PUBLIC, null);
|
||||
|
||||
if (homeQuickLoad != null && homeQuickLoad.statuses != null) {
|
||||
if (homeQuickLoad != null && homeQuickLoad.statuses != null && newStatus != null) {
|
||||
for (int i = 0; i < homeQuickLoad.statuses.size(); i++) {
|
||||
if (homeQuickLoad.statuses.get(i).id.equals(newStatus.id)) {
|
||||
homeQuickLoad.statuses.set(i, newStatus);
|
||||
|
@ -327,7 +327,7 @@ public class QuickLoad {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (localQuickLoad != null && localQuickLoad.statuses != null) {
|
||||
if (localQuickLoad != null && localQuickLoad.statuses != null && newStatus != null) {
|
||||
for (int i = 0; i < localQuickLoad.statuses.size(); i++) {
|
||||
if (localQuickLoad.statuses.get(i).id.equals(newStatus.id)) {
|
||||
localQuickLoad.statuses.set(i, newStatus);
|
||||
|
@ -345,7 +345,7 @@ public class QuickLoad {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (publicQuickLoad != null && publicQuickLoad.statuses != null) {
|
||||
if (publicQuickLoad != null && publicQuickLoad.statuses != null && newStatus != null) {
|
||||
for (int i = 0; i < publicQuickLoad.statuses.size(); i++) {
|
||||
if (publicQuickLoad.statuses.get(i).id.equals(newStatus.id)) {
|
||||
publicQuickLoad.statuses.set(i, newStatus);
|
||||
|
|
|
@ -370,6 +370,10 @@ public class Timeline {
|
|||
LIST("LIST"),
|
||||
@SerializedName("REMOTE")
|
||||
REMOTE("REMOTE"),
|
||||
@SerializedName("TREND_TAG")
|
||||
TREND_TAG("TREND_TAG"),
|
||||
@SerializedName("TREND_MESSAGE")
|
||||
TREND_MESSAGE("TREND_MESSAGE"),
|
||||
@SerializedName("ACCOUNT_TIMELINE")
|
||||
ACCOUNT_TIMELINE("ACCOUNT_TIMELINE"),
|
||||
@SerializedName("MUTED_TIMELINE")
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
package app.fedilab.android.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
|
||||
//Original work at https://stackoverflow.com/a/17830245
|
||||
public class CirclesDrawingView extends View {
|
||||
|
||||
|
||||
// Radius limit in pixels
|
||||
private final static int RADIUS_LIMIT = 100;
|
||||
private static final int CIRCLES_LIMIT = 1;
|
||||
private final Random mRadiusGenerator = new Random();
|
||||
/**
|
||||
* All available circles
|
||||
*/
|
||||
private final HashSet<CircleArea> mCircles = new HashSet<>(CIRCLES_LIMIT);
|
||||
private final SparseArray<CircleArea> mCirclePointer = new SparseArray<>(CIRCLES_LIMIT);
|
||||
/**
|
||||
* Paint to draw circles
|
||||
*/
|
||||
private Paint mCirclePaint;
|
||||
private CircleArea touchedCircle;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param ct {@link android.content.Context}
|
||||
*/
|
||||
public CirclesDrawingView(final Context ct) {
|
||||
super(ct);
|
||||
init(ct);
|
||||
}
|
||||
|
||||
public CirclesDrawingView(final Context ct, final AttributeSet attrs) {
|
||||
super(ct, attrs);
|
||||
init(ct);
|
||||
}
|
||||
|
||||
public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) {
|
||||
super(ct, attrs, defStyle);
|
||||
init(ct);
|
||||
}
|
||||
|
||||
public CircleArea getTouchedCircle() {
|
||||
return this.touchedCircle;
|
||||
}
|
||||
|
||||
private void init(final Context ct) {
|
||||
// Generate bitmap used for background
|
||||
mCirclePaint = new Paint();
|
||||
|
||||
mCirclePaint.setColor(ResourcesCompat.getColor(getContext().getResources(), R.color.cyanea_accent, getContext().getTheme()));
|
||||
mCirclePaint.setStrokeWidth(10);
|
||||
mCirclePaint.setStyle(Paint.Style.STROKE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(final Canvas canv) {
|
||||
// background bitmap to cover all area
|
||||
for (CircleArea circle : mCircles) {
|
||||
canv.drawCircle(circle.centerX, circle.centerY, circle.radius, mCirclePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(final MotionEvent event) {
|
||||
boolean handled = false;
|
||||
|
||||
|
||||
int xTouch;
|
||||
int yTouch;
|
||||
int pointerId;
|
||||
int actionIndex = event.getActionIndex();
|
||||
|
||||
// get touch event coordinates and make transparent circle from it
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// it's the first pointer, so clear all existing pointers data
|
||||
clearCirclePointer();
|
||||
|
||||
xTouch = (int) event.getX(0);
|
||||
yTouch = (int) event.getY(0);
|
||||
|
||||
// check if we've touched inside some circle
|
||||
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
|
||||
touchedCircle.centerX = xTouch;
|
||||
touchedCircle.centerY = yTouch;
|
||||
mCirclePointer.put(event.getPointerId(0), touchedCircle);
|
||||
invalidate();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// It secondary pointers, so obtain their ids and check circles
|
||||
pointerId = event.getPointerId(actionIndex);
|
||||
|
||||
xTouch = (int) event.getX(actionIndex);
|
||||
yTouch = (int) event.getY(actionIndex);
|
||||
|
||||
// check if we've touched inside some circle
|
||||
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
|
||||
|
||||
mCirclePointer.put(pointerId, touchedCircle);
|
||||
touchedCircle.centerX = xTouch;
|
||||
touchedCircle.centerY = yTouch;
|
||||
invalidate();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final int pointerCount = event.getPointerCount();
|
||||
|
||||
for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
|
||||
// Some pointer has moved, search it by pointer id
|
||||
pointerId = event.getPointerId(actionIndex);
|
||||
|
||||
xTouch = (int) event.getX(actionIndex);
|
||||
yTouch = (int) event.getY(actionIndex);
|
||||
|
||||
touchedCircle = mCirclePointer.get(pointerId);
|
||||
|
||||
if (null != touchedCircle) {
|
||||
touchedCircle.centerX = xTouch;
|
||||
touchedCircle.centerY = yTouch;
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
clearCirclePointer();
|
||||
invalidate();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
// not general pointer was up
|
||||
pointerId = event.getPointerId(actionIndex);
|
||||
|
||||
mCirclePointer.remove(pointerId);
|
||||
invalidate();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event) || handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all CircleArea - pointer id relations
|
||||
*/
|
||||
private void clearCirclePointer() {
|
||||
|
||||
mCirclePointer.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search and creates new (if needed) circle based on touch area
|
||||
*
|
||||
* @param xTouch int x of touch
|
||||
* @param yTouch int y of touch
|
||||
* @return obtained {@link CircleArea}
|
||||
*/
|
||||
private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
|
||||
CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);
|
||||
|
||||
if (null == touchedCircle) {
|
||||
touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT);
|
||||
|
||||
if (mCircles.size() == CIRCLES_LIMIT) {
|
||||
// remove first circle
|
||||
mCircles.clear();
|
||||
}
|
||||
|
||||
mCircles.add(touchedCircle);
|
||||
}
|
||||
|
||||
return touchedCircle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines touched circle
|
||||
*
|
||||
* @param xTouch int x touch coordinate
|
||||
* @param yTouch int y touch coordinate
|
||||
* @return {@link CircleArea} touched circle or null if no circle has been touched
|
||||
*/
|
||||
private CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
|
||||
CircleArea touched = null;
|
||||
|
||||
for (CircleArea circle : mCircles) {
|
||||
if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
|
||||
touched = circle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores data about single circle
|
||||
*/
|
||||
public static class CircleArea {
|
||||
public int centerX;
|
||||
public int centerY;
|
||||
int radius;
|
||||
|
||||
CircleArea(int centerX, int centerY, int radius) {
|
||||
this.radius = radius;
|
||||
this.centerX = centerX;
|
||||
this.centerY = centerY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Circle[" + centerX + ", " + centerY + ", " + radius + "]";
|
||||
}
|
||||
}
|
||||
}
|
209
app/src/main/java/app/fedilab/android/helper/GlideFocus.java
Normal file
209
app/src/main/java/app/fedilab/android/helper/GlideFocus.java
Normal file
|
@ -0,0 +1,209 @@
|
|||
package app.fedilab.android.helper;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import static com.bumptech.glide.load.resource.bitmap.TransformationUtils.PAINT_FLAGS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
|
||||
import com.bumptech.glide.util.Synthetic;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import jp.wasabeef.glide.transformations.BitmapTransformation;
|
||||
|
||||
public class GlideFocus extends BitmapTransformation {
|
||||
|
||||
|
||||
private static final int VERSION = 1;
|
||||
private static final String ID = "app.fedilab.android.GlideFocus." + VERSION;
|
||||
private static final Set<String> MODELS_REQUIRING_BITMAP_LOCK =
|
||||
new HashSet<>(
|
||||
Arrays.asList(
|
||||
// Moto X gen 2
|
||||
"XT1085",
|
||||
"XT1092",
|
||||
"XT1093",
|
||||
"XT1094",
|
||||
"XT1095",
|
||||
"XT1096",
|
||||
"XT1097",
|
||||
"XT1098",
|
||||
// Moto G gen 1
|
||||
"XT1031",
|
||||
"XT1028",
|
||||
"XT937C",
|
||||
"XT1032",
|
||||
"XT1008",
|
||||
"XT1033",
|
||||
"XT1035",
|
||||
"XT1034",
|
||||
"XT939G",
|
||||
"XT1039",
|
||||
"XT1040",
|
||||
"XT1042",
|
||||
"XT1045",
|
||||
// Moto G gen 2
|
||||
"XT1063",
|
||||
"XT1064",
|
||||
"XT1068",
|
||||
"XT1069",
|
||||
"XT1072",
|
||||
"XT1077",
|
||||
"XT1078",
|
||||
"XT1079"));
|
||||
private static final Lock BITMAP_DRAWABLE_LOCK =
|
||||
MODELS_REQUIRING_BITMAP_LOCK.contains(Build.MODEL) ? new ReentrantLock() : new NoLock();
|
||||
private static final Paint DEFAULT_PAINT = new Paint(PAINT_FLAGS);
|
||||
private final float focalX;
|
||||
private final float focalY;
|
||||
|
||||
public GlideFocus(float focalX, float focalY) {
|
||||
this.focalX = focalX;
|
||||
this.focalY = focalY;
|
||||
}
|
||||
|
||||
private static void applyMatrix(
|
||||
@NonNull Bitmap inBitmap, @NonNull Bitmap targetBitmap, Matrix matrix) {
|
||||
BITMAP_DRAWABLE_LOCK.lock();
|
||||
try {
|
||||
Canvas canvas = new Canvas(targetBitmap);
|
||||
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
|
||||
clear(canvas);
|
||||
} finally {
|
||||
BITMAP_DRAWABLE_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Bitmap.Config getNonNullConfig(@NonNull Bitmap bitmap) {
|
||||
return bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888;
|
||||
}
|
||||
|
||||
// Avoids warnings in M+.
|
||||
private static void clear(Canvas canvas) {
|
||||
canvas.setBitmap(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(@NonNull Context context, @NonNull BitmapPool pool,
|
||||
@NonNull Bitmap inBitmap, int width, int height) {
|
||||
|
||||
if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) {
|
||||
return inBitmap;
|
||||
}
|
||||
// From ImageView/Bitmap.createScaledBitmap.
|
||||
final float scale;
|
||||
final float dx;
|
||||
final float dy;
|
||||
Matrix m = new Matrix();
|
||||
if (inBitmap.getWidth() * height > width * inBitmap.getHeight()) {
|
||||
scale = (float) height / (float) inBitmap.getHeight();
|
||||
dx = (width - inBitmap.getWidth() * scale) * 0.5f * (1 + focalX);
|
||||
dy = 0;
|
||||
} else {
|
||||
scale = (float) width / (float) inBitmap.getWidth();
|
||||
dx = 0;
|
||||
dy = (height - inBitmap.getHeight() * scale) * 0.5f * (1 + focalY);
|
||||
}
|
||||
|
||||
m.setScale(scale, scale);
|
||||
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
|
||||
|
||||
Bitmap result = pool.get(width, height, getNonNullConfig(inBitmap));
|
||||
// We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
|
||||
TransformationUtils.setAlpha(inBitmap, result);
|
||||
|
||||
applyMatrix(inBitmap, result, m);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||
messageDigest.update((ID + focalX + focalY).getBytes(CHARSET));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof GlideFocus &&
|
||||
((GlideFocus) o).focalX == focalX &&
|
||||
((GlideFocus) o).focalY == focalY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (ID.hashCode() + focalX * 100000 + focalY * 1000);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CropTransformation(width=" + focalX + ", height=" + focalY + ")";
|
||||
}
|
||||
|
||||
private static final class NoLock implements Lock {
|
||||
|
||||
@Synthetic
|
||||
NoLock() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockInterruptibly() throws InterruptedException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(long time, @NonNull TimeUnit unit) throws InterruptedException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Condition newCondition() {
|
||||
throw new UnsupportedOperationException("Should not be called");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1379,6 +1379,9 @@ public class Helper {
|
|||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
|
||||
private static int notificationId = 1;
|
||||
|
||||
/**
|
||||
* Sends notification with intent
|
||||
*
|
||||
|
@ -1389,7 +1392,7 @@ public class Helper {
|
|||
* @param message String message for the notification
|
||||
*/
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
public static void notify_user(Context context, int notificationId, BaseAccount account, Intent intent, Bitmap icon, NotifType notifType, String title, String message) {
|
||||
public static void notify_user(Context context, BaseAccount account, Intent intent, Bitmap icon, NotifType notifType, String title, String message) {
|
||||
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
// prepare intent which is triggered if the user click on the notification
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
|
@ -1440,14 +1443,11 @@ public class Helper {
|
|||
channelTitle = context.getString(R.string.channel_notif_boost);
|
||||
}
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(R.drawable.ic_notification).setTicker(message)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setAutoCancel(true);
|
||||
.setSmallIcon(R.drawable.ic_notification).setTicker(message);
|
||||
if (notifType == NotifType.MENTION) {
|
||||
if (message.length() > 500) {
|
||||
message = message.substring(0, 499) + "…";
|
||||
}
|
||||
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
|
||||
}
|
||||
notificationBuilder.setGroup(account.mastodon_account.acct + "@" + account.instance)
|
||||
.setContentIntent(pIntent)
|
||||
|
@ -1515,19 +1515,21 @@ public class Helper {
|
|||
}
|
||||
notificationBuilder.setContentTitle(title);
|
||||
notificationBuilder.setLargeIcon(icon);
|
||||
notificationManager.notify(notificationId, notificationBuilder.build());
|
||||
|
||||
Notification summaryNotification =
|
||||
new NotificationCompat.Builder(context, channelId)
|
||||
|
||||
Notification summaryNotification = summaryNotification = new NotificationCompat.Builder(context, channelId)
|
||||
.setContentTitle(title)
|
||||
.setContentText(channelTitle)
|
||||
.setContentIntent(pIntent)
|
||||
.setLargeIcon(icon)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
|
||||
.setGroup(account.mastodon_account.acct + "@" + account.instance)
|
||||
.setGroupSummary(true)
|
||||
.build();
|
||||
notificationManager.notify(notificationId, summaryNotification);
|
||||
|
||||
notificationManager.notify(notificationId++, notificationBuilder.build());
|
||||
notificationManager.notify(0, summaryNotification);
|
||||
}
|
||||
|
||||
public static void transfertIfExist(Context context) {
|
||||
|
|
|
@ -163,7 +163,7 @@ public class MediaHelper {
|
|||
Uri uri = Uri.fromFile(backupFile);
|
||||
intent.setDataAndType(uri, mime);
|
||||
if (!share) {
|
||||
notify_user(context, Helper.NOTIFICATION_MEDIA, currentAccount, intent, BitmapFactory.decodeResource(context.getResources(),
|
||||
notify_user(context, currentAccount, intent, BitmapFactory.decodeResource(context.getResources(),
|
||||
R.mipmap.ic_launcher), Helper.NotifType.STORE, context.getString(R.string.save_over), context.getString(R.string.download_from, fileName));
|
||||
Toasty.success(context, context.getString(R.string.save_over), Toasty.LENGTH_LONG).show();
|
||||
} else {
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
package app.fedilab.android.helper
|
||||
|
||||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.widget.FrameLayout
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
|
||||
/**
|
||||
* Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
|
||||
* where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
|
||||
* ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
|
||||
*
|
||||
* This solution has limitations when using multiple levels of nested scrollable elements
|
||||
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
|
||||
*/
|
||||
class NestedScrollableHost : FrameLayout {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
|
||||
private var touchSlop = 0
|
||||
private var initialX = 0f
|
||||
private var initialY = 0f
|
||||
private val parentViewPager: ViewPager2?
|
||||
get() {
|
||||
var v: View? = parent as? View
|
||||
while (v != null && v !is ViewPager2) {
|
||||
v = v.parent as? View
|
||||
}
|
||||
return v as? ViewPager2
|
||||
}
|
||||
|
||||
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
|
||||
|
||||
init {
|
||||
touchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
}
|
||||
|
||||
private fun canChildScroll(orientation: Int, delta: Float): Boolean {
|
||||
val direction = -delta.sign.toInt()
|
||||
return when (orientation) {
|
||||
0 -> child?.canScrollHorizontally(direction) ?: false
|
||||
1 -> child?.canScrollVertically(direction) ?: false
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
|
||||
handleInterceptTouchEvent(e)
|
||||
return super.onInterceptTouchEvent(e)
|
||||
}
|
||||
|
||||
private fun handleInterceptTouchEvent(e: MotionEvent) {
|
||||
val orientation = parentViewPager?.orientation ?: return
|
||||
|
||||
// Early return if child can't scroll in same direction as parent
|
||||
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (e.action == MotionEvent.ACTION_DOWN) {
|
||||
initialX = e.x
|
||||
initialY = e.y
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
} else if (e.action == MotionEvent.ACTION_MOVE) {
|
||||
val dx = e.x - initialX
|
||||
val dy = e.y - initialY
|
||||
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
|
||||
|
||||
// assuming ViewPager2 touch-slop is 2x touch-slop of child
|
||||
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
|
||||
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
|
||||
|
||||
if (scaledDx > touchSlop || scaledDy > touchSlop) {
|
||||
if (isVpHorizontal == (scaledDy > scaledDx)) {
|
||||
// Gesture is perpendicular, allow all parents to intercept
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
} else {
|
||||
// Gesture is parallel, query child if movement in that direction is possible
|
||||
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
|
||||
// Child can scroll, disallow all parents to intercept
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
} else {
|
||||
// Child cannot scroll, allow all parents to intercept
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -317,7 +317,7 @@ public class NotificationsHelper {
|
|||
since_ids.put(account.user_id + "@" + account.instance, lastNotif);
|
||||
editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id);
|
||||
editor.apply();
|
||||
notify_user(context, Helper.NOTIFICATION_USER_NOTIF, account, intent, BitmapFactory.decodeResource(context.getResources(),
|
||||
notify_user(context, account, intent, BitmapFactory.decodeResource(context.getResources(),
|
||||
R.mipmap.ic_launcher), finalNotifType, finalTitle, finalMessage);
|
||||
}
|
||||
return false;
|
||||
|
@ -332,7 +332,7 @@ public class NotificationsHelper {
|
|||
editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id);
|
||||
editor.apply();
|
||||
since_ids.put(account.user_id + "@" + account.instance, lastNotif);
|
||||
notify_user(context, Helper.NOTIFICATION_USER_NOTIF, account, intent, resource, finalNotifType, finalTitle, finalMessage);
|
||||
notify_user(context, account, intent, resource, finalNotifType, finalTitle, finalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,16 +26,14 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -149,18 +147,11 @@ public class PinnedTimelineHelper {
|
|||
activityMainBinding.tabLayout.removeAllTabs();
|
||||
//Small hack to hide first tabs (they represent the item of the bottom menu)
|
||||
int toRemove = itemToRemoveInBottomMenu(activity);
|
||||
List<String> tabTitle = new ArrayList<>();
|
||||
List<RemoteInstance.InstanceType> tabTypeRemote = new ArrayList<>();
|
||||
List<Timeline.TimeLineEnum> tabType = new ArrayList<>();
|
||||
for (int i = 0; i < (BOTTOM_TIMELINE_COUNT - toRemove); i++) {
|
||||
activityMainBinding.tabLayout.addTab(activityMainBinding.tabLayout.newTab());
|
||||
tabTitle.add("");
|
||||
tabType.add(Timeline.TimeLineEnum.HOME);
|
||||
tabTypeRemote.add(RemoteInstance.InstanceType.MASTODON);
|
||||
((ViewGroup) activityMainBinding.tabLayout.getChildAt(0)).getChildAt(i).setVisibility(View.GONE);
|
||||
}
|
||||
List<PinnedTimeline> pinnedTimelineVisibleList = new ArrayList<>();
|
||||
|
||||
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
|
||||
if (pinnedTimeline.displayed) {
|
||||
TabLayout.Tab tab = activityMainBinding.tabLayout.newTab();
|
||||
|
@ -170,22 +161,46 @@ public class PinnedTimelineHelper {
|
|||
name = pinnedTimeline.mastodonList.title;
|
||||
break;
|
||||
case TAG:
|
||||
name = pinnedTimeline.tagTimeline.name;
|
||||
name = pinnedTimeline.tagTimeline.name.replaceAll("#", "");
|
||||
break;
|
||||
case REMOTE:
|
||||
name = pinnedTimeline.remoteInstance.host;
|
||||
break;
|
||||
}
|
||||
TextView tv = (TextView) LayoutInflater.from(activity).inflate(R.layout.custom_tab_instance, new LinearLayout(activity), false);
|
||||
tv.setText(name);
|
||||
tabTitle.add(name);
|
||||
tabType.add(pinnedTimeline.type);
|
||||
if (pinnedTimeline.type == Timeline.TimeLineEnum.REMOTE) {
|
||||
tabTypeRemote.add(pinnedTimeline.remoteInstance.type);
|
||||
} else {
|
||||
tabTypeRemote.add(null);
|
||||
TabCustomViewBinding tabCustomViewBinding = TabCustomViewBinding.inflate(activity.getLayoutInflater());
|
||||
tabCustomViewBinding.title.setText(name);
|
||||
switch (pinnedTimeline.type) {
|
||||
case LIST:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_list);
|
||||
break;
|
||||
case TAG:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_tag);
|
||||
break;
|
||||
case REMOTE:
|
||||
switch (pinnedTimeline.remoteInstance.type) {
|
||||
case PIXELFED:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.pixelfed);
|
||||
break;
|
||||
case MASTODON:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.mastodon_icon_item);
|
||||
break;
|
||||
|
||||
case MISSKEY:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.misskey);
|
||||
break;
|
||||
case NITTER:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.nitter);
|
||||
break;
|
||||
case GNU:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_gnu_social);
|
||||
break;
|
||||
case PEERTUBE:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.peertube_icon);
|
||||
break;
|
||||
}
|
||||
tab.setCustomView(tv);
|
||||
break;
|
||||
}
|
||||
tab.setCustomView(tabCustomViewBinding.getRoot());
|
||||
activityMainBinding.tabLayout.addTab(tab);
|
||||
pinnedTimelineVisibleList.add(pinnedTimeline);
|
||||
}
|
||||
|
@ -211,13 +226,18 @@ public class PinnedTimelineHelper {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
activityMainBinding.viewPager.setAdapter(null);
|
||||
activityMainBinding.viewPager.clearOnPageChangeListeners();
|
||||
activityMainBinding.tabLayout.clearOnTabSelectedListeners();
|
||||
|
||||
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity, activity, pinned, bottomMenu);
|
||||
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity, activity.getSupportFragmentManager(), pinned, bottomMenu);
|
||||
activityMainBinding.viewPager.setAdapter(fedilabPageAdapter);
|
||||
activityMainBinding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(activityMainBinding.tabLayout));
|
||||
activityMainBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
activityMainBinding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (position < BOTTOM_TIMELINE_COUNT - toRemove) {
|
||||
|
@ -230,49 +250,17 @@ public class PinnedTimelineHelper {
|
|||
activityMainBinding.bottomNavView.getMenu().setGroupCheckable(0, true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
new TabLayoutMediator(activityMainBinding.tabLayout, activityMainBinding.viewPager,
|
||||
(tab, position) -> {
|
||||
TabCustomViewBinding tabCustomViewBinding = TabCustomViewBinding.inflate(activity.getLayoutInflater());
|
||||
tabCustomViewBinding.title.setText(tabTitle.get(position));
|
||||
switch (tabType.get(position)) {
|
||||
case LIST:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_list);
|
||||
break;
|
||||
case TAG:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_tl_tag);
|
||||
break;
|
||||
case REMOTE:
|
||||
switch (tabTypeRemote.get(position)) {
|
||||
case PIXELFED:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.pixelfed);
|
||||
break;
|
||||
case MASTODON:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.mastodon_icon_item);
|
||||
break;
|
||||
|
||||
case MISSKEY:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.misskey);
|
||||
break;
|
||||
case NITTER:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.nitter);
|
||||
break;
|
||||
case GNU:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.ic_gnu_social);
|
||||
break;
|
||||
case PEERTUBE:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.peertube_icon);
|
||||
break;
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
tab.setCustomView(tabCustomViewBinding.getRoot());
|
||||
}
|
||||
).attach();
|
||||
});
|
||||
|
||||
|
||||
activityMainBinding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
activityMainBinding.viewPager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -281,7 +269,7 @@ public class PinnedTimelineHelper {
|
|||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag("f" + activityMainBinding.viewPager.getCurrentItem());
|
||||
Fragment fragment = fedilabPageAdapter.getCurrentFragment();
|
||||
if (fragment instanceof FragmentMastodonTimeline) {
|
||||
((FragmentMastodonTimeline) fragment).scrollToTop();
|
||||
} else if (fragment instanceof FragmentMastodonConversation) {
|
||||
|
@ -353,10 +341,9 @@ public class PinnedTimelineHelper {
|
|||
itemShowNSFW.setChecked(showNSFW[0]);
|
||||
popup.setOnDismissListener(menu1 -> {
|
||||
if (changes[0]) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline;
|
||||
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag("f" + activityMainBinding.viewPager.getCurrentItem());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
if (activityMainBinding.viewPager.getAdapter() != null) {
|
||||
Fragment fragmentMastodonTimeline = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition());
|
||||
if (fragmentMastodonTimeline instanceof FragmentMastodonTimeline && fragmentMastodonTimeline.isVisible()) {
|
||||
FragmentTransaction fragTransaction = activity.getSupportFragmentManager().beginTransaction();
|
||||
fragTransaction.detach(fragmentMastodonTimeline).commit();
|
||||
Bundle bundle = new Bundle();
|
||||
|
@ -368,6 +355,7 @@ public class PinnedTimelineHelper {
|
|||
fragTransaction2.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -568,10 +556,12 @@ public class PinnedTimelineHelper {
|
|||
});
|
||||
changes[0] = true;
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = null;
|
||||
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag("f" + activityMainBinding.viewPager.getCurrentItem());
|
||||
if (activityMainBinding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
}
|
||||
}
|
||||
if (fragmentMastodonTimeline == null)
|
||||
return false;
|
||||
FragmentTransaction fragTransaction1 = activity.getSupportFragmentManager().beginTransaction();
|
||||
|
@ -613,11 +603,13 @@ public class PinnedTimelineHelper {
|
|||
MenuItem item = popup.getMenu().add(0, 0, Menu.NONE, title);
|
||||
item.setOnMenuItemClickListener(item1 -> {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = null;
|
||||
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag("f" + activityMainBinding.viewPager.getCurrentItem());
|
||||
if (activityMainBinding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.refreshAllAdapters();
|
||||
}
|
||||
}
|
||||
FragmentTransaction fragTransaction1 = activity.getSupportFragmentManager().beginTransaction();
|
||||
if (fragmentMastodonTimeline == null)
|
||||
return false;
|
||||
|
@ -694,11 +686,13 @@ public class PinnedTimelineHelper {
|
|||
popup.setOnDismissListener(menu -> {
|
||||
if (changes[0]) {
|
||||
FragmentMastodonTimeline fragmentMastodonTimeline = null;
|
||||
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag("f" + activityMainBinding.viewPager.getCurrentItem());
|
||||
if (activityMainBinding.viewPager.getAdapter() != null) {
|
||||
Fragment fragment = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition());
|
||||
if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
|
||||
fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
|
||||
fragmentMastodonTimeline.refreshAllAdapters();
|
||||
}
|
||||
}
|
||||
FragmentTransaction fragTransaction1 = activity.getSupportFragmentManager().beginTransaction();
|
||||
if (fragmentMastodonTimeline == null)
|
||||
return;
|
||||
|
|
|
@ -256,14 +256,15 @@ public class SpannableHelper {
|
|||
content.setSpan(new LongClickableSpan() {
|
||||
@Override
|
||||
public void onLongClick(View view) {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||
Context mContext = view.getContext();
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
|
||||
dialogBuilder.setView(popupLinksBinding.getRoot());
|
||||
AlertDialog alertDialog = dialogBuilder.create();
|
||||
alertDialog.show();
|
||||
|
||||
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||
builder.setMessage(url);
|
||||
builder.setTitle(context.getString(R.string.display_full_link));
|
||||
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||
|
@ -720,14 +721,15 @@ public class SpannableHelper {
|
|||
content.setSpan(new LongClickableSpan() {
|
||||
@Override
|
||||
public void onLongClick(View view) {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle());
|
||||
Context mContext = view.getContext();
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
|
||||
dialogBuilder.setView(popupLinksBinding.getRoot());
|
||||
AlertDialog alertDialog = dialogBuilder.create();
|
||||
alertDialog.show();
|
||||
|
||||
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
|
||||
builder.setMessage(url);
|
||||
builder.setTitle(context.getString(R.string.display_full_link));
|
||||
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.Manifest;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
@ -33,6 +34,7 @@ import java.io.InputStream;
|
|||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.databinding.ActivityEditImageBinding;
|
||||
import app.fedilab.android.helper.CirclesDrawingView;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.imageeditor.base.BaseActivity;
|
||||
import app.fedilab.android.imageeditor.filters.FilterListener;
|
||||
|
@ -58,7 +60,6 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
|
|||
private static final int CAMERA_REQUEST = 52;
|
||||
private static final int PICK_REQUEST = 53;
|
||||
private final int STORE_REQUEST = 54;
|
||||
|
||||
private final EditingToolsAdapter mEditingToolsAdapter = new EditingToolsAdapter(this);
|
||||
private final FilterViewAdapter mFilterViewAdapter = new FilterViewAdapter(this);
|
||||
private final ConstraintSet mConstraintSet = new ConstraintSet();
|
||||
|
@ -117,8 +118,6 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
|
|||
binding.rvFilterView.setLayoutManager(llmFilters);
|
||||
binding.rvFilterView.setAdapter(mFilterViewAdapter);
|
||||
|
||||
//Typeface mTextRobotoTf = ResourcesCompat.getFont(this, R.font.roboto_medium);
|
||||
//Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf");
|
||||
Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf");
|
||||
|
||||
mPhotoEditor = new PhotoEditor.Builder(this, binding.photoEditorView)
|
||||
|
@ -246,6 +245,49 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
|
|||
if (exit) {
|
||||
Intent intentImage = new Intent(Helper.INTENT_SEND_MODIFIED_IMAGE);
|
||||
intentImage.putExtra("imgpath", imagePath);
|
||||
CirclesDrawingView.CircleArea circleArea = binding.focusCircle.getTouchedCircle();
|
||||
if (circleArea != null) {
|
||||
//Dimension of the editor containing the image
|
||||
int pHeight = binding.photoEditorView.getHeight();
|
||||
int pWidth = binding.photoEditorView.getWidth();
|
||||
//Load the original image in a bitmap
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(new File(imagePath).getAbsolutePath(), options);
|
||||
//Get height and width of the original image
|
||||
int imageHeight = options.outHeight;
|
||||
int imageWidth = options.outWidth;
|
||||
|
||||
//Evaluate the dimension of the image in the editor
|
||||
int imgHeightInEditor;
|
||||
int imgWidthInEditor;
|
||||
//If the original image has its height greater than width => heights are equals
|
||||
float focusX = -2, focusY = -2;
|
||||
if (imageHeight > imageWidth) {
|
||||
imgHeightInEditor = pHeight;
|
||||
float ratio = (float) pHeight / (float) imageHeight;
|
||||
imgWidthInEditor = (int) (pWidth * ratio);
|
||||
} else { //Otherwise widths are equals
|
||||
imgWidthInEditor = pWidth;
|
||||
float ratio = (float) pWidth / (float) imageWidth;
|
||||
imgHeightInEditor = (int) (pHeight * ratio);
|
||||
}
|
||||
focusY = (float) (circleArea.centerY * 2 - imgHeightInEditor / 2) / (float) imgHeightInEditor - 0.5f;
|
||||
focusX = (float) (circleArea.centerX * 2 - imgWidthInEditor / 2) / (float) imgWidthInEditor - 0.5f;
|
||||
if (focusX > 1) {
|
||||
focusX = 1;
|
||||
} else if (focusX < -1) {
|
||||
focusX = -1;
|
||||
}
|
||||
if (focusY > 1) {
|
||||
focusY = 1;
|
||||
} else if (focusY < -1) {
|
||||
focusY = -1;
|
||||
}
|
||||
intentImage.putExtra("focusX", focusX);
|
||||
intentImage.putExtra("focusY", focusY);
|
||||
}
|
||||
|
||||
LocalBroadcastManager.getInstance(EditImageActivity.this).sendBroadcast(intentImage);
|
||||
finish();
|
||||
}
|
||||
|
@ -376,6 +418,7 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
|
|||
|
||||
@Override
|
||||
public void onToolSelected(ToolType toolType) {
|
||||
binding.focusCircle.setVisibility(View.GONE);
|
||||
switch (toolType) {
|
||||
case SHAPE:
|
||||
mPhotoEditor.setBrushDrawingMode(true);
|
||||
|
@ -414,6 +457,9 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
|
|||
CropImage.activity(uri)
|
||||
.start(this);
|
||||
break;
|
||||
case FOCUS:
|
||||
binding.focusCircle.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ public class EditingToolsAdapter extends RecyclerView.Adapter<EditingToolsAdapte
|
|||
mToolList.add(new ToolModel("Eraser", R.drawable.ic_eraser, ToolType.ERASER));
|
||||
mToolList.add(new ToolModel("Filter", R.drawable.ic_photo_filter, ToolType.FILTER));
|
||||
mToolList.add(new ToolModel("Emoji", R.drawable.ic_insert_emoticon, ToolType.EMOJI));
|
||||
mToolList.add(new ToolModel("Focus", R.drawable.ic_baseline_filter_center_focus_24, ToolType.FOCUS));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -13,5 +13,6 @@ public enum ToolType {
|
|||
FILTER,
|
||||
EMOJI,
|
||||
STICKER,
|
||||
CROP
|
||||
CROP,
|
||||
FOCUS
|
||||
}
|
||||
|
|
|
@ -294,7 +294,8 @@ public class PostMessageService extends IntentService {
|
|||
}
|
||||
|
||||
private static String postAttachment(MastodonStatusesService mastodonStatusesService, DataPost dataPost, MultipartBody.Part fileMultipartBody, Attachment attachment) {
|
||||
Call<Attachment> attachmentCall = mastodonStatusesService.postMedia(dataPost.token, fileMultipartBody, null, attachment.description, null);
|
||||
|
||||
Call<Attachment> attachmentCall = mastodonStatusesService.postMedia(dataPost.token, fileMultipartBody, null, attachment.description, attachment.focus);
|
||||
|
||||
if (attachmentCall != null) {
|
||||
try {
|
||||
|
|
|
@ -285,25 +285,25 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||
String[] mimetypes = new String[0];
|
||||
if (type == ComposeActivity.mediaType.PHOTO) {
|
||||
if (instanceInfo.getMimeTypeImage() != null && instanceInfo.getMimeTypeImage().size() > 0) {
|
||||
if (instanceInfo != null && instanceInfo.getMimeTypeImage() != null && instanceInfo.getMimeTypeImage().size() > 0) {
|
||||
mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]);
|
||||
} else {
|
||||
mimetypes = new String[]{"image/*"};
|
||||
}
|
||||
} else if (type == ComposeActivity.mediaType.VIDEO) {
|
||||
if (instanceInfo.getMimeTypeVideo() != null && instanceInfo.getMimeTypeVideo().size() > 0) {
|
||||
if (instanceInfo != null && instanceInfo.getMimeTypeVideo() != null && instanceInfo.getMimeTypeVideo().size() > 0) {
|
||||
mimetypes = instanceInfo.getMimeTypeVideo().toArray(new String[0]);
|
||||
} else {
|
||||
mimetypes = new String[]{"video/*"};
|
||||
}
|
||||
} else if (type == ComposeActivity.mediaType.AUDIO) {
|
||||
if (instanceInfo.getMimeTypeAudio() != null && instanceInfo.getMimeTypeAudio().size() > 0) {
|
||||
if (instanceInfo != null && instanceInfo.getMimeTypeAudio() != null && instanceInfo.getMimeTypeAudio().size() > 0) {
|
||||
mimetypes = instanceInfo.getMimeTypeAudio().toArray(new String[0]);
|
||||
} else {
|
||||
mimetypes = new String[]{"audio/mpeg", "audio/opus", "audio/flac", "audio/wav", "audio/ogg"};
|
||||
}
|
||||
} else if (type == ComposeActivity.mediaType.ALL) {
|
||||
if (instanceInfo.getMimeTypeOther() != null && instanceInfo.getMimeTypeOther().size() > 0) {
|
||||
if (instanceInfo != null && instanceInfo.getMimeTypeOther() != null && instanceInfo.getMimeTypeOther().size() > 0) {
|
||||
mimetypes = instanceInfo.getMimeTypeOther().toArray(new String[0]);
|
||||
} else {
|
||||
mimetypes = new String[]{"*/*"};
|
||||
|
@ -706,7 +706,12 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
public void afterTextChanged(Editable s) {
|
||||
int currentLength = MastodonHelper.countLength(holder);
|
||||
//Copy/past
|
||||
int max_car = instanceInfo.max_toot_chars != null ? Integer.parseInt(instanceInfo.max_toot_chars) : instanceInfo.configuration.statusesConf.max_characters;
|
||||
int max_car;
|
||||
if (instanceInfo != null) {
|
||||
max_car = instanceInfo.max_toot_chars != null ? Integer.parseInt(instanceInfo.max_toot_chars) : instanceInfo.configuration.statusesConf.max_characters;
|
||||
} else {
|
||||
max_car = 500;
|
||||
}
|
||||
if (currentLength > max_car + 1) {
|
||||
int from = max_car - holder.binding.contentSpoiler.getText().length();
|
||||
int to = (currentLength - holder.binding.contentSpoiler.getText().length());
|
||||
|
@ -1073,7 +1078,8 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
});
|
||||
|
||||
//Disable buttons to attach media if max has been reached
|
||||
if (statusDraft.media_attachments != null && statusDraft.media_attachments.size() >= instanceInfo.configuration.statusesConf.max_media_attachments) {
|
||||
if (statusDraft.media_attachments != null &&
|
||||
((instanceInfo != null && statusDraft.media_attachments.size() >= instanceInfo.configuration.statusesConf.max_media_attachments) || (instanceInfo == null && statusDraft.media_attachments.size() >= 4))) {
|
||||
holder.binding.buttonAttachImage.setEnabled(false);
|
||||
holder.binding.buttonAttachVideo.setEnabled(false);
|
||||
holder.binding.buttonAttachAudio.setEnabled(false);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package app.fedilab.android.ui.drawer;
|
||||
/* Copyright 2022 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Spannable;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.entities.api.Field;
|
||||
import app.fedilab.android.databinding.DrawerFieldBinding;
|
||||
|
||||
|
||||
public class FieldAdapter extends RecyclerView.Adapter<FieldAdapter.FieldViewHolder> {
|
||||
|
||||
private final List<Field> fields;
|
||||
private Context context;
|
||||
|
||||
public FieldAdapter(List<Field> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FieldViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
context = parent.getContext();
|
||||
DrawerFieldBinding itemBinding = DrawerFieldBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new FieldViewHolder(itemBinding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull FieldViewHolder holder, int position) {
|
||||
Field field = fields.get(position);
|
||||
if (field.verified_at != null) {
|
||||
holder.binding.value.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(context, R.drawable.ic_baseline_verified_24), null);
|
||||
field.value_span.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text)), 0, field.value_span.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
holder.binding.value.setText(field.value_span != null ? field.value_span : field.value, TextView.BufferType.SPANNABLE);
|
||||
holder.binding.value.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
holder.binding.label.setText(field.name);
|
||||
}
|
||||
|
||||
|
||||
public static class FieldViewHolder extends RecyclerView.ViewHolder {
|
||||
DrawerFieldBinding binding;
|
||||
|
||||
FieldViewHolder(DrawerFieldBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -143,13 +143,21 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||
});
|
||||
} else if (viewHolder.getItemViewType() == NOTIFICATION_FETCH_MORE) {
|
||||
StatusAdapter.StatusViewHolder holder = (StatusAdapter.StatusViewHolder) viewHolder;
|
||||
holder.bindingFetchMore.fetchMore.setEnabled(!notification.isFetchMoreHidden);
|
||||
holder.bindingFetchMore.fetchMore.setOnClickListener(v -> {
|
||||
holder.bindingFetchMore.fetchMoreContainer.setEnabled(!notification.isFetchMoreHidden);
|
||||
holder.bindingFetchMore.fetchMoreMin.setOnClickListener(v -> {
|
||||
if (position + 1 < notificationList.size()) {
|
||||
//We hide the button
|
||||
notification.isFetchMoreHidden = true;
|
||||
notifyItemChanged(position);
|
||||
fetchMoreCallBack.onClick(notificationList.get(position + 1).id, notification.id);
|
||||
fetchMoreCallBack.onClickMin(notificationList.get(position + 1).id, notification.id);
|
||||
}
|
||||
});
|
||||
holder.bindingFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
||||
if (position - 1 >= 0) {
|
||||
//We hide the button
|
||||
notification.isFetchMoreHidden = true;
|
||||
notifyItemChanged(position);
|
||||
fetchMoreCallBack.onClickMax(notificationList.get(position - 1).id, notification.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -264,7 +272,9 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
|||
}
|
||||
|
||||
public interface FetchMoreCallBack {
|
||||
void onClick(String min_id, String fetchmoreId);
|
||||
void onClickMin(String min_id, String fetchmoreId);
|
||||
|
||||
void onClickMax(String max_id, String fetchmoreId);
|
||||
}
|
||||
|
||||
static class ViewHolderFollow extends RecyclerView.ViewHolder {
|
||||
|
|
|
@ -117,6 +117,7 @@ import app.fedilab.android.databinding.LayoutMediaBinding;
|
|||
import app.fedilab.android.databinding.LayoutPollItemBinding;
|
||||
import app.fedilab.android.exception.DBException;
|
||||
import app.fedilab.android.helper.CrossActionHelper;
|
||||
import app.fedilab.android.helper.GlideFocus;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.LongClickLinkMovementMethod;
|
||||
import app.fedilab.android.helper.MastodonHelper;
|
||||
|
@ -984,10 +985,18 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
} else {
|
||||
layoutMediaBinding.playMusic.setVisibility(View.GONE);
|
||||
}
|
||||
float focusX = 0.f;
|
||||
float focusY = 0.f;
|
||||
if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) {
|
||||
focusX = statusToDeal.media_attachments.get(0).meta.focus.x;
|
||||
focusY = statusToDeal.media_attachments.get(0).meta.focus.y;
|
||||
}
|
||||
|
||||
if (!mediaObfuscated(statusToDeal) || expand_media) {
|
||||
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24);
|
||||
Glide.with(layoutMediaBinding.media.getContext())
|
||||
.load(statusToDeal.media_attachments.get(0).preview_url)
|
||||
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)))
|
||||
.into(layoutMediaBinding.media);
|
||||
} else {
|
||||
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
|
||||
|
@ -995,6 +1004,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
.load(statusToDeal.media_attachments.get(0).preview_url)
|
||||
.apply(new RequestOptions().transform(new BlurTransformation(50, 3)))
|
||||
// .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
|
||||
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)))
|
||||
.into(layoutMediaBinding.media);
|
||||
}
|
||||
layoutMediaBinding.viewHide.setOnClickListener(v -> {
|
||||
|
@ -1009,6 +1019,13 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
for (Attachment attachment : statusToDeal.media_attachments) {
|
||||
LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false);
|
||||
RelativeLayout.LayoutParams lp;
|
||||
float focusX = 0.f;
|
||||
float focusY = 0.f;
|
||||
if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) {
|
||||
focusX = statusToDeal.media_attachments.get(0).meta.focus.x;
|
||||
focusY = statusToDeal.media_attachments.get(0).meta.focus.y;
|
||||
}
|
||||
|
||||
if (fullAttachement) {
|
||||
lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
layoutMediaBinding.media.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
|
@ -1032,12 +1049,14 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
Glide.with(layoutMediaBinding.media.getContext())
|
||||
.load(attachment.preview_url)
|
||||
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
|
||||
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)))
|
||||
.into(layoutMediaBinding.media);
|
||||
} else {
|
||||
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
|
||||
Glide.with(layoutMediaBinding.media.getContext())
|
||||
.load(attachment.preview_url)
|
||||
.apply(new RequestOptions().transform(new BlurTransformation(50, 3)))
|
||||
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)))
|
||||
// .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
|
||||
.into(layoutMediaBinding.media);
|
||||
}
|
||||
|
@ -1172,6 +1191,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
j++;
|
||||
}
|
||||
} else {
|
||||
if (statusToDeal.poll.voters_count == 0 && statusToDeal.poll.votes_count > 0) {
|
||||
statusToDeal.poll.voters_count = statusToDeal.poll.votes_count;
|
||||
}
|
||||
holder.binding.poll.rated.setVisibility(View.GONE);
|
||||
holder.binding.poll.submitVote.setVisibility(View.VISIBLE);
|
||||
if (statusToDeal.poll.multiple) {
|
||||
|
@ -1828,13 +1850,21 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
});
|
||||
} else if (viewHolder.getItemViewType() == STATUS_FETCH_MORE) {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
holder.bindingFetchMore.fetchMore.setEnabled(!status.isFetchMoreHidden);
|
||||
holder.bindingFetchMore.fetchMore.setOnClickListener(v -> {
|
||||
holder.bindingFetchMore.fetchMoreContainer.setEnabled(!status.isFetchMoreHidden);
|
||||
holder.bindingFetchMore.fetchMoreMin.setOnClickListener(v -> {
|
||||
if (position + 1 < statusList.size()) {
|
||||
//We hide the button
|
||||
status.isFetchMoreHidden = true;
|
||||
notifyItemChanged(position);
|
||||
fetchMoreCallBack.onClick(statusList.get(position + 1).id, status.id);
|
||||
fetchMoreCallBack.onClickMinId(statusList.get(position + 1).id, status.id);
|
||||
}
|
||||
});
|
||||
holder.bindingFetchMore.fetchMoreMax.setOnClickListener(v -> {
|
||||
if (position - 1 >= 0) {
|
||||
//We hide the button
|
||||
status.isFetchMoreHidden = true;
|
||||
notifyItemChanged(position);
|
||||
fetchMoreCallBack.onClickMaxId(statusList.get(position - 1).id, status.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1857,7 +1887,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
}
|
||||
|
||||
public interface FetchMoreCallBack {
|
||||
void onClick(String min_id, String fetchmoreId);
|
||||
void onClickMinId(String min_id, String fetchmoreId);
|
||||
|
||||
void onClickMaxId(String max_id, String fetchmoreId);
|
||||
}
|
||||
|
||||
public static class StatusViewHolder extends RecyclerView.ViewHolder {
|
||||
|
|
|
@ -179,7 +179,13 @@ public class FragmentNotificationsSettings extends PreferenceFragmentCompat impl
|
|||
PushHelper.startStreaming(requireActivity());
|
||||
}
|
||||
if (key.compareToIgnoreCase(getString(R.string.SET_LED_COLOUR_VAL)) == 0) {
|
||||
sharedPreferences.edit().putInt(getString(R.string.SET_LED_COLOUR_VAL), Integer.parseInt(key)).apply();
|
||||
try {
|
||||
int value = Integer.parseInt(key);
|
||||
sharedPreferences.edit().putInt(getString(R.string.SET_LED_COLOUR_VAL), value).apply();
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -571,7 +571,7 @@ public class FragmentThemingSettings extends PreferenceFragmentCompat implements
|
|||
Uri uri = Uri.parse("file://" + fullPath);
|
||||
intentOpen.setDataAndType(uri, "text/csv");
|
||||
String title = getString(R.string.data_export_theme);
|
||||
Helper.notify_user(getActivity(), Helper.NOTIFICATION_THEMING, currentAccount, intentOpen, BitmapFactory.decodeResource(requireActivity().getResources(),
|
||||
Helper.notify_user(getActivity(), currentAccount, intentOpen, BitmapFactory.decodeResource(requireActivity().getResources(),
|
||||
R.mipmap.ic_launcher), Helper.NotifType.BACKUP, title, message);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -45,6 +45,7 @@ import app.fedilab.android.ui.drawer.AccountAdapter;
|
|||
import app.fedilab.android.ui.pageadapter.FedilabProfileTLPageAdapter;
|
||||
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.SearchVM;
|
||||
import es.dmoral.toasty.Toasty;
|
||||
|
||||
|
||||
public class FragmentMastodonAccount extends Fragment {
|
||||
|
@ -116,11 +117,15 @@ public class FragmentMastodonAccount extends Fragment {
|
|||
SearchVM searchVM = new ViewModelProvider(FragmentMastodonAccount.this).get(viewModelKey, SearchVM.class);
|
||||
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, search.trim(), null, "accounts", false, true, false, 0, null, null, MastodonHelper.STATUSES_PER_CALL)
|
||||
.observe(getViewLifecycleOwner(), results -> {
|
||||
if (results != null) {
|
||||
Accounts accounts = new Accounts();
|
||||
Pagination pagination = new Pagination();
|
||||
accounts.accounts = results.accounts;
|
||||
accounts.pagination = pagination;
|
||||
initializeAccountCommonView(accounts);
|
||||
} else {
|
||||
Toasty.error(requireActivity(), getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else if (timelineType == Timeline.TimeLineEnum.MUTED_TIMELINE) {
|
||||
if (firstLoad) {
|
||||
|
|
|
@ -14,8 +14,6 @@ package app.fedilab.android.ui.fragment.timeline;
|
|||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -32,7 +30,6 @@ import app.fedilab.android.BaseMainActivity;
|
|||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.entities.api.Announcement;
|
||||
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.drawer.AnnouncementAdapter;
|
||||
import app.fedilab.android.viewmodel.mastodon.AnnouncementsVM;
|
||||
|
@ -43,7 +40,6 @@ public class FragmentMastodonAnnouncement extends Fragment {
|
|||
|
||||
private FragmentPaginationBinding binding;
|
||||
private AnnouncementsVM announcementsVM;
|
||||
private AnnouncementAdapter announcementAdapter;
|
||||
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
|
@ -65,13 +61,6 @@ public class FragmentMastodonAnnouncement extends Fragment {
|
|||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
NotificationManager notificationManager = (NotificationManager) requireActivity().getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(Helper.NOTIFICATION_USER_NOTIF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intialize the view for announcements
|
||||
*
|
||||
|
@ -92,7 +81,7 @@ public class FragmentMastodonAnnouncement extends Fragment {
|
|||
}
|
||||
|
||||
|
||||
announcementAdapter = new AnnouncementAdapter(announcements);
|
||||
AnnouncementAdapter announcementAdapter = new AnnouncementAdapter(announcements);
|
||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity());
|
||||
binding.recyclerView.setLayoutManager(mLayoutManager);
|
||||
binding.recyclerView.setAdapter(announcementAdapter);
|
||||
|
|
|
@ -20,9 +20,11 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -86,7 +88,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
|||
}
|
||||
}
|
||||
};
|
||||
private String max_id, min_id, min_id_fetch_more;
|
||||
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private String instance, user_id;
|
||||
private ArrayList<String> idOfAddedNotifications;
|
||||
|
@ -179,8 +181,16 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
NotificationManager notificationManager = (NotificationManager) requireActivity().getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(Helper.NOTIFICATION_USER_NOTIF);
|
||||
NotificationManager mNotificationManager = (NotificationManager) requireActivity().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && BaseMainActivity.currentAccount != null && BaseMainActivity.currentAccount.mastodon_account != null) {
|
||||
for (StatusBarNotification statusBarNotification : mNotificationManager.getActiveNotifications()) {
|
||||
if ((BaseMainActivity.currentAccount.mastodon_account.acct + "@" + BaseMainActivity.currentAccount.instance).equals(statusBarNotification.getGroupKey())) {
|
||||
mNotificationManager.cancel(statusBarNotification.getId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -306,8 +316,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
|||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||
.observe(getViewLifecycleOwner(), this::initializeNotificationView);
|
||||
} else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) {
|
||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||
.observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, false));
|
||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||
.observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) {
|
||||
notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null)
|
||||
.observe(getViewLifecycleOwner(), notificationsTop -> dealWithPagination(notificationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -500,7 +510,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onClick(String min_id, String id) {
|
||||
public void onClickMin(String min_id, String id) {
|
||||
//Fetch more has been pressed
|
||||
min_id_fetch_more = min_id;
|
||||
Notification notification = null;
|
||||
|
@ -519,6 +529,25 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
|
|||
route(FragmentMastodonTimeline.DIRECTION.TOP, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMax(String max_id, String id) {
|
||||
//Fetch more has been pressed
|
||||
max_id_fetch_more = max_id;
|
||||
Notification notification = null;
|
||||
int position = 0;
|
||||
for (Notification currentNotification : this.notificationList) {
|
||||
if (currentNotification.id.compareTo(id) == 0) {
|
||||
notification = currentNotification;
|
||||
break;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
if (notification != null) {
|
||||
this.notificationList.remove(position);
|
||||
notificationAdapter.notifyItemRemoved(position);
|
||||
}
|
||||
route(FragmentMastodonTimeline.DIRECTION.BOTTOM, true);
|
||||
}
|
||||
|
||||
public enum NotificationTypeEnum {
|
||||
@SerializedName("ALL")
|
||||
|
|
|
@ -31,12 +31,14 @@ import java.util.List;
|
|||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.entities.api.Tag;
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.databinding.FragmentPaginationBinding;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.MastodonHelper;
|
||||
import app.fedilab.android.helper.ThemeHelper;
|
||||
import app.fedilab.android.ui.drawer.TagAdapter;
|
||||
import app.fedilab.android.viewmodel.mastodon.SearchVM;
|
||||
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||
|
||||
|
||||
public class FragmentMastodonTag extends Fragment {
|
||||
|
@ -45,11 +47,13 @@ public class FragmentMastodonTag extends Fragment {
|
|||
private FragmentPaginationBinding binding;
|
||||
private TagAdapter tagAdapter;
|
||||
private String search;
|
||||
private Timeline.TimeLineEnum timelineType;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
if (getArguments() != null) {
|
||||
search = getArguments().getString(Helper.ARG_SEARCH_KEYWORD, null);
|
||||
timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE);
|
||||
}
|
||||
|
||||
binding = FragmentPaginationBinding.inflate(inflater, container, false);
|
||||
|
@ -74,7 +78,7 @@ public class FragmentMastodonTag extends Fragment {
|
|||
* Router for timelines
|
||||
*/
|
||||
private void router() {
|
||||
if (search != null) {
|
||||
if (search != null && timelineType == null) {
|
||||
SearchVM searchVM = new ViewModelProvider(FragmentMastodonTag.this).get(SearchVM.class);
|
||||
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, search.trim(), null, "hashtags", false, true, false, 0, null, null, MastodonHelper.STATUSES_PER_CALL)
|
||||
.observe(getViewLifecycleOwner(), results -> {
|
||||
|
@ -82,6 +86,12 @@ public class FragmentMastodonTag extends Fragment {
|
|||
initializeTagCommonView(results.hashtags);
|
||||
}
|
||||
});
|
||||
} else if (timelineType == Timeline.TimeLineEnum.TREND_TAG) {
|
||||
TimelinesVM timelinesVM = new ViewModelProvider(FragmentMastodonTag.this).get(TimelinesVM.class);
|
||||
timelinesVM.getTagsTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance)
|
||||
.observe(getViewLifecycleOwner(), tags -> {
|
||||
initializeTagCommonView(tags);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
private List<Status> statuses;
|
||||
private String search, searchCache;
|
||||
private Status statusReport;
|
||||
private String max_id, min_id, min_id_fetch_more;
|
||||
private String max_id, min_id, min_id_fetch_more, max_id_fetch_more;
|
||||
private StatusAdapter statusAdapter;
|
||||
private Timeline.TimeLineEnum timelineType;
|
||||
//Handle actions that can be done in other fragments
|
||||
|
@ -286,11 +286,13 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
binding.loader.setVisibility(View.GONE);
|
||||
binding.noAction.setVisibility(View.GONE);
|
||||
binding.swipeContainer.setRefreshing(false);
|
||||
if (searchCache == null && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) {
|
||||
binding.swipeContainer.setOnRefreshListener(() -> {
|
||||
binding.swipeContainer.setRefreshing(true);
|
||||
flagLoading = false;
|
||||
route(DIRECTION.REFRESH, true);
|
||||
});
|
||||
}
|
||||
|
||||
if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) {
|
||||
binding.noAction.setVisibility(View.VISIBLE);
|
||||
|
@ -356,7 +358,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
binding.recyclerView.scrollToPosition(position);
|
||||
}
|
||||
|
||||
if (searchCache == null) {
|
||||
if (searchCache == null && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) {
|
||||
binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
|
@ -453,9 +455,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
insertedPosition = insertStatus(statusReceived);
|
||||
if (insertedPosition != STATUS_PRESENT && insertedPosition != STATUS_AT_THE_BOTTOM) {
|
||||
numberInserted++;
|
||||
//Find the first position of insertion, the initial id is set to STATUS_PRESENT
|
||||
if (initialInsertedPosition == STATUS_PRESENT) {
|
||||
initialInsertedPosition = insertedPosition;
|
||||
}
|
||||
//If next statuses have a lower id, there are inserted before (normally, that should not happen)
|
||||
if (insertedPosition < initialInsertedPosition) {
|
||||
initialInsertedPosition = lastInsertedPosition;
|
||||
}
|
||||
|
@ -464,12 +468,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
lastInsertedPosition = initialInsertedPosition + numberInserted;
|
||||
//lastInsertedPosition contains the position of the last inserted status
|
||||
//If there were no overlap for top status
|
||||
if (fetchingMissing && insertedPosition != STATUS_PRESENT && insertedPosition != STATUS_AT_THE_BOTTOM && this.statuses.size() > insertedPosition) {
|
||||
if (fetchingMissing && insertedPosition != STATUS_PRESENT && insertedPosition != STATUS_AT_THE_BOTTOM && this.statuses.size() > insertedPosition && numberInserted == MastodonHelper.statusesPerCall(requireActivity())) {
|
||||
Status statusFetchMore = new Status();
|
||||
statusFetchMore.isFetchMore = true;
|
||||
statusFetchMore.id = Helper.generateString();
|
||||
int insertAt;
|
||||
if (direction == DIRECTION.REFRESH) {
|
||||
if (direction == DIRECTION.REFRESH || direction == DIRECTION.BOTTOM) {
|
||||
insertAt = lastInsertedPosition;
|
||||
} else {
|
||||
insertAt = initialInsertedPosition;
|
||||
|
@ -485,7 +489,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
|
||||
/**
|
||||
* Insert a status if not yet in the timeline
|
||||
* Insert a status if not yet in the timeline and returns its position of insertion
|
||||
*
|
||||
* @param statusReceived - Status coming from the api/db
|
||||
* @return int >= 0 | STATUS_PRESENT = -1 | STATUS_AT_THE_BOTTOM = -2
|
||||
|
@ -618,8 +622,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -638,8 +642,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -724,8 +728,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getPublic(null, remoteInstance, true, false, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
timelinesVM.getPublic(null, remoteInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -745,8 +749,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -769,8 +773,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
|
||||
|
@ -847,6 +851,19 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else {
|
||||
flagLoading = false;
|
||||
}
|
||||
} else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE) {
|
||||
if (direction == null) {
|
||||
timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance)
|
||||
.observe(getViewLifecycleOwner(), statusesTrends -> {
|
||||
Statuses statuses = new Statuses();
|
||||
statuses.statuses = new ArrayList<>();
|
||||
if (statusesTrends != null) {
|
||||
statuses.statuses.addAll(statusesTrends);
|
||||
}
|
||||
statuses.pagination = new Pagination();
|
||||
initializeStatusesCommonView(statuses);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
|
@ -905,6 +922,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
|
||||
}
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
if (!fetchingMissing) {
|
||||
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
|
||||
//We first if we get results from cache
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null)
|
||||
|
@ -921,6 +939,10 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
|
||||
}
|
||||
} else {
|
||||
timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id_fetch_more, null, null)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, true));
|
||||
}
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
if (!fetchingMissing) {
|
||||
if (networkAvailable == BaseMainActivity.status.CONNECTED) {
|
||||
|
@ -966,7 +988,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onClick(String min_id, String id) {
|
||||
public void onClickMinId(String min_id, String id) {
|
||||
//Fetch more has been pressed
|
||||
min_id_fetch_more = min_id;
|
||||
Status status = null;
|
||||
|
@ -985,6 +1007,25 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
route(DIRECTION.TOP, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMaxId(String max_id, String id) {
|
||||
max_id_fetch_more = max_id;
|
||||
Status status = null;
|
||||
int position = 0;
|
||||
for (Status currentStatus : this.statuses) {
|
||||
if (currentStatus.id.compareTo(id) == 0) {
|
||||
status = currentStatus;
|
||||
break;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
if (status != null) {
|
||||
this.statuses.remove(position);
|
||||
statusAdapter.notifyItemRemoved(position);
|
||||
}
|
||||
route(DIRECTION.BOTTOM, true);
|
||||
}
|
||||
|
||||
public enum DIRECTION {
|
||||
TOP,
|
||||
BOTTOM,
|
||||
|
|
|
@ -57,8 +57,9 @@ public class FragmentProfileTimeline extends Fragment {
|
|||
binding.tabLayout.addTab(binding.tabLayout.newTab().setText(getString(R.string.media)));
|
||||
binding.tabLayout.setTabTextColors(ThemeHelper.getAttColor(requireActivity(), R.attr.mTextColor), ContextCompat.getColor(requireActivity(), R.color.cyanea_accent_dark_reference));
|
||||
binding.tabLayout.setTabIconTint(ThemeHelper.getColorStateList(requireActivity()));
|
||||
binding.viewpager.setAdapter(new FedilabProfilePageAdapter(requireActivity(), account));
|
||||
binding.viewpager.setAdapter(new FedilabProfilePageAdapter(getChildFragmentManager(), account));
|
||||
binding.viewpager.setOffscreenPageLimit(3);
|
||||
binding.viewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.tabLayout));
|
||||
binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
|
|
|
@ -16,11 +16,12 @@ package app.fedilab.android.ui.pageadapter;
|
|||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.client.entities.app.BottomMenu;
|
||||
|
@ -33,24 +34,46 @@ import app.fedilab.android.ui.fragment.timeline.FragmentMastodonConversation;
|
|||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentNotificationContainer;
|
||||
|
||||
public class FedilabPageAdapter extends FragmentStateAdapter {
|
||||
@SuppressWarnings("deprecation")
|
||||
public class FedilabPageAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public static final int BOTTOM_TIMELINE_COUNT = 5; //home, local, public, notification, DM
|
||||
private final Pinned pinned;
|
||||
private final BottomMenu bottomMenu;
|
||||
private final int toRemove;
|
||||
private Fragment mCurrentFragment;
|
||||
|
||||
public FedilabPageAdapter(BaseMainActivity activity, FragmentActivity fa, Pinned pinned, BottomMenu bottomMenu) {
|
||||
super(fa);
|
||||
public FedilabPageAdapter(BaseMainActivity activity, FragmentManager fm, Pinned pinned, BottomMenu bottomMenu) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
this.pinned = pinned;
|
||||
this.bottomMenu = bottomMenu;
|
||||
toRemove = PinnedTimelineHelper.itemToRemoveInBottomMenu(activity);
|
||||
}
|
||||
|
||||
public Fragment getCurrentFragment() {
|
||||
return mCurrentFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
if (getCurrentFragment() != object) {
|
||||
mCurrentFragment = ((Fragment) object);
|
||||
}
|
||||
super.setPrimaryItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (pinned != null && pinned.pinnedTimelines != null) {
|
||||
return pinned.pinnedTimelines.size() + BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
} else {
|
||||
return BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
FragmentMastodonTimeline fragment = new FragmentMastodonTimeline();
|
||||
Bundle bundle = new Bundle();
|
||||
//Position 3 is for notifications
|
||||
|
@ -95,12 +118,9 @@ public class FedilabPageAdapter extends FragmentStateAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (pinned != null && pinned.pinnedTimelines != null) {
|
||||
return pinned.pinnedTimelines.size() + BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
} else {
|
||||
return BOTTOM_TIMELINE_COUNT - toRemove;
|
||||
}
|
||||
}
|
||||
|
||||
public int getItemPosition(@NonNull Object object) {
|
||||
// POSITION_NONE makes it possible to reload the PagerAdapter
|
||||
return POSITION_NONE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,35 +15,42 @@ package app.fedilab.android.ui.pageadapter;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import app.fedilab.android.client.entities.api.Account;
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
|
||||
public class FedilabProfilePageAdapter extends FragmentStateAdapter {
|
||||
|
||||
public class FedilabProfilePageAdapter extends FragmentStatePagerAdapter {
|
||||
private final Account account;
|
||||
private Fragment mCurrentFragment;
|
||||
|
||||
public FedilabProfilePageAdapter(FragmentActivity fa, Account account) {
|
||||
super(fa);
|
||||
public FedilabProfilePageAdapter(FragmentManager fm, Account account) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Fragment getCurrentFragment() {
|
||||
return mCurrentFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 3;
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
if (getCurrentFragment() != object) {
|
||||
mCurrentFragment = ((Fragment) object);
|
||||
}
|
||||
super.setPrimaryItem(container, position, object);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + position);
|
||||
switch (position) {
|
||||
|
@ -77,5 +84,8 @@ public class FedilabProfilePageAdapter extends FragmentStateAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 3;
|
||||
}
|
||||
}
|
|
@ -15,11 +15,12 @@ package app.fedilab.android.ui.pageadapter;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import app.fedilab.android.client.entities.api.Account;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
|
@ -27,21 +28,31 @@ import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount;
|
|||
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentProfileTimeline;
|
||||
|
||||
public class FedilabProfileTLPageAdapter extends FragmentStateAdapter {
|
||||
|
||||
public class FedilabProfileTLPageAdapter extends FragmentStatePagerAdapter {
|
||||
private final Account account;
|
||||
private Fragment mCurrentFragment;
|
||||
|
||||
public FedilabProfileTLPageAdapter(FragmentActivity fa, Account account) {
|
||||
super(fa);
|
||||
public FedilabProfileTLPageAdapter(FragmentManager fm, Account account) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Fragment getCurrentFragment() {
|
||||
return mCurrentFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
if (getCurrentFragment() != object) {
|
||||
mCurrentFragment = ((Fragment) object);
|
||||
}
|
||||
super.setPrimaryItem(container, position, object);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
|
||||
case 0:
|
||||
FragmentProfileTimeline fragmentProfileTimeline = new FragmentProfileTimeline();
|
||||
Bundle bundle = new Bundle();
|
||||
|
@ -63,10 +74,9 @@ public class FedilabProfileTLPageAdapter extends FragmentStateAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
public int getCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
public enum follow_type {
|
||||
FOLLOWING,
|
||||
FOLLOWERS
|
||||
|
|
|
@ -15,26 +15,39 @@ package app.fedilab.android.ui.pageadapter;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import app.fedilab.android.client.entities.app.Timeline;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.ui.fragment.timeline.FragmentScheduled;
|
||||
|
||||
public class FedilabScheduledPageAdapter extends FragmentStateAdapter {
|
||||
public class FedilabScheduledPageAdapter extends FragmentStatePagerAdapter {
|
||||
private Fragment mCurrentFragment;
|
||||
|
||||
public FedilabScheduledPageAdapter(FragmentManager fm) {
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
public FedilabScheduledPageAdapter(FragmentActivity fa) {
|
||||
super(fa);
|
||||
public Fragment getCurrentFragment() {
|
||||
return mCurrentFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
if (getCurrentFragment() != object) {
|
||||
mCurrentFragment = ((Fragment) object);
|
||||
}
|
||||
super.setPrimaryItem(container, position, object);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
public Fragment getItem(int position) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + position);
|
||||
FragmentScheduled fragmentScheduled = new FragmentScheduled();
|
||||
|
@ -53,7 +66,7 @@ public class FedilabScheduledPageAdapter extends FragmentStateAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
public int getCount() {
|
||||
return 3;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ import app.fedilab.android.client.entities.api.MastodonList;
|
|||
import app.fedilab.android.client.entities.api.Pagination;
|
||||
import app.fedilab.android.client.entities.api.Status;
|
||||
import app.fedilab.android.client.entities.api.Statuses;
|
||||
import app.fedilab.android.client.entities.api.Tag;
|
||||
import app.fedilab.android.client.entities.app.BaseAccount;
|
||||
import app.fedilab.android.client.entities.app.StatusCache;
|
||||
import app.fedilab.android.client.entities.app.StatusDraft;
|
||||
|
@ -75,6 +76,8 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
private MutableLiveData<MastodonList> mastodonListMutableLiveData;
|
||||
private MutableLiveData<List<MastodonList>> mastodonListListMutableLiveData;
|
||||
private MutableLiveData<Marker> markerMutableLiveData;
|
||||
private MutableLiveData<List<Status>> statusListMutableLiveData;
|
||||
private MutableLiveData<List<Tag>> tagListMutableLiveData;
|
||||
|
||||
public TimelinesVM(@NonNull Application application) {
|
||||
super(application);
|
||||
|
@ -107,6 +110,56 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
return retrofit.create(MastodonTimelinesService.class);
|
||||
}
|
||||
|
||||
public LiveData<List<Status>> getStatusTrends(String token, @NonNull String instance) {
|
||||
MastodonTimelinesService mastodonTimelinesService = init(instance);
|
||||
statusListMutableLiveData = new MutableLiveData<>();
|
||||
new Thread(() -> {
|
||||
Call<List<Status>> publicTlCall = mastodonTimelinesService.getStatusTrends(token);
|
||||
List<Status> statusList = null;
|
||||
if (publicTlCall != null) {
|
||||
try {
|
||||
Response<List<Status>> publicTlResponse = publicTlCall.execute();
|
||||
if (publicTlResponse.isSuccessful()) {
|
||||
statusList = publicTlResponse.body();
|
||||
statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
List<Status> finalStatusList = statusList;
|
||||
Runnable myRunnable = () -> statusListMutableLiveData.setValue(finalStatusList);
|
||||
mainHandler.post(myRunnable);
|
||||
}).start();
|
||||
return statusListMutableLiveData;
|
||||
}
|
||||
|
||||
|
||||
public LiveData<List<Tag>> getTagsTrends(String token, @NonNull String instance) {
|
||||
MastodonTimelinesService mastodonTimelinesService = init(instance);
|
||||
tagListMutableLiveData = new MutableLiveData<>();
|
||||
new Thread(() -> {
|
||||
Call<List<Tag>> publicTlCall = mastodonTimelinesService.getTagTrends(token);
|
||||
List<Tag> tagList = null;
|
||||
if (publicTlCall != null) {
|
||||
try {
|
||||
Response<List<Tag>> publicTlResponse = publicTlCall.execute();
|
||||
if (publicTlResponse.isSuccessful()) {
|
||||
tagList = publicTlResponse.body();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
List<Tag> finalTagList = tagList;
|
||||
Runnable myRunnable = () -> tagListMutableLiveData.setValue(finalTagList);
|
||||
mainHandler.post(myRunnable);
|
||||
}).start();
|
||||
return tagListMutableLiveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public timeline
|
||||
*
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M5,15L3,15v4c0,1.1 0.9,2 2,2h4v-2L5,19v-4zM5,5h4L9,3L5,3c-1.1,0 -2,0.9 -2,2v4h2L5,5zM19,3h-4v2h4v4h2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_baseline_trending_up_24.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_trending_up_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_fetch_more_arrow_downward.xml
Normal file
10
app/src/main/res/drawable/ic_fetch_more_arrow_downward.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,8l-1.41,-1.41L13,12.17V0h-2v12.17l-5.58,-5.59L4,8l8,8 8,-8z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_fetch_more_arrow_upward.xml
Normal file
10
app/src/main/res/drawable/ic_fetch_more_arrow_upward.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,16l1.41,1.41L11,11.83V24h2V11.83l5.58,5.59L20,16l-8,-8 -8,8z" />
|
||||
</vector>
|
|
@ -16,14 +16,27 @@
|
|||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_end="?attr/actionBarSize" />
|
||||
|
||||
<ja.burhanrashid52.photoeditor.PhotoEditorView
|
||||
android:id="@+id/photoEditorView"
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:id="@+id/container_image"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ja.burhanrashid52.photoeditor.PhotoEditorView
|
||||
android:id="@+id/photoEditorView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<app.fedilab.android.helper.CirclesDrawingView
|
||||
android:id="@+id/focus_circle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgUndo"
|
||||
|
@ -34,7 +47,6 @@
|
|||
android:src="@drawable/ic_undo"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
|
||||
app:layout_constraintEnd_toStartOf="@+id/imgRedo" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgRedo"
|
||||
android:layout_width="@dimen/top_tool_icon_width"
|
||||
|
|
|
@ -75,19 +75,14 @@
|
|||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<app.fedilab.android.helper.NestedScrollableHost
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/view_pager"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="?attr/actionBarSize"
|
||||
app:defaultNavHost="true" />
|
||||
</app.fedilab.android.helper.NestedScrollableHost>
|
||||
app:defaultNavHost="true"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
android:animateLayoutChanges="true"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/media_viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/profile_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="?attr/actionBarSize"
|
||||
app:layout_collapseMode="parallax">
|
||||
android:paddingTop="?attr/actionBarSize">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/banner_container"
|
||||
|
@ -157,36 +157,18 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/account_dn"
|
||||
tools:text="\@username\@instance.test" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_header_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="?actionBarSize"
|
||||
android:paddingTop="8dp"
|
||||
app:layout_collapseMode="parallax"
|
||||
app:layout_constraintTop_toBottomOf="@id/account_un">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
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">
|
||||
app:layout_constraintTop_toBottomOf="@+id/account_un">
|
||||
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
|
@ -196,7 +178,7 @@
|
|||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/account_bot"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -206,7 +188,7 @@
|
|||
android:textColor="@color/mastodonC4"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/temp_mute"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -214,7 +196,7 @@
|
|||
android:textColor="@color/red"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/account_moved"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -225,7 +207,7 @@
|
|||
android:textSize="16sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/identity_proofs_indicator"
|
||||
|
@ -241,18 +223,20 @@
|
|||
app:layout_constraintStart_toEndOf="@id/names_container"
|
||||
app:layout_constraintTop_toTopOf="@id/names_container" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
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" />
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/names_container" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/personal_note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -262,22 +246,29 @@
|
|||
android:padding="10dp"
|
||||
android:text="@string/action_add_notes"
|
||||
android:textIsSelectable="true"
|
||||
app:drawableStartCompat="@drawable/ic_baseline_note_24" />
|
||||
app:drawableStartCompat="@drawable/ic_baseline_note_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/account_note" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="none">
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/personal_note">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/instance_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -288,7 +279,7 @@
|
|||
android:textColor="@color/mastodonC4"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/account_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -299,7 +290,7 @@
|
|||
android:textColor="@color/mastodonC4"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/account_followed_by"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -311,7 +302,7 @@
|
|||
android:textColor="@color/verified_text"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/account_follow_request"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -322,177 +313,19 @@
|
|||
android:text="@string/request_sent"
|
||||
android:textColor="@color/mastodonC4"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</HorizontalScrollView>
|
||||
<!-- Fields container -->
|
||||
<LinearLayout
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
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" />
|
||||
|
||||
<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" />
|
||||
|
||||
<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" />
|
||||
|
||||
<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" />
|
||||
|
||||
<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>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/info" />
|
||||
<!-- End Fields container -->
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/warning_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -500,8 +333,10 @@
|
|||
android:gravity="center"
|
||||
android:text="@string/disclaimer_full"
|
||||
android:textColor="@color/dark_text"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/fields_container" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
@ -510,6 +345,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:backgroundTint="?colorPrimaryDark"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:layout_collapseMode="pin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
|
@ -532,14 +368,6 @@
|
|||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/account_tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -547,11 +375,12 @@
|
|||
android:background="?backgroundColorLight"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/account_viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -63,7 +63,7 @@
|
|||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/schedule_viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -30,15 +30,9 @@
|
|||
app:tabIndicatorColor="@color/cyanea_accent_dark_reference"
|
||||
app:tabMode="scrollable" />
|
||||
|
||||
<app.fedilab.android.helper.NestedScrollableHost
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/search_viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</app.fedilab.android.helper.NestedScrollableHost>
|
||||
>
|
||||
</LinearLayout>
|
38
app/src/main/res/layout/activity_trends.xml
Normal file
38
app/src/main/res/layout/activity_trends.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2022 Thomas Schneider
|
||||
|
||||
This file is a part of Fedilab
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
see <http://www.gnu.org/licenses>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/search_tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?backgroundColorLight"
|
||||
app:tabGravity="fill"
|
||||
app:tabIndicatorColor="@color/cyanea_accent_dark_reference"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/trends_viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</LinearLayout>
|
|
@ -1,9 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fetch_more"
|
||||
style="@style/MyOutlinedButton"
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/fetch_more_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="6dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fetch_more_min"
|
||||
style="@style/Widget.App.Button.IconOnly.Outline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:contentDescription="@string/fetch_more_messages"
|
||||
app:icon="@drawable/ic_fetch_more_arrow_upward"
|
||||
app:iconPadding="0dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/fetch_more_messages" />
|
||||
android:layout_weight="1"
|
||||
android:text="@string/fetch_more_messages"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fetch_more_max"
|
||||
style="@style/Widget.App.Button.IconOnly.Outline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:contentDescription="@string/fetch_more_messages"
|
||||
app:icon="@drawable/ic_fetch_more_arrow_downward"
|
||||
app:iconPadding="0dp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
|
37
app/src/main/res/layout/drawer_field.xml
Normal file
37
app/src/main/res/layout/drawer_field.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/field1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/label"
|
||||
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" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/valueBG"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="2"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/value"
|
||||
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" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
@ -27,7 +27,7 @@
|
|||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
android:title="@string/action_announcements"
|
||||
android:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_trends"
|
||||
android:icon="@drawable/ic_baseline_trending_up_24"
|
||||
android:title="@string/trending"
|
||||
android:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_scheduled"
|
||||
android:icon="@drawable/ic_baseline_schedule_24"
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
<string name="password">Пароль</string>
|
||||
<string name="email">Ел. пошта</string>
|
||||
<string name="accounts">Облікові записи</string>
|
||||
<string name="toots">Передмухнуте</string>
|
||||
<string name="toots">Повідомлення</string>
|
||||
<string name="tags">Мітки</string>
|
||||
<string name="save">Зберегти</string>
|
||||
<string name="instance">Екземпляр</string>
|
||||
<string name="instance_example">Екземпляр: mastodon.social</string>
|
||||
<string name="toast_account_changed" formatted="false">Тепер працює з обліковим записом %1$s</string>
|
||||
<string name="add_account">Додати обліковий запис</string>
|
||||
<string name="clipboard">Вміст дмуху скопійований в буфер обміну</string>
|
||||
<string name="clipboard_url">The URL of the toot has been copied to the clipboard</string>
|
||||
<string name="clipboard">Вміст повідомлення скопійований в буфер обміну</string>
|
||||
<string name="clipboard_url">URL-адресу повідомлення скопійовано в буфер обміну</string>
|
||||
<string name="camera">Камера</string>
|
||||
<string name="delete_all">Видалити все</string>
|
||||
<string name="schedule">Запланувати</string>
|
||||
|
@ -57,17 +57,17 @@
|
|||
<string name="follow_request">Запити на підписку</string>
|
||||
<string name="settings">Налаштування</string>
|
||||
<string name="send_email">Надіслати ел. лист</string>
|
||||
<string name="scheduled_toots">Заплановані дмухи</string>
|
||||
<string name="scheduled_toots">Заплановані повідомлення</string>
|
||||
<string name="disclaimer_full">Наведена нижче інформація може відображати профіль користувача не повністю.</string>
|
||||
<string name="insert_emoji">Вставити смайли</string>
|
||||
<string name="no_emoji">Застосунок не збирає emojis на даний момент.</string>
|
||||
<string name="logout_account_confirmation">Are you sure you want to logout @%1$s@%2$s?</string>
|
||||
<!-- Status -->
|
||||
<string name="no_status">Немає повідомлень для відображення</string>
|
||||
<string name="favourite_add">Додати цей дмух до обраного?</string>
|
||||
<string name="favourite_remove">Видалити цей дмух з обраного?</string>
|
||||
<string name="reblog_add">Передмухнути?</string>
|
||||
<string name="reblog_remove">Зняти передмух?</string>
|
||||
<string name="favourite_add">Додати це повідомлення до обраного\?</string>
|
||||
<string name="favourite_remove">Видалити це повідомлення з обраного\?</string>
|
||||
<string name="reblog_add">Підштовхнути це повідомлення\?</string>
|
||||
<string name="reblog_remove">Не підштовхувати це повідомлення\?</string>
|
||||
<string name="more_action_1">Заглушити</string>
|
||||
<string name="more_action_2">Заблокувати</string>
|
||||
<string name="more_action_3">Повідомити про порушення</string>
|
||||
|
@ -130,8 +130,8 @@
|
|||
<!-- TOOT -->
|
||||
<string name="toot_select_image_error">Сталася помилка під час вибору медіа-файлу!</string>
|
||||
<string name="toot_delete_media">Видалити цей медіа-файл?</string>
|
||||
<string name="toot_error_no_content">Ваш дмух пустий!</string>
|
||||
<string name="toot_sent">Повідомлення було відправлено!</string>
|
||||
<string name="toot_error_no_content">Ваше повідомлення порожнє!</string>
|
||||
<string name="toot_sent">Повідомлення відправлено!</string>
|
||||
<string name="toot_sensitive">Чутливий вміст?</string>
|
||||
<string name="no_draft">Чернетки відсутні!</string>
|
||||
<string name="choose_accounts">Оберіть обліковий запис</string>
|
||||
|
@ -151,14 +151,15 @@
|
|||
<!-- Accounts -->
|
||||
<string name="no_accounts">Немає облікових записів для відображення</string>
|
||||
<string name="no_follow_request">Запит на відмову від підписки</string>
|
||||
<string name="status_cnt">Дмухи \n %1$s</string>
|
||||
<string name="status_cnt">Повідомлення
|
||||
\n %1$s</string>
|
||||
<string name="following_cnt">Підписані на \n %1$s</string>
|
||||
<string name="followers_cnt">Стежать за вами \n %1$s</string>
|
||||
<string name="reject">Відхилити</string>
|
||||
<!-- Scheduled toots -->
|
||||
<string name="no_scheduled_toots">Немає запланованих дмухів для показу!</string>
|
||||
<string name="remove_scheduled">Видалити запланований дмух?</string>
|
||||
<string name="toot_scheduled">Дмух було заплановано!</string>
|
||||
<string name="no_scheduled_toots">Немає запланованих повідомлень для показу!</string>
|
||||
<string name="remove_scheduled">Видалити заплановане повідомлення\?</string>
|
||||
<string name="toot_scheduled">Повідомлення заплановано!</string>
|
||||
<string name="toot_scheduled_date">Запланована дата має бути більшою за поточну годину!</string>
|
||||
<!-- timed mute -->
|
||||
<string name="timed_mute_date_error">Час для приглушення має бути більшим за одну хвилину.</string>
|
||||
|
@ -184,10 +185,10 @@
|
|||
<string name="toast_unmute">Обліковий запис більше не приглушено!</string>
|
||||
<string name="toast_follow">На обліковий запис підписалися!</string>
|
||||
<string name="toast_unfollow">Ви більше не підписані на обліковий запис!</string>
|
||||
<string name="toast_reblog">Передмухнуто!</string>
|
||||
<string name="toast_unreblog">Більше не передмухнуто!</string>
|
||||
<string name="toast_favourite">Дмух додано до обраного!</string>
|
||||
<string name="toast_unfavourite">Дмух видалено з обраного!</string>
|
||||
<string name="toast_reblog">Повідомлення підштовхнуте!</string>
|
||||
<string name="toast_unreblog">Повідомлення більше не підштовхнуте!</string>
|
||||
<string name="toast_favourite">Повідомлення додано до обраного!</string>
|
||||
<string name="toast_unfavourite">Повідомлення видалено з обраного!</string>
|
||||
<string name="toast_error">Вибачте, виникла помилка!</string>
|
||||
<string name="toast_code_error">Сталася помилка! Екземпляр не повернув код авторизації!</string>
|
||||
<string name="toast_error_instance">Домен екземпляру не валідний!</string>
|
||||
|
@ -196,7 +197,7 @@
|
|||
<string name="nothing_to_do">Ніяка дія не може відбутися</string>
|
||||
<string name="toast_error_translate">Виникла помилка під час перекладу!</string>
|
||||
<!-- Settings -->
|
||||
<string name="set_toots_page">Число дмухів за одиницю завантаження</string>
|
||||
<string name="set_toots_page">Кількість повідомлень за одиницю завантаження</string>
|
||||
<string name="set_disable_gif">Вимкнути GIF аватари</string>
|
||||
<string name="set_notif_follow">Сповіщати, якщо хтось підписується</string>
|
||||
<string name="set_notif_follow_share">Сповіщати, якщо хтось передмухнув ваш статус</string>
|
||||
|
@ -275,7 +276,7 @@
|
|||
<string name="poxy_port">Порт</string>
|
||||
<string name="poxy_login">Логін</string>
|
||||
<string name="poxy_password">Пароль</string>
|
||||
<string name="set_share_details">Додати деталі гудку при поділитися</string>
|
||||
<string name="set_share_details">Додати деталі повідомлення при поширюванні</string>
|
||||
<string name="support_the_app_on_liberapay">Підтримати додаток на Liberapay</string>
|
||||
<string name="alert_regex">Помилка у регулярному виразі!</string>
|
||||
<string name="toast_instance_unavailable">No timelines was found on this instance!</string>
|
||||
|
@ -311,7 +312,7 @@
|
|||
<string name="channel_notif_fav">New Favourite</string>
|
||||
<string name="channel_notif_mention">New Mention</string>
|
||||
<string name="channel_notif_poll">Poll Ended</string>
|
||||
<string name="channel_notif_backup">Toots Backup</string>
|
||||
<string name="channel_notif_backup">Запасне копіювання повідомлень</string>
|
||||
<string name="channel_notif_status">New posts</string>
|
||||
<string name="channel_notif_media">Медіа завантаження</string>
|
||||
<string name="select_sound">Оберіть сигнал</string>
|
||||
|
@ -323,11 +324,11 @@
|
|||
<string name="peertube_instance">Peertube екземпляр</string>
|
||||
<string name="set_display_emoji">Use Emoji One</string>
|
||||
<string name="information">Information</string>
|
||||
<string name="set_display_card">Display previews in all toots</string>
|
||||
<string name="set_display_card">Відображати дрібнообрази в усіх повідомленнях</string>
|
||||
<string name="account_id_clipbloard">The account id has been copied in the clipboard!</string>
|
||||
<string name="set_change_locale">Change the language</string>
|
||||
<string name="truncate_long_toots">Truncate long toots</string>
|
||||
<string name="set_truncate_toot">Truncate toots over \'x\' lines. Zero means disabled.</string>
|
||||
<string name="truncate_long_toots">Обтинати довгі повідомлення</string>
|
||||
<string name="set_truncate_toot">Обтинати повідомлення більші, ніж \'x\' рядків. Нуль означає вимикання.</string>
|
||||
<string name="display_toot_truncate">Display more</string>
|
||||
<string name="hide_toot_truncate">Display less</string>
|
||||
<string name="tags_already_stored">The tag already exists!</string>
|
||||
|
@ -373,8 +374,8 @@
|
|||
<string name="category">Category</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="toots_server">Toots (Server)</string>
|
||||
<string name="toots_client">Toots (Device)</string>
|
||||
<string name="toots_server">Повідомлення (сервер)</string>
|
||||
<string name="toots_client">Повідомлення (пристрій)</string>
|
||||
<string name="settings_category_label_timelines">Timelines</string>
|
||||
<string name="settings_category_label_interface">Interface</string>
|
||||
<string name="contact">Contacts</string>
|
||||
|
@ -396,7 +397,7 @@
|
|||
<string name="poll_finish_at">end at %s</string>
|
||||
<string name="vote">Vote</string>
|
||||
<string name="notif_poll">A poll you have voted in has ended</string>
|
||||
<string name="notif_poll_self">A poll you tooted has ended</string>
|
||||
<string name="notif_poll_self">Опубліковане вами опитування закінчилося</string>
|
||||
<string name="settings_category_notif_categories">Categories</string>
|
||||
<string name="move_timeline">Move timeline</string>
|
||||
<string name="hide_timeline">Hide timeline</string>
|
||||
|
@ -531,7 +532,7 @@
|
|||
<string name="background_status">Background color of posts in timelines</string>
|
||||
<string name="reset_color">Reset colors</string>
|
||||
<string name="clik_reset">Tap here to reset all your custom colors</string>
|
||||
<string name="reset">Reset</string>
|
||||
<string name="reset">Скинути</string>
|
||||
<string name="icons_color_title">Icons</string>
|
||||
<string name="icons_color">Color of bottom icons in timelines</string>
|
||||
<string name="logo_of_the_instance">Logo of the instance</string>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
app:title="@string/embedded_browser" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="true"
|
||||
app:defaultValue="false"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/SET_SEND_CRASH_REPORTS"
|
||||
app:singleLineTitle="false"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<paths>
|
||||
<external-path
|
||||
name="my_images"
|
||||
path="Android/data/app.fedilab.android.test/files/Pictures" />
|
||||
path="Android/data/app.fedilab.android/files/Pictures" />
|
||||
|
||||
<cache-path
|
||||
name="*"
|
||||
|
|
13
src/fdroid/fastlane/metadata/android/en/changelogs/396.txt
Normal file
13
src/fdroid/fastlane/metadata/android/en/changelogs/396.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
Added:
|
||||
- Allow to set a focus point on previews (media editor)
|
||||
- Respect the focus point with previews
|
||||
- Pagination with the fetch more button support reading up or down
|
||||
- Add trends
|
||||
|
||||
Fixed:
|
||||
- Only last push notification is displayed (not grouped)
|
||||
- Bad behavior with the right/left scroll
|
||||
- Fix long profiles not fully displayed
|
||||
- Issues with some polls
|
||||
- Some crashes
|
||||
- Some bad behaviors
|
Loading…
Reference in a new issue