Start playlist support

This commit is contained in:
tom79 2019-05-26 15:56:02 +02:00
parent a6586e38c8
commit 00c8c4fa88
13 changed files with 1116 additions and 43 deletions

View File

@ -1675,6 +1675,12 @@ public abstract class BaseMainActivity extends BaseActivity
fragmentTag = "LISTS";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, displayListsFragment, fragmentTag).commit();
}else if(id == R.id.nav_peertube_playlists){
toot.hide();
DisplayListsFragment displayListsFragment = new DisplayListsFragment();
fragmentTag = "PLAYLISTS";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, displayListsFragment, fragmentTag).commit();
}else if(id == R.id.nav_filters){
toot.hide();
DisplayFiltersFragment displayFiltersFragment = new DisplayFiltersFragment();

View File

@ -0,0 +1,314 @@
/* 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 <http://www.gnu.org/licenses>. */
package app.fedilab.android.activities;
import android.annotation.SuppressLint;
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.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.InputFilter;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.asynctasks.ManageListsAsyncTask;
import app.fedilab.android.asynctasks.RetrieveFeedsAsyncTask;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.client.Entities.Peertube;
import app.fedilab.android.client.Entities.Status;
import app.fedilab.android.drawers.PeertubeAdapter;
import app.fedilab.android.drawers.StatusListAdapter;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.interfaces.OnListActionInterface;
import es.dmoral.toasty.Toasty;
/**
* Created by Thomas on 26/05/2019.
* Display playlists for Peertube
*/
public class PlaylistsActivity extends BaseActivity implements OnListActionInterface {
private String title, listId;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private SwipeRefreshLayout swipeRefreshLayout;
private boolean swiped;
private List<Peertube> peertubes;
private String max_id;
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);
}
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){
title = b.getString("title");
listId = b.getString("id");
}else{
Toasty.error(this,getString(R.string.toast_error_search),Toast.LENGTH_LONG).show();
}
if( getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setTitle(title);
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 ManageListsAsyncTask(PlaylistsActivity.this,listId, max_id ,null, 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 ManageListsAsyncTask(PlaylistsActivity.this,listId, 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 ManageListsAsyncTask(PlaylistsActivity.this,listId, null ,null, PlaylistsActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_list, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_add_user:
Intent intent = new Intent(PlaylistsActivity.this, ManageAccountsInListActivity.class);
intent.putExtra("title", title);
intent.putExtra("id", listId);
startActivity(intent);
return true;
case R.id.action_edit_list:
int style;
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
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(PlaylistsActivity.this, style);
LayoutInflater inflater = getLayoutInflater();
@SuppressLint("InflateParams") View dialogView = inflater.inflate(R.layout.add_list, null);
dialogBuilder.setView(dialogView);
final EditText editText = dialogView.findViewById(R.id.add_list);
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
editText.setText(title);
dialogBuilder.setPositiveButton(R.string.validate, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
if( editText.getText() != null && editText.getText().toString().trim().length() > 0 )
new ManageListsAsyncTask(PlaylistsActivity.this, ManageListsAsyncTask.action.UPDATE_LIST, null, listId, editText.getText().toString(), editText.getText().toString().trim(), PlaylistsActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
dialog.dismiss();
}
});
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_lists_create));
alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialogInterface) {
//Hide keyboard
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
}
});
if( alertDialog.getWindow() != null )
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
alertDialog.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActionDone(ManageListsAsyncTask.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 == ManageListsAsyncTask.action.GET_LIST_TIMELINE) {
int previousPosition = this.lv_playlist.size();
List<Status> statuses = apiResponse.getStatuses();
max_id = apiResponse.getMax_id();
flag_loading = (max_id == null);
if (!swiped && firstLoad && (statuses == null || statuses.size() == 0))
textviewNoAction.setVisibility(View.VISIBLE);
else
textviewNoAction.setVisibility(View.GONE);
if (swiped) {
if (previousPosition > 0) {
for (int i = 0; i < previousPosition; i++) {
this.statuses.remove(0);
}
statusListAdapter.notifyItemRangeRemoved(0, previousPosition);
}
swiped = false;
}
if (statuses != null && statuses.size() > 0) {
this.statuses.addAll(statuses);
statusListAdapter.notifyItemRangeInserted(previousPosition, statuses.size());
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
}else if(actionType == ManageListsAsyncTask.action.UPDATE_LIST) {
}
}
}

View File

