diff --git a/app/build.gradle b/app/build.gradle
index f49868243..d45c2b562 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,8 +6,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
- versionCode 273
- versionName "2.1.0"
+ versionCode 274
+ versionName "2.2.0"
multiDexEnabled true
renderscriptTargetApi 28 as int
renderscriptSupportModeEnabled true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 728ce12f7..b8b38b663 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -157,6 +157,10 @@
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
+
playlistForVideo;
+ private List playlists;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -156,6 +170,7 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
setTheme(R.style.AppThemeDark);
}
fullScreenMode = false;
+ playlistForVideo = new ArrayList<>();
setContentView(R.layout.activity_peertube);
loader = findViewById(R.id.loader);
peertube_view_count = findViewById(R.id.peertube_view_count);
@@ -173,6 +188,7 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
my_pp = findViewById(R.id.my_pp);
add_comment_read = findViewById(R.id.add_comment_read);
add_comment_write = findViewById(R.id.add_comment_write);
+ peertube_playlist = findViewById(R.id.peertube_playlist);
send = findViewById(R.id.send);
add_comment_read.setOnClickListener(new View.OnClickListener() {
@Override
@@ -189,6 +205,10 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
if( MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE){
write_comment_container.setVisibility(View.GONE);
}
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE){
+ peertube_playlist.setVisibility(View.VISIBLE);
+ peertube_bookmark.setVisibility(View.GONE);
+ }
send.setOnClickListener(new View.OnClickListener() {
@Override
@@ -204,6 +224,7 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
}
}
});
+
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(getApplicationContext()));
@@ -296,7 +317,9 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
initFullscreenButton();
}
-
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE){
+ new ManagePlaylistsAsyncTask(PeertubeActivity.this,GET_PLAYLIST, null, null, null , PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
new RetrievePeertubeSingleAsyncTask(PeertubeActivity.this, peertubeInstance, videoId, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -475,6 +498,66 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
}
peertube = apiResponse.getPeertubes().get(0);
+
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE){
+ new ManagePlaylistsAsyncTask(PeertubeActivity.this,GET_PLAYLIST_FOR_VIDEO, null, peertube.getId(), null , PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+
+ peertube_playlist.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if( playlists != null && peertube.getId() != null) {
+ PopupMenu popup = new PopupMenu(PeertubeActivity.this, peertube_playlist);
+
+ for(Playlist playlist: playlists){
+ String title = null;
+ for (String id : playlistForVideo) {
+ if (playlist.getId().equals(id)) {
+ title = "✔ " + playlist.getDisplayName();
+ break;
+ }
+ }
+ if( title == null){
+ title = playlist.getDisplayName();
+ }
+ MenuItem item = popup.getMenu().add(0, 0, Menu.NONE, title);
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
+ item.setActionView(new View(getApplicationContext()));
+ item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ return false;
+ }
+ });
+ if(playlistForVideo.contains(playlist.getId())){
+ item.setTitle(playlist.getDisplayName());
+ new ManagePlaylistsAsyncTask(PeertubeActivity.this,ManagePlaylistsAsyncTask.action.DELETE_VIDEOS, playlist, peertube.getId(), null , PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ playlistForVideo.remove(playlist.getId());
+ }else{
+ item.setTitle( "✔ " + playlist.getDisplayName());
+ new ManagePlaylistsAsyncTask(PeertubeActivity.this,ManagePlaylistsAsyncTask.action.ADD_VIDEOS, playlist, peertube.getId(), null , PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ playlistForVideo.add(playlist.getId());
+ }
+ return false;
+ }
+ });
+ popup.show();
+ }
+ }
+ }
+ });
+
+
+
if( peertube.isCommentsEnabled()) {
new RetrievePeertubeSingleCommentsAsyncTask(PeertubeActivity.this, peertubeInstance, videoId, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE)
@@ -870,4 +953,14 @@ public class PeertubeActivity extends BaseActivity implements OnRetrievePeertube
peertube_like_count.setCompoundDrawablesWithIntrinsicBounds( null, thumbUp, null, null);
peertube_dislike_count.setCompoundDrawablesWithIntrinsicBounds( null, thumbDown, null, null);
}
+
+ @Override
+ public void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode) {
+
+ if( actionType == GET_PLAYLIST_FOR_VIDEO && apiResponse != null) {
+ playlistForVideo = apiResponse.getPlaylistForVideos();
+ }else if( actionType == GET_PLAYLIST && apiResponse != null){
+ playlists = apiResponse.getPlaylists();
+ }
+ }
}
diff --git a/app/src/main/java/app/fedilab/android/activities/PlaylistsActivity.java b/app/src/main/java/app/fedilab/android/activities/PlaylistsActivity.java
new file mode 100644
index 000000000..e2abd241c
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/activities/PlaylistsActivity.java
@@ -0,0 +1,272 @@
+/* Copyright 2017 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 . */
+package app.fedilab.android.activities;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import app.fedilab.android.R;
+import app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask;
+import app.fedilab.android.client.APIResponse;
+import app.fedilab.android.client.Entities.Peertube;
+import app.fedilab.android.client.Entities.Playlist;
+import app.fedilab.android.drawers.PeertubeAdapter;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.interfaces.OnPlaylistActionInterface;
+import es.dmoral.toasty.Toasty;
+
+import static app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask.action.GET_LIST_VIDEOS;
+
+
+/**
+ * Created by Thomas on 26/05/2019.
+ * Display playlists for Peertube
+ */
+
+public class PlaylistsActivity extends BaseActivity implements OnPlaylistActionInterface {
+
+
+ private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
+ private SwipeRefreshLayout swipeRefreshLayout;
+ private boolean swiped;
+ private List peertubes;
+ private String max_id;
+ private Playlist playlist;
+ private boolean firstLoad;
+ private boolean flag_loading;
+ private PeertubeAdapter peertubeAdapter;
+ LinearLayoutManager mLayoutManager;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
+ switch (theme){
+ case Helper.THEME_LIGHT:
+ setTheme(R.style.AppTheme_NoActionBar);
+ break;
+ case Helper.THEME_DARK:
+ setTheme(R.style.AppThemeDark_NoActionBar);
+ break;
+ case Helper.THEME_BLACK:
+ setTheme(R.style.AppThemeBlack_NoActionBar);
+ break;
+ default:
+ setTheme(R.style.AppThemeDark_NoActionBar);
+ }
+ if( getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ ActionBar actionBar = getSupportActionBar();
+ if( actionBar != null ) {
+ LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ assert inflater != null;
+ @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.simple_bar, null);
+ actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+ ImageView toolbar_close = actionBar.getCustomView().findViewById(R.id.toolbar_close);
+ TextView toolbar_title = actionBar.getCustomView().findViewById(R.id.toolbar_title);
+ toolbar_close.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ toolbar_title.setText(R.string.upload_video);
+ if (theme == Helper.THEME_LIGHT){
+ Toolbar toolbar = actionBar.getCustomView().findViewById(R.id.toolbar);
+ Helper.colorizeToolbar(toolbar, R.color.black, PlaylistsActivity.this);
+ }
+ }
+ setContentView(R.layout.activity_playlists);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ if( theme == Helper.THEME_BLACK)
+ toolbar.setBackgroundColor(ContextCompat.getColor(PlaylistsActivity.this, R.color.black));
+ setSupportActionBar(toolbar);
+ if( getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ peertubes = new ArrayList<>();
+
+ RecyclerView lv_playlist = findViewById(R.id.lv_playlist);
+ mainLoader = findViewById(R.id.loader);
+ nextElementLoader = findViewById(R.id.loading_next_status);
+ textviewNoAction = findViewById(R.id.no_action);
+ mainLoader.setVisibility(View.VISIBLE);
+ swipeRefreshLayout = findViewById(R.id.swipeContainer);
+ max_id = null;
+ flag_loading = true;
+ firstLoad = true;
+ swiped = false;
+
+
+ mainLoader.setVisibility(View.VISIBLE);
+ nextElementLoader.setVisibility(View.GONE);
+ boolean isOnWifi = Helper.isOnWIFI(PlaylistsActivity.this);
+
+ peertubeAdapter = new PeertubeAdapter(PlaylistsActivity.this, Helper.getLiveInstance(PlaylistsActivity.this), false, this.peertubes);
+
+ lv_playlist.setAdapter(peertubeAdapter);
+ mLayoutManager = new LinearLayoutManager(PlaylistsActivity.this);
+ lv_playlist.setLayoutManager(mLayoutManager);
+
+ Bundle b = getIntent().getExtras();
+ if(b != null){
+ playlist = b.getParcelable("playlist");
+ }else{
+ Toasty.error(this,getString(R.string.toast_error_search),Toast.LENGTH_LONG).show();
+ return;
+ }
+ if( getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setTitle(playlist.getDisplayName());
+
+
+ lv_playlist.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)
+ {
+ int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
+ if(dy > 0){
+ int visibleItemCount = mLayoutManager.getChildCount();
+ int totalItemCount = mLayoutManager.getItemCount();
+ if(firstVisibleItem + visibleItemCount == totalItemCount ) {
+ if(!flag_loading ) {
+ flag_loading = true;
+ new ManagePlaylistsAsyncTask(PlaylistsActivity.this,GET_LIST_VIDEOS, playlist, null, max_id , PlaylistsActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ nextElementLoader.setVisibility(View.VISIBLE);
+ }
+ } else {
+ nextElementLoader.setVisibility(View.GONE);
+ }
+ }
+ }
+ });
+
+
+ swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ max_id = null;
+ firstLoad = true;
+ flag_loading = true;
+ swiped = true;
+ MainActivity.countNewStatus = 0;
+ new ManagePlaylistsAsyncTask(PlaylistsActivity.this,GET_LIST_VIDEOS, playlist, null, null , PlaylistsActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ });
+
+ switch (theme){
+ case Helper.THEME_LIGHT:
+ swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4,
+ R.color.mastodonC2,
+ R.color.mastodonC3);
+ swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(PlaylistsActivity.this, R.color.white));
+ break;
+ case Helper.THEME_DARK:
+ swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4__,
+ R.color.mastodonC4,
+ R.color.mastodonC4);
+ swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(PlaylistsActivity.this, R.color.mastodonC1_));
+ break;
+ case Helper.THEME_BLACK:
+ swipeRefreshLayout.setColorSchemeResources(R.color.dark_icon,
+ R.color.mastodonC2,
+ R.color.mastodonC3);
+ swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(PlaylistsActivity.this, R.color.black_3));
+ break;
+ }
+
+ new ManagePlaylistsAsyncTask(PlaylistsActivity.this,GET_LIST_VIDEOS, playlist, null, null , PlaylistsActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+
+ @Override
+ public void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode) {
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
+ mainLoader.setVisibility(View.GONE);
+ nextElementLoader.setVisibility(View.GONE);
+ //Discards 404 - error which can often happen due to toots which have been deleted
+ if (apiResponse.getError() != null) {
+ if ( !apiResponse.getError().getError().startsWith("404 -"))
+ Toasty.error(PlaylistsActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ swipeRefreshLayout.setRefreshing(false);
+ swiped = false;
+ flag_loading = false;
+ return;
+ }
+ if( actionType == GET_LIST_VIDEOS) {
+
+ int previousPosition = this.peertubes.size();
+ List videos = apiResponse.getPeertubes();
+ max_id = apiResponse.getMax_id();
+ flag_loading = (max_id == null);
+ if (!swiped && firstLoad && (videos == null || videos.size() == 0))
+ textviewNoAction.setVisibility(View.VISIBLE);
+ else
+ textviewNoAction.setVisibility(View.GONE);
+
+ if (swiped) {
+ if (previousPosition > 0) {
+ for (int i = 0; i < previousPosition; i++) {
+ this.peertubes.remove(0);
+ }
+ peertubeAdapter.notifyItemRangeRemoved(0, previousPosition);
+ }
+ swiped = false;
+ }
+ if (videos != null && videos.size() > 0) {
+ this.peertubes.addAll(videos);
+ peertubeAdapter.notifyItemRangeInserted(previousPosition, videos.size());
+ }
+ swipeRefreshLayout.setRefreshing(false);
+ firstLoad = false;
+ }
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/activities/TootActivity.java b/app/src/main/java/app/fedilab/android/activities/TootActivity.java
index 86250b562..5d10fdd72 100644
--- a/app/src/main/java/app/fedilab/android/activities/TootActivity.java
+++ b/app/src/main/java/app/fedilab/android/activities/TootActivity.java
@@ -47,6 +47,7 @@ import android.text.Html;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextWatcher;
+import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -153,8 +154,8 @@ import app.fedilab.android.interfaces.OnRetrieveAttachmentInterface;
import app.fedilab.android.interfaces.OnRetrieveEmojiInterface;
import app.fedilab.android.interfaces.OnRetrieveSearcAccountshInterface;
import app.fedilab.android.interfaces.OnRetrieveSearchInterface;
-
import static app.fedilab.android.helper.Helper.changeDrawableColor;
+import static app.fedilab.android.helper.Helper.countWithEmoji;
/**
* Created by Thomas on 01/05/2017.
@@ -219,6 +220,9 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
public static HashMap filesMap;
private Poll poll;
private ImageButton poll_action;
+ public static boolean autocomplete;
+ private String newContent;
+ private TextWatcher textWatcher;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -253,6 +257,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}else{
max_media_count = 4;
}
+ autocomplete = false;
setContentView(R.layout.activity_toot);
ActionBar actionBar = getSupportActionBar();
if( actionBar != null ) {
@@ -449,7 +454,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
if( mentionAccount != null){
toot_content.setText(String.format("@%s\n", mentionAccount));
toot_content.setSelection(toot_content.getText().length());
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
if( tootMention != null && urlMention != null) {
if (fileMention != null) {
@@ -476,7 +481,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
});
}
toot_content.setText(String.format("\n\nvia @%s\n\n%s\n\n", tootMention, urlMention));
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
@@ -511,7 +516,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
toot_content.setText(sharedContent);
if (selectionBefore >= 0 && selectionBefore < toot_content.length())
toot_content.setSelection(selectionBefore);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
if (image != null) {
new HttpsConnection(TootActivity.this, instance).download(image, TootActivity.this);
@@ -520,14 +525,12 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
toot_content.setText(String.format("\n%s", sharedContent));
if (selectionBefore >= 0 && selectionBefore < toot_content.length())
toot_content.setSelection(selectionBefore);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
}
attachments = new ArrayList<>();
- int charsInCw = 0;
- int charsInToot = 0;
if (!sharedUri.isEmpty()) {
uploadSharedImage(sharedUri);
@@ -561,7 +564,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
});
- toot_space_left.setText(String.valueOf(charsInToot + charsInCw));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_cw.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -674,83 +677,121 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
- int totalChar = toot_cw_content.length() + toot_content.length();
- toot_space_left.setText(String.valueOf(totalChar));
+ toot_space_left.setText(String.valueOf(countLength()));
}
});
- if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA)
- toot_content.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
+ textWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (autocomplete) {
+ toot_content.removeTextChangedListener(textWatcher);
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ int currentCount = countLength();
+ while (currentCount < 500) {
+ newContent = newContent + new String(Character.toChars(0x1F917));
+ toot_content.setText(newContent);
+ currentCount++;
+ }
+ toot_content.setSelection(toot_content.getText().length());
+ toot_content.addTextChangedListener(textWatcher);
+ autocomplete = false;
+ toot_space_left.setText(String.valueOf(currentCount));
+ }
+ };
+ thread.start();
+ return;
}
- @Override
- public void afterTextChanged(Editable s) {
- if( toot_content.getSelectionStart() != 0)
- currentCursorPosition = toot_content.getSelectionStart();
- if( s.toString().length() == 0 )
- currentCursorPosition = 0;
- //Only check last 15 characters before cursor position to avoid lags
- if( currentCursorPosition < 15 ){ //Less than 15 characters are written before the cursor position
- searchLength = currentCursorPosition;
- }else {
- searchLength = 15;
+
+ if (toot_content.getSelectionStart() != 0)
+ currentCursorPosition = toot_content.getSelectionStart();
+ if (s.toString().length() == 0)
+ currentCursorPosition = 0;
+ //Only check last 15 characters before cursor position to avoid lags
+ if (currentCursorPosition < 15) { //Less than 15 characters are written before the cursor position
+ searchLength = currentCursorPosition;
+ } else {
+ searchLength = 15;
+ }
+
+
+ int totalChar = countLength();
+ toot_space_left.setText(String.valueOf(totalChar));
+ if (currentCursorPosition - (searchLength - 1) < 0 || currentCursorPosition == 0 || currentCursorPosition > s.toString().length())
+ return;
+
+ String patternh = "^(.|\\s)*(:fedilab_hugs:)$";
+ final Pattern hPattern = Pattern.compile(patternh);
+ Matcher mh = hPattern.matcher((s.toString().substring(currentCursorPosition - searchLength, currentCursorPosition)));
+
+ if (mh.matches()) {
+ autocomplete = true;
+ newContent = s.toString().replaceAll(":fedilab_hugs:", " ");
+ return;
+ }
+
+ Matcher m, mt;
+ if (s.toString().charAt(0) == '@')
+ m = sPattern.matcher(s.toString().substring(currentCursorPosition - searchLength, currentCursorPosition));
+ else
+ m = sPattern.matcher(s.toString().substring(currentCursorPosition - (searchLength - 1), currentCursorPosition));
+ if (m.matches()) {
+ String search = m.group(3);
+ if (pp_progress != null && pp_actionBar != null) {
+ pp_progress.setVisibility(View.VISIBLE);
+ pp_actionBar.setVisibility(View.GONE);
}
- int totalChar = toot_cw_content.length() + toot_content.length();
- toot_space_left.setText(String.valueOf(totalChar));
- if( currentCursorPosition- (searchLength-1) < 0 || currentCursorPosition == 0 || currentCursorPosition > s.toString().length())
- return;
- Matcher m, mt;
- if( s.toString().charAt(0) == '@')
- m = sPattern.matcher(s.toString().substring(currentCursorPosition- searchLength, currentCursorPosition));
+ new RetrieveSearchAccountsAsyncTask(getApplicationContext(), search, TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ if (s.toString().charAt(0) == '#')
+ mt = tPattern.matcher(s.toString().substring(currentCursorPosition - searchLength, currentCursorPosition));
else
- m = sPattern.matcher(s.toString().substring(currentCursorPosition- (searchLength-1), currentCursorPosition));
- if(m.matches()) {
- String search = m.group(3);
+ mt = tPattern.matcher(s.toString().substring(currentCursorPosition - (searchLength - 1), currentCursorPosition));
+ if (mt.matches()) {
+ String search = mt.group(3);
if (pp_progress != null && pp_actionBar != null) {
pp_progress.setVisibility(View.VISIBLE);
pp_actionBar.setVisibility(View.GONE);
}
- new RetrieveSearchAccountsAsyncTask(getApplicationContext(),search,TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }else{
- if( s.toString().charAt(0) == '#')
- mt = tPattern.matcher(s.toString().substring(currentCursorPosition- searchLength, currentCursorPosition));
+ new RetrieveSearchAsyncTask(getApplicationContext(), search, true, TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ if (s.toString().charAt(0) == ':')
+ mt = ePattern.matcher(s.toString().substring(currentCursorPosition - searchLength, currentCursorPosition));
else
- mt = tPattern.matcher(s.toString().substring(currentCursorPosition- (searchLength-1), currentCursorPosition));
- if(mt.matches()) {
- String search = mt.group(3);
+ mt = ePattern.matcher(s.toString().substring(currentCursorPosition - (searchLength - 1), currentCursorPosition));
+ if (mt.matches()) {
+ String shortcode = mt.group(3);
if (pp_progress != null && pp_actionBar != null) {
pp_progress.setVisibility(View.VISIBLE);
pp_actionBar.setVisibility(View.GONE);
}
- new RetrieveSearchAsyncTask(getApplicationContext(),search,true, TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }else{
- if( s.toString().charAt(0) == ':')
- mt = ePattern.matcher(s.toString().substring(currentCursorPosition- searchLength, currentCursorPosition));
- else
- mt = ePattern.matcher(s.toString().substring(currentCursorPosition- (searchLength-1), currentCursorPosition));
- if(mt.matches()) {
- String shortcode = mt.group(3);
- if (pp_progress != null && pp_actionBar != null) {
- pp_progress.setVisibility(View.VISIBLE);
- pp_actionBar.setVisibility(View.GONE);
- }
- new RetrieveEmojiAsyncTask(getApplicationContext(),shortcode,TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }else {
- toot_content.dismissDropDown();
- }
+ new RetrieveEmojiAsyncTask(getApplicationContext(), shortcode, TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ toot_content.dismissDropDown();
}
}
-
-
- totalChar = toot_cw_content.length() + toot_content.length();
- toot_space_left.setText(String.valueOf(totalChar));
}
- });
+
+
+ totalChar = countLength();
+ toot_space_left.setText(String.valueOf(totalChar));
+ }
+ };
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA)
+ toot_content.addTextChangedListener(textWatcher);
+
+
if( scheduledstatus != null)
restoreServerSchedule(scheduledstatus.getStatus());
@@ -766,7 +807,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
});
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
@Override
@@ -1225,7 +1266,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
picker.show(getSupportFragmentManager(), "COUNTRY_PICKER");
return true;
case R.id.action_emoji:
- final List emojis = new CustomEmojiDAO(getApplicationContext(), db).getAllEmojis();
+ final List emojis = new CustomEmojiDAO(getApplicationContext(), db).getAllEmojis(account.getInstance());
final AlertDialog.Builder builder = new AlertDialog.Builder(this, style);
int paddingPixel = 15;
float density = getResources().getDisplayMetrics().density;
@@ -1532,7 +1573,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
String tootContent;
if( toot_cw_content.getText() != null && toot_cw_content.getText().toString().trim().length() > 0 )
split_toot_size -= toot_cw_content.getText().toString().trim().length();
- if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || !split_toot || (toot_content.getText().toString().trim().length() < split_toot_size)){
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || !split_toot || (countLength() < split_toot_size)){
tootContent = toot_content.getText().toString().trim();
}else{
splitToot = Helper.splitToots(toot_content.getText().toString().trim(), split_toot_size);
@@ -1740,7 +1781,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
//Adds the shorter text_url of attachment at the end of the toot
int selectionBefore = toot_content.getSelectionStart();
toot_content.setText(String.format("%s\n\n%s",toot_content.getText().toString(), attachment.getText_url()));
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
//Moves the cursor
toot_content.setSelection(selectionBefore);
}
@@ -1897,7 +1938,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
//Clears the text_url at the end of the toot
for this attachment
int selectionBefore = toot_content.getSelectionStart();
toot_content.setText(toot_content.getText().toString().replace(attachment.getText_url(), ""));
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
//Moves the cursor
if (selectionBefore >= 0 && selectionBefore < toot_content.length())
toot_content.setSelection(selectionBefore);
@@ -1995,7 +2036,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
int cwSize = toot_cw_content.getText().toString().trim().length();
int size = toot_content.getText().toString().trim().length() + cwSize;
- if( split_toot && (size >= split_toot_size) && stepSpliToot < splitToot.size()){
+ if( split_toot && splitToot != null && (size >= split_toot_size) && stepSpliToot < splitToot.size()){
String tootContent = splitToot.get(stepSpliToot);
stepSpliToot += 1;
Status toot = new Status();
@@ -2112,7 +2153,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
if (currentCursorPosition < oldContent.length() )
newContent += oldContent.substring(currentCursorPosition, oldContent.length());
toot_content.setText(newContent);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.setSelection(newPosition);
AccountsSearchAdapter accountsListAdapter = new AccountsSearchAdapter(TootActivity.this, new ArrayList<>());
toot_content.setThreshold(1);
@@ -2183,7 +2224,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
if( currentCursorPosition < oldContent.length() )
newContent += oldContent.substring(currentCursorPosition, oldContent.length()-1);
toot_content.setText(newContent);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.setSelection(newPosition);
EmojisSearchAdapter emojisSearchAdapter = new EmojisSearchAdapter(TootActivity.this, new ArrayList<>());
toot_content.setThreshold(1);
@@ -2237,7 +2278,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
if( currentCursorPosition < oldContent.length() )
newContent += oldContent.substring(currentCursorPosition, oldContent.length()-1);
toot_content.setText(newContent);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.setSelection(newPosition);
TagsSearchAdapter tagsSearchAdapter = new TagsSearchAdapter(TootActivity.this, new ArrayList<>());
toot_content.setThreshold(1);
@@ -2367,7 +2408,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
toot_content.setText(content);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.setSelection(toot_content.getText().length());
switch (status.getVisibility()){
case "public":
@@ -2410,7 +2451,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
invalidateOptionsMenu();
initialContent = toot_content.getText().toString();
- toot_space_left.setText(String.valueOf(toot_content.getText().length() + toot_cw_content.getText().length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
@@ -2517,7 +2558,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
toot_content.setText(content);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.setSelection(toot_content.getText().length());
switch (status.getVisibility()){
case "public":
@@ -2552,7 +2593,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
invalidateOptionsMenu();
initialContent = toot_content.getText().toString();
- toot_space_left.setText(String.valueOf(toot_content.getText().length() + toot_cw_content.getText().length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
private void tootReply(){
@@ -2676,7 +2717,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
else
toot_content.append(" ");
}
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
toot_content.requestFocus();
if( capitalize) {
@@ -2700,7 +2741,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
toot_content.setText(toot_content.getText() +" #"+tag.getName());
}
toot_content.setSelection(currentCursorPosition);
- toot_space_left.setText(String.valueOf(toot_content.length()));
+ toot_space_left.setText(String.valueOf(countLength()));
}
}
@@ -3011,4 +3052,26 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface,
}
+ private int countLength(){
+ if( toot_content == null || toot_cw_content == null) {
+ return -1;
+ }
+ String content = toot_content.getText().toString();
+ String cwContent = toot_cw_content.getText().toString();
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA ){
+ Matcher matcherALink = Patterns.WEB_URL.matcher(content);
+ while (matcherALink.find()){
+ int matchStart = matcherALink.start();
+ int matchEnd = matcherALink.end();
+ final String url = content.substring(matcherALink.start(1), matcherALink.end(1));
+ if( matchEnd <= content.length() && matchEnd >= matchStart){
+ content = content.replaceFirst(url,"abcdefghijklmnopkrstuvw");
+ }
+ }
+ }
+ int contentLength = content.length() - countWithEmoji(content);
+ int cwLength = cwContent.length() - countWithEmoji(cwContent);
+ return cwLength + contentLength;
+ }
+
}
diff --git a/app/src/main/java/app/fedilab/android/asynctasks/ManagePlaylistsAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/ManagePlaylistsAsyncTask.java
new file mode 100644
index 000000000..da23e5dc5
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/asynctasks/ManagePlaylistsAsyncTask.java
@@ -0,0 +1,100 @@
+/* Copyright 2019 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 . */
+package app.fedilab.android.asynctasks;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.AsyncTask;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+import app.fedilab.android.client.APIResponse;
+import app.fedilab.android.client.Entities.Account;
+import app.fedilab.android.client.Entities.Playlist;
+import app.fedilab.android.client.PeertubeAPI;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.interfaces.OnPlaylistActionInterface;
+import app.fedilab.android.sqlite.AccountDAO;
+import app.fedilab.android.sqlite.Sqlite;
+
+
+/**
+ * Created by Thomas on 26/05/2019.
+ * Async works to manage Playlists
+ */
+
+public class ManagePlaylistsAsyncTask extends AsyncTask {
+
+ public enum action{
+ GET_PLAYLIST,
+ GET_LIST_VIDEOS,
+ CREATE_PLAYLIST,
+ DELETE_PLAYLIST,
+ UPDATE_PLAYLIST,
+ ADD_VIDEOS,
+ DELETE_VIDEOS,
+ GET_PLAYLIST_FOR_VIDEO,
+ }
+
+ private OnPlaylistActionInterface listener;
+ private APIResponse apiResponse;
+ private int statusCode;
+ private action apiAction;
+ private WeakReference contextReference;
+ private String max_id;
+ private Playlist playlist;
+ private String videoId;
+
+ public ManagePlaylistsAsyncTask(Context context, action apiAction, Playlist playlist, String videoId, String max_id, OnPlaylistActionInterface onPlaylistActionInterface){
+ contextReference = new WeakReference<>(context);
+ this.listener = onPlaylistActionInterface;
+ this.apiAction = apiAction;
+ this.max_id = max_id;
+ this.playlist = playlist;
+ this.videoId = videoId;
+ }
+
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ SharedPreferences sharedpreferences = contextReference.get().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
+ String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(contextReference.get()));
+ SQLiteDatabase db = Sqlite.getInstance(contextReference.get(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ Account account = new AccountDAO(contextReference.get(), db).getAccountByUserIDInstance(userId, instance);
+ if (apiAction == action.GET_PLAYLIST) {
+ apiResponse = new PeertubeAPI(contextReference.get()).getPlayists(account.getUsername());
+ }else if(apiAction == action.GET_LIST_VIDEOS){
+ apiResponse = new PeertubeAPI(contextReference.get()).getPlaylistVideos(playlist.getId(),max_id, null);
+ }else if(apiAction == action.DELETE_PLAYLIST){
+ statusCode = new PeertubeAPI(contextReference.get()).deletePlaylist(playlist.getId());
+ }else if(apiAction == action.ADD_VIDEOS){
+ statusCode = new PeertubeAPI(contextReference.get()).addVideoPlaylist(playlist.getId(),videoId);
+ }else if(apiAction == action.DELETE_VIDEOS){
+ statusCode = new PeertubeAPI(contextReference.get()).deleteVideoPlaylist(playlist.getId(),videoId);
+ }else if(apiAction == action.GET_PLAYLIST_FOR_VIDEO){
+ apiResponse = new PeertubeAPI(contextReference.get()).getPlaylistForVideo(videoId);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ listener.onActionDone(this.apiAction, apiResponse, statusCode);
+ }
+
+}
diff --git a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java
index 17135b964..58af89331 100644
--- a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java
+++ b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java
@@ -94,6 +94,7 @@ public class RetrieveFeedsAsyncTask extends AsyncTask {
PLOCAL,
CHANNEL,
MYVIDEOS,
+ PEERTUBE_HISTORY,
PIXELFED,
PF_HOME,
@@ -311,6 +312,10 @@ public class RetrieveFeedsAsyncTask extends AsyncTask {
PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get());
apiResponse = peertubeAPI.getMyVideos(max_id);
break;
+ case PEERTUBE_HISTORY:
+ peertubeAPI = new PeertubeAPI(this.contextReference.get());
+ apiResponse = peertubeAPI.getMyHistory(max_id);
+ break;
case CHANNEL:
peertubeAPI = new PeertubeAPI(this.contextReference.get());
apiResponse = peertubeAPI.getVideosChannel(targetedID, max_id);
diff --git a/app/src/main/java/app/fedilab/android/client/APIResponse.java b/app/src/main/java/app/fedilab/android/client/APIResponse.java
index 54cdd954e..228382c48 100644
--- a/app/src/main/java/app/fedilab/android/client/APIResponse.java
+++ b/app/src/main/java/app/fedilab/android/client/APIResponse.java
@@ -28,6 +28,7 @@ import app.fedilab.android.client.Entities.Instance;
import app.fedilab.android.client.Entities.Notification;
import app.fedilab.android.client.Entities.Peertube;
import app.fedilab.android.client.Entities.PeertubeNotification;
+import app.fedilab.android.client.Entities.Playlist;
import app.fedilab.android.client.Entities.Relationship;
import app.fedilab.android.client.Entities.Results;
import app.fedilab.android.client.Entities.Status;
@@ -51,6 +52,7 @@ public class APIResponse {
private List peertubes = null;
private List peertubeNotifications = null;
private List filters = null;
+ private List playlists = null;
private List domains = null;
private List lists = null;
private List emojis = null;
@@ -59,6 +61,7 @@ public class APIResponse {
private Instance instance;
private List storedStatuses;
private boolean fetchmore = false;
+ private List playlistForVideos;
public List getAccounts() {
return accounts;
@@ -219,4 +222,20 @@ public class APIResponse {
public void setFetchmore(boolean fetchmore) {
this.fetchmore = fetchmore;
}
+
+ public List getPlaylists() {
+ return playlists;
+ }
+
+ public void setPlaylists(List playlists) {
+ this.playlists = playlists;
+ }
+
+ public List getPlaylistForVideos() {
+ return playlistForVideos;
+ }
+
+ public void setPlaylistForVideos(List playlistForVideos) {
+ this.playlistForVideos = playlistForVideos;
+ }
}
diff --git a/app/src/main/java/app/fedilab/android/client/Entities/PeertubeInformation.java b/app/src/main/java/app/fedilab/android/client/Entities/PeertubeInformation.java
index 67d3a6f7e..504a971a1 100644
--- a/app/src/main/java/app/fedilab/android/client/Entities/PeertubeInformation.java
+++ b/app/src/main/java/app/fedilab/android/client/Entities/PeertubeInformation.java
@@ -25,8 +25,10 @@ public class PeertubeInformation {
private LinkedHashMap languages;
private LinkedHashMap licences;
private LinkedHashMap privacies;
+ private LinkedHashMap playlistPrivacies;
private LinkedHashMap translations;
+
public static final LinkedHashMap langueMapped;
static {
LinkedHashMap aMap = new LinkedHashMap<>();
@@ -86,4 +88,12 @@ public class PeertubeInformation {
public void setPrivacies(LinkedHashMap privacies) {
this.privacies = privacies;
}
+
+ public LinkedHashMap getPlaylistPrivacies() {
+ return playlistPrivacies;
+ }
+
+ public void setPlaylistPrivacies(LinkedHashMap playlistPrivacies) {
+ this.playlistPrivacies = playlistPrivacies;
+ }
}
diff --git a/app/src/main/java/app/fedilab/android/client/Entities/Playlist.java b/app/src/main/java/app/fedilab/android/client/Entities/Playlist.java
new file mode 100644
index 000000000..362409afe
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/client/Entities/Playlist.java
@@ -0,0 +1,204 @@
+/* Copyright 2019 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 . */
+package app.fedilab.android.client.Entities;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Date;
+import java.util.HashMap;
+
+
+/**
+ * Created by Thomas on 26/05/2019.
+ * Manage List
+ */
+
+public class Playlist implements Parcelable {
+
+ private String id;
+ private String uuid;
+ private String displayName;
+ private String description;
+ private String videoChannelId;
+ private Date createdAt;
+ private boolean isLocal;
+ private Account ownerAccount;
+ private HashMap privacy;
+ private String thumbnailPath;
+ private HashMap type;
+ private Date updatedAt;
+ private int videosLength;
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getVideoChannelId() {
+ return videoChannelId;
+ }
+
+ public void setVideoChannelId(String videoChannelId) {
+ this.videoChannelId = videoChannelId;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public boolean isLocal() {
+ return isLocal;
+ }
+
+ public void setLocal(boolean local) {
+ isLocal = local;
+ }
+
+ public Account getOwnerAccount() {
+ return ownerAccount;
+ }
+
+ public void setOwnerAccount(Account ownerAccount) {
+ this.ownerAccount = ownerAccount;
+ }
+
+ public HashMap getPrivacy() {
+ return privacy;
+ }
+
+ public void setPrivacy(HashMap privacy) {
+ this.privacy = privacy;
+ }
+
+ public String getThumbnailPath() {
+ return thumbnailPath;
+ }
+
+ public void setThumbnailPath(String thumbnailPath) {
+ this.thumbnailPath = thumbnailPath;
+ }
+
+ public HashMap getType() {
+ return type;
+ }
+
+ public void setType(HashMap type) {
+ this.type = type;
+ }
+
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Date updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public int getVideosLength() {
+ return videosLength;
+ }
+
+ public void setVideosLength(int videosLength) {
+ this.videosLength = videosLength;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.id);
+ dest.writeString(this.uuid);
+ dest.writeString(this.displayName);
+ dest.writeString(this.description);
+ dest.writeString(this.videoChannelId);
+ dest.writeLong(this.createdAt != null ? this.createdAt.getTime() : -1);
+ dest.writeByte(this.isLocal ? (byte) 1 : (byte) 0);
+ dest.writeParcelable(this.ownerAccount, flags);
+ dest.writeSerializable(this.privacy);
+ dest.writeString(this.thumbnailPath);
+ dest.writeSerializable(this.type);
+ dest.writeLong(this.updatedAt != null ? this.updatedAt.getTime() : -1);
+ dest.writeInt(this.videosLength);
+ }
+
+ public Playlist() {
+ }
+
+ protected Playlist(Parcel in) {
+ this.id = in.readString();
+ this.uuid = in.readString();
+ this.displayName = in.readString();
+ this.description = in.readString();
+ this.videoChannelId = in.readString();
+ long tmpCreatedAt = in.readLong();
+ this.createdAt = tmpCreatedAt == -1 ? null : new Date(tmpCreatedAt);
+ this.isLocal = in.readByte() != 0;
+ this.ownerAccount = in.readParcelable(Account.class.getClassLoader());
+ this.privacy = (HashMap) in.readSerializable();
+ this.thumbnailPath = in.readString();
+ this.type = (HashMap) in.readSerializable();
+ long tmpUpdatedAt = in.readLong();
+ this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
+ this.videosLength = in.readInt();
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public Playlist createFromParcel(Parcel source) {
+ return new Playlist(source);
+ }
+
+ @Override
+ public Playlist[] newArray(int size) {
+ return new Playlist[size];
+ }
+ };
+}
diff --git a/app/src/main/java/app/fedilab/android/client/Entities/Status.java b/app/src/main/java/app/fedilab/android/client/Entities/Status.java
index dd9dca448..eca1acad4 100644
--- a/app/src/main/java/app/fedilab/android/client/Entities/Status.java
+++ b/app/src/main/java/app/fedilab/android/client/Entities/Status.java
@@ -555,7 +555,31 @@ public class Status implements Parcelable{
SpannableString spannableStringContent, spannableStringCW;
if( (status.getReblog() != null && status.getReblog().getContent() == null) || (status.getReblog() == null && status.getContent() == null))
return;
- spannableStringContent = new SpannableString(status.getReblog() != null ?status.getReblog().getContent():status.getContent());
+
+ String content = status.getReblog() != null ?status.getReblog().getContent():status.getContent();
+
+ Pattern aLink = Pattern.compile("]*(((?!<\\/a).)*)<\\/a>");
+ Matcher matcherALink = aLink.matcher(content);
+ while (matcherALink.find()){
+ String beforemodification;
+ String urlText = matcherALink.group(2);
+
+ urlText = urlText.substring(1);
+ beforemodification = urlText;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ urlText = new SpannableString(Html.fromHtml(urlText, Html.FROM_HTML_MODE_LEGACY)).toString();
+ else
+ urlText = new SpannableString(Html.fromHtml(urlText)).toString();
+ if( urlText.startsWith("http") ){
+ urlText = urlText.replace("http://","").replace("https://","").replace("www.","");
+ if( urlText.length() > 31){
+ urlText = urlText.substring(0,30);
+ urlText += '…';
+ }
+ }
+ content = content.replaceAll(Pattern.quote(beforemodification),urlText);
+ }
+ spannableStringContent = new SpannableString(content);
String spoilerText = "";
if( status.getReblog() != null && status.getReblog().getSpoiler_text() != null)
spoilerText = status.getReblog().getSpoiler_text();
diff --git a/app/src/main/java/app/fedilab/android/client/PeertubeAPI.java b/app/src/main/java/app/fedilab/android/client/PeertubeAPI.java
index 15819ce9c..04c2a3a23 100644
--- a/app/src/main/java/app/fedilab/android/client/PeertubeAPI.java
+++ b/app/src/main/java/app/fedilab/android/client/PeertubeAPI.java
@@ -54,6 +54,7 @@ import app.fedilab.android.client.Entities.PeertubeComment;
import app.fedilab.android.client.Entities.PeertubeInformation;
import app.fedilab.android.client.Entities.PeertubeNotification;
import app.fedilab.android.client.Entities.PeertubeVideoNotification;
+import app.fedilab.android.client.Entities.Playlist;
import app.fedilab.android.client.Entities.Relationship;
import app.fedilab.android.client.Entities.Results;
import app.fedilab.android.client.Entities.Status;
@@ -231,55 +232,6 @@ public class PeertubeAPI {
}
- /***
- * Update credential of the authenticated user *synchronously*
- * @return APIResponse
- */
- public APIResponse updateCredential(String display_name, String note, ByteArrayInputStream avatar, String avatarName, ByteArrayInputStream header, String headerName, API.accountPrivacy privacy, HashMap customFields) {
-
- HashMap requestParams = new HashMap<>();
- if( display_name != null)
- try {
- requestParams.put("display_name",URLEncoder.encode(display_name, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- requestParams.put("display_name",display_name);
- }
- if( note != null)
- try {
- requestParams.put("note",URLEncoder.encode(note, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- requestParams.put("note",note);
- }
- if( privacy != null)
- requestParams.put("locked",privacy== API.accountPrivacy.LOCKED?"true":"false");
- int i = 0;
- if( customFields != null && customFields.size() > 0){
- Iterator it = customFields.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry pair = (Map.Entry)it.next();
- requestParams.put("fields_attributes["+i+"][name]",(String)pair.getKey());
- requestParams.put("fields_attributes["+i+"][value]",(String)pair.getValue());
- it.remove();
- i++;
- }
- }
- try {
- new HttpsConnection(context, this.instance).patch(getAbsoluteUrl("/accounts/update_credentials"), 60, requestParams, avatar, avatarName, header, headerName, prefKeyOauthTokenT);
- } catch (HttpsConnection.HttpsConnectionException e) {
- e.printStackTrace();
- setError(e.getStatusCode(), e);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- }
- return apiResponse;
- }
-
-
-
/***
* Verifiy credential of the authenticated user *synchronously*
* @return Account
@@ -319,6 +271,15 @@ public class PeertubeAPI {
peertubeInformation.setPrivacies(_pprivacies);
+ response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/video-playlists/privacies"), 60, null, null);
+ JSONObject plprivacies = new JSONObject(response);
+ LinkedHashMap _plprivacies = new LinkedHashMap<>();
+ for( int i = 1 ; i <= plprivacies.length() ; i++){
+ _plprivacies.put(i, plprivacies.getString(String.valueOf(i)));
+
+ }
+ peertubeInformation.setPlaylistPrivacies(_plprivacies);
+
response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/videos/licences"), 60, null, null);
JSONObject licences = new JSONObject(response);
LinkedHashMap _plicences = new LinkedHashMap<>();
@@ -520,9 +481,61 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getVideos(String acct, String max_id) {
- return getVideos(acct, max_id, null, tootPerPage);
+ return getVideos(acct, max_id, null);
}
+ /**
+ * Retrieves history for videos for the account *synchronously*
+ *
+ * @param max_id String id max
+ * @return APIResponse
+ */
+ public APIResponse getMyHistory(String max_id) {
+ return getMyHistory(max_id, null);
+ }
+
+
+
+ /**
+ * Retrieves history for videos for the account *synchronously*
+ *
+ * @param max_id String id max
+ * @param since_id String since the id
+ * @return APIResponse
+ */
+ @SuppressWarnings("SameParameterValue")
+ private APIResponse getMyHistory(String max_id, String since_id) {
+
+ HashMap params = new HashMap<>();
+ if (max_id != null)
+ params.put("start", max_id);
+ if (since_id != null)
+ params.put("since_id", since_id);
+ params.put("count", String.valueOf(tootPerPage));
+ List peertubes = new ArrayList<>();
+ try {
+
+ HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
+ String response = httpsConnection.get(getAbsoluteUrl("/users/me/history/videos"), 60, params, prefKeyOauthTokenT);
+
+ JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
+ peertubes = parsePeertube(jsonArray);
+
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ apiResponse.setPeertubes(peertubes);
+ return apiResponse;
+ }
/**
* Retrieves videos for the account *synchronously*
@@ -531,7 +544,7 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getMyVideos(String max_id) {
- return getMyVideos(max_id, null, tootPerPage);
+ return getMyVideos(max_id, null);
}
@@ -541,20 +554,17 @@ public class PeertubeAPI {
*
* @param max_id String id max
* @param since_id String since the id
- * @param limit int limit - max value 40
* @return APIResponse
*/
@SuppressWarnings("SameParameterValue")
- private APIResponse getMyVideos(String max_id, String since_id, int limit) {
+ private APIResponse getMyVideos(String max_id, String since_id) {
HashMap params = new HashMap<>();
if (max_id != null)
params.put("start", max_id);
if (since_id != null)
params.put("since_id", since_id);
- if (0 < limit || limit > 40)
- limit = 40;
- params.put("count", String.valueOf(limit));
+ params.put("count", String.valueOf(tootPerPage));
List peertubes = new ArrayList<>();
try {
@@ -565,42 +575,6 @@ public class PeertubeAPI {
peertubes = parsePeertube(jsonArray);
} catch (HttpsConnection.HttpsConnectionException e) {
- if( e.getStatusCode() == 401){ //Avoid the issue with the refresh token
-
- SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
- SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
- String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
- String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
- Account account = new AccountDAO(context, db).getUniqAccount(userId, instance);
- HashMap values = new PeertubeAPI(context).refreshToken(account.getClient_id(), account.getClient_secret(), account.getRefresh_token());
- if( values != null) {
- String newtoken = values.get("access_token");
- String refresh_token = values.get("refresh_token");
- if (newtoken != null)
- account.setToken(newtoken);
- if (refresh_token != null)
- account.setRefresh_token(refresh_token);
- new AccountDAO(context, db).updateAccount(account);
- prefKeyOauthTokenT = newtoken;
- }
- HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
- String response;
- try {
- response = httpsConnection.get(getAbsoluteUrl("/users/me/videos"), 60, params, prefKeyOauthTokenT);
- JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
- peertubes = parsePeertube(jsonArray);
- } catch (IOException e1) {
- e1.printStackTrace();
- } catch (NoSuchAlgorithmException e1) {
- e1.printStackTrace();
- } catch (KeyManagementException e1) {
- e1.printStackTrace();
- } catch (HttpsConnection.HttpsConnectionException e1) {
- e1.printStackTrace();
- } catch (JSONException e1) {
- e1.printStackTrace();
- }
- }
setError(e.getStatusCode(), e);
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
@@ -623,20 +597,17 @@ public class PeertubeAPI {
* @param acct String Id of the account
* @param max_id String id max
* @param since_id String since the id
- * @param limit int limit - max value 40
* @return APIResponse
*/
@SuppressWarnings("SameParameterValue")
- private APIResponse getVideos(String acct, String max_id, String since_id, int limit) {
+ private APIResponse getVideos(String acct, String max_id, String since_id) {
HashMap params = new HashMap<>();
if (max_id != null)
params.put("start", max_id);
if (since_id != null)
params.put("since_id", since_id);
- if (0 < limit || limit > 40)
- limit = 40;
- params.put("count", String.valueOf(limit));
+ params.put("count", String.valueOf(tootPerPage));
List peertubes = new ArrayList<>();
try {
HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
@@ -667,7 +638,7 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getNotifications(String max_id){
- return getNotifications(max_id, null, 20);
+ return getNotifications(max_id, null);
}
/**
@@ -677,7 +648,7 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getNotificationsSince(String since_id){
- return getNotifications(null, since_id, 20);
+ return getNotifications(null, since_id);
}
/**
@@ -685,20 +656,17 @@ public class PeertubeAPI {
*
* @param max_id String id max
* @param since_id String since the id
- * @param limit int limit - max value 40
* @return APIResponse
*/
@SuppressWarnings("SameParameterValue")
- private APIResponse getNotifications(String max_id, String since_id, int limit) {
+ private APIResponse getNotifications(String max_id, String since_id) {
HashMap params = new HashMap<>();
if (max_id != null)
params.put("start", max_id);
if (since_id != null)
params.put("since_id", since_id);
- if (0 < limit || limit > 40)
- limit = 40;
- params.put("count", String.valueOf(limit));
+ params.put("count", String.valueOf(tootPerPage));
List peertubeNotifications = new ArrayList<>();
try {
HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
@@ -730,7 +698,7 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getVideosChannel(String acct, String max_id) {
- return getVideosChannel(acct, max_id, null, tootPerPage);
+ return getVideosChannel(acct, max_id, null);
}
/**
@@ -739,20 +707,17 @@ public class PeertubeAPI {
* @param acct String Id of the account
* @param max_id String id max
* @param since_id String since the id
- * @param limit int limit - max value 40
* @return APIResponse
*/
@SuppressWarnings("SameParameterValue")
- private APIResponse getVideosChannel(String acct, String max_id, String since_id, int limit) {
+ private APIResponse getVideosChannel(String acct, String max_id, String since_id) {
HashMap params = new HashMap<>();
if (max_id != null)
params.put("start", max_id);
if (since_id != null)
params.put("since_id", since_id);
- if (0 < limit || limit > 40)
- limit = 40;
- params.put("count", String.valueOf(limit));
+ params.put("count", String.valueOf(tootPerPage));
List peertubes = new ArrayList<>();
try {
@@ -787,7 +752,37 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getSubscriptionsTL( String max_id) {
- return getTL("/users/me/subscriptions/videos","-publishedAt",null, max_id, null, null, tootPerPage);
+ try {
+ return getTL("/users/me/subscriptions/videos","-publishedAt",null, max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ if( e.getStatusCode() == 401 || e.getStatusCode() == 403) {
+ SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
+ Account targetedAccount = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT);
+ HashMap values = refreshToken(targetedAccount.getClient_id(), targetedAccount.getClient_secret(), targetedAccount.getRefresh_token());
+ SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ if (values.containsKey("access_token") && values.get("access_token") != null) {
+ targetedAccount.setToken(values.get("access_token"));
+ String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
+ //This account is currently logged in, the token is updated
+ if (prefKeyOauthTokenT.equals(token)) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, targetedAccount.getToken());
+ editor.apply();
+ }
+ }
+ if (values.containsKey("refresh_token") && values.get("refresh_token") != null)
+ targetedAccount.setRefresh_token(values.get("refresh_token"));
+ new AccountDAO(context, db).updateAccount(targetedAccount);
+ try {
+ return getTL("/users/me/subscriptions/videos","-publishedAt",null, max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e1) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
+ }
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -796,7 +791,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getOverviewTL( String max_id) {
- return getTL("/overviews/videos",null,null, max_id, null, null, tootPerPage);
+ try {
+ return getTL("/overviews/videos",null,null, max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -805,7 +805,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getTrendingTL( String max_id) {
- return getTL("/videos/","-trending", null,max_id, null, null, tootPerPage);
+ try {
+ return getTL("/videos/","-trending", null,max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -814,7 +819,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getRecentlyAddedTL( String max_id) {
- return getTL("/videos/","-publishedAt",null,max_id, null, null, tootPerPage);
+ try {
+ return getTL("/videos/","-publishedAt",null,max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -823,7 +833,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getLocalTL( String max_id) {
- return getTL("/videos/","-publishedAt", "local",max_id, null, null, tootPerPage);
+ try {
+ return getTL("/videos/","-publishedAt", "local",max_id, null, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -831,7 +846,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getSubscriptionsTLSinceId(String since_id) {
- return getTL("/users/me/subscriptions/videos",null,null,null, since_id, null, tootPerPage);
+ try {
+ return getTL("/users/me/subscriptions/videos",null,null,null, since_id, null);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
/**
@@ -839,7 +859,12 @@ public class PeertubeAPI {
* @return APIResponse
*/
public APIResponse getSubscriptionsTLMinId(String min_id) {
- return getTL("/users/me/subscriptions/videos",null, null,null, null, min_id, tootPerPage);
+ try {
+ return getTL("/users/me/subscriptions/videos",null, null,null, null, min_id);
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ return apiResponse;
+ }
}
@@ -847,10 +872,9 @@ public class PeertubeAPI {
* Retrieves home timeline for the account *synchronously*
* @param max_id String id max
* @param since_id String since the id
- * @param limit int limit - max value 40
* @return APIResponse
*/
- private APIResponse getTL(String action, String sort, String filter, String max_id, String since_id, String min_id, int limit) {
+ private APIResponse getTL(String action, String sort, String filter, String max_id, String since_id, String min_id) throws HttpsConnection.HttpsConnectionException {
HashMap params = new HashMap<>();
if (max_id != null)
@@ -859,9 +883,7 @@ public class PeertubeAPI {
params.put("since_id", since_id);
if (min_id != null)
params.put("min_id", min_id);
- if (0 > limit || limit > 80)
- limit = 80;
- params.put("count",String.valueOf(limit));
+ params.put("count",String.valueOf(tootPerPage));
if( sort != null)
params.put("sort",sort);
else
@@ -926,12 +948,7 @@ public class PeertubeAPI {
peertubes4.get(0).setHeaderTypeValue(videoA.getJSONObject(1).getJSONObject("channel").getString("displayName"));
peertubes.addAll(peertubes4);
}
-
-
-
}
- } catch (HttpsConnection.HttpsConnectionException e) {
- setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -1014,8 +1031,7 @@ public class PeertubeAPI {
HashMap params = new HashMap<>();
if( max_id == null)
max_id = "0";
- params.put("start", max_id);
- params.put("count", "50");
+ params.put("start", String.valueOf(tootPerPage));
try {
HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
String response = httpsConnection.get("https://"+instance+"/api/v1/videos", 60, params, null);
@@ -1073,7 +1089,7 @@ public class PeertubeAPI {
*/
public APIResponse searchPeertube(String instance, String query) {
HashMap params = new HashMap<>();
- params.put("count", "50");
+ params.put("count", String.valueOf(tootPerPage));
try {
params.put("search", URLEncoder.encode(query, "UTF-8"));
} catch (UnsupportedEncodingException e) {
@@ -1293,85 +1309,58 @@ public class PeertubeAPI {
}
-
-
/**
- * Get filters for the user
+ * Video is in play lists
* @return APIResponse
*/
- public APIResponse getFilters(){
+ public APIResponse getPlaylistForVideo(String videoId){
- List filters = null;
- try {
- String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/filters"), 60, null, prefKeyOauthTokenT);
- filters = parseFilters(new JSONArray(response));
- } catch (HttpsConnection.HttpsConnectionException e) {
- setError(e.getStatusCode(), e);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- apiResponse.setFilters(filters);
- return apiResponse;
- }
-
- /**
- * Get a Filter by its id
- * @return APIResponse
- */
- @SuppressWarnings("unused")
- public APIResponse getFilters(String filterId){
-
- List filters = new ArrayList<>();
- Filters filter;
- try {
- String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/filters/%s", filterId)), 60, null, prefKeyOauthTokenT);
- filter = parseFilter(new JSONObject(response));
- filters.add(filter);
- } catch (HttpsConnection.HttpsConnectionException e) {
- setError(e.getStatusCode(), e);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- apiResponse.setFilters(filters);
- return apiResponse;
- }
-
-
- /**
- * Create a filter
- * @param filter Filter
- * @return APIResponse
- */
- public APIResponse addFilters(Filters filter){
HashMap params = new HashMap<>();
- params.put("phrase", filter.getPhrase());
- StringBuilder parameters = new StringBuilder();
- for(String context: filter.getContext())
- parameters.append("context[]=").append(context).append("&");
- if( parameters.length() > 0) {
- parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(10));
- params.put("context[]", parameters.toString());
- }
- params.put("irreversible", String.valueOf(filter.isIrreversible()));
- params.put("whole_word", String.valueOf(filter.isWhole_word()));
- params.put("expires_in", String.valueOf(filter.getExpires_in()));
- ArrayList filters = new ArrayList<>();
+ params.put("videoIds",videoId);
+ List ids = new ArrayList<>();
try {
- String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/filters"), 60, params, prefKeyOauthTokenT);
- Filters resfilter = parseFilter(new JSONObject(response));
- filters.add(resfilter);
+ String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/users/me/video-playlists/videos-exist"), 60, params, prefKeyOauthTokenT);
+
+ JSONArray jsonArray = new JSONObject(response).getJSONArray(videoId);
+ try {
+ int i = 0;
+ while (i < jsonArray.length() ) {
+ JSONObject resobj = jsonArray.getJSONObject(i);
+ String playlistId = resobj.getString("playlistId");
+ ids.add(playlistId);
+ i++;
+ }
+ } catch (JSONException e) {
+ setDefaultError(e);
+ }
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ apiResponse = new APIResponse();
+ apiResponse.setPlaylistForVideos(ids);
+ return apiResponse;
+ }
+
+
+ /**
+ * Get lists for the user
+ * @return APIResponse
+ */
+ public APIResponse getPlayists(String username){
+
+ List playlists = new ArrayList<>();
+ try {
+ String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/accounts/%s/video-playlists", username)), 60, null, prefKeyOauthTokenT);
+ playlists = parsePlaylists(context, new JSONObject(response).getJSONArray("data"));
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
@@ -1383,20 +1372,46 @@ public class PeertubeAPI {
} catch (JSONException e) {
e.printStackTrace();
}
- apiResponse.setFilters(filters);
+ apiResponse.setPlaylists(playlists);
return apiResponse;
}
- /**
- * Delete a filter
- * @param filter Filter
- * @return APIResponse
- */
- public int deleteFilters(Filters filter){
+
+
+ /**
+ * Delete a Playlist
+ * @param playlistId String, the playlist id
+ * @return int
+ */
+ public int deletePlaylist(String playlistId){
try {
HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
- httpsConnection.delete(getAbsoluteUrl(String.format("/filters/%s", filter.getId())), 60, null, prefKeyOauthTokenT);
+ httpsConnection.delete(getAbsoluteUrl(String.format("/video-playlists/%s", playlistId)), 60, null, prefKeyOauthTokenT);
+ actionCode = httpsConnection.getActionCode();
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ }
+ return actionCode;
+ }
+
+
+ /**
+ * Delete video in a Playlist
+ * @param playlistId String, the playlist id
+ * @param videoId String, the video id
+ * @return int
+ */
+ public int deleteVideoPlaylist(String playlistId, String videoId){
+ try {
+ HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
+ httpsConnection.delete(getAbsoluteUrl(String.format("/video-playlists/%s/videos/%s", playlistId, videoId)), 60, null, prefKeyOauthTokenT);
actionCode = httpsConnection.getActionCode();
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
@@ -1411,30 +1426,61 @@ public class PeertubeAPI {
}
/**
- * Delete a filter
- * @param filter Filter
+ * Add video in a Playlist
+ * @param playlistId String, the playlist id
+ * @param videoId String, the video id
+ * @return int
+ */
+ public int addVideoPlaylist(String playlistId, String videoId){
+ try {
+ HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
+ HashMap params = new HashMap<>();
+ params.put("videoId", videoId);
+ httpsConnection.post(getAbsoluteUrl(String.format("/video-playlists/%s/videos", playlistId)), 60, params, prefKeyOauthTokenT);
+ actionCode = httpsConnection.getActionCode();
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ }
+ return actionCode;
+ }
+
+
+
+ /**
+ * Retrieves status for the account *synchronously*
+ *
+ * @param playlistid String Id of the playlist
+ * @param max_id String id max
+ * @param since_id String since the id
* @return APIResponse
*/
- public APIResponse updateFilters(Filters filter){
+ @SuppressWarnings("SameParameterValue")
+ public APIResponse getPlaylistVideos(String playlistid, String max_id, String since_id) {
+
HashMap params = new HashMap<>();
- params.put("phrase", filter.getPhrase());
- StringBuilder parameters = new StringBuilder();
- for(String context: filter.getContext())
- parameters.append("context[]=").append(context).append("&");
- if( parameters.length() > 0) {
- parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(10));
- params.put("context[]", parameters.toString());
- }
- params.put("irreversible", String.valueOf(filter.isIrreversible()));
- params.put("whole_word", String.valueOf(filter.isWhole_word()));
- params.put("expires_in", String.valueOf(filter.getExpires_in()));
- ArrayList filters = new ArrayList<>();
+ if (max_id != null)
+ params.put("start", max_id);
+ if (since_id != null)
+ params.put("since_id", since_id);
+ params.put("count", String.valueOf(tootPerPage));
+ params.put("sort","-updatedAt");
+ List peertubes = new ArrayList<>();
try {
- String response = new HttpsConnection(context, this.instance).put(getAbsoluteUrl(String.format("/filters/%s", filter.getId())), 60, params, prefKeyOauthTokenT);
- Filters resfilter = parseFilter(new JSONObject(response));
- filters.add(resfilter);
+ HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
+ String response = httpsConnection.get(getAbsoluteUrl(String.format("/video-playlists/%s/videos", playlistid)), 60, params, prefKeyOauthTokenT);
+ JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
+ peertubes = parsePeertube(jsonArray);
+
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
+ e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -1444,62 +1490,10 @@ public class PeertubeAPI {
} catch (JSONException e) {
e.printStackTrace();
}
- apiResponse.setFilters(filters);
+ apiResponse.setPeertubes(peertubes);
return apiResponse;
}
- /**
- * Get lists for the user
- * @return APIResponse
- */
- public APIResponse getLists(){
-
- List lists = new ArrayList<>();
- try {
- String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/lists"), 60, null, prefKeyOauthTokenT);
- lists = parseLists(new JSONArray(response));
- } catch (HttpsConnection.HttpsConnectionException e) {
- setError(e.getStatusCode(), e);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- apiResponse.setLists(lists);
- return apiResponse;
- }
-
- /**
- * Get lists for a user by its id
- * @return APIResponse
- */
- @SuppressWarnings("unused")
- public APIResponse getLists(String userId){
-
- List lists = new ArrayList<>();
- app.fedilab.android.client.Entities.List list;
- try {
- String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/accounts/%s/lists", userId)), 60, null, prefKeyOauthTokenT);
- list = parseList(new JSONObject(response));
- lists.add(list);
- } catch (HttpsConnection.HttpsConnectionException e) {
- setError(e.getStatusCode(), e);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- apiResponse.setLists(lists);
- return apiResponse;
- }
/**
* Parse json response for several howto
@@ -2025,24 +2019,24 @@ public class PeertubeAPI {
/**
- * Parse Lists
+ * Parse Playlists
* @param jsonArray JSONArray
- * @return List of lists
+ * @return List of lists
*/
- private List parseLists(JSONArray jsonArray){
- List lists = new ArrayList<>();
+ private List parsePlaylists(Context context, JSONArray jsonArray){
+ List playlists = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length() ) {
JSONObject resobj = jsonArray.getJSONObject(i);
- app.fedilab.android.client.Entities.List list = parseList(resobj);
- lists.add(list);
+ Playlist playlist = parsePlaylist(context, resobj);
+ playlists.add(playlist);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
- return lists;
+ return playlists;
}
@@ -2051,13 +2045,34 @@ public class PeertubeAPI {
* @param resobj JSONObject
* @return Emojis
*/
- private static app.fedilab.android.client.Entities.List parseList(JSONObject resobj){
- app.fedilab.android.client.Entities.List list = new app.fedilab.android.client.Entities.List();
+ private static Playlist parsePlaylist(Context context, JSONObject resobj){
+ Playlist playlist = new Playlist();
try {
- list.setId(resobj.get("id").toString());
- list.setTitle(resobj.get("title").toString());
- }catch (Exception ignored){}
- return list;
+ playlist.setId(resobj.getString("id"));
+ playlist.setUuid(resobj.getString("uuid"));
+ playlist.setCreatedAt(Helper.stringToDate(context, resobj.getString("createdAt")));
+ playlist.setDescription(resobj.getString("description"));
+ playlist.setDisplayName(resobj.getString("displayName"));
+ playlist.setLocal(resobj.getBoolean("isLocal"));
+ playlist.setVideoChannelId(resobj.getString("videoChannel"));
+ playlist.setThumbnailPath(resobj.getString("thumbnailPath"));
+ playlist.setOwnerAccount(parseAccountResponsePeertube(context, resobj.getJSONObject("ownerAccount")));
+ playlist.setVideosLength(resobj.getInt("videosLength"));
+ try {
+ LinkedHashMap type = new LinkedHashMap<>();
+ LinkedHashMap privacy = new LinkedHashMap<>();
+ privacy.put(resobj.getJSONObject("privacy").getInt("id"), resobj.getJSONObject("privacy").get("label").toString());
+ type.put(resobj.getJSONObject("type").getInt("id"), resobj.getJSONObject("type").get("label").toString());
+ playlist.setType(type);
+ playlist.setPrivacy(privacy);
+ }catch (Exception ignored){ignored.printStackTrace();}
+
+
+ try{
+ playlist.setUpdatedAt(Helper.stringToDate(context, resobj.getString("updatedAt")));
+ }catch (Exception ignored){ignored.printStackTrace();}
+ }catch (Exception ignored){ignored.printStackTrace();}
+ return playlist;
}
private List parseAccountResponsePeertube(Context context, String instance, JSONArray jsonArray){
diff --git a/app/src/main/java/app/fedilab/android/drawers/EmojisSearchAdapter.java b/app/src/main/java/app/fedilab/android/drawers/EmojisSearchAdapter.java
index bd8c16860..42625b7b1 100644
--- a/app/src/main/java/app/fedilab/android/drawers/EmojisSearchAdapter.java
+++ b/app/src/main/java/app/fedilab/android/drawers/EmojisSearchAdapter.java
@@ -85,12 +85,13 @@ public class EmojisSearchAdapter extends ArrayAdapter implements Filtera
} else {
holder = (ViewHolder) convertView.getTag();
}
-
- holder.emoji_shortcode.setText(String.format("%s", emoji.getShortcode()));
- //Profile picture
- Glide.with(holder.emoji_icon.getContext())
- .load(emoji.getUrl())
- .into(holder.emoji_icon);
+ if( emoji != null) {
+ holder.emoji_shortcode.setText(String.format("%s", emoji.getShortcode()));
+ //Profile picture
+ Glide.with(holder.emoji_icon.getContext())
+ .load(emoji.getUrl())
+ .into(holder.emoji_icon);
+ }
return convertView;
}
@@ -124,15 +125,18 @@ public class EmojisSearchAdapter extends ArrayAdapter implements Filtera
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
- ArrayList c = (ArrayList) results.values;
- if (results.count > 0) {
- clear();
- addAll(c);
- notifyDataSetChanged();
- } else{
- clear();
- notifyDataSetChanged();
- }
+ try{
+ ArrayList c = (ArrayList) results.values;
+ if (results.count > 0) {
+ clear();
+ addAll(c);
+ notifyDataSetChanged();
+ } else{
+ clear();
+ notifyDataSetChanged();
+ }
+ }catch (Exception ignored){}
+
}
};
diff --git a/app/src/main/java/app/fedilab/android/drawers/PlaylistAdapter.java b/app/src/main/java/app/fedilab/android/drawers/PlaylistAdapter.java
new file mode 100644
index 000000000..7419961bb
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/drawers/PlaylistAdapter.java
@@ -0,0 +1,178 @@
+package app.fedilab.android.drawers;
+/* Copyright 2019 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 . */
+
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.List;
+
+import app.fedilab.android.R;
+import app.fedilab.android.activities.PlaylistsActivity;
+import app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask;
+import app.fedilab.android.client.APIResponse;
+import app.fedilab.android.client.Entities.Playlist;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.interfaces.OnPlaylistActionInterface;
+
+
+/**
+ * Created by Thomas on 26/05/2019.
+ * Adapter for playlists
+ */
+public class PlaylistAdapter extends BaseAdapter implements OnPlaylistActionInterface {
+
+ private List playlists;
+ private LayoutInflater layoutInflater;
+ private Context context;
+ private PlaylistAdapter playlistAdapter;
+ private RelativeLayout textviewNoAction;
+
+ public PlaylistAdapter(Context context, List lists, RelativeLayout textviewNoAction){
+ this.playlists = lists;
+ layoutInflater = LayoutInflater.from(context);
+ this.context = context;
+ playlistAdapter = this;
+ this.textviewNoAction = textviewNoAction;
+ }
+
+ @Override
+ public int getCount() {
+ return playlists.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return playlists.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+
+ final Playlist playlist = playlists.get(position);
+ final ViewHolder holder;
+ if (convertView == null) {
+ convertView = layoutInflater.inflate(R.layout.drawer_search, parent, false);
+ holder = new ViewHolder();
+ holder.search_title = convertView.findViewById(R.id.search_keyword);
+ holder.search_container = convertView.findViewById(R.id.search_container);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
+
+ if( theme == Helper.THEME_LIGHT){
+ holder.search_container.setBackgroundResource(R.color.mastodonC3__);
+ Helper.changeDrawableColor(context, R.drawable.ic_keyboard_arrow_right,R.color.black);
+ }else if(theme == Helper.THEME_DARK){
+ holder.search_container.setBackgroundResource(R.color.mastodonC1_);
+ Helper.changeDrawableColor(context, R.drawable.ic_keyboard_arrow_right,R.color.dark_text);
+ }else if(theme == Helper.THEME_BLACK) {
+ holder.search_container.setBackgroundResource(R.color.black_2);
+ Helper.changeDrawableColor(context, R.drawable.ic_keyboard_arrow_right,R.color.dark_text);
+ }
+ Drawable next = ContextCompat.getDrawable(context, R.drawable.ic_keyboard_arrow_right);
+ holder.search_title.setText(playlist.getDisplayName());
+ assert next != null;
+ final float scale = context.getResources().getDisplayMetrics().density;
+ next.setBounds(0,0,(int) (30 * scale + 0.5f),(int) (30 * scale + 0.5f));
+ holder.search_title.setCompoundDrawables(null, null, next, null);
+
+ holder.search_container.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(context, PlaylistsActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("playlist", playlist);
+ intent.putExtras(b);
+ context.startActivity(intent);
+ }
+ });
+ int style;
+ if (theme == Helper.THEME_DARK) {
+ style = R.style.DialogDark;
+ } else if (theme == Helper.THEME_BLACK){
+ style = R.style.DialogBlack;
+ }else {
+ style = R.style.Dialog;
+ }
+
+ holder.search_container.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context, style);
+ builder.setTitle(context.getString(R.string.action_lists_delete) + ": " + playlist.getDisplayName());
+ builder.setMessage(context.getString(R.string.action_lists_confirm_delete) );
+ builder.setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ playlists.remove(playlist);
+ playlistAdapter.notifyDataSetChanged();
+ new ManagePlaylistsAsyncTask(context, ManagePlaylistsAsyncTask.action.DELETE_PLAYLIST,playlist, null, null, PlaylistAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ if( playlists.size() == 0 && textviewNoAction != null && textviewNoAction.getVisibility() == View.GONE)
+ textviewNoAction.setVisibility(View.VISIBLE);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+
+ return false;
+ }
+ });
+ return convertView;
+ }
+
+ @Override
+ public void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode) {
+
+ }
+
+ private class ViewHolder {
+ LinearLayout search_container;
+ TextView search_title;
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java
index e9870a4a4..3fd5a9308 100644
--- a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java
+++ b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java
@@ -2757,7 +2757,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct
if (getItemViewType(viewHolder.getAdapterPosition()) == FOCUSED_STATUS && status.getApplication() != null && status.getApplication().getName() != null && status.getApplication().getName().length() > 0) {
Application application = status.getApplication();
holder.status_toot_app.setText(application.getName());
- if (application.getWebsite() != null && !application.getWebsite().trim().equals("null") && application.getWebsite().trim().length() == 0) {
+ if (application.getWebsite() != null && !application.getWebsite().trim().equals("null") && application.getWebsite().trim().length() > 0) {
holder.status_toot_app.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayPlaylistsFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayPlaylistsFragment.java
new file mode 100644
index 000000000..08376006e
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/fragments/DisplayPlaylistsFragment.java
@@ -0,0 +1,461 @@
+package app.fedilab.android.fragments;
+/* Copyright 2019 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 . */
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.AlertDialog;
+import android.text.InputFilter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import com.jaredrummler.materialspinner.MaterialSpinner;
+
+import net.gotev.uploadservice.MultipartUploadRequest;
+import net.gotev.uploadservice.ServerResponse;
+import net.gotev.uploadservice.UploadInfo;
+import net.gotev.uploadservice.UploadNotificationConfig;
+import net.gotev.uploadservice.UploadStatusDelegate;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import app.fedilab.android.R;
+import app.fedilab.android.activities.MainActivity;
+import app.fedilab.android.activities.PeertubeUploadActivity;
+import app.fedilab.android.activities.PlaylistsActivity;
+import app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask;
+import app.fedilab.android.asynctasks.RetrievePeertubeChannelsAsyncTask;
+import app.fedilab.android.client.APIResponse;
+import app.fedilab.android.client.Entities.Account;
+import app.fedilab.android.client.Entities.Playlist;
+import app.fedilab.android.drawers.PlaylistAdapter;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.interfaces.OnPlaylistActionInterface;
+import app.fedilab.android.interfaces.OnRetrievePeertubeInterface;
+import es.dmoral.toasty.Toasty;
+
+import static app.fedilab.android.asynctasks.RetrievePeertubeInformationAsyncTask.peertubeInformation;
+
+
+/**
+ * Created by Thomas on 26/05/2019.
+ * Fragment to display Playlists
+ */
+public class DisplayPlaylistsFragment extends Fragment implements OnPlaylistActionInterface, OnRetrievePeertubeInterface {
+
+
+ private Context context;
+ private AsyncTask asyncTask;
+ private List playlists;
+ private RelativeLayout mainLoader;
+ private FloatingActionButton add_new;
+ private PlaylistAdapter playlistAdapter;
+ private RelativeLayout textviewNoAction;
+ private HashMap privacyToSend;
+ private HashMap channelToSend;
+ private MaterialSpinner set_upload_channel;
+ private MaterialSpinner set_upload_privacy;
+ private HashMap channels;
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ //View for fragment is the same that fragment accounts
+ View rootView = inflater.inflate(R.layout.fragment_playlists, container, false);
+
+ context = getContext();
+ playlists = new ArrayList<>();
+
+
+ ListView lv_playlist = rootView.findViewById(R.id.lv_playlist);
+ textviewNoAction = rootView.findViewById(R.id.no_action);
+ mainLoader = rootView.findViewById(R.id.loader);
+ RelativeLayout nextElementLoader = rootView.findViewById(R.id.loading_next_items);
+ mainLoader.setVisibility(View.VISIBLE);
+ nextElementLoader.setVisibility(View.GONE);
+ playlists = new ArrayList<>();
+ playlistAdapter = new PlaylistAdapter(context, playlists, textviewNoAction);
+ lv_playlist.setAdapter(playlistAdapter);
+ asyncTask = new ManagePlaylistsAsyncTask(context, ManagePlaylistsAsyncTask.action.GET_PLAYLIST, null, null, null,DisplayPlaylistsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ try {
+ add_new = ((MainActivity) context).findViewById(R.id.add_new);
+ }catch (Exception ignored){}
+
+
+ LinkedHashMap translations = null;
+ if( peertubeInformation != null && peertubeInformation.getTranslations() != null)
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Map.Entry entryInt = privaciesInit.entrySet().iterator().next();
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(entryInt.getKey(), entryInt.getValue());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ //Populate privacies
+ String[] privaciesA = new String[privacies.size()];
+ Iterator it = privacies.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = (Map.Entry)it.next();
+ if( translations == null || translations.size() == 0 || !translations.containsKey((String)pair.getValue()))
+ privaciesA[i] = (String)pair.getValue();
+ else
+ privaciesA[i] = translations.get((String)pair.getValue());
+ it.remove();
+ i++;
+ }
+
+
+
+
+ if( add_new != null){
+ add_new.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
+ int style;
+ if (theme == Helper.THEME_DARK) {
+ style = R.style.DialogDark;
+ } else if (theme == Helper.THEME_BLACK){
+ style = R.style.DialogBlack;
+ }else {
+ style = R.style.Dialog;
+ }
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, style);
+ LayoutInflater inflater = ((Activity)context).getLayoutInflater();
+ @SuppressLint("InflateParams") View dialogView = inflater.inflate(R.layout.add_playlist, null);
+ dialogBuilder.setView(dialogView);
+ EditText display_name = dialogView.findViewById(R.id.display_name);
+ EditText description = dialogView.findViewById(R.id.description);
+ set_upload_channel = dialogView.findViewById(R.id.set_upload_channel);
+ set_upload_privacy = dialogView.findViewById(R.id.set_upload_privacy);
+
+ Helper.changeMaterialSpinnerColor(context, set_upload_privacy);
+ Helper.changeMaterialSpinnerColor(context, set_upload_channel);
+
+ new RetrievePeertubeChannelsAsyncTask(context, DisplayPlaylistsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+
+ display_name.setFilters(new InputFilter[]{new InputFilter.LengthFilter(120)});
+ description.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1000)});
+
+ dialogBuilder.setPositiveButton(R.string.validate, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+
+ if( display_name.getText() != null && display_name.getText().toString().trim().length() > 0 ) {
+
+ Playlist playlist = new Playlist();
+ playlist.setDisplayName(display_name.getText().toString().trim());
+ if( description.getText() != null && description.getText().toString().trim().length() > 0 ){
+ playlist.setDescription(description.getText().toString().trim());
+ }
+ String idChannel = null;
+ if( channelToSend != null ) {
+ Map.Entry channelM = channelToSend.entrySet().iterator().next();
+ idChannel = channelM.getValue();
+ if( idChannel.length() > 0)
+ playlist.setVideoChannelId(idChannel);
+ }
+ Map.Entry privacyM = privacyToSend.entrySet().iterator().next();
+ String label = privacyM.getValue();
+ String idPrivacy = String.valueOf(privacyM.getKey());
+ if( label.equals("Public") && (playlist.getVideoChannelId() == null || playlist.getVideoChannelId().equals(""))){
+ Toasty.error(context, context.getString(R.string.error_channel_mandatory),Toast.LENGTH_LONG).show();
+ }else{
+ if( privacyToSend != null){
+ playlist.setPrivacy(privacyToSend);
+ }
+ //new ManagePlaylistsAsyncTask(context, ManagePlaylistsAsyncTask.action.CREATE_PLAYLIST, playlist, null, null, DisplayPlaylistsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ UploadNotificationConfig uploadConfig = new UploadNotificationConfig();
+ uploadConfig.getCompleted().autoClear = true;
+ try {
+ String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
+ new MultipartUploadRequest(context, "https://" + Helper.getLiveInstance(context) + "/api/v1/video-playlists/")
+ //.addFileToUpload(uri.toString().replace("file://",""), "videofile")
+ .addHeader("Authorization", "Bearer " + token)
+ .setNotificationConfig(uploadConfig)
+ // .addParameter("name", filename)
+ .addParameter("videoChannelId", idChannel)
+ .addParameter("privacy", idPrivacy)
+ .addParameter("displayName", playlist.getDisplayName())
+ .addParameter("description", playlist.getDescription())
+ .setMaxRetries(1)
+ .setDelegate(new UploadStatusDelegate() {
+ @Override
+ public void onProgress(Context context, UploadInfo uploadInfo) {
+ // your code here
+ }
+
+ @Override
+ public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse,
+ Exception exception) {
+ // your code here
+ exception.printStackTrace();
+ }
+
+ @Override
+ public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
+ DisplayPlaylistsFragment displayPlaylistsFragment;
+ if( getActivity() == null)
+ return;
+ displayPlaylistsFragment = (DisplayPlaylistsFragment) getActivity().getSupportFragmentManager().findFragmentByTag("PLAYLISTS");
+ final FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
+ if( displayPlaylistsFragment != null) {
+ ft.detach(displayPlaylistsFragment);
+ ft.attach(displayPlaylistsFragment);
+ ft.commit();
+ }
+ }
+
+ @Override
+ public void onCancelled(Context context, UploadInfo uploadInfo) {
+ // your code here
+ }
+ })
+ .startUpload();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+
+ dialog.dismiss();
+ add_new.setEnabled(false);
+ }
+ }else{
+ Toasty.error(context, context.getString(R.string.error_display_name),Toast.LENGTH_LONG).show();
+ }
+
+ }
+ });
+ dialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ });
+
+
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.setTitle(getString(R.string.action_playlist_create));
+ alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ //Hide keyboard
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ assert imm != null;
+ imm.hideSoftInputFromWindow(display_name.getWindowToken(), 0);
+ }
+ });
+ if( alertDialog.getWindow() != null )
+ alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ alertDialog.show();
+ }
+ });
+ }
+ return rootView;
+ }
+
+
+
+ @Override
+ public void onCreate(Bundle saveInstance)
+ {
+ super.onCreate(saveInstance);
+ }
+
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+ public void onDestroy() {
+ super.onDestroy();
+ if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
+ asyncTask.cancel(true);
+ }
+
+
+
+
+ @Override
+ public void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode) {
+ mainLoader.setVisibility(View.GONE);
+ add_new.setEnabled(true);
+ if( apiResponse.getError() != null){
+ Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ if( actionType == ManagePlaylistsAsyncTask.action.GET_PLAYLIST) {
+ if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
+ this.playlists.addAll(apiResponse.getPlaylists());
+ playlistAdapter.notifyDataSetChanged();
+ textviewNoAction.setVisibility(View.GONE);
+ } else {
+ textviewNoAction.setVisibility(View.VISIBLE);
+ }
+ }else if( actionType == ManagePlaylistsAsyncTask.action.CREATE_PLAYLIST){
+ if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) {
+ Intent intent = new Intent(context, PlaylistsActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("playlist", apiResponse.getPlaylists().get(0));
+ intent.putExtras(b);
+ context.startActivity(intent);
+ this.playlists.add(0, apiResponse.getPlaylists().get(0));
+ playlistAdapter.notifyDataSetChanged();
+ textviewNoAction.setVisibility(View.GONE);
+ }else{
+ Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
+ }
+ }else if( actionType == ManagePlaylistsAsyncTask.action.DELETE_PLAYLIST){
+ if( this.playlists.size() == 0)
+ textviewNoAction.setVisibility(View.VISIBLE);
+ }
+ }
+
+
+ @Override
+ public void onRetrievePeertube(APIResponse apiResponse) {
+
+ }
+
+ @Override
+ public void onRetrievePeertubeComments(APIResponse apiResponse) {
+
+ }
+
+ @Override
+ public void onRetrievePeertubeChannels(APIResponse apiResponse) {
+ if (apiResponse.getError() != null || apiResponse.getAccounts() == null || apiResponse.getAccounts().size() == 0) {
+ if (apiResponse.getError() != null && apiResponse.getError().getError() != null)
+ Toasty.error(context, apiResponse.getError().getError(), Toast.LENGTH_LONG).show();
+ else
+ Toasty.error(context, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ //Populate channels
+ List accounts = apiResponse.getAccounts();
+ String[] channelName = new String[accounts.size()+1];
+ String[] channelId= new String[accounts.size()+1];
+ int i = 1;
+ channelName[0] = "";
+ channelId[0] = "";
+ channels = new HashMap<>();
+ for(Account account: accounts){
+ channels.put(account.getUsername(),account.getId());
+ channelName[i] = account.getUsername();
+ channelId[i] = account.getId();
+ i++;
+ }
+
+ channelToSend = new HashMap<>();
+ channelToSend.put(channelName[0], channelId[0]);
+ ArrayAdapter adapterChannel = new ArrayAdapter<>(context,
+ android.R.layout.simple_spinner_dropdown_item, channelName);
+ set_upload_channel.setAdapter(adapterChannel);
+
+ LinkedHashMap translations = null;
+ if (peertubeInformation.getTranslations() != null)
+ translations = new LinkedHashMap<>(peertubeInformation.getTranslations());
+
+ LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
+ Map.Entry entryInt = privaciesInit.entrySet().iterator().next();
+ privacyToSend = new HashMap<>();
+ privacyToSend.put(entryInt.getKey(), entryInt.getValue());
+ LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPlaylistPrivacies());
+ //Populate privacies
+ String[] privaciesA = new String[privacies.size()];
+ Iterator it = privacies.entrySet().iterator();
+ i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = (Map.Entry) it.next();
+ if (translations == null || translations.size() == 0 || !translations.containsKey((String) pair.getValue()))
+ privaciesA[i] = (String) pair.getValue();
+ else
+ privaciesA[i] = translations.get((String) pair.getValue());
+ it.remove();
+ i++;
+ }
+
+ ArrayAdapter adapterPrivacies = new ArrayAdapter<>(context,
+ android.R.layout.simple_spinner_dropdown_item, privaciesA);
+ set_upload_privacy.setAdapter(adapterPrivacies);
+
+ //Manage privacies
+ set_upload_privacy.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(MaterialSpinner view, int position, long id, String item) {
+ LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies());
+ Iterator it = privaciesCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = (Map.Entry) it.next();
+ if (i == position) {
+ privacyToSend = new HashMap<>();
+ privacyToSend.put((Integer) pair.getKey(), (String) pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+ });
+ //Manage languages
+ set_upload_channel.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(MaterialSpinner view, int position, long id, String item) {
+ LinkedHashMap channelsCheck = new LinkedHashMap<>(channels);
+ Iterator it = channelsCheck.entrySet().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry pair = (Map.Entry)it.next();
+ if( i == position){
+ channelToSend = new HashMap<>();
+ channelToSend.put((String)pair.getKey(), (String)pair.getValue());
+ break;
+ }
+ it.remove();
+ i++;
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java
index e8ab3296c..22609f1d3 100644
--- a/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java
+++ b/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java
@@ -460,7 +460,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
if( max_id == null)
max_id = "0";
//max_id needs to work like an offset
- max_id = String.valueOf(Integer.valueOf(max_id) + 50);
+ int tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40);
+ max_id = String.valueOf(Integer.valueOf(max_id) + tootPerPage);
if( apiResponse.getPeertubes() == null){
return;
}
@@ -953,7 +954,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
ArrayList tmpStatuses = new ArrayList<>();
for (Status tmpStatus : statuses) {
//Put the toot at its place in the list (id desc)
- if( !apiResponse.isFetchmore() && !this.statuses.contains(tmpStatus) && tmpStatus.getCreated_at().after(this.statuses.get(0).getCreated_at())) { //Element not already added
+ if( !apiResponse.isFetchmore() && !this.statuses.contains(tmpStatus) && tmpStatus.getCreated_at() != null && this.statuses.get(0).getCreated_at() != null && tmpStatus.getCreated_at().after(this.statuses.get(0).getCreated_at())) { //Element not already added
//Mark status at new ones when their id is greater than the last read toot id
if (type == RetrieveFeedsAsyncTask.Type.HOME && lastReadTootDate != null && tmpStatus.getCreated_at().after(lastReadTootDate) ) {
tmpStatus.setNew(true);
diff --git a/app/src/main/java/app/fedilab/android/helper/Helper.java b/app/src/main/java/app/fedilab/android/helper/Helper.java
index 0568d54cf..90e1d2654 100644
--- a/app/src/main/java/app/fedilab/android/helper/Helper.java
+++ b/app/src/main/java/app/fedilab/android/helper/Helper.java
@@ -3288,7 +3288,7 @@ public class Helper {
* @return ArrayList split toot
*/
public static ArrayList splitToots(String content, int maxChars){
- String[] splitContent = content.split("(\\.\\s){1}");
+ String[] splitContent = content.split("((\\.\\s)|(,\\s)|(;\\s)|(\\?\\s)|(!\\s)){1}");
ArrayList splitToot = new ArrayList<>();
StringBuilder tempContent = new StringBuilder(splitContent[0]);
ArrayList mentions = new ArrayList<>();
@@ -3309,7 +3309,7 @@ public class Helper {
int mentionLength = mentionString.length();
int maxCharsMention = maxChars - mentionLength;
for(int i= 0 ; i < splitContent.length ; i++){
- if (i < (splitContent.length - 1) && (tempContent.length() + splitContent[i + 1].length()) < (maxChars - 10)) {
+ if (i < (splitContent.length - 1) && (countLength(tempContent.toString()) + countLength(splitContent[i + 1])) < (maxChars - 10)) {
tempContent.append(". ").append(splitContent[i + 1]);
} else {
splitToot.add(tempContent.toString());
@@ -3334,6 +3334,37 @@ public class Helper {
+ public static int countLength(String text){
+ if( text == null) {
+ return 0;
+ }
+ if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA ){
+ Matcher matcherALink = Patterns.WEB_URL.matcher(text);
+ while (matcherALink.find()){
+ int matchStart = matcherALink.start();
+ int matchEnd = matcherALink.end();
+ final String url = text.substring(matcherALink.start(1), matcherALink.end(1));
+ if( matchEnd <= text.length() && matchEnd >= matchStart){
+ if( url.length() > 23){
+ text = text.replaceFirst(url,"abcdefghijklmnopkrstuvw");
+ }
+ }
+ }
+ }
+ return text.length() - countWithEmoji(text);
+ }
+
+ public static int countWithEmoji(String text){
+ int emojiCount = 0;
+ for (int i = 0; i < text.length(); i++) {
+ int type = Character.getType(text.charAt(i));
+ if (type == Character.SURROGATE || type == Character.OTHER_SYMBOL) {
+ emojiCount++;
+ }
+ }
+ return emojiCount/2;
+ }
+
public static boolean filterToots(Context context, Status status, List timedMute, RetrieveFeedsAsyncTask.Type type){
String filter;
if( status == null)
diff --git a/app/src/main/java/app/fedilab/android/helper/MastalabAutoCompleteTextView.java b/app/src/main/java/app/fedilab/android/helper/MastalabAutoCompleteTextView.java
index 50fd0cda1..cc5032d8c 100644
--- a/app/src/main/java/app/fedilab/android/helper/MastalabAutoCompleteTextView.java
+++ b/app/src/main/java/app/fedilab/android/helper/MastalabAutoCompleteTextView.java
@@ -16,6 +16,8 @@ import com.vanniktech.emoji.emoji.Emoji;
import app.fedilab.android.R;
+import static app.fedilab.android.activities.TootActivity.autocomplete;
+
public class MastalabAutoCompleteTextView extends android.support.v7.widget.AppCompatAutoCompleteTextView implements EmojiEditTextInterface {
private float emojiSize;
@@ -56,7 +58,7 @@ public class MastalabAutoCompleteTextView extends android.support.v7.widget.AppC
protected void onTextChanged(final CharSequence text, final int start, final int lengthBefore, final int lengthAfter) {
final Paint.FontMetrics fontMetrics = getPaint().getFontMetrics();
final float defaultEmojiSize = fontMetrics.descent - fontMetrics.ascent;
- if( emoji) {
+ if( emoji && !autocomplete) {
EmojiManager.getInstance().replaceWithImages(getContext(), getText(), emojiSize, defaultEmojiSize);
}
}
diff --git a/app/src/main/java/app/fedilab/android/interfaces/OnPlaylistActionInterface.java b/app/src/main/java/app/fedilab/android/interfaces/OnPlaylistActionInterface.java
new file mode 100644
index 000000000..4a2843d30
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/interfaces/OnPlaylistActionInterface.java
@@ -0,0 +1,27 @@
+/* Copyright 2019 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 . */
+package app.fedilab.android.interfaces;
+
+
+import app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask;
+import app.fedilab.android.client.APIResponse;
+
+/**
+ * Created by Thomas on 26/05/2019
+ * Interface when actions have been done with playlists
+ */
+public interface OnPlaylistActionInterface {
+ void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode);
+}
diff --git a/app/src/main/java/app/fedilab/android/sqlite/CustomEmojiDAO.java b/app/src/main/java/app/fedilab/android/sqlite/CustomEmojiDAO.java
index e86010ddd..b7f106c0d 100644
--- a/app/src/main/java/app/fedilab/android/sqlite/CustomEmojiDAO.java
+++ b/app/src/main/java/app/fedilab/android/sqlite/CustomEmojiDAO.java
@@ -117,6 +117,18 @@ public class CustomEmojiDAO {
}
}
+ /**
+ * Returns all emojis in db for an instance
+ * @return emojis List
+ */
+ public List getAllEmojis(String instance){
+ try {
+ Cursor c = db.query(Sqlite.TABLE_CUSTOM_EMOJI, null, Sqlite.COL_INSTANCE + " = '" + instance+ "'", null, Sqlite.COL_SHORTCODE , null, Sqlite.COL_SHORTCODE + " ASC", null);
+ return cursorToListEmojis(c);
+ } catch (Exception e) {
+ return null;
+ }
+ }
/**
* Returns an emoji by its shortcode in db
diff --git a/app/src/main/res/drawable/ic_history_black.xml b/app/src/main/res/drawable/ic_history_black.xml
new file mode 100644
index 000000000..a61de1bc9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_history_black.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_list_peertube.xml b/app/src/main/res/drawable/ic_list_peertube.xml
new file mode 100644
index 000000000..4c2fb8834
--- /dev/null
+++ b/app/src/main/res/drawable/ic_list_peertube.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_list_peertube_activity.xml b/app/src/main/res/drawable/ic_list_peertube_activity.xml
new file mode 100644
index 000000000..fd7111d5d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_list_peertube_activity.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_peertube.xml b/app/src/main/res/layout/activity_peertube.xml
index 243e94311..6eed34f91 100644
--- a/app/src/main/res/layout/activity_peertube.xml
+++ b/app/src/main/res/layout/activity_peertube.xml
@@ -58,7 +58,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/add_playlist.xml b/app/src/main/res/layout/add_playlist.xml
new file mode 100644
index 000000000..bebcece80
--- /dev/null
+++ b/app/src/main/res/layout/add_playlist.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_playlists.xml b/app/src/main/res/layout/fragment_playlists.xml
new file mode 100644
index 000000000..0a063b4bb
--- /dev/null
+++ b/app/src/main/res/layout/fragment_playlists.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index 12f32670a..de6f5baf4 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -63,13 +63,18 @@
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 8a94c3bb1..84ef9e493 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -860,7 +860,17 @@
عرض الخيوط الزمنية
Mark bot accounts in toots
إدارة الوسوم
- Remember the position in Home timeline
+ تذكّر وضعيتي على الخيط الرئيسي
+ التاريخ
+ قوائم التشغيل
+ الاسم العلني
+ الخصوصية
+ إنشاء
+ ليس لديك أي قوائم تشغيل. انقر على أيقونة \"+\" لإضافة قائمة تشغيل جديدة
+ يجب عليك إدخال اسم علني!
+ The channel is required when the playlist is public.
+ إنشاء قائمة تشغيل
+ قائمة التشغيل هذه فارغة حاليا.
- لا صوت
- صوت واحد
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 396bf4f2f..536c57452 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -690,7 +690,7 @@ Quan s\'esborra l\'aplicació s\'eliminen les dades immediatament.\n
Qualsevol d\'aquestes paraules (separades-per-espais)
Totes aquestes paraules (separades-per-espais)
Cap d\'aquestes paraules (separades per espais)
- Add some words to filter (space-separated)
+ Afegir paraules al filtre (separades per espais)
Canvi de nom de columna
No hi ha instàncies de Misskey
Instància Misskey
@@ -835,9 +835,19 @@ Quan s\'esborra l\'aplicació s\'eliminen les dades immediatament.\n
Tornar borrosos els mèdia sensibles
Mostra pissarres com a llista
Mostra pissarres
- Mark bot accounts in toots
- Manage tags
- Remember the position in Home timeline
+ Marcar els brams fets per bots
+ Gestionar etiquetes
+ Recordar la posició en la pissarra principal
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vot
- %d vots
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 657fc840c..5f881ea72 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -851,6 +851,16 @@ Uživatelské jméno a heslo nejsou nikdy ukládány. Jsou použity pouze během
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d hlas
- %d hlasy
diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml
index 3cb5f202d..a4ebc860a 100644
--- a/app/src/main/res/values-cy/strings.xml
+++ b/app/src/main/res/values-cy/strings.xml
@@ -861,6 +861,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d votes
- %d vote
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index f694223f0..db76624be 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1c1714c30..cb3a5e416 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -689,7 +689,7 @@ Sobald Sie die ersten Buchstaben eintippen, werden Namensvorschläge angezeigt\n
Eines dieser Wörter (durch Leerzeichen getrennt)
Alle diese Wörter (durch Leerzeichen getrennt)
Keines dieser Wörter (durch Leerzeichen getrennt)
- Fügen Sie einige Wörter zum Filter hinzu (durch Leerzeichen getrennt)
+ Wörter zum Filter hinzufügen (durch Leerzeichen getrennt)
Spaltenname ändern
Keine Misskey-Instanzen
Misskey Instanz
@@ -826,17 +826,27 @@ Sobald Sie die ersten Buchstaben eintippen, werden Namensvorschläge angezeigt\n
Hauptzeitleisten können nur ausgeblendet werden!
BBCode
Zeitleiste hinzufügen
- Medien immer als vertraulich kennzeichnen
+ Medien immer als sensibel kennzeichnen
GNU-Instanz
Gespeicherter Status
- Schlagwörter in Antworten weiterleiten
+ Schlagwörter in Antworten übernehmen
Lange Drücken, um Medien zu speichern
- Blur sensitive media
+ Sensible Medien unscharf darstellen
Zeitleisten in einer Liste anzeigen
Zeitleisten anzeigen
- Bot-Konten als Toots kennzeichnen
+ Toots von Bot-Konten kennzeichnen
Schlagwörter verwalten
- Remember the position in Home timeline
+ Position der Startseiten-Zeitleiste merken
+ Verlauf
+ Wiedergabelisten
+ Anzeigenamen
+ Datenschutz
+ Erstellen
+ Sie haben noch keine Wiedergabelisten. Klicken Sie auf das Symbol „➕”, um eine neue Wiedergabeliste hinzuzufügen.
+ Sie müssen einen Anzeigenamen angeben!
+ Der Kanal wird benötigt, wenn es sich um eine öffentliche Wiedergabeliste handelt.
+ Wiedergabeliste erstellen
+ Diese Wiedergabeliste ist leer.
- %d Stimme
- %d Stimmen
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 04ec54bf3..bbc55014d 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d ψήφοι
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index afa20dfbb..6941652b2 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -840,6 +840,16 @@ https://yandex.ru/legal/confidential/?lang=en
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 4e081389f..01deee0eb 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -845,6 +845,16 @@
Markatu bot kontuak toot-etan
Kudeatu etiketak
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- boto %d
- %d boto
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index cd0802c95..2f99dcf36 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 0f9af7455..bcae817da 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 94e617324..b6e1effab 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -843,6 +843,16 @@ Le bouton de connexion s’activera une fois qu’un domaine valide sera renseig
Marquer les comptes bot dans les pouets
Gestion des étiquettes
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Confidentialité
+ Créer
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d voix
- %d voix
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 70acb429a..d4f134d91 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -699,7 +699,7 @@
Calquera de estas palabras (separadas por espazos)
Todas estas palabras (separadas por espazos)
Ningunha de estas palabras (separadas por espazos)
- Add some words to filter (space-separated)
+ Engade algunha palabra para filtrar (separadas por espazos)
Cambiar o nome da columna
Sen instancias Misskey
Instancia Misskey
@@ -844,9 +844,19 @@
Difuminar medios sensibles
Mostrar liñas temporais nunha lista
Mostrar liñas temporais
- Mark bot accounts in toots
- Manage tags
- Remember the position in Home timeline
+ Marcar contas de bots nos toots
+ Xestionar etiquetas
+ Lembrar posición da liña temporal de Inicio
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d voto
- %d votos
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 72373d031..dccfc350d 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -854,6 +854,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 2842d158e..67d6fb182 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -836,6 +836,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index e889d5a2f..19454fa26 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -844,6 +844,16 @@ A Yandexnek megvan a saját adatvédelmi szabályzata, ami itt található: http
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d szavazat
- %d szavazat
diff --git a/app/src/main/res/values-hy/strings.xml b/app/src/main/res/values-hy/strings.xml
index d56b10fb1..4d204f123 100644
--- a/app/src/main/res/values-hy/strings.xml
+++ b/app/src/main/res/values-hy/strings.xml
@@ -845,6 +845,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 04338338f..bf8469f9c 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -844,6 +844,16 @@ https://yandex.ru/legal/confidential/?lang=en
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d votes
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index eb9170262..c6828872d 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -698,7 +698,7 @@
Qualsiasi tra queste parole (separate da spazi)
Tutte queste parole (separate da spazi)
Nessuna di queste parole (separate da spazi)
- Add some words to filter (space-separated)
+ Aggiungi alcune parole da filtrare (separate da spazio)
Rinomina colonna
Nessuna istanza Misskey
Istanza Misskey
@@ -843,9 +843,19 @@
Sfoca media con contenuti sensibili
Mostra le timeline in un elenco
Mostra timeline
- Mark bot accounts in toots
- Manage tags
- Remember the position in Home timeline
+ Segna account bot nei toot
+ Gestisci etichette
+ Ricorda la posizione nella timeline principale
+ Cronologia
+ Playlist
+ Nome visualizzato
+ Privacy
+ Crea
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d voto
- %d voti
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 06da80e6c..fb8099f6d 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -834,6 +834,16 @@
トゥート内でbotアカウントをマークする
タグ管理
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d票
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index 970d32c1a..adc3610fd 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 81beea55d..d1e83df2f 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -842,6 +842,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d votes
diff --git a/app/src/main/res/values-lmo/strings.xml b/app/src/main/res/values-lmo/strings.xml
index 0f9af7455..bcae817da 100644
--- a/app/src/main/res/values-lmo/strings.xml
+++ b/app/src/main/res/values-lmo/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 0f9af7455..bcae817da 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index aad51968b..8ed635dc1 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -843,6 +843,16 @@ Je kunt beginnen met typen en er zullen namen gesuggereerd worden.\n\n
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d stem
- %d stemmen
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index 566d3a97b..60bdd04ff 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -835,6 +835,16 @@ Adresser vil bli foreslått når du begynner å skrive.\n\n
Marker robot-kontoer i toots
Administrer stikkord
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d stemme
- %d stemmer
diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml
index 0f9af7455..bcae817da 100644
--- a/app/src/main/res/values-oc/strings.xml
+++ b/app/src/main/res/values-oc/strings.xml
@@ -846,6 +846,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 96a717d40..bcf6c67ba 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -849,6 +849,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index c9e37843f..8e760f849 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -46,7 +46,7 @@
Excluir tudo
Traduzir este toot.
Agendar
- Tamanho dos textos e dos ícones
+ Tamanho dos textos e ícones
Mudar o tamanho atual dos textos:
Mudar o tamanho atual dos ícones:
Próximo
@@ -311,7 +311,7 @@
Notificar?
Silenciar notificações
Modo noturno
- Segundos para expirar a visualização de NSFW, 0 para desativar.
+ Segundos para expirar a prévia de mídia sensível, 0 para desativar.
Editar perfil
Compartilhamento externo personalizado
Seu link de compartilhamento externo…
@@ -332,7 +332,7 @@
Usar navegador interno
Abas personalizadas
Ativar Javascript
- Expandir ac automaticamente
+ Expandir AC automaticamente
Permitir cookies de terceiros
Modelo das timelines:
@@ -563,7 +563,7 @@
Adicionando contas à lista
Sem listas. Você pode criar uma tocando no botão \"+\".
Sem instâncias. Você pode seguir uma tocando no botão \"+\".
- Quem seguir
+ Quem seguir:
API Trunk
Desculpe, é impossível seguir
Carregando conta remota!
@@ -638,7 +638,7 @@
Mostrar timeline de Arte
Agendar boost
Boost agendado!
- Sem boost agendado!
+ Sem boosts agendados!
Agendar boost.]]>
Timeline de Arte
Abrir menu
@@ -683,7 +683,7 @@
Idiomas
Apenas mídia
- Mostrar NSFW
+ Mostrar mídia sensível
Traduções no Crowdin
Administrador do Crowdin
Tradução do aplicativo
@@ -698,7 +698,7 @@
Qualquer uma destas palavras (separadas por espaço)
Todas estas palavras (separadas por espaço)
Nenhuma destas palavras (separadas por espaço)
- Add some words to filter (space-separated)
+ Adicione palavras para filtrar (separadas por espaço)
Mudar nome da aba
Sem instâncias Misskey
Instância Misskey
@@ -733,7 +733,7 @@
Excluir vídeo
Tem certeza de que deseja excluir este vídeo?
Sem vídeos enviados!
- Mostrar vídeos NSFW
+ Mostrar vídeos sensíveis
Canal %s por padrão
Sem vídeos!
Favoritar mídia
@@ -839,13 +839,23 @@
Instância GNU
Toots em cache
Encaminhar tags nas respostas
- Long press to store media
- Blur sensitive media
- Display timelines in a list
- Display timelines
- Mark bot accounts in toots
- Manage tags
- Remember the position in Home timeline
+ Toque longo para salvar mídia
+ Blur na mídia sensível
+ Mostrar timelines em uma lista
+ Mostrar timelines
+ Marcar robôs em toots
+ Gerenciar tags
+ Lembrar a posição na página inicial
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d voto
- %d votos
@@ -865,6 +875,6 @@
- Webview
- - Stream direto
+ - Transmissão direta
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index ea8bf47bb..1c1458c3e 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -848,6 +848,16 @@ Aceste date sunt strict confidențiale și pot fi folosite doar de aplicație.
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index b72f5a188..3bc736c36 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -853,7 +853,17 @@
Отображать ленты
Отметить аккаунты ботов в тутах
Управлять тегами
- Remember the position in Home timeline
+ Запоминать положение в домашней ленте
+ История
+ Плейлисты
+ Отображаемое имя
+ Конфиденциальность
+ Создать
+ У вас нет плейлистов. Нажмите на \"+\", чтобы добавить новый
+ Вы должны указать отображаемое имя!
+ Этот канал необходим, когда плейлист общедоступен.
+ Создать плейлист
+ В этом плейлисте пока ничего нет.
- %d голос
- %d голоса
diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml
index d0160380b..98230b98d 100644
--- a/app/src/main/res/values-si/strings.xml
+++ b/app/src/main/res/values-si/strings.xml
@@ -845,6 +845,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index cf46547a2..78d81f309 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -854,6 +854,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 388cff8c2..e4123ac70 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -850,6 +850,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 1e85aa505..95e6fd074 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -845,7 +845,17 @@
Visa tidslinjer
Markera robotkonton i toot
Hantera taggar
- Remember the position in Home timeline
+ Kom ihåg positionen i hemtidslinjen
+ Historik
+ Spellistor
+ Visningsnamn
+ Sekretess
+ Skapa
+ Du har inte några spellistor. Klicka på \"+\"-ikonen för att lägga till en ny spellista
+ Du måste ange ett visningsnamn!
+ Kanalen krävs när spellistan är offentliga.
+ Skapa spellista
+ Det finns ingenting i denna spellista ännu.
- %d röst
- %d röster
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 6e2a97d56..281cec689 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -840,6 +840,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index e80bf8c05..28a78923f 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -844,6 +844,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
- %d votes
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 4e04e642c..462485f1f 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -840,6 +840,16 @@ và %d toots khác để khám phá
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d votes
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index b73c27249..80eb31731 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -842,6 +842,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d 票
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index dc9214f83..e267c948a 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -694,7 +694,7 @@ Yandex 有適當的隱私權政策,可以在這裡找到:https://yandex.ru/l
這些字詞的其中一個(以空格分開)
這些字詞全部(以空格分開)
這些字詞都不要(以空格分開)
- Add some words to filter (space-separated)
+ 新增要過濾的字詞(以空格分隔)
變更欄位名稱
沒有 Misskey 站台
Misskey 站台
@@ -839,9 +839,19 @@ Yandex 有適當的隱私權政策,可以在這裡找到:https://yandex.ru/l
將敏感內容模糊化
以清單顯示時間軸
顯示時間軸
- Mark bot accounts in toots
- Manage tags
- Remember the position in Home timeline
+ 在嘟文中標記機器人帳號
+ 管理標籤
+ 記住家時間軸的位置
+ 歷史紀錄
+ 播放清單
+ 顯示名稱
+ 隱私
+ 建立
+ 您沒有任何播放清單。在「+」圖示上按一下以新增播放清單
+ 您必須提供顯示名稱!
+ 當播放清單公開時,頻道為必填。
+ 建立播放清單
+ 目前播放清單內還沒有東西。
- %d 人投票
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ff27585dd..3df62c503 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -950,6 +950,16 @@
Mark bot accounts in toots
Manage tags
Remember the position in Home timeline
+ History
+ Playlists
+ Display name
+ Privacy
+ Create
+ You don\'t have any playlists. Click on the \"+\" icon to add a new playlist
+ You must provide a display name!
+ The channel is required when the playlist is public.
+ Create a playlist
+ There is nothing in this playlist yet.
- %d vote
diff --git a/fastlane/metadata/android/en-US/changelogs/274.txt b/fastlane/metadata/android/en-US/changelogs/274.txt
new file mode 100644
index 000000000..65ff04d9a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/274.txt
@@ -0,0 +1,13 @@
+Added
+- Peertube playlists
+- Video history timeline for Peertube
+- URLs shortened in toots
+
+Changed
+- Emoji count one char
+- URL length is 23 max in counter
+- Improve split feature
+
+Fixed
+- App name not clickable in toots
+- Custom emoji with cross-account actions
\ No newline at end of file