@ -0,0 +1,105 @@
/* 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 <http://www.gnu.org/licenses>. */
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 app.fedilab.android.client.API;
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<Void, Void, Void> {
public enum action{
GET_PLAYLIST,
GET_LIST_TIMELINE,
GET_LIST_ACCOUNT,
CREATE_PLAYLIST,
DELETE_LIST,
UPDATE_LIST,
ADD_USERS,
DELETE_USERS,
SEARCH_USER
}
private OnPlaylistActionInterface listener;
private APIResponse apiResponse;
private int statusCode;
private action apiAction;
private WeakReference<Context> contextReference;
private String max_id, since_id;
public ManagePlaylistsAsyncTask(Context context, action apiAction, Playlist playlist, String max_id, String since_id, OnPlaylistActionInterface onPlaylistActionInterface){
contextReference = new WeakReference<>(context);
this.listener = onPlaylistActionInterface;
this.apiAction = apiAction;
this.max_id = max_id;
this.since_id = since_id;
}
@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_TIMELINE){
apiResponse = new API(contextReference.get()).getListTimeline(this.listId, this.max_id, this.since_id, this.limit);
}else if(apiAction == action.GET_LIST_ACCOUNT){
apiResponse = new API(contextReference.get()).getAccountsInList(this.listId,0);
}else if( apiAction == action.CREATE_PLAYLIST){
apiResponse = new API(contextReference.get()).createPlaylist(this.title);
}else if(apiAction == action.DELETE_LIST){
statusCode = new API(contextReference.get()).deleteList(this.listId);
}else if(apiAction == action.UPDATE_LIST){
apiResponse = new API(contextReference.get()).updateList(this.listId, this.title);
}else if(apiAction == action.ADD_USERS){
apiResponse = new API(contextReference.get()).addAccountToList(this.listId, this.accountsId);
}else if(apiAction == action.DELETE_USERS){
statusCode = new API(contextReference.get()).deleteAccountFromList(this.listId, this.accountsId);
}else if( apiAction == action.SEARCH_USER){
apiResponse = new API(contextReference.get()).searchAccounts(this.search, 20, true);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onActionDone(this.apiAction, apiResponse, statusCode);
}
}

View File

@ -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<Peertube> peertubes = null;
private List<PeertubeNotification> peertubeNotifications = null;
private List<Filters> filters = null;
private List<Playlist> playlists = null;
private List<String> domains = null;
private List<app.fedilab.android.client.Entities.List> lists = null;
private List<Emojis> emojis = null;
@ -219,4 +221,12 @@ public class APIResponse {
public void setFetchmore(boolean fetchmore) {
this.fetchmore = fetchmore;
}
public List<Playlist> getPlaylists() {
return playlists;
}
public void setPlaylists(List<Playlist> playlists) {
this.playlists = playlists;
}
}

View File

@ -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 <http://www.gnu.org/licenses>. */
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<Integer, String> privacy;
private String thumbnailPath;
private HashMap<Integer, String> 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<Integer, String> getPrivacy() {
return privacy;
}
public void setPrivacy(HashMap<Integer, String> privacy) {
this.privacy = privacy;
}
public String getThumbnailPath() {
return thumbnailPath;
}
public void setThumbnailPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public HashMap<Integer, String> getType() {
return type;
}
public void setType(HashMap<Integer, String> 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<Integer, String>) in.readSerializable();
this.thumbnailPath = in.readString();
this.type = (HashMap<Integer, String>) in.readSerializable();
long tmpUpdatedAt = in.readLong();
this.updatedAt = tmpUpdatedAt == -1 ? null : new Date(tmpUpdatedAt);
this.videosLength = in.readInt();
}
public static final Parcelable.Creator<Playlist> CREATOR = new Parcelable.Creator<Playlist>() {
@Override
public Playlist createFromParcel(Parcel source) {
return new Playlist(source);
}
@Override
public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
}

View File

@ -55,6 +55,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;
@ -1489,12 +1490,12 @@ public class PeertubeAPI {
* Get lists for the user
* @return APIResponse
*/
public APIResponse getLists(){
public APIResponse getPlayists(String username){
List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
List<Playlist> playlists = new ArrayList<>();
try {
String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/lists"), 60, null, prefKeyOauthTokenT);
lists = parseLists(new JSONArray(response));
String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/accounts/%s/video-playlists", username)), 60, null, prefKeyOauthTokenT);
playlists = parsePlaylists(context, new JSONArray(response));
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
@ -1506,37 +1507,11 @@ public class PeertubeAPI {
} catch (JSONException e) {
e.printStackTrace();
}
apiResponse.setLists(lists);
apiResponse.setPlaylists(playlists);
return apiResponse;
}
/**
* Get lists for a user by its id
* @return APIResponse
*/
@SuppressWarnings("unused")
public APIResponse getLists(String userId){
List<app.fedilab.android.client.Entities.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
@ -2062,24 +2037,24 @@ public class PeertubeAPI {
/**
* Parse Lists
* Parse Playlists
* @param jsonArray JSONArray
* @return List<List> of lists
* @return List<Playlist> of lists
*/
private List<app.fedilab.android.client.Entities.List> parseLists(JSONArray jsonArray){
List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
private List<Playlist> parsePlaylists(Context context, JSONArray jsonArray){
List<Playlist> 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;
}
@ -2088,13 +2063,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());
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<Integer, String> type = new LinkedHashMap<>();
LinkedHashMap<Integer, String> 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){}
try{
playlist.setUpdatedAt(Helper.stringToDate(context, resobj.getString("updatedAt")));
}catch (Exception ignored){}
}catch (Exception ignored){}
return list;
return playlist;
}
private List<Account> parseAccountResponsePeertube(Context context, String instance, JSONArray jsonArray){

View File

@ -0,0 +1,211 @@
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 <http://www.gnu.org/licenses>. */
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.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.EditText;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.activities.ListActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.asynctasks.ManageListsAsyncTask;
import app.fedilab.android.asynctasks.ManagePlaylistsAsyncTask;
import app.fedilab.android.client.APIResponse;
import app.fedilab.android.drawers.ListAdapter;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.interfaces.OnListActionInterface;
import app.fedilab.android.interfaces.OnPlaylistActionInterface;
import es.dmoral.toasty.Toasty;
/**
* Created by Thomas on 26/05/2019.
* Fragment to display Playlists
*/
public class DisplayPlaylistsFragment extends Fragment implements OnPlaylistActionInterface {
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private List<app.fedilab.android.client.Entities.List> lists;
private RelativeLayout mainLoader;
private FloatingActionButton add_new;
private ListAdapter listAdapter;
private RelativeLayout textviewNoAction;
@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();
lists = 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);
lists = new ArrayList<>();
listAdapter = new ListAdapter(context, lists, textviewNoAction);
lv_playlist.setAdapter(listAdapter);
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){}
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_list, null);
dialogBuilder.setView(dialogView);
final EditText editText = dialogView.findViewById(R.id.add_list);
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
dialogBuilder.setPositiveButton(R.string.validate, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
if( editText.getText() != null && editText.getText().toString().trim().length() > 0 )
new ManagePlaylistsAsyncTask(context, ManagePlaylistsAsyncTask.action.CREATE_PLAYLIST, null, null, null, editText.getText().toString().trim(), DisplayPlaylistsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
dialog.dismiss();
add_new.setEnabled(false);
}
});
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_lists_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(editText.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.getLists() != null && apiResponse.getLists().size() > 0) {
this.lists.addAll(apiResponse.getLists());
listAdapter.notifyDataSetChanged();
textviewNoAction.setVisibility(View.GONE);
} else {
textviewNoAction.setVisibility(View.VISIBLE);
}
}else if( actionType == ManagePlaylistsAsyncTask.action.CREATE_PLAYLIST){
if (apiResponse.getLists() != null && apiResponse.getLists().size() > 0) {
String listId = apiResponse.getLists().get(0).getId();
String title = apiResponse.getLists().get(0).getTitle();
Intent intent = new Intent(context, ListActivity.class);
Bundle b = new Bundle();
b.putString("id", listId);
b.putString("title", title);
intent.putExtras(b);
context.startActivity(intent);
this.lists.add(0, apiResponse.getLists().get(0));
listAdapter.notifyDataSetChanged();
textviewNoAction.setVisibility(View.GONE);
}else{
Toasty.error(context, apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
}
}else if( actionType == ManagePlaylistsAsyncTask.action.DELETE_LIST){
if( this.lists.size() == 0)
textviewNoAction.setVisibility(View.VISIBLE);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses>. */
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);
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z"/>
</vector>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="app.fedilab.android.activities.PlaylistsActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="?attr/popupOverlay"/>
</android.support.design.widget.AppBarLayout>
<RelativeLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Listview status -->
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:id="@+id/swipeContainer"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/lv_playlist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
/>
</android.support.v4.widget.SwipeRefreshLayout>
<RelativeLayout
android:id="@+id/no_action"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:padding="10dp"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"
android:textStyle="italic|bold"
android:typeface="serif"
android:text="@string/action_lists_empty_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout
android:id="@+id/loader"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<!-- Loader for next status -->
<RelativeLayout
android:id="@+id/loading_next_status"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center_horizontal"
android:gravity="bottom|center_horizontal"
android:layout_height="20dp">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Listview list -->
<ListView
android:id="@+id/lv_playlist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"
/>
<RelativeLayout
android:id="@+id/no_action"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/no_action_text"
android:padding="10dp"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"
android:textStyle="italic|bold"
android:typeface="serif"
android:text="@string/action_list_add"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout
android:id="@+id/loader"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<!-- Loader for next items -->
<RelativeLayout
android:id="@+id/loading_next_items"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center_horizontal"
android:gravity="bottom|center_horizontal"
android:layout_height="20dp">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

View File

@ -74,6 +74,10 @@
android:id="@+id/nav_peertube_history"
android:icon="@drawable/ic_history_black"
android:title="@string/history" />
<item
android:id="@+id/nav_peertube_playlists"
android:icon="@drawable/ic_list_peertube"
android:title="@string/playlists" />
<item
android:id="@+id/nav_upload"
android:icon="@drawable/ic_cloud_upload"

View File

@ -951,6 +951,7 @@
<string name="add_tags">Manage tags</string>
<string name="set_remember_position">Remember the position in Home timeline</string>
<string name="history">History</string>
<string name="playlists">Playlists</string>
<plurals name="number_of_vote">
<item quantity="one">%d vote</item>