diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java new file mode 100644 index 0000000..9866fd6 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java @@ -0,0 +1,834 @@ + +package app.fedilab.fedilabtube; + + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +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.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.PopupMenu; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.exoplayer2.ExoPlayerFactory; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import com.google.android.exoplayer2.ui.PlayerControlView; +import com.google.android.exoplayer2.ui.PlayerView; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; +import com.google.android.exoplayer2.util.Util; + +import org.jetbrains.annotations.NotNull; + +import java.lang.ref.WeakReference; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.net.ssl.HttpsURLConnection; + + +import app.fedilab.fedilabtube.asynctasks.ManagePlaylistsAsyncTask; +import app.fedilab.fedilabtube.asynctasks.PostActionAsyncTask; +import app.fedilab.fedilabtube.asynctasks.RetrievePeertubeSingleCommentsAsyncTask; +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.TLSSocketFactory; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.client.entities.Playlist; +import app.fedilab.fedilabtube.helper.FullScreenMediaController; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnPlaylistActionInterface; +import app.fedilab.fedilabtube.interfaces.OnPostActionInterface; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeInterface; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.PeertubeFavoritesDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; +import app.fedilab.fedilabtube.webview.CustomWebview; +import app.fedilab.fedilabtube.webview.MastalabWebChromeClient; +import app.fedilab.fedilabtube.webview.MastalabWebViewClient; +import es.dmoral.toasty.Toasty; + +import static app.fedilab.fedilabtube.asynctasks.ManagePlaylistsAsyncTask.action.GET_PLAYLIST_FOR_VIDEO; + + +public class PeertubeActivity extends AppCompatActivity implements OnRetrievePeertubeInterface, OnPostActionInterface, OnPlaylistActionInterface { + + public static String video_id; + private String peertubeInstance, videoId; + private FullScreenMediaController.fullscreen fullscreen; + private RelativeLayout loader; + private TextView peertube_view_count, peertube_playlist, peertube_bookmark, peertube_like_count, peertube_dislike_count, peertube_share, peertube_download, peertube_description, peertube_title; + private ScrollView peertube_information_container; + private Peertube peertube; + private TextView toolbar_title; + private PlayerView playerView; + private SimpleExoPlayer player; + private boolean fullScreenMode; + private Dialog fullScreenDialog; + private AppCompatImageView fullScreenIcon; + private TextView resolution; + private int mode; + private LinearLayout write_comment_container; + private ImageView send; + private TextView add_comment_read; + private EditText add_comment_write; + private String instance; + private List playlistForVideo; + private List playlists; + + public static void hideKeyboard(Activity activity) { + if (activity != null && activity.getWindow() != null) { + activity.getWindow().getDecorView(); + InputMethodManager imm = (InputMethodManager) activity.getSystemService(INPUT_METHOD_SERVICE); + assert imm != null; + imm.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + fullscreen = FullScreenMediaController.fullscreen.OFF; + fullScreenMode = false; + playlistForVideo = new ArrayList<>(); + setContentView(R.layout.activity_peertube); + loader = findViewById(R.id.loader); + peertube_view_count = findViewById(R.id.peertube_view_count); + peertube_bookmark = findViewById(R.id.peertube_bookmark); + peertube_like_count = findViewById(R.id.peertube_like_count); + peertube_dislike_count = findViewById(R.id.peertube_dislike_count); + peertube_share = findViewById(R.id.peertube_share); + peertube_download = findViewById(R.id.peertube_download); + peertube_description = findViewById(R.id.peertube_description); + peertube_title = findViewById(R.id.peertube_title); + peertube_information_container = findViewById(R.id.peertube_information_container); + CustomWebview webview_video = findViewById(R.id.webview_video); + playerView = findViewById(R.id.media_video); + write_comment_container = findViewById(R.id.write_comment_container); + ImageView 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(v -> { + add_comment_read.setVisibility(View.GONE); + add_comment_write.setVisibility(View.VISIBLE); + send.setVisibility(View.VISIBLE); + add_comment_write.requestFocus(); + add_comment_write.setSelection(add_comment_write.getText().length()); + + }); + + peertube_playlist.setVisibility(View.VISIBLE); + peertube_bookmark.setVisibility(View.GONE); + + send.setOnClickListener(v -> { + String comment = add_comment_write.getText().toString(); + if (comment.trim().length() > 0) { + new PostActionAsyncTask(PeertubeActivity.this, PeertubeAPI.StatusAction.PEERTUBECOMMENT, peertube.getId(), null, comment, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + add_comment_write.setText(""); + add_comment_read.setVisibility(View.VISIBLE); + add_comment_write.setVisibility(View.GONE); + send.setVisibility(View.GONE); + add_comment_read.requestFocus(); + } + }); + + 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(PeertubeActivity.this)); + Account account = new AccountDAO(PeertubeActivity.this, db).getUniqAccount(userId, instance); + Helper.loadGiF(PeertubeActivity.this, account, my_pp); + Bundle b = getIntent().getExtras(); + if (b != null) { + peertubeInstance = b.getString("peertube_instance", null); + videoId = b.getString("video_id", null); + } + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE); + assert inflater != null; + View view = inflater.inflate(R.layout.simple_bar, new LinearLayout(PeertubeActivity.this), false); + view.setBackground(new ColorDrawable(ContextCompat.getColor(PeertubeActivity.this, R.color.cyanea_primary))); + 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); + toolbar_title = actionBar.getCustomView().findViewById(R.id.toolbar_title); + toolbar_close.setOnClickListener(v -> finish()); + } + + + mode = sharedpreferences.getInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_DIRECT); + if (mode != Helper.VIDEO_MODE_WEBVIEW && mode != Helper.VIDEO_MODE_DIRECT) + mode = Helper.VIDEO_MODE_DIRECT; + if (mode == Helper.VIDEO_MODE_WEBVIEW) { + webview_video.setVisibility(View.VISIBLE); + playerView.setVisibility(View.GONE); + + webview_video = Helper.initializeWebview(PeertubeActivity.this, R.id.webview_video, null); + FrameLayout webview_container = findViewById(R.id.main_media_frame); + final ViewGroup videoLayout = findViewById(R.id.videoLayout); + + MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, webview_container, videoLayout); + mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> { + + if (fullscreen) { + videoLayout.setVisibility(View.VISIBLE); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + getWindow().setAttributes(attrs); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); + peertube_information_container.setVisibility(View.GONE); + } else { + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + getWindow().setAttributes(attrs); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + videoLayout.setVisibility(View.GONE); + peertube_information_container.setVisibility(View.VISIBLE); + } + }); + String user_agent = sharedpreferences.getString(Helper.SET_CUSTOM_USER_AGENT, Helper.USER_AGENT); + webview_video.getSettings().setUserAgentString(user_agent); + webview_video.getSettings().setAllowFileAccess(true); + webview_video.setWebChromeClient(mastalabWebChromeClient); + webview_video.getSettings().setDomStorageEnabled(true); + webview_video.getSettings().setAppCacheEnabled(true); + webview_video.getSettings().setMediaPlaybackRequiresUserGesture(false); + webview_video.setWebViewClient(new MastalabWebViewClient(PeertubeActivity.this)); + webview_video.loadUrl("https://" + peertubeInstance + "/videos/embed/" + videoId); + } else { + webview_video.setVisibility(View.GONE); + playerView.setVisibility(View.VISIBLE); + loader.setVisibility(View.VISIBLE); + } + + + if (mode != Helper.VIDEO_MODE_WEBVIEW) { + playerView.setControllerShowTimeoutMs(1000); + playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); + initFullscreenDialog(); + 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); + } + + public void change() { + if (fullscreen == FullScreenMediaController.fullscreen.ON) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + Objects.requireNonNull(getSupportActionBar()).hide(); + peertube_information_container.setVisibility(View.GONE); + } else { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); + Objects.requireNonNull(getSupportActionBar()).show(); + peertube_information_container.setVisibility(View.VISIBLE); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + View v = getCurrentFocus(); + + if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && + v instanceof EditText && + v.getId() == R.id.add_comment_write) { + int[] scrcoords = new int[2]; + v.getLocationOnScreen(scrcoords); + float x = ev.getRawX() + v.getLeft() - scrcoords[0]; + float y = ev.getRawY() + v.getTop() - scrcoords[1]; + + if (x < v.getLeft() || x > v.getRight() || y < v.getTop() || y > v.getBottom()) { + add_comment_read.setVisibility(View.VISIBLE); + add_comment_write.setVisibility(View.GONE); + send.setVisibility(View.GONE); + hideKeyboard(PeertubeActivity.this); + } + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onCreateOptionsMenu(@NotNull Menu menu) { + getMenuInflater().inflate(R.menu.main_webview, menu); + menu.findItem(R.id.action_go).setVisible(false); + menu.findItem(R.id.action_block).setVisible(false); + menu.findItem(R.id.action_comment).setVisible(true); + if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { + MenuItem item = menu.findItem(R.id.action_comment); + if (item != null) + item.setVisible(false); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.action_comment: + if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + Toasty.info(PeertubeActivity.this, getString(R.string.retrieve_remote_status), Toast.LENGTH_LONG).show(); + new commentTask(new WeakReference<>(PeertubeActivity.this), peertube).execute(); + } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { + if (!peertube.isCommentsEnabled()) { + Toasty.info(PeertubeActivity.this, getString(R.string.comment_no_allowed_peertube), Toast.LENGTH_LONG).show(); + return true; + } + 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 builderInner; + builderInner = new AlertDialog.Builder(PeertubeActivity.this, style); + builderInner.setTitle(R.string.comment); + EditText input = new EditText(PeertubeActivity.this); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + input.setLayoutParams(lp); + builderInner.setView(input); + builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); + builderInner.setPositiveButton(R.string.validate, (dialog, which) -> { + String comment = input.getText().toString(); + if (comment.trim().length() > 0) { + new PostActionAsyncTask(PeertubeActivity.this, API.StatusAction.PEERTUBECOMMENT, peertube.getId(), null, comment, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + dialog.dismiss(); + } + }); + builderInner.show(); + } + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + public FullScreenMediaController.fullscreen getFullscreen() { + return fullscreen; + } + + public void setFullscreen(FullScreenMediaController.fullscreen fullscreen) { + this.fullscreen = fullscreen; + } + + @Override + public void onRetrievePeertube(APIResponse apiResponse) { + + if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { + Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + loader.setVisibility(View.GONE); + return; + } + if (apiResponse.getPeertubes() == null || apiResponse.getPeertubes().get(0) == null || apiResponse.getPeertubes().get(0).getFileUrl(null, apiResponse.getPeertubes().get(0).isStreamService()) == null) { + Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + loader.setVisibility(View.GONE); + return; + } + + peertube = apiResponse.getPeertubes().get(0); + + new ManagePlaylistsAsyncTask(PeertubeActivity.this, GET_PLAYLIST_FOR_VIDEO, null, peertube.getId(), null, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + + + peertube_playlist.setOnClickListener(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(item1 -> { + item1.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + item1.setActionView(new View(PeertubeActivity.this)); + item1.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item1) { + return false; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item1) { + return false; + } + }); + if (playlistForVideo.contains(playlist.getId())) { + item1.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 { + item1.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); + write_comment_container.setVisibility(View.VISIBLE); + + } else { + RelativeLayout no_action = findViewById(R.id.no_action); + TextView no_action_text = findViewById(R.id.no_action_text); + no_action_text.setText(getString(R.string.comment_no_allowed_peertube)); + no_action.setVisibility(View.VISIBLE); + write_comment_container.setVisibility(View.GONE); + } + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + + toolbar_title.setText(peertube.getName()); + peertube_description.setText(peertube.getDescription()); + peertube_title.setText(peertube.getName()); + peertube_dislike_count.setText(String.valueOf(peertube.getDislike())); + peertube_like_count.setText(String.valueOf(peertube.getLike())); + peertube_view_count.setText(String.valueOf(peertube.getView())); + video_id = peertube.getId(); + + changeColor(); + initResolution(); + + peertube_like_count.setOnClickListener(v -> { + String newState = peertube.getMyRating().equals("like") ? "none" : "like"; + new PostActionAsyncTask(PeertubeActivity.this, PeertubeAPI.StatusAction.RATEVIDEO, peertube.getId(), null, newState, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + peertube.setMyRating(newState); + changeColor(); + }); + peertube_dislike_count.setOnClickListener(v -> { + String newState = peertube.getMyRating().equals("dislike") ? "none" : "dislike"; + new PostActionAsyncTask(PeertubeActivity.this, PeertubeAPI.StatusAction.RATEVIDEO, peertube.getId(), null, newState, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + peertube.setMyRating(newState); + changeColor(); + }); + try { + HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory(instance)); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + if (mode == Helper.VIDEO_MODE_DIRECT) { + + String userAgent = sharedpreferences.getString(Helper.SET_CUSTOM_USER_AGENT, Helper.USER_AGENT); + int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); + ProgressiveMediaSource videoSource; + if (video_cache == 0) { + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this, + Util.getUserAgent(PeertubeActivity.this, userAgent), null); + videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, apiResponse.getPeertubes().get(0).isStreamService()))); + } else { + CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this); + videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) + .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, apiResponse.getPeertubes().get(0).isStreamService()))); + } + + player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); + playerView.setPlayer(player); + loader.setVisibility(View.GONE); + player.prepare(videoSource); + player.setPlayWhenReady(true); + } + + + peertube_download.setOnClickListener(v -> { + if (Build.VERSION.SDK_INT >= 23) { + if (ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(PeertubeActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE); + } else { + Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null, peertube.isStreamService())); + } + } else { + Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null, peertube.isStreamService())); + } + }); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List peertubes = new PeertubeFavoritesDAO(PeertubeActivity.this, db).getSinglePeertube(peertube); + + Drawable img; + + if (peertubes == null || peertubes.size() == 0) + img = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_bookmark_peertube_border); + else + img = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_bookmark_peertube); + peertube_bookmark.setCompoundDrawablesWithIntrinsicBounds(null, img, null, null); + + peertube_bookmark.setOnClickListener(v -> { + List peertubes1 = new PeertubeFavoritesDAO(PeertubeActivity.this, db).getSinglePeertube(peertube); + if (peertubes1 == null || peertubes1.size() == 0) { + new PeertubeFavoritesDAO(PeertubeActivity.this, db).insert(peertube); + Toasty.success(PeertubeActivity.this, getString(R.string.bookmark_add_peertube), Toast.LENGTH_SHORT).show(); + } else { + new PeertubeFavoritesDAO(PeertubeActivity.this, db).remove(peertube); + Toasty.success(PeertubeActivity.this, getString(R.string.bookmark_remove_peertube), Toast.LENGTH_SHORT).show(); + } + if (peertubes1 != null && peertubes1.size() > 0) //Was initially in cache + peertube_bookmark.setCompoundDrawablesWithIntrinsicBounds(null, ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_bookmark_peertube_border), null, null); + else + peertube_bookmark.setCompoundDrawablesWithIntrinsicBounds(null, ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_bookmark_peertube), null, null); + }); + + peertube_share.setOnClickListener(v -> { + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.shared_via)); + String url; + + url = "https://" + peertube.getInstance() + "/videos/watch/" + peertube.getUuid(); + boolean share_details = sharedpreferences.getBoolean(Helper.SET_SHARE_DETAILS, true); + String extra_text; + if (share_details) { + extra_text = "@" + peertube.getAccount().getAcct(); + extra_text += "\r\n\r\n" + peertube.getName(); + extra_text += "\n\n\uD83D\uDD17 " + url + "\r\n-\n"; + final String contentToot; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + contentToot = Html.fromHtml(peertube.getDescription(), Html.FROM_HTML_MODE_LEGACY).toString(); + else + contentToot = Html.fromHtml(peertube.getDescription()).toString(); + extra_text += contentToot; + } else { + extra_text = url; + } + sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text); + sendIntent.setType("text/plain"); + startActivity(Intent.createChooser(sendIntent, getString(R.string.share_with))); + }); + } + + @Override + public void onConfigurationChanged(@NotNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + if (mode != Helper.VIDEO_MODE_WEBVIEW) { + openFullscreenDialog(); + } + setFullscreen(FullScreenMediaController.fullscreen.ON); + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + if (mode != Helper.VIDEO_MODE_WEBVIEW) { + closeFullscreenDialog(); + } + setFullscreen(FullScreenMediaController.fullscreen.OFF); + } + change(); + + } + + @Override + public void onRetrievePeertubeComments(APIResponse apiResponse) { + if (apiResponse == null || (apiResponse.getError() != null && apiResponse.getError().getStatusCode() != 404 && apiResponse.getError() != null && apiResponse.getError().getStatusCode() != 501)) { + if (apiResponse == null) + Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + else + Toasty.error(PeertubeActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); + return; + } + List statuses = apiResponse.getStatuses(); + RecyclerView lv_comments = findViewById(R.id.peertube_comments); + if (statuses == null || statuses.size() == 0) { + if (MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { + RelativeLayout no_action = findViewById(R.id.no_action); + no_action.setVisibility(View.VISIBLE); + lv_comments.setVisibility(View.GONE); + } + } else { + lv_comments.setVisibility(View.VISIBLE); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + boolean isOnWifi = Helper.isOnWIFI(PeertubeActivity.this); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + StatusDrawerParams statusDrawerParams = new StatusDrawerParams(); + statusDrawerParams.setType(RetrieveFeedsAsyncTask.Type.REMOTE_INSTANCE); + statusDrawerParams.setTargetedId(userId); + statusDrawerParams.setOnWifi(isOnWifi); + statusDrawerParams.setStatuses(statuses); + StatusListAdapter statusListAdapter = new StatusListAdapter(statusDrawerParams); + LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); + lv_comments.setLayoutManager(mLayoutManager); + lv_comments.setNestedScrollingEnabled(false); + lv_comments.setAdapter(statusListAdapter); + + } + } + + @Override + public void onRetrievePeertubeChannels(APIResponse apiResponse) { + + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (player != null) + player.release(); + } + + @Override + protected void onPause() { + super.onPause(); + if (player != null) { + player.setPlayWhenReady(false); + } + } + + @Override + public void onResume() { + super.onResume(); + if (player != null) { + player.setPlayWhenReady(true); + } + } + + public void displayResolution() { + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, 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 builderSingle = new AlertDialog.Builder(PeertubeActivity.this, style); + builderSingle.setTitle(R.string.pickup_resolution); + final ArrayAdapter arrayAdapter = new ArrayAdapter<>(PeertubeActivity.this, android.R.layout.select_dialog_item); + for (String resolution : peertube.getResolution()) + arrayAdapter.add(resolution + "p"); + builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); + builderSingle.setAdapter(arrayAdapter, (dialog, which) -> { + String res = Objects.requireNonNull(arrayAdapter.getItem(which)).substring(0, Objects.requireNonNull(arrayAdapter.getItem(which)).length() - 1); + + if (playerView != null) { + loader.setVisibility(View.VISIBLE); + long position = player.getCurrentPosition(); + PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); + resolution = controlView.findViewById(R.id.resolution); + resolution.setText(String.format("%sp", res)); + if (mode == Helper.VIDEO_MODE_DIRECT) { + if (player != null) + player.release(); + player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); + playerView.setPlayer(player); + loader.setVisibility(View.GONE); + String userAgent = sharedpreferences.getString(Helper.SET_CUSTOM_USER_AGENT, Helper.USER_AGENT); + int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); + ProgressiveMediaSource videoSource; + if (video_cache == 0) { + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this, + Util.getUserAgent(PeertubeActivity.this, userAgent), null); + videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(Uri.parse(peertube.getFileUrl(res, peertube.isStreamService()))); + } else { + CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this); + videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) + .createMediaSource(Uri.parse(peertube.getFileUrl(res, peertube.isStreamService()))); + } + player.prepare(videoSource); + player.seekTo(0, position); + player.setPlayWhenReady(true); + } + } + + }); + builderSingle.show(); + } + + @Override + public void onPostAction(int statusCode, API.StatusAction statusAction, String userId, Error error) { + + if (peertube.isCommentsEnabled() && statusAction == API.StatusAction.PEERTUBECOMMENT) + new RetrievePeertubeSingleCommentsAsyncTask(PeertubeActivity.this, peertubeInstance, videoId, PeertubeActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + } + + private void initFullscreenDialog() { + + fullScreenDialog = new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen) { + public void onBackPressed() { + if (fullScreenMode) + closeFullscreenDialog(); + super.onBackPressed(); + } + }; + } + + private void openFullscreenDialog() { + + ((ViewGroup) playerView.getParent()).removeView(playerView); + fullScreenDialog.addContentView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_fullscreen_exit)); + fullScreenMode = true; + fullScreenDialog.show(); + } + + private void closeFullscreenDialog() { + + ((ViewGroup) playerView.getParent()).removeView(playerView); + ((FrameLayout) findViewById(R.id.main_media_frame)).addView(playerView); + fullScreenMode = false; + fullScreenDialog.dismiss(); + fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_fullscreen)); + } + + private void initFullscreenButton() { + + PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); + fullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon); + View fullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button); + fullScreenButton.setOnClickListener(v -> { + if (!fullScreenMode) + openFullscreenDialog(); + else + closeFullscreenDialog(); + }); + } + + private void initResolution() { + PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); + resolution = controlView.findViewById(R.id.resolution); + resolution.setText(String.format("%sp", peertube.getResolution().get(0))); + resolution.setOnClickListener(v -> displayResolution()); + } + + private void changeColor() { + if (peertube.getMyRating() != null && peertube.getMyRating().equals("like")) { + changeDrawableColor(PeertubeActivity.this, R.drawable.ic_thumb_up_peertube, R.color.positive_thumbs); + Drawable thumbUp = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_thumb_up_peertube); + peertube_like_count.setCompoundDrawablesWithIntrinsicBounds(null, thumbUp, null, null); + } else if (peertube.getMyRating() != null && peertube.getMyRating().equals("dislike")) { + changeDrawableColor(PeertubeActivity.this, R.drawable.ic_thumb_down_peertube, R.color.negative_thumbs); + Drawable thumbDown = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_thumb_down_peertube); + 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(); + } + } + + private static class commentTask extends AsyncTask { + + private WeakReference contextReference; + private Peertube peertube; + + commentTask(WeakReference contextReference, Peertube peertube) { + this.peertube = peertube; + this.contextReference = contextReference; + } + + @Override + protected app.fedilab.android.client.Entities.Status doInBackground(Void... voids) { + + List remoteStatuses = null; + if (peertube != null) { + APIResponse search = new API(contextReference.get()).search("https://" + peertube.getAccount().getHost() + "/videos/watch/" + peertube.getUuid()); + if (search != null && search.getResults() != null) { + remoteStatuses = search.getResults().getStatuses(); + } + } + if (remoteStatuses != null && remoteStatuses.size() > 0) { + return remoteStatuses.get(0); + } else { + return null; + } + } + + @Override + protected void onPostExecute(app.fedilab.android.client.Entities.Status remoteStatuses) { + Intent intent = new Intent(contextReference.get(), TootActivity.class); + Bundle b = new Bundle(); + if (remoteStatuses == null) { + Toasty.error(contextReference.get(), contextReference.get().getString(R.string.toast_error), Toast.LENGTH_SHORT).show(); + return; + } + if (remoteStatuses.getReblog() != null) { + b.putParcelable("tootReply", remoteStatuses.getReblog()); + } else { + b.putParcelable("tootReply", remoteStatuses); + } + intent.putExtras(b); + contextReference.get().startActivity(intent); + } + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java new file mode 100644 index 0000000..b02600d --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java @@ -0,0 +1,588 @@ +package app.fedilab.fedilabtube; + + + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.ColorDrawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import app.fedilab.android.asynctasks.PostActionAsyncTask; +import app.fedilab.android.asynctasks.PostPeertubeAsyncTask; +import app.fedilab.android.asynctasks.RetrievePeertubeChannelsAsyncTask; +import app.fedilab.android.asynctasks.RetrievePeertubeSingleAsyncTask; +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.Peertube; +import app.fedilab.android.helper.Helper; +import app.fedilab.android.interfaces.OnPostActionInterface; +import app.fedilab.android.interfaces.OnRetrievePeertubeInterface; +import es.dmoral.toasty.Toasty; +import mabbas007.tagsedittext.TagsEditText; + +import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; +import static app.fedilab.android.asynctasks.RetrievePeertubeInformationAsyncTask.peertubeInformation; + +public class PeertubeEditUploadActivity extends BaseActivity implements OnRetrievePeertubeInterface, OnPostActionInterface { + + + HashMap categoryToSend; + HashMap licenseToSend; + HashMap privacyToSend; + HashMap languageToSend; + HashMap channelToSend; + private Button set_upload_submit; + private Spinner set_upload_privacy, set_upload_categories, set_upload_licenses, set_upload_languages, set_upload_channel; + private EditText p_video_title, p_video_description; + private TagsEditText p_video_tags; + private CheckBox set_upload_nsfw, set_upload_enable_comments; + private LinkedHashMap channels; + private String videoId; + private Account channel; + + @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_Fedilab); + break; + case Helper.THEME_BLACK: + setTheme(R.style.AppThemeBlack); + break; + default: + setTheme(R.style.AppThemeDark); + } + Bundle b = getIntent().getExtras(); + + if (b != null) { + videoId = b.getString("video_id", null); + } + if (videoId == null) { + videoId = sharedpreferences.getString(Helper.VIDEO_ID, null); + } + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE); + assert inflater != null; + View view = inflater.inflate(R.layout.simple_bar, new LinearLayout(PeertubeEditUploadActivity.this), false); + view.setBackground(new ColorDrawable(ContextCompat.getColor(PeertubeEditUploadActivity.this, R.color.cyanea_primary))); + 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(v -> finish()); + toolbar_title.setText(R.string.update_video); + } + setContentView(R.layout.activity_peertube_edit); + + + set_upload_submit = findViewById(R.id.set_upload_submit); + Button set_upload_delete = findViewById(R.id.set_upload_delete); + set_upload_privacy = findViewById(R.id.set_upload_privacy); + set_upload_channel = findViewById(R.id.set_upload_channel); + set_upload_categories = findViewById(R.id.set_upload_categories); + set_upload_licenses = findViewById(R.id.set_upload_licenses); + set_upload_languages = findViewById(R.id.set_upload_languages); + p_video_title = findViewById(R.id.p_video_title); + p_video_description = findViewById(R.id.p_video_description); + p_video_tags = findViewById(R.id.p_video_tags); + set_upload_nsfw = findViewById(R.id.set_upload_nsfw); + set_upload_enable_comments = findViewById(R.id.set_upload_enable_comments); + + + set_upload_delete.setOnClickListener(v -> { + AlertDialog.Builder builderInner; + SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + int theme1 = sharedpreferences1.getInt(Helper.SET_THEME, Helper.THEME_DARK); + int style; + if (theme1 == Helper.THEME_DARK) { + style = R.style.DialogDark; + } else if (theme1 == Helper.THEME_BLACK) { + style = R.style.DialogBlack; + } else { + style = R.style.Dialog; + } + builderInner = new AlertDialog.Builder(PeertubeEditUploadActivity.this, style); + builderInner.setMessage(getString(R.string.delete_video_confirmation)); + builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); + builderInner.setPositiveButton(R.string.yes, (dialog, which) -> { + new PostActionAsyncTask(PeertubeEditUploadActivity.this, API.StatusAction.PEERTUBEDELETEVIDEO, videoId, PeertubeEditUploadActivity.this).executeOnExecutor(THREAD_POOL_EXECUTOR); + dialog.dismiss(); + }); + builderInner.show(); + }); + //Get params from the API + LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories()); + LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences()); + LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages()); + LinkedHashMap translations = null; + if (peertubeInformation.getTranslations() != null) + translations = new LinkedHashMap<>(peertubeInformation.getTranslations()); + //Populate catgories + String[] categoriesA = new String[categories.size()]; + Iterator> it = categories.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue())) + categoriesA[i] = pair.getValue(); + else + categoriesA[i] = translations.get(pair.getValue()); + it.remove(); + i++; + } + ArrayAdapter adapterCatgories = new ArrayAdapter<>(PeertubeEditUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, categoriesA); + set_upload_categories.setAdapter(adapterCatgories); + + + //Populate licenses + String[] licensesA = new String[licences.size()]; + it = licences.entrySet().iterator(); + i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue())) + licensesA[i] = pair.getValue(); + else + licensesA[i] = translations.get(pair.getValue()); + it.remove(); + i++; + } + ArrayAdapter adapterLicenses = new ArrayAdapter<>(PeertubeEditUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, licensesA); + set_upload_licenses.setAdapter(adapterLicenses); + + + //Populate languages + String[] languagesA = new String[languages.size()]; + Iterator> itl = languages.entrySet().iterator(); + i = 0; + while (it.hasNext()) { + Map.Entry pair = itl.next(); + if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue())) + languagesA[i] = pair.getValue(); + else + languagesA[i] = translations.get(pair.getValue()); + it.remove(); + i++; + } + ArrayAdapter adapterLanguages = new ArrayAdapter<>(PeertubeEditUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, languagesA); + set_upload_languages.setAdapter(adapterLanguages); + + + //Populate languages + String[] privaciesA = new String[privacies.size()]; + it = privacies.entrySet().iterator(); + i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue())) + privaciesA[i] = pair.getValue(); + else + privaciesA[i] = translations.get(pair.getValue()); + it.remove(); + i++; + } + ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeEditUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, privaciesA); + set_upload_privacy.setAdapter(adapterPrivacies); + + + String peertubeInstance = Helper.getLiveInstance(PeertubeEditUploadActivity.this); + new RetrievePeertubeSingleAsyncTask(PeertubeEditUploadActivity.this, peertubeInstance, videoId, PeertubeEditUploadActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + channels = new LinkedHashMap<>(); + + + } + + + @Override + public void onRetrievePeertube(APIResponse apiResponse) { + if (apiResponse.getError() != null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { + if (apiResponse.getError() != null && apiResponse.getError().getError() != null) + Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); + else + Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + set_upload_submit.setEnabled(true); + return; + } + + //Peertube video + Peertube peertube = apiResponse.getPeertubes().get(0); + + if (peertube.isUpdate()) { + Toasty.success(PeertubeEditUploadActivity.this, getString(R.string.toast_peertube_video_updated), Toast.LENGTH_LONG).show(); + peertube.setUpdate(false); + set_upload_submit.setEnabled(true); + } else { + new RetrievePeertubeChannelsAsyncTask(PeertubeEditUploadActivity.this, PeertubeEditUploadActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + languageToSend = peertube.getLanguage(); + licenseToSend = peertube.getLicense(); + privacyToSend = peertube.getPrivacy(); + categoryToSend = peertube.getCategory(); + + + if (languageToSend == null) { + LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages()); + Map.Entry entryString = languages.entrySet().iterator().next(); + languageToSend = new HashMap<>(); + languageToSend.put(entryString.getKey(), entryString.getValue()); + } + + if (licenseToSend == null) { + LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences()); + Map.Entry entryInt = licences.entrySet().iterator().next(); + licenseToSend = new HashMap<>(); + licenseToSend.put(entryInt.getKey(), entryInt.getValue()); + } + + if (categoryToSend == null) { + LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories()); + Map.Entry entryInt = categories.entrySet().iterator().next(); + categoryToSend = new HashMap<>(); + categoryToSend.put(entryInt.getKey(), entryInt.getValue()); + } + if (privacyToSend == null) { + LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + Map.Entry entryInt = privacies.entrySet().iterator().next(); + privacyToSend = new HashMap<>(); + privacyToSend.put(entryInt.getKey(), entryInt.getValue()); + } + + String language = null; + + if (languageToSend != null) { + Map.Entry entryString = languageToSend.entrySet().iterator().next(); + language = entryString.getValue(); + } + + String license = null; + if (licenseToSend != null) { + Map.Entry entryInt = licenseToSend.entrySet().iterator().next(); + license = entryInt.getValue(); + } + + String privacy = null; + if (privacyToSend != null) { + Map.Entry entryInt = privacyToSend.entrySet().iterator().next(); + privacy = entryInt.getValue(); + } + + String category = null; + if (categoryToSend != null) { + Map.Entry entryInt = categoryToSend.entrySet().iterator().next(); + category = entryInt.getValue(); + } + + channel = peertube.getChannel(); + String title = peertube.getName(); + boolean commentEnabled = peertube.isCommentsEnabled(); + boolean isNSFW = peertube.isSensitive(); + + set_upload_enable_comments.setChecked(commentEnabled); + set_upload_nsfw.setChecked(isNSFW); + + p_video_title.setText(title); + p_video_description.setText(peertube.getDescription()); + + + LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories()); + LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences()); + LinkedHashMap privacies = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + LinkedHashMap languages = new LinkedHashMap<>(peertubeInformation.getLanguages()); + + + int languagePosition = 0; + if (languages.containsValue(language)) { + Iterator> it = languages.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (pair.getValue().equals(language)) + break; + it.remove(); + languagePosition++; + } + } + int privacyPosition = 0; + if (privacy != null && privacies.containsValue(privacy)) { + Iterator> it = privacies.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (pair.getValue().equals(privacy)) + break; + it.remove(); + privacyPosition++; + } + } + int licensePosition = 0; + if (license != null && licences.containsValue(license)) { + Iterator> it = licences.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (pair.getValue().equals(license)) + break; + it.remove(); + licensePosition++; + } + } + int categoryPosition = 0; + if (category != null && categories.containsValue(category)) { + Iterator> it = categories.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (pair.getValue().equals(category)) + break; + it.remove(); + categoryPosition++; + } + } + + //Manage privacies + set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + Iterator> it = privaciesCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + privacyToSend = new HashMap<>(); + privacyToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + set_upload_licenses.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap licensesCheck = new LinkedHashMap<>(peertubeInformation.getLicences()); + Iterator> it = licensesCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + licenseToSend = new HashMap<>(); + licenseToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + //Manage categories + set_upload_categories.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap categoriesCheck = new LinkedHashMap<>(peertubeInformation.getCategories()); + Iterator> it = categoriesCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + categoryToSend = new HashMap<>(); + categoryToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + //Manage languages + set_upload_languages.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap languagesCheck = new LinkedHashMap<>(peertubeInformation.getLanguages()); + Iterator> it = languagesCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + languageToSend = new HashMap<>(); + languageToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + //Manage languages + set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap channelsCheck = new LinkedHashMap<>(channels); + Iterator> it = channelsCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + channelToSend = new HashMap<>(); + channelToSend.put(pair.getKey(), pair.getValue()); + + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + + set_upload_submit.setOnClickListener(v -> { + String title1 = p_video_title.getText().toString().trim(); + String description = p_video_description.getText().toString().trim(); + boolean isNSFW1 = set_upload_nsfw.isChecked(); + boolean commentEnabled1 = set_upload_enable_comments.isChecked(); + peertube.setName(title1); + peertube.setDescription(description); + peertube.setSensitive(isNSFW1); + peertube.setCommentsEnabled(commentEnabled1); + peertube.setCategory(categoryToSend); + peertube.setLicense(licenseToSend); + peertube.setLanguage(languageToSend); + peertube.setChannelForUpdate(channelToSend); + peertube.setPrivacy(privacyToSend); + List tags = p_video_tags.getTags(); + peertube.setTags(tags); + set_upload_submit.setEnabled(false); + new PostPeertubeAsyncTask(PeertubeEditUploadActivity.this, peertube, PeertubeEditUploadActivity.this).executeOnExecutor(THREAD_POOL_EXECUTOR); + }); + + set_upload_privacy.setSelection(privacyPosition); + set_upload_languages.setSelection(languagePosition); + set_upload_licenses.setSelection(licensePosition); + set_upload_categories.setSelection(categoryPosition); + + List tags = peertube.getTags(); + if (tags != null && tags.size() > 0) { + String[] tagsA = tags.toArray(new String[0]); + p_video_tags.setTags(tagsA); + } + + } + + @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().getError() != null) + Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); + else + Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + return; + } + + //Populate channels + List accounts = apiResponse.getAccounts(); + String[] channelName = new String[accounts.size()]; + int i = 0; + for (Account account : accounts) { + channels.put(account.getUsername(), account.getId()); + channelName[i] = account.getUsername(); + i++; + } + ArrayAdapter adapterChannel = new ArrayAdapter<>(PeertubeEditUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, channelName); + set_upload_channel.setAdapter(adapterChannel); + + int channelPosition = 0; + if (channels.containsKey(channel.getUsername())) { + LinkedHashMap channelsIterator = new LinkedHashMap<>(channels); + Iterator> it = channelsIterator.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (pair.getKey().equals(channel.getUsername())) { + channelToSend = new HashMap<>(); + channelToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + channelPosition++; + } + } + set_upload_channel.setSelection(channelPosition); + + set_upload_submit.setEnabled(true); + } + + @Override + public void onPostAction(int statusCode, API.StatusAction statusAction, String userId, Error error) { + Intent intent = new Intent(PeertubeEditUploadActivity.this, MainActivity.class); + intent.putExtra(Helper.INTENT_ACTION, Helper.RELOAD_MYVIDEOS); + startActivity(intent); + finish(); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java new file mode 100644 index 0000000..16ea246 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java @@ -0,0 +1,384 @@ +package app.fedilab.fedilabtube; + + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.provider.OpenableColumns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import net.gotev.uploadservice.MultipartUploadRequest; +import net.gotev.uploadservice.ServerResponse; +import net.gotev.uploadservice.UploadInfo; +import net.gotev.uploadservice.UploadNotificationAction; +import net.gotev.uploadservice.UploadNotificationConfig; +import net.gotev.uploadservice.UploadServiceSingleBroadcastReceiver; +import net.gotev.uploadservice.UploadStatusDelegate; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeInterface; + + +public class PeertubeUploadActivity extends AppCompatActivity implements OnRetrievePeertubeInterface, UploadStatusDelegate { + + + private final int PICK_IVDEO = 52378; + private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724; + private Button set_upload_file, set_upload_submit; + private Spinner set_upload_privacy, set_upload_channel; + private TextView set_upload_file_name; + private EditText video_title; + private HashMap channels; + private Uri uri; + private String filename; + private HashMap privacyToSend; + private HashMap channelToSend; + private UploadServiceSingleBroadcastReceiver uploadReceiver; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + assert inflater != null; + View view = inflater.inflate(R.layout.simple_bar, new LinearLayout(PeertubeUploadActivity.this), false); + view.setBackground(new ColorDrawable(ContextCompat.getColor(PeertubeUploadActivity.this, R.color.cyanea_primary))); + 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(v -> finish()); + toolbar_title.setText(R.string.upload_video); + } + setContentView(R.layout.activity_peertube_upload); + + set_upload_file = findViewById(R.id.set_upload_file); + set_upload_file_name = findViewById(R.id.set_upload_file_name); + set_upload_channel = findViewById(R.id.set_upload_channel); + set_upload_privacy = findViewById(R.id.set_upload_privacy); + set_upload_submit = findViewById(R.id.set_upload_submit); + video_title = findViewById(R.id.video_title); + + new RetrievePeertubeChannelsAsyncTask(PeertubeUploadActivity.this, PeertubeUploadActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + channels = new HashMap<>(); + + uploadReceiver = new UploadServiceSingleBroadcastReceiver(this); + uploadReceiver.register(this); + + } + + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == PICK_IVDEO && resultCode == Activity.RESULT_OK) { + if (data == null || data.getData() == null) { + Toasty.error(PeertubeUploadActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show(); + return; + } + set_upload_submit.setEnabled(true); + + uri = data.getData(); + String uriString = uri.toString(); + File myFile = new File(uriString); + filename = null; + if (uriString.startsWith("content://")) { + Cursor cursor = null; + try { + cursor = getContentResolver().query(uri, null, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + } + } finally { + assert cursor != null; + cursor.close(); + } + } else if (uriString.startsWith("file://")) { + filename = myFile.getName(); + } + if (filename == null) { + filename = new Date().toString(); + } + set_upload_file_name.setVisibility(View.VISIBLE); + set_upload_file_name.setText(filename); + + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + uploadReceiver.unregister(this); + } + + @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(PeertubeUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); + else + Toasty.error(PeertubeUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + return; + } + + //Populate channels + List accounts = apiResponse.getAccounts(); + String[] channelName = new String[accounts.size()]; + String[] channelId = new String[accounts.size()]; + int i = 0; + 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<>(PeertubeUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, channelName); + set_upload_channel.setAdapter(adapterChannel); + + if (peertubeInformation == null) { + return; + } + LinkedHashMap translations = null; + if (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(); + i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (translations == null || translations.size() == 0 || !translations.containsKey(pair.getValue())) + privaciesA[i] = pair.getValue(); + else + privaciesA[i] = translations.get(pair.getValue()); + it.remove(); + i++; + } + + ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeUploadActivity.this, + android.R.layout.simple_spinner_dropdown_item, privaciesA); + set_upload_privacy.setAdapter(adapterPrivacies); + + //Manage privacies + set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + Iterator> it = privaciesCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + privacyToSend = new HashMap<>(); + privacyToSend.put(pair.getKey(), pair.getValue()); + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + set_upload_file.setEnabled(true); + + set_upload_file.setOnClickListener(v -> { + if (ContextCompat.checkSelfPermission(PeertubeUploadActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(PeertubeUploadActivity.this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); + return; + } + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + intent.setType("*/*"); + String[] mimetypes = {"video/*"}; + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes); + startActivityForResult(intent, PICK_IVDEO); + } else { + intent.setType("video/*"); + Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + Intent chooserIntent = Intent.createChooser(intent, getString(R.string.toot_select_image)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{pickIntent}); + startActivityForResult(chooserIntent, PICK_IVDEO); + } + + }); + + //Manage languages + set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + LinkedHashMap channelsCheck = new LinkedHashMap<>(channels); + Iterator> it = channelsCheck.entrySet().iterator(); + int i = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + if (i == position) { + channelToSend = new HashMap<>(); + channelToSend.put(pair.getKey(), pair.getValue()); + + break; + } + it.remove(); + i++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + set_upload_submit.setOnClickListener(v -> { + if (uri != null) { + Map.Entry channelM = channelToSend.entrySet().iterator().next(); + String idChannel = channelM.getValue(); + Map.Entry privacyM = privacyToSend.entrySet().iterator().next(); + Integer idPrivacy = privacyM.getKey(); + + try { + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + UploadNotificationConfig uploadConfig = new UploadNotificationConfig(); + Intent in = new Intent(PeertubeUploadActivity.this, PeertubeEditUploadActivity.class); + PendingIntent clickIntent = PendingIntent.getActivity(PeertubeUploadActivity.this, 1, in, PendingIntent.FLAG_UPDATE_CURRENT); + uploadConfig + .setClearOnActionForAllStatuses(true); + + + uploadConfig.getProgress().message = getString(R.string.uploading); + uploadConfig.getCompleted().message = getString(R.string.upload_video_success); + uploadConfig.getError().message = getString(R.string.toast_error); + uploadConfig.getCancelled().message = getString(R.string.toast_cancelled); + uploadConfig.getCompleted().actions.add(new UploadNotificationAction(R.drawable.ic_check, getString(R.string.video_uploaded_action), clickIntent)); + + if (video_title != null && video_title.getText() != null && video_title.getText().toString().trim().length() > 0) { + filename = video_title.getText().toString().trim(); + } + String uploadId = UUID.randomUUID().toString(); + uploadReceiver.setUploadID(uploadId); + new MultipartUploadRequest(PeertubeUploadActivity.this, uploadId, "https://" + Helper.getLiveInstance(PeertubeUploadActivity.this) + "/api/v1/videos/upload") + .addFileToUpload(uri.toString().replace("file://", ""), "videofile") + .addHeader("Authorization", "Bearer " + token) + .setNotificationConfig(uploadConfig) + .addParameter("name", filename) + .addParameter("channelId", idChannel) + .addParameter("privacy", String.valueOf(idPrivacy)) + .addParameter("nsfw", "false") + .addParameter("commentsEnabled", "true") + .addParameter("waitTranscoding", "true") + .setMaxRetries(2) + .startUpload(); + finish(); + } catch (Exception exc) { + exc.printStackTrace(); + } + } + }); + } + + + @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(); + } + + @SuppressLint("ApplySharedPref") + @Override + public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { + try { + JSONObject response = new JSONObject(serverResponse.getBodyAsString()); + String videoID = response.getJSONObject("video").get("id").toString(); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.VIDEO_ID, videoID); + editor.commit(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public void onCancelled(Context context, UploadInfo uploadInfo) { + // your code here + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/ManagePlaylistsAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/ManagePlaylistsAsyncTask.java new file mode 100644 index 0000000..c907c07 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/ManagePlaylistsAsyncTask.java @@ -0,0 +1,80 @@ + +package app.fedilab.fedilabtube.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.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Playlist; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnPlaylistActionInterface; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; + + +public class ManagePlaylistsAsyncTask extends AsyncTask { + + 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().getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(contextReference.get(), db).getUniqAccount(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); + } + + public enum action { + GET_PLAYLIST, + GET_LIST_VIDEOS, + CREATE_PLAYLIST, + DELETE_PLAYLIST, + UPDATE_PLAYLIST, + ADD_VIDEOS, + DELETE_VIDEOS, + GET_PLAYLIST_FOR_VIDEO, + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/PostActionAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/PostActionAsyncTask.java new file mode 100644 index 0000000..c963288 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/PostActionAsyncTask.java @@ -0,0 +1,88 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.interfaces.OnPostActionInterface; + + +public class PostActionAsyncTask extends AsyncTask { + + private OnPostActionInterface listener; + private int statusCode; + private PeertubeAPI.StatusAction apiAction; + private String targetedId, targetedComment; + private String comment; + private WeakReference contextReference; + private Error error; + private Account account; + + + public PostActionAsyncTask(Context context, Account account, PeertubeAPI.StatusAction apiAction, String targetedId, OnPostActionInterface onPostActionInterface) { + this.contextReference = new WeakReference<>(context); + this.listener = onPostActionInterface; + this.apiAction = apiAction; + this.targetedId = targetedId; + this.account = account; + } + + public PostActionAsyncTask(Context context, String targetedId, String comment, String targetedComment, OnPostActionInterface onPostActionInterface) { + this.contextReference = new WeakReference<>(context); + this.listener = onPostActionInterface; + this.apiAction = PeertubeAPI.StatusAction.PEERTUBEREPLY; + this.targetedId = targetedId; + this.comment = comment; + this.targetedComment = targetedComment; + } + + public PostActionAsyncTask(Context context, PeertubeAPI.StatusAction apiAction, String targetedId, app.fedilab.fedilabtube.client.entities.Status status, String comment, OnPostActionInterface onPostActionInterface) { + contextReference = new WeakReference<>(context); + this.listener = onPostActionInterface; + this.apiAction = apiAction; + this.targetedId = targetedId; + this.comment = comment; + } + + + @Override + protected Void doInBackground(Void... params) { + + //Remote action + PeertubeAPI peertubeAPI; + if (account != null) + peertubeAPI = new PeertubeAPI(contextReference.get(), account.getInstance(), account.getToken()); + else + peertubeAPI = new PeertubeAPI(contextReference.get()); + + if (apiAction == PeertubeAPI.StatusAction.FOLLOW || apiAction == PeertubeAPI.StatusAction.UNFOLLOW) + statusCode = peertubeAPI.postAction(apiAction, targetedId); + else if (apiAction == PeertubeAPI.StatusAction.RATEVIDEO) + statusCode = peertubeAPI.postRating(targetedId, comment); + else if (apiAction == PeertubeAPI.StatusAction.PEERTUBECOMMENT) + statusCode = peertubeAPI.postComment(targetedId, comment); + else if (apiAction == PeertubeAPI.StatusAction.PEERTUBEREPLY) + statusCode = peertubeAPI.postReply(targetedId, comment, targetedComment); + else if (apiAction == PeertubeAPI.StatusAction.PEERTUBEDELETECOMMENT) { + statusCode = peertubeAPI.deleteComment(targetedId, comment); + targetedId = comment; + } else if (apiAction == PeertubeAPI.StatusAction.PEERTUBEDELETEVIDEO) { + statusCode = peertubeAPI.deleteVideo(targetedId); + } + error = peertubeAPI.getError(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + if (listener != null) { + listener.onPostAction(statusCode, apiAction, targetedId, error); + } + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrieveFeedsAsyncTask.java new file mode 100644 index 0000000..964d2ba --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrieveFeedsAsyncTask.java @@ -0,0 +1,165 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.interfaces.OnRetrieveFeedsInterface; +import app.fedilab.fedilabtube.sqlite.Sqlite; + + +public class RetrieveFeedsAsyncTask extends AsyncTask { + + + private Type action; + private APIResponse apiResponse; + private String max_id; + private OnRetrieveFeedsInterface listener; + private String targetedID; + private String tag; + private boolean showMediaOnly = false; + private boolean showPinned = false; + private boolean showReply = false; + private WeakReference contextReference; + private String instanceName, remoteInstance, name; + private int timelineId; + private String search; + + + private String social; + + + public RetrieveFeedsAsyncTask(Context context, Type action, String max_id, String search, OnRetrieveFeedsInterface onRetrieveFeedsInterface) { + this.contextReference = new WeakReference<>(context); + this.action = action; + this.max_id = max_id; + this.listener = onRetrieveFeedsInterface; + this.search = search; + } + + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get()); + SQLiteDatabase db = Sqlite.getInstance(this.contextReference.get().getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + if (action == null) + return null; + switch (action) { + case REMOTE_INSTANCE_FILTERED: + apiResponse = peertubeAPI.searchPeertube(this.remoteInstance, search); + break; + case USER: + apiResponse = peertubeAPI.getVideos(targetedID, max_id); + break; + case MYVIDEOS: + 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); + break; + + case CACHE_BOOKMARKS_PEERTUBE: + apiResponse = new APIResponse(); + db = Sqlite.getInstance(contextReference.get().getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List peertubes = new PeertubeFavoritesDAO(contextReference.get(), db).getAllPeertube(); + apiResponse.setPeertubes(peertubes); + break; + case PSUBSCRIPTIONS: + peertubeAPI = new PeertubeAPI(this.contextReference.get()); + apiResponse = peertubeAPI.getSubscriptionsTL(max_id); + break; + case POVERVIEW: + peertubeAPI = new PeertubeAPI(this.contextReference.get()); + apiResponse = peertubeAPI.getOverviewTL(max_id); + break; + case PTRENDING: + peertubeAPI = new PeertubeAPI(this.contextReference.get()); + apiResponse = peertubeAPI.getTrendingTL(max_id); + break; + case PRECENTLYADDED: + peertubeAPI = new PeertubeAPI(this.contextReference.get()); + apiResponse = peertubeAPI.getRecentlyAddedTL(max_id); + break; + case PLOCAL: + peertubeAPI = new PeertubeAPI(this.contextReference.get()); + apiResponse = peertubeAPI.getLocalTL(max_id); + break; + + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrieveFeeds(apiResponse); + } + + public enum Type { + HOME, + LOCAL, + DIRECT, + CONVERSATION, + PUBLIC, + HASHTAG, + LIST, + USER, + FAVOURITES, + ONESTATUS, + CONTEXT, + TAG, + REMOTE_INSTANCE, + REMOTE_INSTANCE_FILTERED, + ART, + PEERTUBE, + NOTIFICATION, + SEARCH, + NEWS, + ANNOUNCEMENTS, + + PSUBSCRIPTIONS, + POVERVIEW, + PTRENDING, + PRECENTLYADDED, + PLOCAL, + CHANNEL, + MYVIDEOS, + PEERTUBE_HISTORY, + + PIXELFED, + PF_HOME, + PF_LOCAL, + PF_DISCOVER, + PF_NOTIFICATION, + PF_REPLIES, + + + GNU_HOME, + GNU_LOCAL, + GNU_WHOLE, + GNU_NOTIFICATION, + GNU_DM, + GNU_ART, + GNU_TAG, + GNU_GROUP_TIMELINE, + + SCHEDULED_TOOTS, + CACHE_BOOKMARKS, + CACHE_BOOKMARKS_PEERTUBE, + CACHE_STATUS, + + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeChannelsAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeChannelsAsyncTask.java new file mode 100644 index 0000000..f8a0f84 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeChannelsAsyncTask.java @@ -0,0 +1,49 @@ + +package app.fedilab.fedilabtube.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.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeInterface; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; + + +public class RetrievePeertubeChannelsAsyncTask extends AsyncTask { + + + private APIResponse apiResponse; + private OnRetrievePeertubeInterface listener; + private WeakReference contextReference; + + + public RetrievePeertubeChannelsAsyncTask(Context context, OnRetrievePeertubeInterface onRetrievePeertubeInterface) { + this.contextReference = new WeakReference<>(context); + this.listener = onRetrievePeertubeInterface; + } + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get()); + SQLiteDatabase db = Sqlite.getInstance(contextReference.get().getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + 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())); + Account account = new AccountDAO(contextReference.get(), db).getUniqAccount(userId, instance); + apiResponse = peertubeAPI.getPeertubeChannel(account.getUsername()); + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrievePeertubeChannels(apiResponse); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeInformationAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeInformationAsyncTask.java new file mode 100644 index 0000000..4731d1f --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeInformationAsyncTask.java @@ -0,0 +1,39 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.HttpsConnection; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.PeertubeInformation; + + +public class RetrievePeertubeInformationAsyncTask extends AsyncTask { + + + public static PeertubeInformation peertubeInformation; + private WeakReference contextReference; + + + public RetrievePeertubeInformationAsyncTask(Context context) { + this.contextReference = new WeakReference<>(context); + } + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get()); + try { + peertubeInformation = peertubeAPI.getPeertubeInformation(); + } catch (HttpsConnection.HttpsConnectionException e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeNotificationsAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeNotificationsAsyncTask.java new file mode 100644 index 0000000..08ac8df --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeNotificationsAsyncTask.java @@ -0,0 +1,55 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeNotificationsInterface; + + +public class RetrievePeertubeNotificationsAsyncTask extends AsyncTask { + + + private APIResponse apiResponse; + private String max_id; + private Account account; + private OnRetrievePeertubeNotificationsInterface listener; + private WeakReference contextReference; + + public RetrievePeertubeNotificationsAsyncTask(Context context, Account account, String max_id, OnRetrievePeertubeNotificationsInterface onRetrievePeertubeNotificationsInterface) { + this.contextReference = new WeakReference<>(context); + this.max_id = max_id; + this.listener = onRetrievePeertubeNotificationsInterface; + this.account = account; + } + + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI api; + if (account == null) { + api = new PeertubeAPI(this.contextReference.get()); + apiResponse = api.getNotifications(max_id); + } else { + if (this.contextReference.get() == null) { + apiResponse.setError(new Error()); + return null; + } + api = new PeertubeAPI(this.contextReference.get(), account.getInstance(), account.getToken()); + apiResponse = api.getNotificationsSince(max_id); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrievePeertubeNotifications(apiResponse, account); + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSearchAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSearchAsyncTask.java new file mode 100644 index 0000000..adbb2e6 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSearchAsyncTask.java @@ -0,0 +1,41 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.interfaces.OnRetrieveFeedsInterface; + + +public class RetrievePeertubeSearchAsyncTask extends AsyncTask { + + private String query, instance; + private APIResponse apiResponse; + private OnRetrieveFeedsInterface listener; + private WeakReference contextReference; + + public RetrievePeertubeSearchAsyncTask(Context context, String instance, String query, OnRetrieveFeedsInterface onRetrieveFeedsInterface) { + this.contextReference = new WeakReference<>(context); + this.query = query; + this.listener = onRetrieveFeedsInterface; + this.instance = instance; + } + + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI api = new PeertubeAPI(this.contextReference.get()); + apiResponse = api.searchPeertube(instance, query); + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrieveFeeds(apiResponse); + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleAsyncTask.java new file mode 100644 index 0000000..cbe9957 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleAsyncTask.java @@ -0,0 +1,52 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeInterface; + + +public class RetrievePeertubeSingleAsyncTask extends AsyncTask { + + + private APIResponse apiResponse; + private String videoId; + private OnRetrievePeertubeInterface listener; + private WeakReference contextReference; + private String instanceName; + + + public RetrievePeertubeSingleAsyncTask(Context context, String instanceName, String videoId, OnRetrievePeertubeInterface onRetrievePeertubeInterface) { + this.contextReference = new WeakReference<>(context); + this.videoId = videoId; + this.listener = onRetrievePeertubeInterface; + this.instanceName = instanceName; + } + + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI peertubeAPI = new PeertubeAPI(this.contextReference.get()); + SharedPreferences sharedpreferences = contextReference.get().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + apiResponse = peertubeAPI.getSinglePeertube(this.instanceName, videoId, token); + if (apiResponse.getPeertubes() != null && apiResponse.getPeertubes().size() > 0 && apiResponse.getPeertubes().get(0) != null) { + String rate = new PeertubeAPI(this.contextReference.get()).getRating(videoId); + if (rate != null) + apiResponse.getPeertubes().get(0).setMyRating(rate); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrievePeertube(apiResponse); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleCommentsAsyncTask.java b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleCommentsAsyncTask.java new file mode 100644 index 0000000..5d75b11 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/asynctasks/RetrievePeertubeSingleCommentsAsyncTask.java @@ -0,0 +1,43 @@ + +package app.fedilab.fedilabtube.asynctasks; + +import android.content.Context; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeInterface; + + +public class RetrievePeertubeSingleCommentsAsyncTask extends AsyncTask { + + + private APIResponse apiResponse; + private String videoId; + private OnRetrievePeertubeInterface listener; + private WeakReference contextReference; + private String instanceName; + + + public RetrievePeertubeSingleCommentsAsyncTask(Context context, String instanceName, String videoId, OnRetrievePeertubeInterface onRetrievePeertubeInterface) { + this.contextReference = new WeakReference<>(context); + this.videoId = videoId; + this.listener = onRetrievePeertubeInterface; + this.instanceName = instanceName; + } + + + @Override + protected Void doInBackground(Void... params) { + PeertubeAPI api = new PeertubeAPI(this.contextReference.get()); + apiResponse = api.getSinglePeertubeComments(this.instanceName, videoId); + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrievePeertubeComments(apiResponse); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/APIResponse.java b/app/src/main/java/app/fedilab/fedilabtube/client/APIResponse.java new file mode 100644 index 0000000..22553b0 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/APIResponse.java @@ -0,0 +1,134 @@ +package app.fedilab.fedilabtube.client; + + + + +import java.util.List; + +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Instance; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.client.entities.PeertubeNotification; +import app.fedilab.fedilabtube.client.entities.Playlist; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.client.entities.Status; + +public class APIResponse { + + private List accounts = null; + private List statuses = null; + private String targetedId = null; + private List peertubes = null; + private List peertubeNotifications = null; + private List playlists = null; + private List domains = null; + private Error error = null; + private String since_id, max_id; + private List playlistForVideos; + private Instance instance; + + public List getAccounts() { + return accounts; + } + + public void setAccounts(List accounts) { + this.accounts = accounts; + } + + public Error getError() { + return error; + } + + public void setError(Error error) { + this.error = error; + } + + public String getMax_id() { + return max_id; + } + + public void setMax_id(String max_id) { + this.max_id = max_id; + } + + public String getSince_id() { + return since_id; + } + + public void setSince_id(String since_id) { + this.since_id = since_id; + } + + + public List getDomains() { + return domains; + } + + public void setDomains(List domains) { + this.domains = domains; + } + + + + public List getPeertubes() { + return peertubes; + } + + public void setPeertubes(List peertubes) { + this.peertubes = peertubes; + } + + + + public List getPeertubeNotifications() { + return peertubeNotifications; + } + + public void setPeertubeNotifications(List peertubeNotifications) { + this.peertubeNotifications = peertubeNotifications; + } + + + + 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; + } + + + public String getTargetedId() { + return targetedId; + } + + public void setTargetedId(String targetedId) { + this.targetedId = targetedId; + } + + + public Instance getInstance() { + return instance; + } + + public void setInstance(Instance instance) { + this.instance = instance; + } + + public List getStatuses() { + return statuses; + } + + public void setStatuses(List statuses) { + this.statuses = statuses; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/HttpsConnection.java b/app/src/main/java/app/fedilab/fedilabtube/client/HttpsConnection.java new file mode 100644 index 0000000..3714349 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/HttpsConnection.java @@ -0,0 +1,1360 @@ +package app.fedilab.fedilabtube.client; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.text.Html; +import android.text.SpannableString; + + +import com.google.gson.JsonObject; + +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 org.apache.poi.util.IOUtils; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.HttpsURLConnection; + +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.helper.FileNameCleaner; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.interfaces.OnDownloadInterface; + +public class HttpsConnection { + + + private HttpsURLConnection httpsURLConnection; + private HttpURLConnection httpURLConnection; + private String since_id, max_id; + private Context context; + private int CHUNK_SIZE = 4096; + private SharedPreferences sharedpreferences; + private Proxy proxy; + private String instance; + private String USER_AGENT; + + + public HttpsConnection(Context context, String instance) { + this.instance = instance; + this.context = context; + sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean proxyEnabled = sharedpreferences.getBoolean(Helper.SET_PROXY_ENABLED, false); + int type = sharedpreferences.getInt(Helper.SET_PROXY_TYPE, 0); + proxy = null; + + USER_AGENT = sharedpreferences.getString(Helper.SET_CUSTOM_USER_AGENT, Helper.USER_AGENT); + if (proxyEnabled) { + try { + String host = sharedpreferences.getString(Helper.SET_PROXY_HOST, "127.0.0.1"); + int port = sharedpreferences.getInt(Helper.SET_PROXY_PORT, 8118); + if (type == 0) + proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); + else + proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(host, port)); + final String login = sharedpreferences.getString(Helper.SET_PROXY_LOGIN, null); + final String pwd = sharedpreferences.getString(Helper.SET_PROXY_PASSWORD, null); + if (login != null) { + Authenticator authenticator = new Authenticator() { + public PasswordAuthentication getPasswordAuthentication() { + assert pwd != null; + return (new PasswordAuthentication(login, + pwd.toCharArray())); + } + }; + Authenticator.setDefault(authenticator); + } + } catch (Exception e) { + proxy = null; + } + + } + + if (instance != null && instance.endsWith(".onion")) { + HttpsURLConnection.setDefaultHostnameVerifier((arg0, arg1) -> true); + } + } + + + /** + * Get calls + * + * @param urlConnection String url + * @param timeout int timeout + * @param paramaters HashMap paramaters + * @param token String token + * @return String + * @throws IOException IOException + * @throws NoSuchAlgorithmException NoSuchAlgorithmException + * @throws KeyManagementException KeyManagementException + * @throws HttpsConnectionException HttpsConnectionException + */ + public String get(String urlConnection, int timeout, HashMap paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + + Map params = new LinkedHashMap<>(); + if (paramaters != null) { + Iterator> it = paramaters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + params.put(pair.getKey(), pair.getValue()); + it.remove(); + } + } + StringBuilder postData = new StringBuilder(); + URL url; + if (params.size() > 0) { + for (Map.Entry param : params.entrySet()) { + if (postData.length() != 0) postData.append('&'); + postData.append(param.getKey()); + postData.append('='); + postData.append(param.getValue()); + } + url = new URL(urlConnection + "?" + postData); + } else { + url = new URL(urlConnection); + } + + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setRequestProperty("http.keepAlive", "false"); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setRequestProperty("Content-Type", "application/json"); + httpsURLConnection.setRequestProperty("Accept", "application/json"); + httpsURLConnection.setUseCaches(true); + httpsURLConnection.setDefaultUseCaches(true); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestMethod("GET"); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } + + + /** + * Will check if the current url is redirecting + * + * @param urlConnection String the url to check + * @return String null|string url redirection + */ + public String checkUrl(String urlConnection) { + URL url; + String redirect = null; + try { + url = new URL(urlConnection); + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("http.keepAlive", "false"); + httpsURLConnection.setInstanceFollowRedirects(false); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestMethod("HEAD"); + if (httpsURLConnection.getResponseCode() == 301) { + Map> map = httpsURLConnection.getHeaderFields(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.toString().toLowerCase().startsWith("location")) { + Matcher matcher = Helper.urlPattern.matcher(entry.toString()); + if (matcher.find()) { + redirect = matcher.group(1); + } + } + } + } + httpsURLConnection.getInputStream().close(); + if (redirect != null && redirect.compareTo(urlConnection) != 0) { + URL redirectURL = new URL(redirect); + String host = redirectURL.getHost(); + String protocol = redirectURL.getProtocol(); + if (protocol == null || host == null) { + redirect = null; + } + } + return redirect; + } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + } + return null; + } + + + public String get(String urlConnection) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + + URL url = new URL(urlConnection); + if (urlConnection.startsWith("https://")) { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(30 * 1000); + httpsURLConnection.setRequestProperty("http.keepAlive", "false"); + httpsURLConnection.setRequestProperty("Content-Type", "application/json"); + httpsURLConnection.setRequestProperty("Accept", "application/json"); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestMethod("GET"); + httpsURLConnection.setDefaultUseCaches(true); + httpsURLConnection.setUseCaches(true); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } else { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setConnectTimeout(30 * 1000); + httpURLConnection.setRequestProperty("http.keepAlive", "false"); + httpURLConnection.setRequestProperty("Content-Type", "application/json"); + httpURLConnection.setRequestProperty("Accept", "application/json"); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setRequestMethod("GET"); + httpURLConnection.setDefaultUseCaches(true); + httpURLConnection.setUseCaches(true); + String response; + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpURLConnection.getInputStream()); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpURLConnection.getInputStream().close(); + return response; + } + + + } + + + public String post(String urlConnection, int timeout, HashMap paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + URL url = new URL(urlConnection); + Map params = new LinkedHashMap<>(); + if (paramaters != null) { + Iterator> it = paramaters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + params.put(pair.getKey(), pair.getValue()); + it.remove(); + } + } + StringBuilder postData = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + if (postData.length() != 0) postData.append('&'); + postData.append(param.getKey()); + postData.append('='); + postData.append(param.getValue()); + } + byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8); + if (urlConnection.startsWith("https://")) { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setDoOutput(true); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestMethod("POST"); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + + httpsURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } else { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setConnectTimeout(timeout * 1000); + httpURLConnection.setDoOutput(true); + httpURLConnection.setRequestMethod("POST"); + if (token != null && !token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", token); + httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpURLConnection.getInputStream()); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpURLConnection.getInputStream().close(); + return response; + } + + } + + + String postJson(String urlConnection, int timeout, JsonObject jsonObject, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + URL url = new URL(urlConnection); + byte[] postDataBytes; + postDataBytes = jsonObject.toString().getBytes(StandardCharsets.UTF_8); + if (urlConnection.startsWith("https://")) { + + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setDoOutput(true); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestProperty("Content-Type", "application/json"); + httpsURLConnection.setRequestProperty("Accept", "application/json"); + httpsURLConnection.setRequestMethod("POST"); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + + httpsURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } else { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setConnectTimeout(timeout * 1000); + httpURLConnection.setDoOutput(true); + httpURLConnection.setRequestMethod("POST"); + if (token != null && !token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", token); + httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpURLConnection.getInputStream()); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpURLConnection.getInputStream().close(); + return response; + } + + } + + @SuppressWarnings("SameParameterValue") + String postMisskey(String urlConnection, int timeout, JSONObject paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + URL url = new URL(urlConnection); + byte[] postDataBytes = paramaters.toString().getBytes(StandardCharsets.UTF_8); + + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setDoOutput(true); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestMethod("POST"); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + + httpsURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } + + + /*** + * Download method which works for http and https connections + * @param downloadUrl String download url + * @param listener OnDownloadInterface, listener which manages progress + */ + public void download(final String downloadUrl, final OnDownloadInterface listener) { + new Thread(() -> { + URL url; + HttpsURLConnection httpsURLConnection; + HttpURLConnection httpURLConnection; + if (downloadUrl.startsWith("https://")) { + try { + url = new URL(downloadUrl); + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + int responseCode = httpsURLConnection.getResponseCode(); + + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + String fileName = ""; + String disposition = httpsURLConnection.getHeaderField("Content-Disposition"); + + if (disposition != null) { + // extracts file name from header field + int index = disposition.indexOf("filename="); + if (index > 0) { + fileName = disposition.substring(index + 10, + disposition.length() - 1); + } + } else { + // extracts file name from URL + fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1 + ); + } + fileName = FileNameCleaner.cleanFileName(fileName); + // opens input stream from the HTTP connection + InputStream inputStream = httpsURLConnection.getInputStream(); + File saveDir = context.getCacheDir(); + final String saveFilePath = saveDir + File.separator + fileName; + + // opens an output stream to save into file + FileOutputStream outputStream = new FileOutputStream(saveFilePath); + + int bytesRead; + byte[] buffer = new byte[CHUNK_SIZE]; + int contentSize = httpsURLConnection.getContentLength(); + int downloadedFileSize = 0; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + downloadedFileSize += bytesRead; + } + outputStream.close(); + inputStream.close(); + } else { + final Error error = new Error(); + error.setError(String.valueOf(responseCode)); + + } + } catch (IOException e) { + Error error = new Error(); + error.setError(context.getString(R.string.toast_error)); + } + + } else { + try { + url = new URL(downloadUrl); + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + int responseCode = httpURLConnection.getResponseCode(); + + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + String fileName = ""; + String disposition = httpURLConnection.getHeaderField("Content-Disposition"); + + if (disposition != null) { + // extracts file name from header field + int index = disposition.indexOf("filename="); + if (index > 0) { + fileName = disposition.substring(index + 10, + disposition.length() - 1); + } + } else { + // extracts file name from URL + fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1 + ); + } + fileName = FileNameCleaner.cleanFileName(fileName); + // opens input stream from the HTTP connection + InputStream inputStream = httpURLConnection.getInputStream(); + File saveDir = context.getCacheDir(); + final String saveFilePath = saveDir + File.separator + fileName; + + // opens an output stream to save into file + FileOutputStream outputStream = new FileOutputStream(saveFilePath); + + int bytesRead; + byte[] buffer = new byte[CHUNK_SIZE]; + int contentSize = httpURLConnection.getContentLength(); + int downloadedFileSize = 0; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + downloadedFileSize += bytesRead; + } + outputStream.close(); + inputStream.close(); + } else { + final Error error = new Error(); + error.setError(String.valueOf(responseCode)); + + } + } catch (IOException e) { + Error error = new Error(); + error.setError(context.getString(R.string.toast_error)); + } + + } + }).start(); + } + + + public InputStream getPicture(final String downloadUrl) { + + URL url; + try { + url = new URL(downloadUrl); + } catch (MalformedURLException e) { + return null; + } + if (downloadUrl.startsWith("https://")) { + try { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + int responseCode = httpsURLConnection.getResponseCode(); + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + // opens input stream from the HTTP connection + return httpsURLConnection.getInputStream(); + } + httpsURLConnection.getInputStream().close(); + } catch (IOException | NoSuchAlgorithmException | KeyManagementException ignored) { + } + if (httpsURLConnection != null) + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + } else { + try { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + int responseCode = httpURLConnection.getResponseCode(); + // always check HTTP response code first + if (responseCode == HttpURLConnection.HTTP_OK) { + // opens input stream from the HTTP connection + return httpURLConnection.getInputStream(); + } + httpURLConnection.getInputStream().close(); + } catch (IOException ignored) { + } + if (httpURLConnection != null) + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + } + return null; + } + + private void uploadMedia(String urlConnection, InputStream avatar, InputStream header, String filename) { + UploadNotificationConfig uploadConfig = new UploadNotificationConfig(); + uploadConfig.getCompleted().autoClear = true; + final File file = new File(context.getCacheDir() + "/" + filename); + OutputStream outputStream; + try { + outputStream = new FileOutputStream(file); + if (avatar != null) { + IOUtils.copy(avatar, outputStream); + } else { + IOUtils.copy(header, outputStream); + } + } catch (IOException e) { + e.printStackTrace(); + } + + try { + String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + MultipartUploadRequest m = new MultipartUploadRequest(context, urlConnection) + .setMethod("PATCH"); + if (avatar != null) { + m.addFileToUpload(file.getPath(), "avatar"); + } else { + m.addFileToUpload(file.getPath(), "header"); + } + m.addParameter("name", filename) + .addHeader("Authorization", "Bearer " + token) + .setNotificationConfig(uploadConfig) + .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) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + + @Override + public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + + @Override + public void onCancelled(Context context, UploadInfo uploadInfo) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + }) + .startUpload(); + } catch (MalformedURLException | FileNotFoundException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("SameParameterValue") + public String patch(String urlConnection, int timeout, HashMap paramaters, InputStream avatar, String avatarName, InputStream header, String headerName, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + URL url = new URL(urlConnection); + Map params = new LinkedHashMap<>(); + if (paramaters != null) { + Iterator> it = paramaters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + params.put(pair.getKey(), pair.getValue()); + it.remove(); + } + } + StringBuilder postData = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + if (postData.length() != 0) postData.append('&'); + postData.append(param.getKey()); + postData.append('='); + postData.append(param.getValue()); + } + byte[] postDataBytes = (postData.toString()).getBytes(StandardCharsets.UTF_8); + + if (urlConnection.startsWith("https://")) { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + httpsURLConnection.setRequestMethod("PATCH"); + } else { + httpsURLConnection.setRequestProperty("X-HTTP-Method-Override", "PATCH"); + httpsURLConnection.setRequestMethod("POST"); + } + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + httpsURLConnection.setDoOutput(true); + + String response; + OutputStream outputStream = httpsURLConnection.getOutputStream(); + outputStream.write(postDataBytes); + if (avatar != null) { + uploadMedia(urlConnection, avatar, null, avatarName); + } + if (header != null) { + uploadMedia(urlConnection, null, header, headerName); + } + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + httpsURLConnection.getInputStream().close(); + return response; + } else { + if (proxy != null) + httpURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpsURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setConnectTimeout(timeout * 1000); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + httpURLConnection.setRequestMethod("PATCH"); + } else { + httpURLConnection.setRequestProperty("X-HTTP-Method-Override", "PATCH"); + httpURLConnection.setRequestMethod("POST"); + } + if (token != null && !token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", token); + httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + httpURLConnection.setDoOutput(true); + + + OutputStream outputStream = httpURLConnection.getOutputStream(); + outputStream.write(postDataBytes); + if (avatar != null) { + uploadMedia(urlConnection, avatar, null, avatarName); + } + if (header != null) { + uploadMedia(urlConnection, null, header, headerName); + } + String response; + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + + throw new HttpsConnectionException(responseCode, error); + } + httpURLConnection.getInputStream().close(); + return response; + } + + } + + public String put(String urlConnection, int timeout, HashMap paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + URL url = new URL(urlConnection); + Map params = new LinkedHashMap<>(); + if (paramaters != null) { + Iterator> it = paramaters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + params.put(pair.getKey(), pair.getValue()); + it.remove(); + } + } + StringBuilder postData = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + if (postData.length() != 0) postData.append('&'); + postData.append(param.getKey()); + postData.append('='); + postData.append(param.getValue()); + } + byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8); + + if (urlConnection.startsWith("https://")) { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpsURLConnection.setRequestMethod("PUT"); + httpsURLConnection.setDoInput(true); + httpsURLConnection.setDoOutput(true); + + httpsURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpsURLConnection.getInputStream()); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return response; + } else { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setConnectTimeout(timeout * 1000); + if (token != null && !token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpURLConnection.setRequestProperty("Authorization", token); + httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpURLConnection.setRequestMethod("PUT"); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + httpURLConnection.getOutputStream().write(postDataBytes); + String response; + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + response = converToString(httpURLConnection.getInputStream()); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + getSinceMaxId(); + httpURLConnection.getInputStream().close(); + return response; + } + + } + + public int delete(String urlConnection, int timeout, HashMap paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException { + + URL url = new URL(urlConnection); + Map params = new LinkedHashMap<>(); + if (paramaters != null) { + Iterator> it = paramaters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + params.put(pair.getKey(), pair.getValue()); + it.remove(); + } + } + StringBuilder postData = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + if (postData.length() != 0) postData.append('&'); + postData.append(param.getKey()); + postData.append('='); + postData.append(param.getValue()); + } + byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8); + + if (urlConnection.startsWith("https://")) { + if (proxy != null) + httpsURLConnection = (HttpsURLConnection) url.openConnection(proxy); + else + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory(this.instance)); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpsURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpsURLConnection.setRequestMethod("DELETE"); + httpsURLConnection.setConnectTimeout(timeout * 1000); + httpsURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpsURLConnection.getOutputStream().write(postDataBytes); + if (httpsURLConnection.getResponseCode() >= 200 && httpsURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + httpsURLConnection.getInputStream().close(); + return httpsURLConnection.getResponseCode(); + } else { + String error = null; + if (httpsURLConnection.getErrorStream() != null) { + InputStream stream = httpsURLConnection.getErrorStream(); + if (stream == null) { + stream = httpsURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpsURLConnection.getResponseCode(); + try { + httpsURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + } else { + if (proxy != null) + httpURLConnection = (HttpURLConnection) url.openConnection(proxy); + else + httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + if (token != null && !token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); + else if (token != null && token.startsWith("Basic ")) + httpsURLConnection.setRequestProperty("Authorization", token); + httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpURLConnection.setRequestMethod("DELETE"); + httpURLConnection.setConnectTimeout(timeout * 1000); + httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length)); + + httpURLConnection.getOutputStream().write(postDataBytes); + + + if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) { + getSinceMaxId(); + httpURLConnection.getInputStream().close(); + return httpURLConnection.getResponseCode(); + } else { + String error = null; + if (httpURLConnection.getErrorStream() != null) { + InputStream stream = httpURLConnection.getErrorStream(); + if (stream == null) { + stream = httpURLConnection.getInputStream(); + } + try (Scanner scanner = new Scanner(stream)) { + scanner.useDelimiter("\\Z"); + if (scanner.hasNext()) { + error = scanner.next(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + int responseCode = httpURLConnection.getResponseCode(); + try { + httpURLConnection.getInputStream().close(); + } catch (Exception ignored) { + } + throw new HttpsConnectionException(responseCode, error); + } + } + } + + public String getSince_id() { + return since_id; + } + + public String getMax_id() { + return max_id; + } + + + private void getSinceMaxId() { + if (Helper.getLiveInstanceWithProtocol(context) == null) + return; + if (Helper.getLiveInstanceWithProtocol(context).startsWith("https://")) { + if (httpsURLConnection == null) + return; + Map> map = httpsURLConnection.getHeaderFields(); + + for (Map.Entry> entry : map.entrySet()) { + if (entry.toString().startsWith("Link") || entry.toString().startsWith("link")) { + Pattern patternMaxId = Pattern.compile("max_id=([0-9a-zA-Z]+).*"); + Matcher matcherMaxId = patternMaxId.matcher(entry.toString()); + if (matcherMaxId.find()) { + max_id = matcherMaxId.group(1); + } + if (entry.toString().startsWith("Link")) { + Pattern patternSinceId = Pattern.compile("since_id=([0-9a-zA-Z]+).*"); + Matcher matcherSinceId = patternSinceId.matcher(entry.toString()); + if (matcherSinceId.find()) { + since_id = matcherSinceId.group(1); + } + + } + } else if (entry.toString().startsWith("Min-Id") || entry.toString().startsWith("min-id")) { + Pattern patternMaxId = Pattern.compile("min-id=\\[([0-9a-zA-Z]+).*]"); + Matcher matcherMaxId = patternMaxId.matcher(entry.toString()); + if (matcherMaxId.find()) { + max_id = matcherMaxId.group(1); + } + } + } + } else { + if (httpURLConnection == null) + return; + Map> map = httpURLConnection.getHeaderFields(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.toString().startsWith("Link") || entry.toString().startsWith("link")) { + Pattern patternMaxId = Pattern.compile("max_id=([0-9a-zA-Z]+).*"); + Matcher matcherMaxId = patternMaxId.matcher(entry.toString()); + if (matcherMaxId.find()) { + max_id = matcherMaxId.group(1); + } + if (entry.toString().startsWith("Link")) { + Pattern patternSinceId = Pattern.compile("since_id=([0-9a-zA-Z]+).*"); + Matcher matcherSinceId = patternSinceId.matcher(entry.toString()); + if (matcherSinceId.find()) { + since_id = matcherSinceId.group(1); + } + + } + } + } + } + } + + private String converToString(InputStream inputStream) { + Scanner s = new Scanner(inputStream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + + int getActionCode() { + if (Helper.getLiveInstanceWithProtocol(context).startsWith("https://")) { + try { + return httpsURLConnection.getResponseCode(); + } catch (IOException e) { + return -1; + } + } else { + try { + return httpURLConnection.getResponseCode(); + } catch (IOException e) { + return -1; + } + } + } + + + public class HttpsConnectionException extends Exception { + + private int statusCode; + private String message; + + HttpsConnectionException(int statusCode, String message) { + this.statusCode = statusCode; + SpannableString spannableString; + if (message != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spannableString = new SpannableString(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY)); + else + spannableString = new SpannableString(Html.fromHtml(message)); + } else { + spannableString = new SpannableString(context.getString(R.string.toast_error)); + } + this.message = spannableString.toString(); + } + + + public int getStatusCode() { + return statusCode; + } + + @Override + public String getMessage() { + return message; + } + + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeAPI.java b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeAPI.java new file mode 100644 index 0000000..fe05669 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeAPI.java @@ -0,0 +1,1983 @@ +package app.fedilab.fedilabtube.client; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.os.Build; +import android.text.Html; +import android.text.SpannableString; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + + +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.AccountCreation; +import app.fedilab.fedilabtube.client.entities.Instance; +import app.fedilab.fedilabtube.client.entities.InstanceNodeInfo; +import app.fedilab.fedilabtube.client.entities.InstanceReg; +import app.fedilab.fedilabtube.client.entities.NodeInfo; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.client.entities.PeertubeAccountNotification; +import app.fedilab.fedilabtube.client.entities.PeertubeActorFollow; +import app.fedilab.fedilabtube.client.entities.PeertubeComment; +import app.fedilab.fedilabtube.client.entities.PeertubeInformation; +import app.fedilab.fedilabtube.client.entities.PeertubeNotification; +import app.fedilab.fedilabtube.client.entities.PeertubeVideoNotification; +import app.fedilab.fedilabtube.client.entities.Playlist; +import app.fedilab.fedilabtube.client.entities.Status; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; + +import app.fedilab.fedilabtube.client.entities.Error; + +/** + * Created by Thomas on 02/01/2019. + * Manage Calls to the Peertube REST API + */ + +public class PeertubeAPI { + + + private Account account; + private Context context; + private int tootPerPage; + private int actionCode; + private String instance; + private String prefKeyOauthTokenT; + private APIResponse apiResponse; + private Error APIError; + + + public enum StatusAction { + FOLLOW, + UNFOLLOW, + RATEVIDEO, + PEERTUBECOMMENT, + PEERTUBEREPLY, + PEERTUBEDELETECOMMENT, + PEERTUBEDELETEVIDEO, + } + + public PeertubeAPI(Context context) { + this.context = context; + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + tootPerPage = Helper.VIDEOS_PER_PAGE; + if (Helper.getLiveInstance(context) != null) + this.instance = Helper.getLiveInstance(context); + else { + SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + 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); + if (account == null) { + apiResponse = new APIResponse(); + APIError = new Error(); + return; + } + this.instance = account.getInstance().trim(); + } + this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + apiResponse = new APIResponse(); + APIError = null; + } + + public PeertubeAPI(Context context, String instance, String token) { + this.context = context; + if (context == null) { + apiResponse = new APIResponse(); + APIError = new Error(); + return; + } + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + tootPerPage = Helper.VIDEOS_PER_PAGE; + if (instance != null) + this.instance = instance; + else + this.instance = Helper.getLiveInstance(context); + + if (token != null) + this.prefKeyOauthTokenT = token; + else + this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + apiResponse = new APIResponse(); + APIError = null; + } + + /** + * Parse json response for unique how to + * + * @param resobj JSONObject + * @return Peertube + */ + private static PeertubeNotification parsePeertubeNotifications(JSONObject resobj) { + PeertubeNotification peertubeNotification = new PeertubeNotification(); + try { + peertubeNotification.setId(resobj.get("id").toString()); + peertubeNotification.setType(resobj.getInt("type")); + peertubeNotification.setUpdatedAt(Helper.mstStringToDate(resobj.get("updatedAt").toString())); + peertubeNotification.setCreatedAt(Helper.mstStringToDate(resobj.get("createdAt").toString())); + peertubeNotification.setRead(resobj.getBoolean("read")); + + if (resobj.has("comment")) { + PeertubeComment peertubeComment = new PeertubeComment(); + JSONObject comment = resobj.getJSONObject("comment"); + if (comment.has("account")) { + JSONObject account = comment.getJSONObject("account"); + PeertubeAccountNotification peertubeAccountNotification = new PeertubeAccountNotification(); + peertubeAccountNotification.setDisplayName(account.get("displayName").toString()); + peertubeAccountNotification.setName(account.get("name").toString()); + peertubeAccountNotification.setId(account.get("id").toString()); + if (account.has("host")) { + peertubeAccountNotification.setHost(account.get("host").toString()); + } + peertubeAccountNotification.setAvatar(account.getJSONObject("avatar").get("path").toString()); + peertubeComment.setPeertubeAccountNotification(peertubeAccountNotification); + } + if (comment.has("video")) { + JSONObject video = comment.getJSONObject("video"); + PeertubeVideoNotification peertubeVideoNotification = new PeertubeVideoNotification(); + peertubeVideoNotification.setUuid(video.get("uuid").toString()); + peertubeVideoNotification.setName(video.get("name").toString()); + peertubeVideoNotification.setId(video.get("id").toString()); + peertubeComment.setPeertubeVideoNotification(peertubeVideoNotification); + } + peertubeComment.setId(comment.get("id").toString()); + peertubeComment.setThreadId(comment.get("threadId").toString()); + peertubeNotification.setPeertubeComment(peertubeComment); + } + + if (resobj.has("video")) { + PeertubeVideoNotification peertubeVideoNotification = new PeertubeVideoNotification(); + JSONObject video = resobj.getJSONObject("video"); + peertubeVideoNotification.setUuid(video.get("uuid").toString()); + peertubeVideoNotification.setName(video.get("name").toString()); + peertubeVideoNotification.setId(video.get("id").toString()); + if (video.has("channel")) { + PeertubeAccountNotification peertubeAccountNotification = new PeertubeAccountNotification(); + JSONObject channel = video.getJSONObject("channel"); + peertubeAccountNotification.setDisplayName(channel.get("displayName").toString()); + peertubeAccountNotification.setName(channel.get("name").toString()); + peertubeAccountNotification.setId(channel.get("id").toString()); + if (channel.has("avatar")) { + peertubeAccountNotification.setAvatar(channel.getJSONObject("avatar").get("path").toString()); + } + peertubeVideoNotification.setPeertubeAccountNotification(peertubeAccountNotification); + } + peertubeNotification.setPeertubeVideoNotification(peertubeVideoNotification); + } + + if (resobj.has("actorFollow")) { + PeertubeActorFollow peertubeActorFollow = new PeertubeActorFollow(); + JSONObject actorFollow = resobj.getJSONObject("actorFollow"); + + JSONObject follower = actorFollow.getJSONObject("follower"); + JSONObject following = actorFollow.getJSONObject("following"); + + PeertubeAccountNotification peertubeAccountNotification = new PeertubeAccountNotification(); + peertubeAccountNotification.setDisplayName(follower.get("displayName").toString()); + peertubeAccountNotification.setName(follower.get("name").toString()); + peertubeAccountNotification.setId(follower.get("id").toString()); + if (follower.has("host")) { + peertubeAccountNotification.setHost(follower.get("host").toString()); + } + if (follower.has("avatar")) { + peertubeAccountNotification.setAvatar(follower.getJSONObject("avatar").get("path").toString()); + } + peertubeActorFollow.setFollower(peertubeAccountNotification); + + PeertubeAccountNotification peertubeAccounFollowingNotification = new PeertubeAccountNotification(); + peertubeAccounFollowingNotification.setDisplayName(following.get("displayName").toString()); + peertubeAccounFollowingNotification.setName(following.get("name").toString()); + try { + peertubeAccounFollowingNotification.setId(following.get("id").toString()); + } catch (Exception ignored) { + } + if (following.has("avatar")) { + peertubeAccounFollowingNotification.setAvatar(following.getJSONObject("avatar").get("path").toString()); + } + peertubeActorFollow.setFollowing(peertubeAccounFollowingNotification); + peertubeActorFollow.setId(actorFollow.get("id").toString()); + peertubeNotification.setPeertubeActorFollow(peertubeActorFollow); + + } + + } catch (JSONException | ParseException e) { + e.printStackTrace(); + } + + return peertubeNotification; + } + + /** + * Parse json response for unique how to + * + * @param resobj JSONObject + * @return Peertube + */ + public static Peertube parsePeertube(JSONObject resobj) { + Peertube peertube = new Peertube(); + if (resobj.has("video")) { + try { + resobj = resobj.getJSONObject("video"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + try { + peertube.setId(resobj.get("id").toString()); + peertube.setCache(resobj); + peertube.setUuid(resobj.get("uuid").toString()); + peertube.setName(resobj.get("name").toString()); + peertube.setDescription(resobj.get("description").toString()); + peertube.setEmbedPath(resobj.get("embedPath").toString()); + peertube.setPreviewPath(resobj.get("previewPath").toString()); + peertube.setThumbnailPath(resobj.get("thumbnailPath").toString()); + peertube.setAccount(parseAccountResponsePeertube(resobj.getJSONObject("account"))); + try { + peertube.setChannel(parseAccountResponsePeertube(resobj.getJSONObject("channel"))); + } catch (Exception ignored) { + } + peertube.setView(Integer.parseInt(resobj.get("views").toString())); + peertube.setLike(Integer.parseInt(resobj.get("likes").toString())); + peertube.setDislike(Integer.parseInt(resobj.get("dislikes").toString())); + peertube.setDuration(Integer.parseInt(resobj.get("duration").toString())); + peertube.setSensitive(Boolean.parseBoolean(resobj.get("nsfw").toString())); + try { + peertube.setCommentsEnabled(Boolean.parseBoolean(resobj.get("commentsEnabled").toString())); + } catch (Exception ignored) { + } + + try { + peertube.setCreated_at(Helper.mstStringToDate(resobj.get("createdAt").toString())); + } catch (ParseException e) { + e.printStackTrace(); + } + + try { + LinkedHashMap langue = new LinkedHashMap<>(); + LinkedHashMap category = new LinkedHashMap<>(); + LinkedHashMap license = new LinkedHashMap<>(); + LinkedHashMap privacy = new LinkedHashMap<>(); + category.put(resobj.getJSONObject("category").getInt("id"), resobj.getJSONObject("category").get("label").toString()); + license.put(resobj.getJSONObject("licence").getInt("id"), resobj.getJSONObject("licence").get("label").toString()); + privacy.put(resobj.getJSONObject("privacy").getInt("id"), resobj.getJSONObject("privacy").get("label").toString()); + langue.put(resobj.getJSONObject("language").get("id").toString(), resobj.getJSONObject("language").get("label").toString()); + + peertube.setCategory(category); + peertube.setLicense(license); + peertube.setLanguage(langue); + peertube.setPrivacy(privacy); + } catch (Exception ignored) { + } + } catch (JSONException e) { + e.printStackTrace(); + } + + return peertube; + } + + /** + * Parse json response for unique how to + * + * @param resobj JSONObject + * @return Peertube + */ + private static Peertube parseSinglePeertube(String instance, JSONObject resobj) { + Peertube peertube = new Peertube(); + try { + peertube.setId(resobj.get("id").toString()); + peertube.setUuid(resobj.get("uuid").toString()); + peertube.setName(resobj.get("name").toString()); + peertube.setCache(resobj); + peertube.setInstance(instance); + peertube.setHost(resobj.getJSONObject("account").get("host").toString()); + peertube.setDescription(resobj.get("description").toString()); + peertube.setEmbedPath(resobj.get("embedPath").toString()); + peertube.setPreviewPath(resobj.get("previewPath").toString()); + peertube.setThumbnailPath(resobj.get("thumbnailPath").toString()); + peertube.setView(Integer.parseInt(resobj.get("views").toString())); + peertube.setLike(Integer.parseInt(resobj.get("likes").toString())); + peertube.setCommentsEnabled(Boolean.parseBoolean(resobj.get("commentsEnabled").toString())); + peertube.setDislike(Integer.parseInt(resobj.get("dislikes").toString())); + peertube.setDuration(Integer.parseInt(resobj.get("duration").toString())); + peertube.setAccount(parseAccountResponsePeertube(resobj.getJSONObject("account"))); + List tags = new ArrayList<>(); + try { + JSONArray tagsA = resobj.getJSONArray("tags"); + for (int i = 0; i < tagsA.length(); i++) { + String value = tagsA.getString(i); + tags.add(value); + } + peertube.setTags(tags); + } catch (Exception ignored) { + } + try { + peertube.setChannel(parseAccountResponsePeertube(resobj.getJSONObject("channel"))); + } catch (Exception ignored) { + } + peertube.setSensitive(Boolean.parseBoolean(resobj.get("nsfw").toString())); + try { + peertube.setCreated_at(Helper.mstStringToDate(resobj.get("createdAt").toString())); + } catch (ParseException e) { + e.printStackTrace(); + } + + try { + peertube.setCreated_at(Helper.mstStringToDate(resobj.get("createdAt").toString())); + } catch (ParseException e) { + e.printStackTrace(); + } + ArrayList resolutions = new ArrayList<>(); + if (resobj.has("streamingPlaylists") && resobj.getJSONArray("streamingPlaylists").length() > 0) { + JSONArray files = resobj.getJSONArray("streamingPlaylists").getJSONObject(0).getJSONArray("files"); + + for (int j = 0; j < files.length(); j++) { + JSONObject attObj = files.getJSONObject(j); + resolutions.add(attObj.getJSONObject("resolution").get("id").toString()); + } + peertube.setResolution(resolutions); + peertube.setStreamService(true); + } else { + JSONArray files = resobj.getJSONArray("files"); + for (int j = 0; j < files.length(); j++) { + JSONObject attObj = files.getJSONObject(j); + resolutions.add(attObj.getJSONObject("resolution").get("id").toString()); + } + peertube.setResolution(resolutions); + peertube.setStreamService(false); + } + try { + LinkedHashMap langue = new LinkedHashMap<>(); + LinkedHashMap category = new LinkedHashMap<>(); + LinkedHashMap license = new LinkedHashMap<>(); + LinkedHashMap privacy = new LinkedHashMap<>(); + category.put(resobj.getJSONObject("category").getInt("id"), resobj.getJSONObject("category").get("label").toString()); + license.put(resobj.getJSONObject("licence").getInt("id"), resobj.getJSONObject("licence").get("label").toString()); + privacy.put(resobj.getJSONObject("privacy").getInt("id"), resobj.getJSONObject("privacy").get("label").toString()); + langue.put(resobj.getJSONObject("language").get("id").toString(), resobj.getJSONObject("language").get("label").toString()); + + peertube.setCategory(category); + peertube.setLicense(license); + peertube.setLanguage(langue); + peertube.setPrivacy(privacy); + } catch (Exception ignored) { + } + peertube.setResolution(resolutions); + } catch (JSONException e) { + e.printStackTrace(); + } + + return peertube; + } + + + /** + * Parse json response for emoji + * + * @param resobj JSONObject + * @return Emojis + */ + private static Playlist parsePlaylist(Context context, JSONObject resobj) { + Playlist playlist = new Playlist(); + try { + 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(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) { + } + try { + playlist.setUpdatedAt(Helper.stringToDate(context, resobj.getString("updatedAt"))); + } catch (Exception ignored) { + } + } catch (Exception ignored) { + } + return playlist; + } + + /** + * Parse json response an unique peertube account + * + * @param accountObject JSONObject + * @return Account + */ + private static Account parseAccountResponsePeertube(JSONObject accountObject) { + Account account = new Account(); + try { + account.setId(accountObject.get("id").toString()); + account.setUuid(accountObject.get("id").toString()); + account.setUsername(accountObject.get("name").toString()); + account.setAcct(accountObject.get("name").toString() + "@" + accountObject.get("host")); + account.setDisplay_name(accountObject.get("name").toString()); + account.setHost(accountObject.get("host").toString()); + account.setSocial("PEERTUBE"); + + if (accountObject.has("createdAt")) + account.setCreated_at(Helper.mstStringToDate(accountObject.get("createdAt").toString())); + else + account.setCreated_at(new Date()); + if (accountObject.has("followersCount")) + account.setFollowers_count(accountObject.getInt("followersCount")); + else + account.setFollowers_count(0); + if (accountObject.has("followingCount")) + account.setFollowing_count(accountObject.getInt("followingCount")); + else + account.setFollowing_count(0); + account.setStatuses_count(0); + if (accountObject.has("description")) + account.setNote(accountObject.get("description").toString()); + else + account.setNote(""); + + account.setUrl(accountObject.get("url").toString()); + if (accountObject.has("avatar") && !accountObject.isNull("avatar")) { + account.setAvatar(accountObject.getJSONObject("avatar").get("path").toString()); + account.setAvatar_static(accountObject.getJSONObject("avatar").get("path").toString()); + } else { + account.setAvatar("null"); + account.setAvatar_static("null"); + } + account.setHeader("null"); + account.setHeader_static("null"); + + } catch (JSONException | ParseException e) { + e.printStackTrace(); + } + return account; + } + + + /*** + * Get info on the current Instance *synchronously* + * @return APIResponse + */ + public APIResponse getInstance() { + try { + String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/instance"), 30, null, prefKeyOauthTokenT); + Instance instanceEntity = parseInstance(new JSONObject(response)); + apiResponse.setInstance(instanceEntity); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return apiResponse; + } + + /** + * Update video meta data *synchronously* + * + * @param peertube Peertube + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + public APIResponse updateVideo(Peertube peertube) { + + LinkedHashMap params = new LinkedHashMap<>(); + + params.put("name", peertube.getName()); + //Category + Map.Entry categoryM = peertube.getCategory().entrySet().iterator().next(); + Integer idCategory = categoryM.getKey(); + params.put("category", String.valueOf(idCategory)); + //License + Map.Entry licenseM = peertube.getLicense().entrySet().iterator().next(); + Integer idLicense = licenseM.getKey(); + params.put("licence", String.valueOf(idLicense)); + //language + Map.Entry languagesM = peertube.getLanguage().entrySet().iterator().next(); + String iDlanguage = languagesM.getKey(); + params.put("language", iDlanguage); + params.put("support", "null"); + params.put("description", peertube.getDescription()); + //Channel + Map.Entry channelsM = peertube.getChannelForUpdate().entrySet().iterator().next(); + String iDChannel = channelsM.getValue(); + params.put("channelId", iDChannel); + //Privacy + Map.Entry privacyM = peertube.getPrivacy().entrySet().iterator().next(); + Integer idPrivacy = privacyM.getKey(); + params.put("privacy", String.valueOf(idPrivacy)); + if (peertube.getTags() != null && peertube.getTags().size() > 0) { + StringBuilder parameters = new StringBuilder(); + parameters.append("[]&"); + for (String tag : peertube.getTags()) + parameters.append("tags=").append(tag).append("&"); + String strParam = parameters.toString(); + strParam = strParam.substring(0, strParam.length() - 1); + params.put("tags[]", strParam); + } else { + params.put("tags", "null"); + } + params.put("nsfw", String.valueOf(peertube.isSensitive())); + params.put("waitTranscoding", "true"); + params.put("commentsEnabled", String.valueOf(peertube.isCommentsEnabled())); + params.put("scheduleUpdate", "null"); + List peertubes = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + httpsConnection.put(getAbsoluteUrl(String.format("/videos/%s", peertube.getId())), 60, params, prefKeyOauthTokenT); + peertubes.add(peertube); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + e.printStackTrace(); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /*** + * Verifiy PeertubeInformation of the authenticated user *synchronously* + * @return Account + */ + public PeertubeInformation getPeertubeInformation() throws HttpsConnection.HttpsConnectionException { + PeertubeInformation peertubeInformation = new PeertubeInformation(); + try { + + String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/videos/categories"), 60, null, null); + JSONObject categories = new JSONObject(response); + LinkedHashMap _pcategories = new LinkedHashMap<>(); + for (int i = 1; i <= categories.length(); i++) { + _pcategories.put(i, categories.getString(String.valueOf(i))); + + } + peertubeInformation.setCategories(_pcategories); + + response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/videos/languages"), 60, null, null); + JSONObject languages = new JSONObject(response); + LinkedHashMap _languages = new LinkedHashMap<>(); + Iterator iter = languages.keys(); + while (iter.hasNext()) { + String key = iter.next(); + try { + _languages.put(key, (String) languages.get(key)); + } catch (JSONException ignored) { + } + } + peertubeInformation.setLanguages(_languages); + + response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/videos/privacies"), 60, null, null); + JSONObject privacies = new JSONObject(response); + LinkedHashMap _pprivacies = new LinkedHashMap<>(); + for (int i = 1; i <= privacies.length(); i++) { + _pprivacies.put(i, privacies.getString(String.valueOf(i))); + + } + 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<>(); + for (int i = 1; i <= licences.length(); i++) { + _plicences.put(i, licences.getString(String.valueOf(i))); + + } + peertubeInformation.setLicences(_plicences); + + + String instance = Helper.getLiveInstance(context); + String lang = null; + if (PeertubeInformation.langueMapped.containsKey(Locale.getDefault().getLanguage())) + lang = PeertubeInformation.langueMapped.get(Locale.getDefault().getLanguage()); + + if (lang != null && !lang.startsWith("en")) { + response = new HttpsConnection(context, this.instance).get(String.format("https://" + instance + "/client/locales/%s/server.json", lang), 60, null, null); + JSONObject translations = new JSONObject(response); + LinkedHashMap _translations = new LinkedHashMap<>(); + Iterator itertrans = translations.keys(); + while (itertrans.hasNext()) { + String key = itertrans.next(); + try { + _translations.put(key, (String) translations.get(key)); + } catch (JSONException ignored) { + } + } + peertubeInformation.setTranslations(_translations); + } + + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return peertubeInformation; + } + + /*** + * Verifiy credential of the authenticated user *synchronously* + * @return Account + */ + public Account verifyCredentials() { + account = new Account(); + InstanceNodeInfo nodeinfo = displayNodeInfo(instance); + String social = null; + if (nodeinfo != null) { + social = nodeinfo.getName(); + } + try { + String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/users/me"), 60, null, prefKeyOauthTokenT); + JSONObject accountObject = new JSONObject(response).getJSONObject("account"); + account = parseAccountResponsePeertube(accountObject); + if (social != null) { + account.setSocial(social.toUpperCase()); + } + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e) { + if (e.getStatusCode() == 401 || e.getStatusCode() == 403) { + SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account targetedAccount = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT); + if (targetedAccount != null) { + HashMap values = refreshToken(targetedAccount.getClient_id(), targetedAccount.getClient_secret(), targetedAccount.getRefresh_token()); + if (values.containsKey("access_token") && values.get("access_token") != null) { + targetedAccount.setToken(values.get("access_token")); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + //This account is currently logged in, the token is updated + if (prefKeyOauthTokenT != null && 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); + + String response; + try { + response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/users/me"), 60, null, targetedAccount.getToken()); + JSONObject accountObject = new JSONObject(response).getJSONObject("account"); + account = parseAccountResponsePeertube(accountObject); + if (social != null) { + account.setSocial(social.toUpperCase()); + } + } catch (IOException | NoSuchAlgorithmException | KeyManagementException | JSONException e1) { + e1.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e1) { + e1.printStackTrace(); + setError(e.getStatusCode(), e); + } + } else { + setError(e.getStatusCode(), e); + } + e.printStackTrace(); + } + } + return account; + } + + + + public InstanceNodeInfo displayNodeInfo(String domain) { + + String response; + InstanceNodeInfo instanceNodeInfo = new InstanceNodeInfo(); + if (domain.startsWith("http://")) { + domain = domain.replace("http://", ""); + } + if (domain.startsWith("https://")) { + domain = domain.replace("https://", ""); + } + try { + response = new HttpsConnection(context, domain).get("https://" + domain + "/.well-known/nodeinfo", 30, null, null); + JSONArray jsonArray = new JSONObject(response).getJSONArray("links"); + ArrayList nodeInfos = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + + JSONObject resobj = jsonArray.getJSONObject(i); + NodeInfo nodeInfo = new NodeInfo(); + nodeInfo.setHref(resobj.getString("href")); + nodeInfo.setRel(resobj.getString("rel")); + i++; + nodeInfos.add(nodeInfo); + } + if (nodeInfos.size() > 0) { + NodeInfo nodeInfo = nodeInfos.get(nodeInfos.size() - 1); + response = new HttpsConnection(context, this.instance).get(nodeInfo.getHref(), 30, null, null); + JSONObject resobj = new JSONObject(response); + JSONObject jsonObject = resobj.getJSONObject("software"); + String name = jsonObject.getString("name").toUpperCase(); + if (name.compareTo("CORGIDON") == 0) { + name = "MASTODON"; + } + instanceNodeInfo.setName(name); + instanceNodeInfo.setVersion(jsonObject.getString("version")); + instanceNodeInfo.setOpenRegistrations(resobj.getBoolean("openRegistrations")); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } catch (IOException | JSONException | NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + try { + response = new HttpsConnection(context, this.instance).get("https://" + domain + "/api/v1/instance", 30, null, null); + JSONObject jsonObject = new JSONObject(response); + instanceNodeInfo.setName("MASTODON"); + instanceNodeInfo.setVersion(jsonObject.getString("version")); + instanceNodeInfo.setOpenRegistrations(true); + } catch (IOException e1) { + instanceNodeInfo.setConnectionError(true); + e1.printStackTrace(); + } catch (NoSuchAlgorithmException | JSONException | KeyManagementException e1) { + e1.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e1) { + if (e1.getStatusCode() == 404 || e1.getStatusCode() == 501) { + instanceNodeInfo.setName("GNU"); + instanceNodeInfo.setVersion("unknown"); + instanceNodeInfo.setOpenRegistrations(true); + e1.printStackTrace(); + } else { + instanceNodeInfo.setName("MASTODON"); + instanceNodeInfo.setVersion("3.0"); + instanceNodeInfo.setOpenRegistrations(false); + } + } + } catch (HttpsConnection.HttpsConnectionException e) { + try { + response = new HttpsConnection(context, this.instance).get("https://" + domain + "/api/v1/instance", 30, null, null); + JSONObject jsonObject = new JSONObject(response); + instanceNodeInfo.setName("MASTODON"); + instanceNodeInfo.setVersion(jsonObject.getString("version")); + instanceNodeInfo.setOpenRegistrations(true); + } catch (IOException e1) { + instanceNodeInfo.setConnectionError(true); + e1.printStackTrace(); + } catch (NoSuchAlgorithmException | JSONException | KeyManagementException e1) { + e1.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e1) { + if (e1.getStatusCode() == 404 || e1.getStatusCode() == 501) { + instanceNodeInfo.setName("GNU"); + instanceNodeInfo.setVersion("unknown"); + instanceNodeInfo.setOpenRegistrations(true); + e1.printStackTrace(); + } else { + instanceNodeInfo.setName("MASTODON"); + instanceNodeInfo.setVersion("3.0"); + instanceNodeInfo.setOpenRegistrations(false); + } + } + e.printStackTrace(); + } + return instanceNodeInfo; + } + + public APIResponse createAccount(AccountCreation accountCreation) { + apiResponse = new APIResponse(); + + try { + HashMap params = new HashMap<>(); + params.put("username", accountCreation.getUsername()); + params.put("email", accountCreation.getEmail()); + params.put("password", accountCreation.getPassword()); + new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/users/register"), 30, params, null); + + } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) { + e.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + e.printStackTrace(); + } + return apiResponse; + } + + /*** + * Verifiy credential of the authenticated user *synchronously* + * @return Account + */ + private HashMap refreshToken(String client_id, String client_secret, String refresh_token) { + account = new Account(); + HashMap params = new HashMap<>(); + HashMap newValues = new HashMap<>(); + params.put("grant_type", "refresh_token"); + params.put("client_id", client_id); + params.put("client_secret", client_secret); + params.put("refresh_token", refresh_token); + try { + String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/users/token"), 60, params, null); + JSONObject resobj = new JSONObject(response); + String token = resobj.get("access_token").toString(); + if (resobj.has("refresh_token")) + refresh_token = resobj.get("refresh_token").toString(); + newValues.put("access_token", token); + newValues.put("refresh_token", refresh_token); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException | HttpsConnection.HttpsConnectionException e) { + e.printStackTrace(); + } + return newValues; + } + + /** + * Returns an account + * + * @param accountId String account fetched + * @return Account entity + */ + public Account getAccount(String accountId) { + + account = new Account(); + try { + String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/accounts/%s", accountId)), 60, null, prefKeyOauthTokenT); + account = parseAccountResponsePeertube(new JSONObject(response)); + } catch (HttpsConnection.HttpsConnectionException e) { + e.printStackTrace(); + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return account; + } + + /** + * Returns a relationship between the authenticated account and an account + * + * @param uri String accounts fetched + * @return Relationship entity + */ + public boolean isFollowing(String uri) { + HashMap params = new HashMap<>(); + + params.put("uris", uri); + + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl("/users/me/subscriptions/exist"), 60, params, prefKeyOauthTokenT); + return new JSONObject(response).getBoolean(uri); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return false; + } + + /** + * Retrieves videos for the account *synchronously* + * + * @param acct String Id of the account + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getVideos(String acct, String max_id) { + 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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /** + * Retrieves videos for the account *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getMyVideos(String max_id) { + return getMyVideos(max_id, null); + } + + /** + * Retrieves status for the account *synchronously* + * + * @param max_id String id max + * @param since_id String since the id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + 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); + 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/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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /** + * Retrieves status for the account *synchronously* + * + * @param acct String Id of the account + * @param max_id String id max + * @param since_id String since the id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + 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); + params.put("count", String.valueOf(tootPerPage)); + List peertubes = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl(String.format("/accounts/%s/videos", acct)), 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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /** + * Retrieves Peertube notifications for the account *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getNotifications(String max_id) { + return getNotifications(max_id, null); + } + + /** + * Retrieves Peertube notifications since id for the account *synchronously* + * + * @param since_id String id since + * @return APIResponse + */ + public APIResponse getNotificationsSince(String since_id) { + return getNotifications(null, since_id); + } + + /** + * Retrieves Peertube notifications for the account *synchronously* + * + * @param max_id String id max + * @param since_id String since the id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + 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); + params.put("count", String.valueOf(tootPerPage)); + List peertubeNotifications = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl("/users/me/notifications"), 60, params, prefKeyOauthTokenT); + JSONArray jsonArray = new JSONObject(response).getJSONArray("data"); + peertubeNotifications = parsePeertubeNotifications(jsonArray); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + e.printStackTrace(); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubeNotifications(peertubeNotifications); + return apiResponse; + } + + /** + * Retrieves videos channel for the account *synchronously* + * + * @param acct String Id of the account + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getVideosChannel(String acct, String max_id) { + return getVideosChannel(acct, max_id, null); + } + + /** + * Retrieves status for the account *synchronously* + * + * @param acct String Id of the account + * @param max_id String id max + * @param since_id String since the id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + 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); + params.put("count", String.valueOf(tootPerPage)); + List peertubes = new ArrayList<>(); + try { + + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl(String.format("/video-channels/%s/videos", acct)), 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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /** + * Retrieves subscription videos *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getSubscriptionsTL(String max_id) { + try { + return getTL("/users/me/subscriptions/videos", "-publishedAt", null, max_id, null, null); + } catch (HttpsConnection.HttpsConnectionException e) { + SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account targetedAccount = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT); + if (targetedAccount != null && (e.getStatusCode() == 401 || e.getStatusCode() == 403)) { + 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")); + //This account is currently logged in, the token is updated + SharedPreferences.Editor editor = sharedpreferences.edit(); + prefKeyOauthTokenT = targetedAccount.getToken(); + 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; + } + } + + /** + * Retrieves overview videos *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getOverviewTL(String max_id) { + try { + return getTL("/overviews/videos", null, null, max_id, null, null); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + return apiResponse; + } + } + + /** + * Retrieves trending videos *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getTrendingTL(String max_id) { + try { + return getTL("/videos/", "-trending", null, max_id, null, null); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + return apiResponse; + } + } + + /** + * Retrieves trending videos *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getRecentlyAddedTL(String max_id) { + try { + return getTL("/videos/", "-publishedAt", null, max_id, null, null); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + return apiResponse; + } + } + + /** + * Retrieves trending videos *synchronously* + * + * @param max_id String id max + * @return APIResponse + */ + public APIResponse getLocalTL(String max_id) { + try { + return getTL("/videos/", "-publishedAt", "local", max_id, null, null); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + return apiResponse; + } + } + + + /** + * Retrieves home timeline for the account *synchronously* + * + * @param max_id String id max + * @param since_id String since the id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + 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) + params.put("start", max_id); + if (since_id != null) + params.put("since_id", since_id); + if (min_id != null) + params.put("min_id", min_id); + params.put("count", String.valueOf(tootPerPage)); + if (sort != null) + params.put("sort", sort); + else + params.put("sort", "publishedAt"); + if (filter != null) + params.put("filter", filter); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean nsfw = sharedpreferences.getBoolean(Helper.SET_VIDEO_NSFW, false); + params.put("nsfw", String.valueOf(nsfw)); + List peertubes = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl(action), 60, params, prefKeyOauthTokenT); + if (!action.equals("/overviews/videos")) { + JSONArray values = new JSONObject(response).getJSONArray("data"); + peertubes = parsePeertube(values); + } else { + JSONArray categories = new JSONObject(response).getJSONArray("categories"); + JSONArray channels = new JSONObject(response).getJSONArray("channels"); + JSONArray tags = new JSONObject(response).getJSONArray("tags"); + + for (int i = 0; i < categories.length(); i++) { + JSONArray categoriesVideos = categories.getJSONObject(i).getJSONArray("videos"); + List peertubeCategories = parsePeertube(categoriesVideos); + if (peertubeCategories.size() > 0) { + peertubeCategories.get(0).setHeaderType("categories"); + peertubeCategories.get(0).setHeaderTypeValue(categories.getJSONObject(i).getJSONObject("category").getString("label")); + peertubes.addAll(peertubeCategories); + } + } + + + for (int i = 0; i < channels.length(); i++) { + JSONArray channelsVideos = channels.getJSONObject(i).getJSONArray("videos"); + List peertubeChannels = parsePeertube(channelsVideos); + if (peertubeChannels.size() > 0) { + peertubeChannels.get(0).setHeaderType("channels"); + peertubeChannels.get(0).setHeaderTypeValue(channels.getJSONObject(i).getJSONObject("channel").getString("displayName")); + peertubes.addAll(peertubeChannels); + } + } + + for (int i = 0; i < tags.length(); i++) { + JSONArray tagsVideos = tags.getJSONObject(i).getJSONArray("videos"); + List peertubeTags = parsePeertube(tagsVideos); + if (peertubeTags.size() > 0) { + peertubeTags.get(0).setHeaderType("tags"); + peertubeTags.get(0).setHeaderTypeValue(tags.getJSONObject(i).getString("tag")); + peertubes.addAll(peertubeTags); + } + } + + + } + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + /** + * Retrieves Peertube channel from an account *synchronously* + * Peertube channels are dealt like accounts + * + * @return APIResponse + */ + public APIResponse getPeertubeChannel(String name) { + + List accounts = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl(String.format("/accounts/%s/video-channels", name)), 60, null, null); + JSONArray jsonArray = new JSONObject(response).getJSONArray("data"); + accounts = parseAccountResponsePeertube(jsonArray); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setAccounts(accounts); + return apiResponse; + } + + /** + * Retrieves Peertube videos from an instance *synchronously* + * + * @return APIResponse + */ + public APIResponse getSinglePeertube(String instance, String videoId, String token) { + + Peertube peertube = null; + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/videos/%s", videoId), 60, null, token); + JSONObject jsonObject = new JSONObject(response); + peertube = parseSinglePeertube(instance, jsonObject); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + List peertubes = new ArrayList<>(); + peertubes.add(peertube); + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + + /** + * Retrieves rating of user on a video *synchronously* + * + * @param id String id + * @return APIResponse + */ + @SuppressWarnings("SameParameterValue") + public String getRating(String id) { + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUrl(String.format("/users/me/videos/%s/rating", id)), 60, null, prefKeyOauthTokenT); + return new JSONObject(response).get("rating").toString(); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return null; + } + + /** + * Makes the post action for a status + * + * @param statusAction Enum + * @param targetedId String id of the targeted Id *can be this of a status or an account* + * @return in status code - Should be equal to 200 when action is done + */ + public int postAction(StatusAction statusAction, String targetedId) { + return postAction(statusAction, targetedId, null, null); + } + + public int postRating(String targetedId, String actionMore) { + return postAction(StatusAction.RATEVIDEO, targetedId, actionMore, null); + } + + public int postComment(String targetedId, String actionMore) { + return postAction(StatusAction.PEERTUBECOMMENT, targetedId, actionMore, null); + } + + public int postReply(String targetedId, String actionMore, String targetedComment) { + return postAction(StatusAction.PEERTUBEREPLY, targetedId, actionMore, targetedComment); + } + + public int deleteComment(String targetedId, String targetedComment) { + return postAction(StatusAction.PEERTUBEDELETECOMMENT, targetedId, null, targetedComment); + } + + public int deleteVideo(String targetedId) { + return postAction(StatusAction.PEERTUBEDELETEVIDEO, targetedId, null, null); + } + + /** + * Makes the post action + * + * @param statusAction Enum + * @param targetedId String id of the targeted Id *can be this of a status or an account* + * @param actionMore String another action + * @param targetedComment String another action + * @return in status code - Should be equal to 200 when action is done + */ + private int postAction(StatusAction statusAction, String targetedId, String actionMore, String targetedComment) { + + String action; + String actionCall = "POST"; + HashMap params = null; + switch (statusAction) { + case FOLLOW: + action = "/users/me/subscriptions"; + params = new HashMap<>(); + params.put("uri", targetedId); + break; + case UNFOLLOW: + action = String.format("/users/me/subscriptions/%s", targetedId); + actionCall = "DELETE"; + break; + case RATEVIDEO: + action = String.format("/videos/%s/rate", targetedId); + params = new HashMap<>(); + params.put("rating", actionMore); + actionCall = "PUT"; + break; + case PEERTUBECOMMENT: + action = String.format("/videos/%s/comment-threads", targetedId); + params = new HashMap<>(); + params.put("text", actionMore); + break; + case PEERTUBEDELETECOMMENT: + action = String.format("/videos/%s/comments/%s", targetedId, targetedComment); + actionCall = "DELETE"; + break; + case PEERTUBEDELETEVIDEO: + action = String.format("/videos/%s", targetedId); + actionCall = "DELETE"; + break; + case PEERTUBEREPLY: + action = String.format("/videos/%s/comment/%s", targetedId, targetedComment); + params = new HashMap<>(); + params.put("text", actionMore); + break; + default: + return -1; + } + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + switch (actionCall) { + case "POST": + httpsConnection.post(getAbsoluteUrl(action), 60, params, prefKeyOauthTokenT); + break; + case "DELETE": + httpsConnection.delete(getAbsoluteUrl(action), 60, null, prefKeyOauthTokenT); + break; + default: + httpsConnection.put(getAbsoluteUrl(action), 60, params, prefKeyOauthTokenT); + break; + } + actionCode = httpsConnection.getActionCode(); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) { + e.printStackTrace(); + } + return actionCode; + } + + + /** + * Video is in play lists + * + * @return APIResponse + */ + public APIResponse getPlaylistForVideo(String videoId) { + + HashMap params = new HashMap<>(); + params.put("videoIds", videoId); + List ids = new ArrayList<>(); + try { + 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 | IOException | KeyManagementException | 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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPlaylists(playlists); + return apiResponse; + } + + /** + * 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("/video-playlists/%s", playlistId)), 60, null, prefKeyOauthTokenT); + actionCode = httpsConnection.getActionCode(); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | 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); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) { + e.printStackTrace(); + } + return actionCode; + } + + /** + * 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 | KeyManagementException | IOException 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 + */ + @SuppressWarnings("SameParameterValue") + public APIResponse getPlaylistVideos(String playlistid, 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)); + params.put("sort", "-updatedAt"); + List peertubes = new ArrayList<>(); + try { + 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 | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + + /** + * Retrieves Peertube videos from an instance *synchronously* + * + * @return APIResponse + */ + public APIResponse getSinglePeertubeComments(String instance, String videoId) { + List statuses = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/videos/%s/comment-threads", videoId), 10, null, null); + JSONObject jsonObject = new JSONObject(response); + statuses = parseSinglePeertubeComments(context, instance, jsonObject); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setStatuses(statuses); + return apiResponse; + } + + + /** + * Parse json response for peertube comments + * + * @param resobj JSONObject + * @return Peertube + */ + private static List parseSinglePeertubeComments(Context context, String instance, JSONObject resobj) { + List statuses = new ArrayList<>(); + try { + JSONArray jsonArray = resobj.getJSONArray("data"); + int i = 0; + while (i < jsonArray.length()) { + Status status = new Status(); + JSONObject comment = jsonArray.getJSONObject(i); + status.setId(comment.get("id").toString()); + status.setUri(comment.get("url").toString()); + status.setUrl(comment.get("url").toString()); + status.setSensitive(false); + status.setSpoiler_text(""); + status.setContent(context, comment.get("text").toString()); + status.setIn_reply_to_id(comment.get("inReplyToCommentId").toString()); + status.setAccount(parseAccountResponsePeertube(instance, comment.getJSONObject("account"))); + status.setCreated_at(Helper.mstStringToDate(comment.get("createdAt").toString())); + status.setVisibility("public"); + status.setViewType(context); + SpannableString spannableString; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spannableString = new SpannableString(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY)); + else + spannableString = new SpannableString(Html.fromHtml(status.getContent())); + status.setContentSpan(new SpannableString(spannableString)); + i++; + statuses.add(status); + } + } catch (JSONException | ParseException e) { + e.printStackTrace(); + } + return statuses; + } + + + /** + * Parse json response an unique peertube account + * + * @param resobj JSONObject + * @return Account + */ + private static Account parseAccountResponsePeertube(String instance, JSONObject resobj) { + Account account = new Account(); + try { + account.setId(resobj.get("id").toString()); + account.setUsername(resobj.get("name").toString()); + account.setAcct(resobj.get("name").toString() + "@" + resobj.get("host").toString()); + account.setDisplay_name(resobj.get("displayName").toString()); + account.setHost(resobj.get("host").toString()); + if (resobj.has("createdAt")) + account.setCreated_at(Helper.mstStringToDate(resobj.get("createdAt").toString())); + else + account.setCreated_at(new Date()); + + if (resobj.has("followersCount")) + account.setFollowers_count(resobj.getInt("followersCount")); + else + account.setFollowers_count(0); + if (resobj.has("followingCount")) + account.setFollowing_count(resobj.getInt("followingCount")); + else + account.setFollowing_count(0); + account.setStatuses_count(0); + if (resobj.has("description")) + account.setNote(resobj.get("description").toString()); + else + account.setNote(""); + account.setUrl(resobj.get("url").toString()); + account.setSocial("PEERTUBE"); + if (resobj.has("avatar") && !resobj.get("avatar").toString().equals("null")) { + account.setAvatar("https://" + instance + resobj.getJSONObject("avatar").get("path")); + account.setAvatar_static("https://" + instance + resobj.getJSONObject("avatar").get("path")); + } else + account.setAvatar(null); + account.setAvatar_static(resobj.get("avatar").toString()); + } catch (JSONException ignored) { + } catch (ParseException e) { + e.printStackTrace(); + } + return account; + } + + /** + * Retrieves peertube search *synchronously* + * + * @param query String search + * @return APIResponse + */ + public APIResponse searchPeertube(String instance, String query) { + HashMap params = new HashMap<>(); + params.put("count", "50"); + if (query == null) + return null; + try { + params.put("search", URLEncoder.encode(query, "UTF-8")); + } catch (UnsupportedEncodingException e) { + params.put("search", query); + } + + List peertubes = new ArrayList<>(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get("https://" + instance + "/api/v1/search/videos", 10, params, null); + JSONArray jsonArray = new JSONObject(response).getJSONArray("data"); + peertubes = parsePeertube(jsonArray); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + e.printStackTrace(); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + apiResponse.setPeertubes(peertubes); + return apiResponse; + } + + + /** + * Parse json response for peertube notifications + * + * @param jsonArray JSONArray + * @return List + */ + private List parsePeertubeNotifications(JSONArray jsonArray) { + List peertubeNotifications = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + JSONObject resobj = jsonArray.getJSONObject(i); + PeertubeNotification peertubeNotification = parsePeertubeNotifications(resobj); + i++; + peertubeNotifications.add(peertubeNotification); + } + } catch (JSONException e) { + setDefaultError(e); + } + return peertubeNotifications; + } + + /** + * Parse json response for several instance reg + * + * @param jsonArray JSONArray + * @return List + */ + private List parseInstanceReg(JSONArray jsonArray) { + + List instanceRegs = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + JSONObject resobj = jsonArray.getJSONObject(i); + InstanceReg instanceReg = parseInstanceReg(resobj); + i++; + instanceRegs.add(instanceReg); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return instanceRegs; + } + + /** + * Parse json response an unique instance for registering + * + * @param resobj JSONObject + * @return InstanceReg + */ + private InstanceReg parseInstanceReg(JSONObject resobj) { + InstanceReg instanceReg = new InstanceReg(); + try { + instanceReg.setDomain(resobj.getString("host")); + instanceReg.setVersion(resobj.getString("version")); + instanceReg.setDescription(resobj.getString("shortDescription")); + instanceReg.setLanguage(resobj.getString("country")); + instanceReg.setCategory(""); + instanceReg.setProxied_thumbnail(""); + instanceReg.setTotal_users(resobj.getInt("totalUsers")); + instanceReg.setTotalInstanceFollowers(resobj.getInt("totalInstanceFollowers")); + instanceReg.setTotalInstanceFollowing(resobj.getInt("totalInstanceFollowing")); + instanceReg.setLast_week_users(0); + instanceReg.setCountry(resobj.getString("country")); + } catch (JSONException e) { + e.printStackTrace(); + } + return instanceReg; + } + + /** + * Parse json response for several howto + * + * @param jsonArray JSONArray + * @return List + */ + private List parsePeertube(JSONArray jsonArray) { + + List peertubes = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + JSONObject resobj = jsonArray.getJSONObject(i); + Peertube peertube = parsePeertube(resobj); + i++; + peertubes.add(peertube); + } + } catch (JSONException e) { + setDefaultError(e); + } + return peertubes; + } + + /** + * Parse json response an unique instance + * + * @param resobj JSONObject + * @return Instance + */ + private Instance parseInstance(JSONObject resobj) { + + Instance instance = new Instance(); + try { + instance.setUri(resobj.get("uri").toString()); + instance.setTitle(resobj.get("title").toString()); + instance.setDescription(resobj.get("description").toString()); + instance.setEmail(resobj.get("email").toString()); + instance.setVersion(resobj.get("version").toString()); + } catch (JSONException e) { + setDefaultError(e); + } + return instance; + } + + /** + * Parse Playlists + * + * @param jsonArray JSONArray + * @return List of lists + */ + private List parsePlaylists(Context context, JSONArray jsonArray) { + List playlists = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + JSONObject resobj = jsonArray.getJSONObject(i); + Playlist playlist = parsePlaylist(context, resobj); + playlists.add(playlist); + i++; + } + } catch (JSONException e) { + setDefaultError(e); + } + return playlists; + } + + private List parseAccountResponsePeertube(JSONArray jsonArray) { + List accounts = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + JSONObject resobj = jsonArray.getJSONObject(i); + Account account = parseAccountResponsePeertube(resobj); + accounts.add(account); + i++; + } + } catch (JSONException e) { + setDefaultError(e); + } + return accounts; + } + + + /** + * Set the error message + * + * @param statusCode int code + * @param error Throwable error + */ + private void setError(int statusCode, Throwable error) { + APIError = new Error(); + APIError.setStatusCode(statusCode); + String message = statusCode + " - " + error.getMessage(); + try { + JSONObject jsonObject = new JSONObject(Objects.requireNonNull(error.getMessage())); + String errorM = jsonObject.get("error").toString(); + message = "Error " + statusCode + " : " + errorM; + } catch (JSONException e) { + if (error.getMessage().split(".").length > 0) { + String errorM = error.getMessage().split(".")[0]; + message = "Error " + statusCode + " : " + errorM; + } + } + APIError.setError(message); + apiResponse.setError(APIError); + } + + private void setDefaultError(Exception e) { + APIError = new Error(); + if (e.getLocalizedMessage() != null && e.getLocalizedMessage().trim().length() > 0) + APIError.setError(e.getLocalizedMessage()); + else if (e.getMessage() != null && e.getMessage().trim().length() > 0) + APIError.setError(e.getMessage()); + else + APIError.setError(context.getString(R.string.toast_error)); + apiResponse.setError(APIError); + } + + + public Error getError() { + return APIError; + } + + + private String getAbsoluteUrl(String action) { + return Helper.instanceWithProtocol(this.instance) + "/api/v1" + action; + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/TLSSocketFactory.java b/app/src/main/java/app/fedilab/fedilabtube/client/TLSSocketFactory.java new file mode 100644 index 0000000..6df65a0 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/TLSSocketFactory.java @@ -0,0 +1,79 @@ +package app.fedilab.fedilabtube.client; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + + +public class TLSSocketFactory extends SSLSocketFactory { + + private SSLSocketFactory sSLSocketFactory; + private SSLContext sslContext; + + + public TLSSocketFactory(String instance) throws KeyManagementException, NoSuchAlgorithmException { + + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + sSLSocketFactory = sslContext.getSocketFactory(); + + } + + public SSLContext getSSLContext() { + return this.sslContext; + } + + + @Override + public String[] getDefaultCipherSuites() { + return sSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return sSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket()); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(sSLSocketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if ((socket instanceof SSLSocket)) { + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2",}); + } + return socket; + } +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Account.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Account.java new file mode 100644 index 0000000..b25b9f8 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Account.java @@ -0,0 +1,532 @@ + +package app.fedilab.fedilabtube.client.entities; + + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.SpannableString; +import android.text.TextUtils; + + +import org.jetbrains.annotations.NotNull; + +import java.util.Date; + + + +public class Account implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Account createFromParcel(Parcel source) { + return new Account(source); + } + + @Override + public Account[] newArray(int size) { + return new Account[size]; + } + }; + private String id; + private String uuid; + private String username; + private String acct; + private String display_name; + private boolean locked; + private Date created_at; + private Date updated_at; + private int followers_count; + private int following_count; + private int statuses_count; + private String followers_count_str; + private String following_count_str; + private String statuses_count_str; + private String note; + private SpannableString noteSpan; + private String url; + private String avatar; + private String avatar_static; + private String header; + private String header_static; + private String token; + private String instance; + private boolean isFollowing; + private followAction followType = followAction.NOTHING; + private boolean isMakingAction = false; + private Account moved_to_account; + private boolean muting_notifications; + private String host; + private boolean isBot; + private String social; + private String client_id; + private String client_secret; + private String refresh_token; + private boolean isModerator = false; + private boolean isAdmin = false; + private String privacy = "public"; + private boolean sensitive = false; + private String locale; + private String invite_request; + private String created_by_application_id; + private String invited_by_account_id; + + public Account() { + } + + protected Account(Parcel in) { + this.id = in.readString(); + this.uuid = in.readString(); + this.username = in.readString(); + this.acct = in.readString(); + this.display_name = in.readString(); + this.locked = in.readByte() != 0; + long tmpCreated_at = in.readLong(); + this.created_at = tmpCreated_at == -1 ? null : new Date(tmpCreated_at); + long tmpUpdated_at = in.readLong(); + this.updated_at = tmpUpdated_at == -1 ? null : new Date(tmpUpdated_at); + this.followers_count = in.readInt(); + this.following_count = in.readInt(); + this.statuses_count = in.readInt(); + this.followers_count_str = in.readString(); + this.following_count_str = in.readString(); + this.statuses_count_str = in.readString(); + this.note = in.readString(); + this.noteSpan = (SpannableString) TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + this.url = in.readString(); + this.avatar = in.readString(); + this.avatar_static = in.readString(); + this.header = in.readString(); + this.header_static = in.readString(); + this.token = in.readString(); + this.instance = in.readString(); + this.isFollowing = in.readByte() != 0; + int tmpFollowType = in.readInt(); + this.followType = tmpFollowType == -1 ? null : followAction.values()[tmpFollowType]; + this.isMakingAction = in.readByte() != 0; + this.moved_to_account = in.readParcelable(Account.class.getClassLoader()); + this.muting_notifications = in.readByte() != 0; + this.host = in.readString(); + this.isBot = in.readByte() != 0; + this.social = in.readString(); + this.client_id = in.readString(); + this.client_secret = in.readString(); + this.refresh_token = in.readString(); + this.isModerator = in.readByte() != 0; + this.isAdmin = in.readByte() != 0; + this.privacy = in.readString(); + this.sensitive = in.readByte() != 0; + this.locale = in.readString(); + this.invite_request = in.readString(); + this.created_by_application_id = in.readString(); + this.invited_by_account_id = in.readString(); + } + + + @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.username); + dest.writeString(this.acct); + dest.writeString(this.display_name); + dest.writeByte(this.locked ? (byte) 1 : (byte) 0); + dest.writeLong(this.created_at != null ? this.created_at.getTime() : -1); + dest.writeLong(this.updated_at != null ? this.updated_at.getTime() : -1); + dest.writeInt(this.followers_count); + dest.writeInt(this.following_count); + dest.writeInt(this.statuses_count); + dest.writeString(this.followers_count_str); + dest.writeString(this.following_count_str); + dest.writeString(this.statuses_count_str); + dest.writeString(this.note); + TextUtils.writeToParcel(this.noteSpan, dest, flags); + dest.writeString(this.url); + dest.writeString(this.avatar); + dest.writeString(this.avatar_static); + dest.writeString(this.header); + dest.writeString(this.header_static); + dest.writeString(this.token); + dest.writeString(this.instance); + dest.writeByte(this.isFollowing ? (byte) 1 : (byte) 0); + dest.writeInt(this.followType == null ? -1 : this.followType.ordinal()); + dest.writeByte(this.isMakingAction ? (byte) 1 : (byte) 0); + dest.writeParcelable(this.moved_to_account, flags); + dest.writeByte(this.muting_notifications ? (byte) 1 : (byte) 0); + dest.writeString(this.host); + dest.writeByte(this.isBot ? (byte) 1 : (byte) 0); + dest.writeString(this.social); + dest.writeString(this.client_id); + dest.writeString(this.client_secret); + dest.writeString(this.refresh_token); + dest.writeByte(this.isModerator ? (byte) 1 : (byte) 0); + dest.writeByte(this.isAdmin ? (byte) 1 : (byte) 0); + dest.writeString(this.privacy); + dest.writeByte(this.sensitive ? (byte) 1 : (byte) 0); + dest.writeString(this.locale); + dest.writeString(this.invite_request); + dest.writeString(this.created_by_application_id); + dest.writeString(this.invited_by_account_id); + } + + public followAction getFollowType() { + return followType; + } + + public void setFollowType(followAction followType) { + this.followType = followType; + } + + public boolean isMakingAction() { + return isMakingAction; + } + + public void setMakingAction(boolean makingAction) { + isMakingAction = makingAction; + } + + public Account getMoved_to_account() { + return moved_to_account; + } + + public void setMoved_to_account(Account moved_to_account) { + this.moved_to_account = moved_to_account; + } + + public boolean isMuting_notifications() { + return muting_notifications; + } + + public void setMuting_notifications(boolean muting_notifications) { + this.muting_notifications = muting_notifications; + } + + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public boolean isBot() { + return isBot; + } + + public void setBot(boolean bot) { + isBot = bot; + } + + public String getSocial() { + return social; + } + + public void setSocial(String social) { + this.social = social; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getClient_id() { + return client_id; + } + + public void setClient_id(String client_id) { + this.client_id = client_id; + } + + public String getClient_secret() { + return client_secret; + } + + public void setClient_secret(String client_secret) { + this.client_secret = client_secret; + } + + public String getRefresh_token() { + return refresh_token; + } + + public void setRefresh_token(String refresh_token) { + this.refresh_token = refresh_token; + } + + public boolean isModerator() { + return isModerator; + } + + public void setModerator(boolean moderator) { + isModerator = moderator; + } + + public boolean isAdmin() { + return isAdmin; + } + + public void setAdmin(boolean admin) { + isAdmin = admin; + } + + public Date getUpdated_at() { + return updated_at; + } + + public void setUpdated_at(Date updated_at) { + this.updated_at = updated_at; + } + + public String getPrivacy() { + return privacy; + } + + public void setPrivacy(String privacy) { + this.privacy = privacy; + } + + public boolean isSensitive() { + return sensitive; + } + + public void setSensitive(boolean sensitive) { + this.sensitive = sensitive; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public String getInvite_request() { + return invite_request; + } + + public void setInvite_request(String invite_request) { + this.invite_request = invite_request; + } + + public String getCreated_by_application_id() { + return created_by_application_id; + } + + public void setCreated_by_application_id(String created_by_application_id) { + this.created_by_application_id = created_by_application_id; + } + + public String getInvited_by_account_id() { + return invited_by_account_id; + } + + public void setInvited_by_account_id(String invited_by_account_id) { + this.invited_by_account_id = invited_by_account_id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + + public String getAcct() { + return acct; + } + + public void setAcct(String acct) { + this.acct = acct; + } + + public String getDisplay_name() { + return display_name; + } + + public void setDisplay_name(String display_name) { + this.display_name = display_name; + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public Date getCreated_at() { + return created_at; + } + + public void setCreated_at(Date created_at) { + this.created_at = created_at; + } + + public int getFollowers_count() { + return followers_count; + } + + public void setFollowers_count(int followers_count) { + this.followers_count = followers_count; + } + + public int getFollowing_count() { + return following_count; + } + + public void setFollowing_count(int following_count) { + this.following_count = following_count; + } + + public int getStatuses_count() { + return statuses_count; + } + + public void setStatuses_count(int statuses_count) { + this.statuses_count = statuses_count; + } + + public SpannableString getNoteSpan() { + return noteSpan; + } + + public void setNoteSpan(SpannableString noteSpan) { + this.noteSpan = noteSpan; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getAvatar_static() { + return avatar_static; + } + + public void setAvatar_static(String avatar_static) { + this.avatar_static = avatar_static; + } + + public String getHeader() { + return header; + } + + public void setHeader(String header) { + this.header = header; + } + + public String getHeader_static() { + return header_static; + } + + public void setHeader_static(String header_static) { + this.header_static = header_static; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public boolean isFollowing() { + return isFollowing; + } + + public void setFollowing(boolean following) { + isFollowing = following; + } + + public String getFollowers_count_str() { + return followers_count_str; + } + + public void setFollowers_count_str(String followers_count_str) { + this.followers_count_str = followers_count_str; + } + + public String getFollowing_count_str() { + return following_count_str; + } + + public void setFollowing_count_str(String following_count_str) { + this.following_count_str = following_count_str; + } + + public String getStatuses_count_str() { + return statuses_count_str; + } + + public void setStatuses_count_str(String statuses_count_str) { + this.statuses_count_str = statuses_count_str; + } + + + + + @NotNull + public String toString() { + return this.getAcct() + " - " + this.getUrl(); + } + + public enum followAction { + FOLLOW, + NOT_FOLLOW, + BLOCK, + MUTE, + REQUEST_SENT, + NOTHING + } + + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/AccountCreation.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/AccountCreation.java new file mode 100644 index 0000000..d220040 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/AccountCreation.java @@ -0,0 +1,41 @@ +package app.fedilab.fedilabtube.client.entities; + +public class AccountCreation { + + private String username; + private String email; + private String password; + private String passwordConfirm; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Error.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Error.java new file mode 100644 index 0000000..71a092a --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Error.java @@ -0,0 +1,26 @@ + +package app.fedilab.fedilabtube.client.entities; + + + +public class Error { + + private String error = null; + private int statusCode = -1; + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Instance.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Instance.java new file mode 100644 index 0000000..5087307 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Instance.java @@ -0,0 +1,128 @@ + +package app.fedilab.fedilabtube.client.entities; + +import java.util.HashMap; + + + +public class Instance { + + private String uri; + private String title; + private String description; + private String email; + private String version; + private boolean registration; + private boolean approval_required; + private Account contactAccount; + private int userCount; + private int statusCount; + private int domainCount; + private String thumbnail; + + private HashMap poll_limits; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public HashMap getPoll_limits() { + return poll_limits; + } + + public void setPoll_limits(HashMap poll_limits) { + this.poll_limits = poll_limits; + } + + public boolean isRegistration() { + return registration; + } + + public void setRegistration(boolean registration) { + this.registration = registration; + } + + public boolean isApproval_required() { + return approval_required; + } + + public void setApproval_required(boolean approval_required) { + this.approval_required = approval_required; + } + + public Account getContactAccount() { + return contactAccount; + } + + public void setContactAccount(Account contactAccount) { + this.contactAccount = contactAccount; + } + + public int getUserCount() { + return userCount; + } + + public void setUserCount(int userCount) { + this.userCount = userCount; + } + + public int getStatusCount() { + return statusCount; + } + + public void setStatusCount(int statusCount) { + this.statusCount = statusCount; + } + + public int getDomainCount() { + return domainCount; + } + + public void setDomainCount(int domainCount) { + this.domainCount = domainCount; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceNodeInfo.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceNodeInfo.java new file mode 100644 index 0000000..ed6c15e --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceNodeInfo.java @@ -0,0 +1,124 @@ + +package app.fedilab.fedilabtube.client.entities; + + +public class InstanceNodeInfo { + + private String name; + private String title; + private String version; + private boolean openRegistrations; + private boolean connectionError; + private int numberOfUsers = 0; + private int numberOfPosts = 0; + private int numberOfInstance = 0; + private String staffAccountStr; + private Account staffAccount; + private String nodeName; + private String nodeDescription; + private String thumbnail; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public boolean isOpenRegistrations() { + return openRegistrations; + } + + public void setOpenRegistrations(boolean openRegistrations) { + this.openRegistrations = openRegistrations; + } + + public boolean isConnectionError() { + return connectionError; + } + + public void setConnectionError(boolean connectionError) { + this.connectionError = connectionError; + } + + public int getNumberOfUsers() { + return numberOfUsers; + } + + public void setNumberOfUsers(int numberOfUsers) { + this.numberOfUsers = numberOfUsers; + } + + public int getNumberOfPosts() { + return numberOfPosts; + } + + public void setNumberOfPosts(int numberOfPosts) { + this.numberOfPosts = numberOfPosts; + } + + public String getStaffAccountStr() { + return staffAccountStr; + } + + public void setStaffAccountStr(String staffAccountStr) { + this.staffAccountStr = staffAccountStr; + } + + public Account getStaffAccount() { + return staffAccount; + } + + public void setStaffAccount(Account staffAccount) { + this.staffAccount = staffAccount; + } + + public String getNodeName() { + return nodeName; + } + + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + public String getNodeDescription() { + return nodeDescription; + } + + public void setNodeDescription(String nodeDescription) { + this.nodeDescription = nodeDescription; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } + + public int getNumberOfInstance() { + return numberOfInstance; + } + + public void setNumberOfInstance(int numberOfInstance) { + this.numberOfInstance = numberOfInstance; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceReg.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceReg.java new file mode 100644 index 0000000..c395e92 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/InstanceReg.java @@ -0,0 +1,124 @@ +package app.fedilab.fedilabtube.client.entities; + + + +public class InstanceReg { + + private String domain; + private String version; + private String description; + private String language; + private String category; + private String proxied_thumbnail; + private int total_users; + private int last_week_users; + private boolean selected = false; + private int totalVideos; + private String country; + private int totalInstanceFollowers; + private int totalInstanceFollowing; + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getProxied_thumbnail() { + return proxied_thumbnail; + } + + public void setProxied_thumbnail(String proxied_thumbnail) { + this.proxied_thumbnail = proxied_thumbnail; + } + + public int getTotal_users() { + return total_users; + } + + public void setTotal_users(int total_users) { + this.total_users = total_users; + } + + public int getLast_week_users() { + return last_week_users; + } + + public void setLast_week_users(int last_week_users) { + this.last_week_users = last_week_users; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public int getTotalVideos() { + return totalVideos; + } + + public void setTotalVideos(int totalVideos) { + this.totalVideos = totalVideos; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public int getTotalInstanceFollowers() { + return totalInstanceFollowers; + } + + public void setTotalInstanceFollowers(int totalInstanceFollowers) { + this.totalInstanceFollowers = totalInstanceFollowers; + } + + public int getTotalInstanceFollowing() { + return totalInstanceFollowing; + } + + public void setTotalInstanceFollowing(int totalInstanceFollowing) { + this.totalInstanceFollowing = totalInstanceFollowing; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Mention.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Mention.java new file mode 100644 index 0000000..47dd10b --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Mention.java @@ -0,0 +1,80 @@ + +package app.fedilab.fedilabtube.client.entities; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class Mention implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Mention createFromParcel(Parcel in) { + return new Mention(in); + } + + @Override + public Mention[] newArray(int size) { + return new Mention[size]; + } + }; + private String url; + private String username; + private String acct; + private String id; + + private Mention(Parcel in) { + url = in.readString(); + username = in.readString(); + acct = in.readString(); + id = in.readString(); + } + + public Mention() { + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAcct() { + return acct; + } + + public void setAcct(String acct) { + this.acct = acct; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(url); + dest.writeString(username); + dest.writeString(acct); + dest.writeString(id); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/NodeInfo.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/NodeInfo.java new file mode 100644 index 0000000..70138f7 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/NodeInfo.java @@ -0,0 +1,37 @@ +/* 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.fedilabtube.client.entities; + +@SuppressWarnings("unused") +public class NodeInfo { + private String rel; + private String href; + + public String getRel() { + return rel; + } + + public void setRel(String rel) { + this.rel = rel; + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Peertube.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Peertube.java new file mode 100644 index 0000000..f1a04ab --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Peertube.java @@ -0,0 +1,345 @@ +package app.fedilab.fedilabtube.client.entities; + + +import org.json.JSONObject; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; + + +@SuppressWarnings("unused") +public class Peertube { + + private String id; + private String uuid; + private String name; + private String description; + private String host; + private String thumbnailPath; + private String previewPath; + private String embedPath; + private int view; + private int like; + private int dislike; + private Date created_at; + private int duration; + private String instance; + private Account account; + private Account channel; + private List resolution; + private List tags; + private boolean commentsEnabled; + private boolean sensitive; + private HashMap category; + private HashMap license; + private HashMap language; + private HashMap privacy; + private HashMap channelForUpdate; + private String myRating = "none"; + private boolean isUpdate = false; + private String headerType = null; + private String headerTypeValue = null; + private JSONObject cache; + private boolean streamService; + + public Peertube() { + } + + + 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 getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getThumbnailPath() { + return thumbnailPath; + } + + public void setThumbnailPath(String thumbnailPath) { + this.thumbnailPath = thumbnailPath; + } + + public String getPreviewPath() { + return previewPath; + } + + public void setPreviewPath(String previewPath) { + this.previewPath = previewPath; + } + + public String getEmbedPath() { + return embedPath; + } + + public void setEmbedPath(String embedPath) { + this.embedPath = embedPath; + } + + + public int getView() { + return view; + } + + public void setView(int view) { + this.view = view; + } + + public int getLike() { + return like; + } + + public void setLike(int like) { + this.like = like; + } + + public int getDislike() { + return dislike; + } + + public void setDislike(int dislike) { + this.dislike = dislike; + } + + public Date getCreated_at() { + return created_at; + } + + public void setCreated_at(Date created_at) { + this.created_at = created_at; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } + + public String getFileUrl(String resolution, boolean streamService) { + if (resolution == null) + resolution = this.getResolution().get(0); + if (resolution == null) + return null; + if (streamService) { + return "https://" + this.host + "/static/streaming-playlists/hls/" + getUuid() + "/" + getUuid() + "-" + resolution + "-fragmented.mp4"; + } else { + return "https://" + this.host + "/static/webseed/" + getUuid() + "-" + resolution + ".mp4"; + + } + } + + + public String getTorrentUrl(String resolution) { + if (resolution == null) + resolution = this.getResolution().get(0); + if (resolution == null) + return null; + return "https://" + this.host + "/static/torrents/" + getUuid() + "-" + resolution + ".torrent"; + + } + + public String getTorrentDownloadUrl(String resolution) { + if (resolution == null) + resolution = this.getResolution().get(0); + if (resolution == null) + return null; + return "https://" + this.host + "/download/torrents/" + getUuid() + "-" + resolution + ".torrent"; + + } + + public String getFileDownloadUrl(String resolution, boolean streamService) { + if (resolution == null) + resolution = this.getResolution().get(0); + if (resolution == null) + return null; + if (streamService) { + return "https://" + this.host + "/download/streaming-playlists/hls/videos/" + getUuid() + "/" + getUuid() + "-" + resolution + "-fragmented.mp4"; + } else { + return "https://" + this.host + "/download/videos/" + getUuid() + "-" + resolution + ".mp4"; + } + + } + + public List getResolution() { + return resolution; + } + + public void setResolution(List resolution) { + this.resolution = resolution; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public boolean isCommentsEnabled() { + return commentsEnabled; + } + + public void setCommentsEnabled(boolean commentsEnabled) { + this.commentsEnabled = commentsEnabled; + } + + public JSONObject getCache() { + return cache; + } + + public void setCache(JSONObject cache) { + this.cache = cache; + } + + public boolean isSensitive() { + return sensitive; + } + + public void setSensitive(boolean sensitive) { + this.sensitive = sensitive; + } + + + public String getMyRating() { + return myRating; + } + + public void setMyRating(String myRating) { + this.myRating = myRating; + } + + public Account getChannel() { + return channel; + } + + public void setChannel(Account channel) { + this.channel = channel; + } + + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public HashMap getCategory() { + return category; + } + + public void setCategory(HashMap category) { + this.category = category; + } + + public HashMap getLicense() { + return license; + } + + public void setLicense(HashMap license) { + this.license = license; + } + + public HashMap getLanguage() { + return language; + } + + public void setLanguage(HashMap language) { + this.language = language; + } + + public HashMap getPrivacy() { + return privacy; + } + + public void setPrivacy(HashMap privacy) { + this.privacy = privacy; + } + + public HashMap getChannelForUpdate() { + return channelForUpdate; + } + + public void setChannelForUpdate(HashMap channelForUpdate) { + this.channelForUpdate = channelForUpdate; + } + + public boolean isUpdate() { + return isUpdate; + } + + public void setUpdate(boolean update) { + isUpdate = update; + } + + public String getHeaderType() { + return headerType; + } + + public void setHeaderType(String headerType) { + this.headerType = headerType; + } + + public String getHeaderTypeValue() { + return headerTypeValue; + } + + public void setHeaderTypeValue(String headerTypeValue) { + this.headerTypeValue = headerTypeValue; + } + + public boolean isStreamService() { + return streamService; + } + + public void setStreamService(boolean streamService) { + this.streamService = streamService; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeAccountNotification.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeAccountNotification.java new file mode 100644 index 0000000..98fd6ab --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeAccountNotification.java @@ -0,0 +1,62 @@ + +package app.fedilab.fedilabtube.client.entities; + + + +public class PeertubeAccountNotification { + + private String id; + private String avatar; + private String displayName; + private String name; + private String type; + private String host; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeActorFollow.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeActorFollow.java new file mode 100644 index 0000000..19381d8 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeActorFollow.java @@ -0,0 +1,38 @@ + +package app.fedilab.fedilabtube.client.entities; + + +public class PeertubeActorFollow { + + private String id; + private PeertubeAccountNotification follower; + private PeertubeAccountNotification following; + + public PeertubeActorFollow() { + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public PeertubeAccountNotification getFollower() { + return follower; + } + + public void setFollower(PeertubeAccountNotification follower) { + this.follower = follower; + } + + public PeertubeAccountNotification getFollowing() { + return following; + } + + public void setFollowing(PeertubeAccountNotification following) { + this.following = following; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeComment.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeComment.java new file mode 100644 index 0000000..27d4840 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeComment.java @@ -0,0 +1,47 @@ + +package app.fedilab.fedilabtube.client.entities; + + +public class PeertubeComment { + + private String id; + private String threadId; + private PeertubeVideoNotification peertubeVideoNotification; + private PeertubeAccountNotification peertubeAccountNotification; + + public PeertubeComment() { + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getThreadId() { + return threadId; + } + + public void setThreadId(String threadId) { + this.threadId = threadId; + } + + public PeertubeVideoNotification getPeertubeVideoNotification() { + return peertubeVideoNotification; + } + + public void setPeertubeVideoNotification(PeertubeVideoNotification peertubeVideoNotification) { + this.peertubeVideoNotification = peertubeVideoNotification; + } + + public PeertubeAccountNotification getPeertubeAccountNotification() { + return peertubeAccountNotification; + } + + public void setPeertubeAccountNotification(PeertubeAccountNotification peertubeAccountNotification) { + this.peertubeAccountNotification = peertubeAccountNotification; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeInformation.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeInformation.java new file mode 100644 index 0000000..216cc04 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeInformation.java @@ -0,0 +1,83 @@ +package app.fedilab.fedilabtube.client.entities; + +import java.util.LinkedHashMap; + + +public class PeertubeInformation { + + + public static final LinkedHashMap langueMapped; + + static { + LinkedHashMap aMap = new LinkedHashMap<>(); + aMap.put("ca", "ca-ES"); + aMap.put("de", "de-DE"); + aMap.put("en", "en-US"); + aMap.put("es", "es-ES"); + aMap.put("eo", "eo"); + aMap.put("eu", "eu-ES"); + aMap.put("fr", "fr-FR"); + aMap.put("oc", "oc"); + aMap.put("pt", "pt-BR"); + aMap.put("sv", "sv-SE"); + aMap.put("cs", "cs-CZ"); + aMap.put("zh-CN", "zh-Hans-CN"); + aMap.put("zh-TW", "zh-Hans-TW"); + langueMapped = aMap; + } + + private LinkedHashMap categories; + private LinkedHashMap languages; + private LinkedHashMap licences; + private LinkedHashMap privacies; + private LinkedHashMap playlistPrivacies; + private LinkedHashMap translations; + + public LinkedHashMap getTranslations() { + return translations; + } + + public void setTranslations(LinkedHashMap translations) { + this.translations = translations; + } + + public LinkedHashMap getCategories() { + return categories; + } + + public void setCategories(LinkedHashMap categories) { + this.categories = categories; + } + + public LinkedHashMap getLanguages() { + return languages; + } + + public void setLanguages(LinkedHashMap languages) { + this.languages = languages; + } + + public LinkedHashMap getLicences() { + return licences; + } + + public void setLicences(LinkedHashMap licences) { + this.licences = licences; + } + + public LinkedHashMap getPrivacies() { + return privacies; + } + + 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/fedilabtube/client/entities/PeertubeNotification.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeNotification.java new file mode 100644 index 0000000..0031705 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeNotification.java @@ -0,0 +1,85 @@ + +package app.fedilab.fedilabtube.client.entities; + + +import java.util.Date; + +public class PeertubeNotification { + + private String id; + private boolean read; + private Date updatedAt, createdAt; + private int type; + private PeertubeComment peertubeComment; + private PeertubeVideoNotification peertubeVideoNotification; + private PeertubeActorFollow peertubeActorFollow; + + public PeertubeNotification() { + } + + public String getId() { + return id; + } + + + public void setId(String id) { + this.id = id; + } + + public boolean isRead() { + return read; + } + + public void setRead(boolean read) { + this.read = read; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public PeertubeComment getPeertubeComment() { + return peertubeComment; + } + + public void setPeertubeComment(PeertubeComment peertubeComment) { + this.peertubeComment = peertubeComment; + } + + + public PeertubeActorFollow getPeertubeActorFollow() { + return peertubeActorFollow; + } + + public void setPeertubeActorFollow(PeertubeActorFollow peertubeActorFollow) { + this.peertubeActorFollow = peertubeActorFollow; + } + + public PeertubeVideoNotification getPeertubeVideoNotification() { + return peertubeVideoNotification; + } + + public void setPeertubeVideoNotification(PeertubeVideoNotification peertubeVideoNotification) { + this.peertubeVideoNotification = peertubeVideoNotification; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeVideoNotification.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeVideoNotification.java new file mode 100644 index 0000000..bff8ab4 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/PeertubeVideoNotification.java @@ -0,0 +1,43 @@ + +package app.fedilab.fedilabtube.client.entities; + + +public class PeertubeVideoNotification { + + private String id; + private PeertubeAccountNotification peertubeAccountNotification; + private String uuid; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PeertubeAccountNotification getPeertubeAccountNotification() { + return peertubeAccountNotification; + } + + public void setPeertubeAccountNotification(PeertubeAccountNotification peertubeAccountNotification) { + this.peertubeAccountNotification = peertubeAccountNotification; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Playlist.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Playlist.java new file mode 100644 index 0000000..e05ed00 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Playlist.java @@ -0,0 +1,185 @@ + +package app.fedilab.fedilabtube.client.entities; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Date; +import java.util.HashMap; + + +@SuppressWarnings("unused") +public class Playlist implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Playlist createFromParcel(Parcel source) { + return new Playlist(source); + } + + @Override + public Playlist[] newArray(int size) { + return new Playlist[size]; + } + }; + 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 Playlist() { + } + + private 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 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); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Status.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Status.java new file mode 100644 index 0000000..de050ab --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Status.java @@ -0,0 +1,1508 @@ +package app.fedilab.fedilabtube.client.entities; + + +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.PreferenceManager; +import android.text.Html; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.text.style.ImageSpan; +import android.text.style.QuoteSpan; +import android.text.style.URLSpan; +import android.util.Patterns; +import android.view.View; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.PopupMenu; +import androidx.core.content.ContextCompat; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; + +import java.lang.ref.WeakReference; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import app.fedilab.fedilabtube.PeertubeActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.asynctasks.RetrieveFeedsAsyncTask; +import app.fedilab.fedilabtube.helper.Helper; +import es.dmoral.toasty.Toasty; + +import static android.content.Context.MODE_PRIVATE; + + + + +@SuppressWarnings({"WeakerAccess", "unused"}) +public class Status implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Status createFromParcel(Parcel source) { + return new Status(source); + } + + @Override + public Status[] newArray(int size) { + return new Status[size]; + } + }; + private String id; + private String uri; + private String url; + private Account account; + private String in_reply_to_id; + private String in_reply_to_account_id; + private Status reblog; + private Date created_at; + private int reblogs_count; + private int favourites_count; + private int replies_count; + private boolean reblogged; + private boolean favourited; + private boolean muted; + private boolean pinned; + private boolean sensitive; + private boolean bookmarked; + private String visibility; + private boolean attachmentShown = false; + private boolean spoilerShown = false; + private List mentions; + private List tags; + private String language; + private boolean isTranslated = false; + private transient boolean isEmojiFound = false; + private transient boolean isPollEmojiFound = false; + private transient boolean isImageFound = false; + private transient boolean isEmojiTranslateFound = false; + private boolean isTranslationShown = false; + private boolean isNew = false; + private boolean isVisible = true; + private boolean fetchMore = false; + private String content, contentCW, contentTranslated; + private SpannableString contentSpan, contentSpanCW, contentSpanTranslated; + private transient RetrieveFeedsAsyncTask.Type type; + private int itemViewType; + private String conversationId; + private boolean isExpanded = false; + private int numberLines = -1; + private boolean showSpoiler = false; + private String quickReplyContent; + private String quickReplyPrivacy; + private boolean showBottomLine = false; + private boolean showTopLine = false; + private List conversationProfilePicture; + private String webviewURL = null; + + private boolean isBoostAnimated = false, isFavAnimated = false; + private String scheduled_at; + private String contentType; + private boolean isNotice = false; + + private int media_height; + private boolean cached = false; + private boolean autoHiddenCW = false; + private boolean customFeaturesDisplayed = false; + private boolean shortReply = false; + + private int warningFetched = -1; + private List imageURL; + private int viewType; + private boolean isFocused = false; + private transient long db_id; + private transient boolean commentsFetched = false; + private transient List comments = new ArrayList<>(); + + public Status() { + } + + protected Status(Parcel in) { + this.id = in.readString(); + this.uri = in.readString(); + this.url = in.readString(); + this.account = in.readParcelable(Account.class.getClassLoader()); + this.in_reply_to_id = in.readString(); + this.in_reply_to_account_id = in.readString(); + this.reblog = in.readParcelable(Status.class.getClassLoader()); + long tmpCreated_at = in.readLong(); + this.created_at = tmpCreated_at == -1 ? null : new Date(tmpCreated_at); + this.reblogs_count = in.readInt(); + this.favourites_count = in.readInt(); + this.replies_count = in.readInt(); + this.reblogged = in.readByte() != 0; + this.favourited = in.readByte() != 0; + this.muted = in.readByte() != 0; + this.pinned = in.readByte() != 0; + this.sensitive = in.readByte() != 0; + this.bookmarked = in.readByte() != 0; + this.visibility = in.readString(); + this.attachmentShown = in.readByte() != 0; + this.spoilerShown = in.readByte() != 0; + this.mentions = in.createTypedArrayList(Mention.CREATOR); + this.tags = in.createTypedArrayList(Tag.CREATOR); + this.language = in.readString(); + this.isTranslated = in.readByte() != 0; + this.isTranslationShown = in.readByte() != 0; + this.isNew = in.readByte() != 0; + this.isVisible = in.readByte() != 0; + this.fetchMore = in.readByte() != 0; + this.content = in.readString(); + this.contentCW = in.readString(); + this.contentTranslated = in.readString(); + this.contentSpan = (SpannableString) TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + this.contentSpanCW = (SpannableString) TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + this.contentSpanTranslated = (SpannableString) TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + int tmpType = in.readInt(); + this.type = tmpType == -1 ? null : RetrieveFeedsAsyncTask.Type.values()[tmpType]; + this.itemViewType = in.readInt(); + this.conversationId = in.readString(); + this.isExpanded = in.readByte() != 0; + this.numberLines = in.readInt(); + this.conversationProfilePicture = in.createStringArrayList(); + this.webviewURL = in.readString(); + this.isBoostAnimated = in.readByte() != 0; + this.isFavAnimated = in.readByte() != 0; + this.scheduled_at = in.readString(); + this.contentType = in.readString(); + this.showSpoiler = in.readByte() != 0; + this.isNotice = in.readByte() != 0; + this.media_height = in.readInt(); + this.cached = in.readByte() != 0; + this.autoHiddenCW = in.readByte() != 0; + this.customFeaturesDisplayed = in.readByte() != 0; + this.shortReply = in.readByte() != 0; + this.warningFetched = in.readInt(); + this.imageURL = in.createStringArrayList(); + this.viewType = in.readInt(); + this.isFocused = in.readByte() != 0; + this.quickReplyContent = in.readString(); + this.quickReplyPrivacy = in.readString(); + this.showBottomLine = in.readByte() != 0; + this.showTopLine = in.readByte() != 0; + } + + + public static void fillSpan(WeakReference contextWeakReference, Status status) { + Status.transform(contextWeakReference, status); + Status.makeImage(contextWeakReference, status); + } + + + private static void transform(WeakReference contextWeakReference, Status status) { + Context context = contextWeakReference.get(); + if (status == null) + return; + SpannableString spannableStringContent, spannableStringCW; + if ((status.getReblog() != null && status.getReblog().getContent() == null) || (status.getReblog() == null && status.getContent() == null)) + return; + + String content = status.getReblog() != null ? status.getReblog().getContent() : status.getContent(); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + + Pattern aLink = Pattern.compile("]*(((?!"); + Matcher matcherALink = aLink.matcher(content); + int count = 0; + while (matcherALink.find()) { + String beforemodification; + String urlText = matcherALink.group(3); + + assert urlText != null; + urlText = urlText.substring(1); + beforemodification = urlText; + if (!beforemodification.startsWith("http")) { + 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 += "…" + count; + count++; + } + } else if (urlText.startsWith("@")) { + urlText += "|" + count; + count++; + } + content = content.replaceFirst(Pattern.quote(beforemodification), Matcher.quoteReplacement(urlText)); + } + } + + + + + int i = 1; + + content = content.replaceAll("(<\\s?p\\s?>)>(((?!(

)|(<\\s?p\\s?>|<\\s?br\\s?/?>|<\\s?/p\\s?>$)", "
$2

"); + content = content.replaceAll("^<\\s?p\\s?>(.*)<\\s?/p\\s?>$", "$1"); + spannableStringContent = new SpannableString(content); + String spoilerText = ""; + if (status.getReblog() != null && status.getReblog().getSpoiler_text() != null) + spoilerText = status.getReblog().getSpoiler_text(); + else if (status.getSpoiler_text() != null) + spoilerText = status.getSpoiler_text(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spannableStringCW = new SpannableString(Html.fromHtml(spoilerText, Html.FROM_HTML_MODE_LEGACY)); + else + spannableStringCW = new SpannableString(Html.fromHtml(spoilerText)); + if (spannableStringContent.length() > 0) + status.setContentSpan(treatment(context, spannableStringContent, status)); + if (spannableStringCW.length() > 0) + status.setContentSpanCW(spannableStringCW); + + } + + private static SpannableString treatment(final Context context, SpannableString spannableString, Status status) { + + URLSpan[] urls = spannableString.getSpans(0, spannableString.length(), URLSpan.class); + for (URLSpan span : urls) + spannableString.removeSpan(span); + List mentions = status.getReblog() != null ? status.getReblog().getMentions() : status.getMentions(); + + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + Matcher matcher; + Pattern linkPattern = Pattern.compile("]*(((?!"); + matcher = linkPattern.matcher(spannableString); + LinkedHashMap targetedURL = new LinkedHashMap<>(); + HashMap accountsMentionUnknown = new HashMap<>(); + String liveInstance = Helper.getLiveInstance(context); + int i = 1; + while (matcher.find()) { + String key; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + key = new SpannableString(Html.fromHtml(matcher.group(3), Html.FROM_HTML_MODE_LEGACY)).toString(); + else + key = new SpannableString(Html.fromHtml(matcher.group(3))).toString(); + key = key.substring(1); + + if (!key.startsWith("#") && !key.startsWith("@") && !key.trim().equals("") && !Objects.requireNonNull(matcher.group(2)).contains("search?tag=") && !Objects.requireNonNull(matcher.group(2)).contains(liveInstance + "/users/")) { + String url; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + url = Html.fromHtml(matcher.group(2), Html.FROM_HTML_MODE_LEGACY).toString(); + } else { + url = Html.fromHtml(matcher.group(2)).toString(); + } + targetedURL.put(key + "|" + i, url); + i++; + } else if (key.startsWith("@") || Objects.requireNonNull(matcher.group(2)).contains(liveInstance + "/users/")) { + String acct; + String url; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + url = Html.fromHtml(matcher.group(2), Html.FROM_HTML_MODE_LEGACY).toString(); + } else { + url = Html.fromHtml(matcher.group(2)).toString(); + } + + URI uri; + String instance = null; + try { + uri = new URI(url); + instance = uri.getHost(); + } catch (URISyntaxException e) { + if (url.contains("|")) { + try { + uri = new URI(url.split("\\|")[0]); + instance = uri.getHost(); + } catch (URISyntaxException ex) { + ex.printStackTrace(); + } + + } + } + if (key.startsWith("@")) + acct = key.substring(1).split("\\|")[0]; + else + acct = key.split("\\|")[0]; + Account account = new Account(); + account.setAcct(acct); + account.setInstance(instance); + account.setUrl(url); + String accountId = null; + if (mentions != null) { + for (Mention mention : mentions) { + String[] accountMentionAcct = mention.getAcct().split("@"); + //Different isntance + if (accountMentionAcct.length > 1) { + if (mention.getAcct().equals(account.getAcct() + "@" + account.getInstance())) { + accountId = mention.getId(); + break; + } + } else { + if (mention.getAcct().equals(account.getAcct())) { + accountId = mention.getId(); + break; + } + } + } + } + + if (accountId != null) { + account.setId(accountId); + } + accountsMentionUnknown.put(key, account); + } + } + + SpannableStringBuilder spannableStringT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spannableStringT = new SpannableStringBuilder(Html.fromHtml(spannableString.toString().replaceAll("[\\s]{2}", "  "), Html.FROM_HTML_MODE_LEGACY)); + else + spannableStringT = new SpannableStringBuilder(Html.fromHtml(spannableString.toString().replaceAll("[\\s]{2}", "  "))); + replaceQuoteSpans(context, spannableStringT); + URLSpan[] spans = spannableStringT.getSpans(0, spannableStringT.length(), URLSpan.class); + for (URLSpan span : spans) { + spannableStringT.removeSpan(span); + } + + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + if (accountsMentionUnknown.size() > 0) { + Iterator> it = accountsMentionUnknown.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = it.next(); + String key = pair.getKey(); + Account account = pair.getValue(); + String targetedAccount = "@" + account.getAcct(); + if (spannableStringT.toString().toLowerCase().contains(targetedAccount.toLowerCase())) { + + int startPosition = spannableStringT.toString().toLowerCase().indexOf(key.toLowerCase()); + int endPosition = startPosition + key.length(); + if (key.contains("|")) { + key = key.split("\\|")[0]; + SpannableStringBuilder ssb = new SpannableStringBuilder(); + ssb.append(spannableStringT, 0, spannableStringT.length()); + if (ssb.length() >= endPosition) { + ssb.replace(startPosition, endPosition, key); + } + spannableStringT = SpannableStringBuilder.valueOf(ssb); + endPosition = startPosition + key.length(); + } + //Accounts can be mentioned several times so we have to loop + if (startPosition >= 0 && endPosition <= spannableStringT.toString().length() && endPosition >= startPosition) + spannableStringT.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + if (account.getId() == null) { + CrossActions.doCrossProfile(context, account); + } else { + Intent intent = new Intent(context, ShowAccountActivity.class); + Bundle b = new Bundle(); + b.putString("accountId", account.getId()); + intent.putExtras(b); + context.startActivity(intent); + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + } + }, + startPosition, endPosition, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + it.remove(); + } + } + if (targetedURL.size() > 0) { + Iterator> it = targetedURL.entrySet().iterator(); + int endPosition = 0; + while (it.hasNext()) { + Map.Entry pair = it.next(); + String key = (pair.getKey()).split("\\|")[0]; + String url = pair.getValue(); + if (spannableStringT.toString().toLowerCase().contains(key.toLowerCase())) { + //Accounts can be mentioned several times so we have to loop + int startPosition = spannableStringT.toString().toLowerCase().indexOf(key.toLowerCase(), endPosition); + if (startPosition >= 0) { + endPosition = startPosition + key.length(); + if (key.contains("…") && !key.endsWith("…")) { + key = key.split("…")[0] + "…"; + SpannableStringBuilder ssb = new SpannableStringBuilder(); + ssb.append(spannableStringT, 0, spannableStringT.length()); + if (ssb.length() >= endPosition) { + ssb.replace(startPosition, endPosition, key); + } + spannableStringT = SpannableStringBuilder.valueOf(ssb); + endPosition = startPosition + key.length(); + } + if (endPosition <= spannableStringT.toString().length() && endPosition >= startPosition) { + spannableStringT.setSpan(new LongClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + String finalUrl = url; + Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); + Matcher matcherLink = link.matcher(url); + link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$"); + matcherLink = link.matcher(url); + if (matcherLink.find()) { //Peertubee video + Intent intent = new Intent(context, PeertubeActivity.class); + Bundle b = new Bundle(); + String url = matcherLink.group(1) + "/videos/watch/" + matcherLink.group(2); + b.putString("peertubeLinkToFetch", url); + b.putString("peertube_instance", Objects.requireNonNull(matcherLink.group(1)).replace("https://", "").replace("http://", "")); + b.putString("video_id", matcherLink.group(2)); + intent.putExtras(b); + context.startActivity(intent); + } else { + if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) + finalUrl = "http://" + url; + Helper.openBrowser(context, finalUrl); + } + } + + @Override + public void onLongClick(@NonNull View textView) { + PopupMenu popup = new PopupMenu(context, textView); + popup.getMenuInflater() + .inflate(R.menu.links_popup, popup.getMenu()); + 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; + } + popup.setOnMenuItemClickListener(item -> { + switch (item.getItemId()) { + case R.id.action_show_link: + AlertDialog.Builder builder = new AlertDialog.Builder(context, style); + builder.setMessage(url); + builder.setTitle(context.getString(R.string.display_full_link)); + builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) + .show(); + break; + case R.id.action_share_link: + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); + sendIntent.putExtra(Intent.EXTRA_TEXT, url); + sendIntent.setType("text/plain"); + context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with))); + break; + + case R.id.action_open_other_app: + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + try { + context.startActivity(intent); + } catch (Exception e) { + Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + } + break; + case R.id.action_copy_link: + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, url); + if (clipboard != null) { + clipboard.setPrimaryClip(clip); + Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); + } + break; + case R.id.action_unshorten: + Thread thread = new Thread() { + @Override + public void run() { + String response = new HttpsConnection(context, null).checkUrl(url); + + Handler mainHandler = new Handler(context.getMainLooper()); + + Runnable myRunnable = () -> { + AlertDialog.Builder builder1 = new AlertDialog.Builder(context, style); + if (response != null) { + builder1.setMessage(context.getString(R.string.redirect_detected, url, response)); + builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> { + ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, response); + if (clipboard1 != null) { + clipboard1.setPrimaryClip(clip1); + Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); + } + dialog.dismiss(); + }); + builder1.setNeutralButton(R.string.share_link, (dialog, which) -> { + Intent sendIntent1 = new Intent(Intent.ACTION_SEND); + sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); + sendIntent1.putExtra(Intent.EXTRA_TEXT, url); + sendIntent1.setType("text/plain"); + context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with))); + dialog.dismiss(); + }); + } else { + builder1.setMessage(R.string.no_redirect); + } + builder1.setTitle(context.getString(R.string.check_redirect)); + builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) + .show(); + + }; + mainHandler.post(myRunnable); + + } + }; + thread.start(); + break; + } + return true; + }); + popup.setOnDismissListener(menu -> BaseActivity.canShowActionMode = true); + popup.show(); + textView.clearFocus(); + BaseActivity.canShowActionMode = false; + } + + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(link_color); + } + }, + startPosition, endPosition, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + + } + it.remove(); + } + } + matcher = Helper.hashtagPattern.matcher(spannableStringT); + while (matcher.find()) { + int matchStart = matcher.start(1); + int matchEnd = matcher.end(); + final String tag = spannableStringT.toString().substring(matchStart, matchEnd); + if (matchStart >= 0 && matchEnd <= spannableStringT.toString().length() && matchEnd >= matchStart) + spannableStringT.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + if (MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.FRIENDICA) { + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString("tag", tag.substring(1)); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(link_color); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + + } + + if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { + matcher = Helper.groupPattern.matcher(spannableStringT); + while (matcher.find()) { + int matchStart = matcher.start(1); + int matchEnd = matcher.end(); + final String groupname = spannableStringT.toString().substring(matchStart, matchEnd); + if (matchStart >= 0 && matchEnd <= spannableStringT.toString().length() && matchEnd >= matchStart) + spannableStringT.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + if (MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.FRIENDICA) { + Intent intent = new Intent(context, GroupActivity.class); + Bundle b = new Bundle(); + b.putString("groupname", groupname.substring(1)); + intent.putExtras(b); + context.startActivity(intent); + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(link_color); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + + } + } + + Pattern carriagePattern = Pattern.compile("(\\n)+$"); + matcher = carriagePattern.matcher(spannableStringT); + while (matcher.find()) { + int matchStart = matcher.start(); + int matchEnd = matcher.end(); + if (matchStart >= 0 && matchEnd <= spannableStringT.toString().length() && matchEnd >= matchStart) { + spannableStringT.delete(matchStart, matchEnd); + } + } + return SpannableString.valueOf(spannableStringT); + } + + public static void transformTranslation(Context context, Status status) { + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int l_c = prefs.getInt("theme_link_color", -1); + if (l_c == -1) { + l_c = ThemeHelper.getAttColor(context, R.attr.linkColor); + } + final int link_color = l_c; + + if ((context instanceof Activity && ((Activity) context).isFinishing()) || status == null) + return; + if ((status.getReblog() != null && status.getReblog().getContent() == null) || (status.getReblog() == null && status.getContent() == null)) + return; + SpannableString spannableStringTranslated; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spannableStringTranslated = new SpannableString(Html.fromHtml(status.getContentTranslated(), Html.FROM_HTML_MODE_LEGACY)); + else + spannableStringTranslated = new SpannableString(Html.fromHtml(status.getContentTranslated())); + + status.setContentSpanTranslated(treatment(context, spannableStringTranslated, status)); + + SpannableString contentSpanTranslated = status.getContentSpanTranslated(); + Matcher matcherALink = Patterns.WEB_URL.matcher(contentSpanTranslated.toString()); + while (matcherALink.find()) { + int matchStart = matcherALink.start(); + int matchEnd = matcherALink.end(); + final String url = contentSpanTranslated.toString().substring(matcherALink.start(1), matcherALink.end(1)); + if (matchStart >= 0 && matchEnd <= contentSpanTranslated.toString().length() && matchEnd >= matchStart) + contentSpanTranslated.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + String finalUrl = url; + if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) + finalUrl = "http://" + url; + Helper.openBrowser(context, finalUrl); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(link_color); + } + }, + matchStart, matchEnd, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + + } + status.setContentSpanTranslated(contentSpanTranslated); + } + + + private static void makeImage(final WeakReference contextWeakReference, Status status) { + Context context = contextWeakReference.get(); + if (context instanceof Activity && ((Activity) context).isFinishing()) + return; + if (status.getAccount() == null) + return; + if (status.getImageURL() == null || status.getImageURL().size() == 0) + return; + + SpannableString contentSpan = status.getContentSpan(); + + final int[] i = {0}; + for (final String img : status.getImageURL()) { + final String name = img.split("\\|")[0]; + final String imgURL = img.split("\\|")[1]; + Glide.with(context) + .asBitmap() + .load(imgURL) + .into(new CustomTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, Transition transition) { + + int w = resource.getWidth(); + int h = resource.getHeight(); + if (w > 300) { + h = (h * 300) / w; + w = 300; + } + if (contentSpan != null && contentSpan.toString().contains(name)) { + //emojis can be used several times so we have to loop + for (int startPosition = -1; (startPosition = contentSpan.toString().indexOf(name, startPosition + 1)) != -1; startPosition++) { + final int endPosition = startPosition + name.length(); + if (endPosition <= contentSpan.toString().length() && endPosition >= startPosition) + contentSpan.setSpan( + new ImageSpan(context, + Bitmap.createScaledBitmap(resource, (int) Helper.convertDpToPixel(w, context), + (int) Helper.convertDpToPixel(h, context), false)), startPosition, + endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }); + + } + } + + + private static void replaceQuoteSpans(Context context, Spannable spannable) { + QuoteSpan[] quoteSpans = spannable.getSpans(0, spannable.length(), QuoteSpan.class); + for (QuoteSpan quoteSpan : quoteSpans) { + int start = spannable.getSpanStart(quoteSpan); + int end = spannable.getSpanEnd(quoteSpan); + int flags = spannable.getSpanFlags(quoteSpan); + spannable.removeSpan(quoteSpan); + int colord = ContextCompat.getColor(context, R.color.cyanea_accent_reference); + spannable.setSpan(new CustomQuoteSpan( + ContextCompat.getColor(context, R.color.transparent), + colord, + 10, + 20), + start, + end, + flags); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.id); + dest.writeString(this.uri); + dest.writeString(this.url); + dest.writeParcelable(this.account, flags); + dest.writeString(this.in_reply_to_id); + dest.writeString(this.in_reply_to_account_id); + dest.writeParcelable(this.reblog, flags); + dest.writeLong(this.created_at != null ? this.created_at.getTime() : -1); + dest.writeInt(this.reblogs_count); + dest.writeInt(this.favourites_count); + dest.writeInt(this.replies_count); + dest.writeByte(this.reblogged ? (byte) 1 : (byte) 0); + dest.writeByte(this.favourited ? (byte) 1 : (byte) 0); + dest.writeByte(this.muted ? (byte) 1 : (byte) 0); + dest.writeByte(this.pinned ? (byte) 1 : (byte) 0); + dest.writeByte(this.sensitive ? (byte) 1 : (byte) 0); + dest.writeByte(this.bookmarked ? (byte) 1 : (byte) 0); + dest.writeString(this.visibility); + dest.writeByte(this.attachmentShown ? (byte) 1 : (byte) 0); + dest.writeByte(this.spoilerShown ? (byte) 1 : (byte) 0); + dest.writeTypedList(this.media_attachments); + dest.writeParcelable(this.art_attachment, flags); + dest.writeTypedList(this.mentions); + dest.writeTypedList(this.emojis); + dest.writeTypedList(this.reactions); + dest.writeTypedList(this.tags); + dest.writeParcelable(this.application, flags); + dest.writeParcelable(this.card, flags); + dest.writeString(this.language); + dest.writeByte(this.isTranslated ? (byte) 1 : (byte) 0); + dest.writeByte(this.isTranslationShown ? (byte) 1 : (byte) 0); + dest.writeByte(this.isNew ? (byte) 1 : (byte) 0); + dest.writeByte(this.isVisible ? (byte) 1 : (byte) 0); + dest.writeByte(this.fetchMore ? (byte) 1 : (byte) 0); + dest.writeString(this.content); + dest.writeString(this.contentCW); + dest.writeString(this.contentTranslated); + TextUtils.writeToParcel(this.contentSpan, dest, flags); + TextUtils.writeToParcel(this.contentSpanCW, dest, flags); + TextUtils.writeToParcel(this.contentSpanTranslated, dest, flags); + dest.writeInt(this.type == null ? -1 : this.type.ordinal()); + dest.writeInt(this.itemViewType); + dest.writeString(this.conversationId); + dest.writeByte(this.isExpanded ? (byte) 1 : (byte) 0); + dest.writeInt(this.numberLines); + dest.writeStringList(this.conversationProfilePicture); + dest.writeString(this.webviewURL); + dest.writeByte(this.isBoostAnimated ? (byte) 1 : (byte) 0); + dest.writeByte(this.isFavAnimated ? (byte) 1 : (byte) 0); + dest.writeString(this.scheduled_at); + dest.writeString(this.contentType); + dest.writeByte(this.showSpoiler ? (byte) 1 : (byte) 0); + dest.writeByte(this.isNotice ? (byte) 1 : (byte) 0); + dest.writeParcelable(this.poll, flags); + dest.writeInt(this.media_height); + dest.writeByte(this.cached ? (byte) 1 : (byte) 0); + dest.writeByte(this.autoHiddenCW ? (byte) 1 : (byte) 0); + dest.writeByte(this.customFeaturesDisplayed ? (byte) 1 : (byte) 0); + dest.writeByte(this.shortReply ? (byte) 1 : (byte) 0); + dest.writeInt(this.warningFetched); + dest.writeStringList(this.imageURL); + dest.writeInt(this.viewType); + dest.writeByte(this.isFocused ? (byte) 1 : (byte) 0); + dest.writeString(this.quickReplyContent); + dest.writeString(this.quickReplyPrivacy); + dest.writeByte(this.showBottomLine ? (byte) 1 : (byte) 0); + dest.writeByte(this.showTopLine ? (byte) 1 : (byte) 0); + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } + + public String getIn_reply_to_id() { + return in_reply_to_id; + } + + public void setIn_reply_to_id(String in_reply_to_id) { + this.in_reply_to_id = in_reply_to_id; + } + + public String getIn_reply_to_account_id() { + return in_reply_to_account_id; + } + + public void setIn_reply_to_account_id(String in_reply_to_account_id) { + this.in_reply_to_account_id = in_reply_to_account_id; + } + + public String getContent() { + return content; + } + + public void setContent(Context context, String content) { + //Remove UTM by default + this.content = Helper.remove_tracking_param(context, content); + } + + public boolean isShortReply() { + return shortReply; + } + + public void setShortReply(boolean shortReply) { + this.shortReply = shortReply; + } + + public Status getReblog() { + return reblog; + } + + public void setReblog(Status reblog) { + this.reblog = reblog; + } + + public int getReblogs_count() { + return reblogs_count; + } + + public void setReblogs_count(int reblogs_count) { + this.reblogs_count = reblogs_count; + } + + public Date getCreated_at() { + return created_at; + } + + public void setCreated_at(Date created_at) { + this.created_at = created_at; + } + + public int getFavourites_count() { + return favourites_count; + } + + public void setFavourites_count(int favourites_count) { + this.favourites_count = favourites_count; + } + + + public boolean isReblogged() { + return reblogged; + } + + public void setReblogged(boolean reblogged) { + this.reblogged = reblogged; + } + + public boolean isFavourited() { + return favourited; + } + + public void setFavourited(boolean favourited) { + this.favourited = favourited; + } + + public boolean isPinned() { + return pinned; + } + + public void setPinned(boolean pinned) { + this.pinned = pinned; + } + + public boolean isSensitive() { + return sensitive; + } + + public void setSensitive(boolean sensitive) { + this.sensitive = sensitive; + } + + public String getSpoiler_text() { + return contentCW; + } + + public void setSpoiler_text(String spoiler_text) { + this.contentCW = spoiler_text; + } + + public ArrayList getMedia_attachments() { + return media_attachments; + } + + public void setMedia_attachments(ArrayList media_attachments) { + this.media_attachments = media_attachments; + } + + public List getMentions() { + return mentions; + } + + public void setMentions(List mentions) { + this.mentions = mentions; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public String getTagsString() { + //iterate through tags and create comma delimited string of tag names + StringBuilder tag_names = new StringBuilder(); + for (Tag t : tags) { + if (tag_names.toString().equals("")) { + tag_names = new StringBuilder(t.getName()); + } else { + tag_names.append(", ").append(t.getName()); + } + } + return tag_names.toString(); + } + + public Application getApplication() { + return application; + } + + public void setApplication(Application application) { + this.application = application; + } + + public String getVisibility() { + return visibility; + } + + public void setVisibility(String visibility) { + this.visibility = visibility; + } + + public boolean isAttachmentShown() { + return attachmentShown; + } + + public void setAttachmentShown(boolean attachmentShown) { + this.attachmentShown = attachmentShown; + } + + public boolean isSpoilerShown() { + return spoilerShown; + } + + public void setSpoilerShown(boolean spoilerShown) { + this.spoilerShown = spoilerShown; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public boolean isTranslated() { + return isTranslated; + } + + public void setTranslated(boolean translated) { + isTranslated = translated; + } + + public boolean isTranslationShown() { + return isTranslationShown; + } + + public void setTranslationShown(boolean translationShown) { + isTranslationShown = translationShown; + } + + public String getContentTranslated() { + return contentTranslated; + } + + public void setContentTranslated(String content_translated) { + this.contentTranslated = content_translated; + } + + public boolean isNew() { + return isNew; + } + + public void setNew(boolean aNew) { + isNew = aNew; + } + + public boolean isVisible() { + return isVisible; + } + + public void setVisible(boolean visible) { + isVisible = visible; + } + + public List getEmojis() { + return emojis; + } + + public void setEmojis(List emojis) { + this.emojis = emojis; + } + + public boolean isEmojiFound() { + return isEmojiFound; + } + + public void setEmojiFound(boolean emojiFound) { + isEmojiFound = emojiFound; + } + + public boolean isImageFound() { + return isImageFound; + } + + public void setImageFound(boolean imageFound) { + isImageFound = imageFound; + } + + public SpannableString getContentSpan() { + return contentSpan; + } + + public void setContentSpan(SpannableString contentSpan) { + this.contentSpan = contentSpan; + } + + public SpannableString getContentSpanCW() { + return contentSpanCW; + } + + public void setContentSpanCW(SpannableString contentSpanCW) { + this.contentSpanCW = contentSpanCW; + } + + public SpannableString getContentSpanTranslated() { + return contentSpanTranslated; + } + + public void setContentSpanTranslated(SpannableString contentSpanTranslated) { + this.contentSpanTranslated = contentSpanTranslated; + } + + + public boolean isEmojiTranslateFound() { + return isEmojiTranslateFound; + } + + public void setEmojiTranslateFound(boolean emojiTranslateFound) { + isEmojiTranslateFound = emojiTranslateFound; + } + + public boolean isFetchMore() { + return fetchMore; + } + + public void setFetchMore(boolean fetchMore) { + this.fetchMore = fetchMore; + } + + + @Override + public boolean equals(Object otherStatus) { + return otherStatus != null && (otherStatus == this || otherStatus instanceof Status && this.getId().equals(((Status) otherStatus).getId())); + } + + public Card getCard() { + return card; + } + + public void setCard(Card card) { + this.card = card; + } + + public boolean isMuted() { + return muted; + } + + public void setMuted(boolean muted) { + this.muted = muted; + } + + public boolean isBookmarked() { + if (this.getReblog() != null && this.getReblog().isBookmarked()) { + bookmarked = true; + } + return bookmarked; + } + + public void setBookmarked(boolean bookmarked) { + this.bookmarked = bookmarked; + } + + public int getReplies_count() { + return replies_count; + } + + public void setReplies_count(int replies_count) { + this.replies_count = replies_count; + } + + public RetrieveFeedsAsyncTask.Type getType() { + return type; + } + + public void setType(RetrieveFeedsAsyncTask.Type type) { + this.type = type; + } + + public List getConversationProfilePicture() { + return conversationProfilePicture; + } + + public void setConversationProfilePicture(List conversationProfilePicture) { + this.conversationProfilePicture = conversationProfilePicture; + } + + public String getWebviewURL() { + return webviewURL; + } + + public void setWebviewURL(String webviewURL) { + this.webviewURL = webviewURL; + } + + public int getItemViewType() { + return itemViewType; + } + + public void setItemViewType(int itemViewType) { + this.itemViewType = itemViewType; + } + + public String getConversationId() { + return conversationId; + } + + public void setConversationId(String conversationId) { + this.conversationId = conversationId; + } + + public boolean isExpanded() { + return isExpanded; + } + + public void setExpanded(boolean expanded) { + isExpanded = expanded; + } + + public int getNumberLines() { + return numberLines; + } + + public void setNumberLines(int numberLines) { + this.numberLines = numberLines; + } + + public boolean isBoostAnimated() { + return isBoostAnimated; + } + + public void setBoostAnimated(boolean boostAnimated) { + isBoostAnimated = boostAnimated; + } + + public boolean isFavAnimated() { + return isFavAnimated; + } + + public void setFavAnimated(boolean favAnimated) { + isFavAnimated = favAnimated; + } + + public Attachment getArt_attachment() { + return art_attachment; + } + + public void setArt_attachment(Attachment art_attachment) { + this.art_attachment = art_attachment; + } + + @Override + public int describeContents() { + return 0; + } + + + public String getScheduled_at() { + return scheduled_at; + } + + public void setScheduled_at(String scheduled_at) { + this.scheduled_at = scheduled_at; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public boolean isShowSpoiler() { + return showSpoiler; + } + + public void setShowSpoiler(boolean showSpoiler) { + this.showSpoiler = showSpoiler; + } + + + public boolean isNotice() { + return isNotice; + } + + public void setNotice(boolean notice) { + isNotice = notice; + } + + public Poll getPoll() { + return poll; + } + + public void setPoll(Poll poll) { + this.poll = poll; + } + + public int getMedia_height() { + return media_height; + } + + public void setMedia_height(int media_height) { + this.media_height = media_height; + } + + public boolean iscached() { + return cached; + } + + public void setcached(boolean cached) { + this.cached = cached; + } + + public boolean isAutoHiddenCW() { + return autoHiddenCW; + } + + public void setAutoHiddenCW(boolean autoHiddenCW) { + this.autoHiddenCW = autoHiddenCW; + } + + public boolean isCustomFeaturesDisplayed() { + return customFeaturesDisplayed; + } + + public void setCustomFeaturesDisplayed(boolean customFeaturesDisplayed) { + this.customFeaturesDisplayed = customFeaturesDisplayed; + } + + public int getWarningFetched() { + return warningFetched; + } + + public void setWarningFetched(int warningFetched) { + this.warningFetched = warningFetched; + } + + public List getImageURL() { + return imageURL; + } + + public void setImageURL(List imageURL) { + this.imageURL = imageURL; + } + + public int getViewType() { + return viewType; + } + + public void setViewType(Context context) { + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + boolean isCompactMode = sharedpreferences.getBoolean(Helper.SET_COMPACT_MODE, false); + boolean isConsoleMode = sharedpreferences.getBoolean(Helper.SET_CONSOLE_MODE, false); + if (isCompactMode) + this.viewType = COMPACT_STATUS; + else if (isConsoleMode) + this.viewType = CONSOLE_STATUS; + else + this.viewType = DISPLAYED_STATUS; + + } + + public boolean isFocused() { + return isFocused; + } + + public void setFocused(boolean focused) { + isFocused = focused; + } + + public long getDb_id() { + return db_id; + } + + public void setDb_id(long db_id) { + this.db_id = db_id; + } + + + public boolean isCommentsFetched() { + return commentsFetched; + } + + public void setCommentsFetched(boolean commentsFetched) { + this.commentsFetched = commentsFetched; + } + + public List getComments() { + return comments; + } + + public void setComments(List comments) { + this.comments = comments; + } + + public String getQuickReplyContent() { + return quickReplyContent; + } + + public void setQuickReplyContent(String quickReplyContent) { + this.quickReplyContent = quickReplyContent; + } + + public String getQuickReplyPrivacy() { + return quickReplyPrivacy; + } + + public void setQuickReplyPrivacy(String quickReplyPrivacy) { + this.quickReplyPrivacy = quickReplyPrivacy; + } + + public boolean isShowBottomLine() { + return showBottomLine; + } + + public void setShowBottomLine(boolean showBottomLine) { + this.showBottomLine = showBottomLine; + } + + public boolean isShowTopLine() { + return showTopLine; + } + + public void setShowTopLine(boolean showTopLine) { + this.showTopLine = showTopLine; + } + + public boolean isPollEmojiFound() { + return isPollEmojiFound; + } + + public void setPollEmojiFound(boolean pollEmojiFound) { + isPollEmojiFound = pollEmojiFound; + } + + public List getReactions() { + return reactions; + } + + public void setReactions(List reactions) { + this.reactions = reactions; + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/Tag.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Tag.java new file mode 100644 index 0000000..12c6cee --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/Tag.java @@ -0,0 +1,60 @@ + +package app.fedilab.fedilabtube.client.entities; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class Tag implements Parcelable { + + public static final Creator CREATOR = new Creator() { + @Override + public Tag createFromParcel(Parcel source) { + return new Tag(source); + } + + @Override + public Tag[] newArray(int size) { + return new Tag[size]; + } + }; + private String name; + private String url; + + public Tag() { + } + + protected Tag(Parcel in) { + this.name = in.readString(); + this.url = in.readString(); + } + + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.name); + dest.writeString(this.url); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/FileNameCleaner.java b/app/src/main/java/app/fedilab/fedilabtube/helper/FileNameCleaner.java new file mode 100644 index 0000000..404c6d4 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/FileNameCleaner.java @@ -0,0 +1,27 @@ +package app.fedilab.fedilabtube.helper; + +import java.util.Arrays; + +/** + * Work from https://stackoverflow.com/a/26420820 + */ + +public class FileNameCleaner { + private final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47}; + + static { + Arrays.sort(illegalChars); + } + + public static String cleanFileName(String badFileName) { + StringBuilder cleanName = new StringBuilder(); + int len = badFileName.codePointCount(0, badFileName.length()); + for (int i = 0; i < len; i++) { + int c = badFileName.codePointAt(i); + if (Arrays.binarySearch(illegalChars, c) < 0) { + cleanName.appendCodePoint(c); + } + } + return cleanName.toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/FullScreenMediaController.java b/app/src/main/java/app/fedilab/fedilabtube/helper/FullScreenMediaController.java new file mode 100644 index 0000000..c01fe5f --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/FullScreenMediaController.java @@ -0,0 +1,115 @@ +package app.fedilab.fedilabtube.helper; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.MediaController; + +import app.fedilab.fedilabtube.PeertubeActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.entities.Peertube; + + +/** + * Created by Thomas + * FullScreenMediaController. Inspired from the work at http://www.zoftino.com/android-videoview-playing-videos-full-screen + */ +public class FullScreenMediaController extends MediaController { + + private ImageButton fullScreen; + private Button resolution; + private Context context; + private Peertube peertube; + private String resolutionVal; + + public FullScreenMediaController(Context context) { + super(context); + this.context = context; + } + + @SuppressWarnings("unused") + public FullScreenMediaController(Context context, Peertube peertube) { + super(context); + this.peertube = peertube; + this.context = context; + } + + @Override + public void setAnchorView(View view) { + + super.setAnchorView(view); + + //image button for full screen to be added to media controller + fullScreen = new ImageButton(context); + LayoutParams params = + new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + params.gravity = Gravity.END; + params.rightMargin = 80; + params.topMargin = 22; + addView(fullScreen, params); + + if (resolutionVal == null) + resolutionVal = peertube.getResolution().get(0) + "p"; + resolution = new Button(context); + resolution.setAllCaps(false); + resolution.setBackgroundColor(Color.TRANSPARENT); + resolution.setText(resolutionVal); + resolution.setPadding(0, 0, 0, 0); + LayoutParams paramsButton = + new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + paramsButton.gravity = Gravity.START; + paramsButton.rightMargin = 80; + paramsButton.topMargin = 22; + resolution.setOnClickListener(v -> ((PeertubeActivity) getContext()).displayResolution()); + addView(resolution, paramsButton); + + if (((PeertubeActivity) getContext()).getFullscreen() == fullscreen.ON) { + Resources resources = getResources(); + fullScreen.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_fullscreen_exit_24)); + } else { + Resources resources = getResources(); + fullScreen.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_fullscreen_24)); + } + + //add listener to image button to handle full screen and exit full screen events + fullScreen.setOnClickListener(v -> { + + if (((PeertubeActivity) getContext()).getFullscreen() == fullscreen.ON) { + ((PeertubeActivity) getContext()).setFullscreen(fullscreen.OFF); + } else { + ((PeertubeActivity) getContext()).setFullscreen(fullscreen.ON); + } + ((PeertubeActivity) getContext()).change(); + changeIcon(); + }); + } + + @SuppressWarnings("unused") + public void setResolutionVal(String resolutionVal) { + this.resolutionVal = resolutionVal; + if (resolution != null) + resolution.setText(String.format("%sp", resolutionVal)); + } + + public void changeIcon() { + //fullscreen indicator from intent + if (((PeertubeActivity) getContext()).getFullscreen() == fullscreen.ON) { + Resources resources = getResources(); + fullScreen.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_fullscreen_exit_24)); + } else { + Resources resources = getResources(); + fullScreen.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_fullscreen_24)); + } + } + + public enum fullscreen { + OFF, + ON + } +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java index 5ca95f3..52e8f38 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java @@ -1,8 +1,44 @@ package app.fedilab.fedilabtube.helper; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.DownloadManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.view.WindowManager; +import android.webkit.URLUtil; +import android.widget.ImageView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.Locale; +import java.util.TimeZone; +import java.util.regex.Pattern; + +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.entities.Account; +import es.dmoral.toasty.Toasty; + +import static android.content.Context.DOWNLOAD_SERVICE; public class Helper { @@ -40,6 +76,59 @@ public class Helper { "ac-versailles.fr" )); + + public static final String TAG = "mastodon_etalab"; + public static final String CLIENT_NAME_VALUE = "Fedilab"; + public static final String OAUTH_SCOPES = "read write follow"; + public static final String OAUTH_SCOPES_ADMIN = "read write follow admin:read admin:write admin"; + public static final String OAUTH_SCOPES_PEERTUBE = "user"; + public static final String PREF_KEY_OAUTH_TOKEN = "oauth_token"; + + public static final Pattern urlPattern = Pattern.compile( + "(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,10}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))", + + Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); + + public static final String SET_ALLOW_STREAM = "set_allow_stream"; + public static final String SET_CUSTOM_USER_AGENT = "set_custom_user_agent"; + public static final String SET_VIDEO_CACHE = "set_video_cache"; + public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"; + //Proxy + public static final String SET_PROXY_ENABLED = "set_proxy_enabled"; + public static final String SET_PROXY_TYPE = "set_proxy_type"; + public static final String SET_PROXY_HOST = "set_proxy_host"; + public static final String SET_PROXY_PORT = "set_proxy_port"; + public static final String SET_PROXY_LOGIN = "set_proxy_login"; + public static final String SET_PROXY_PASSWORD = "set_proxy_password"; + + public static final String PREF_KEY_ID = "userID"; + public static final String PREF_IS_MODERATOR = "is_moderator"; + public static final String PREF_IS_ADMINISTRATOR = "is_administrator"; + public static final String PREF_INSTANCE = "instance"; + public static final String REDIRECT_CONTENT = "urn:ietf:wg:oauth:2.0:oob"; + public static final String REDIRECT_CONTENT_WEB = "mastalab://backtomastalab"; + public static final int EXTERNAL_STORAGE_REQUEST_CODE = 84; + + + public static final String CLIENT_NAME = "client_name"; + public static final String APP_PREFS = "app_prefs"; + public static final String ID = "id"; + public static final String CLIENT_ID = "client_id"; + public static final String CLIENT_SECRET = "client_secret"; + public static final String REDIRECT_URI = "redirect_uri"; + public static final String REDIRECT_URIS = "redirect_uris"; + public static final String RESPONSE_TYPE = "response_type"; + public static final String SCOPE = "scope"; + public static final String SCOPES = "scopes"; + public static final String WEBSITE = "website"; + public static final String SHOW_ACCOUNT_BOOSTS = "show_account_boosts"; + public static final String SHOW_ACCOUNT_REPLIES = "show_account_replies"; + public static final String WEBSITE_VALUE = "https://fedilab.app"; + public static final String SHOW_BATTERY_SAVER_MESSAGE = "show_battery_saver_message"; + public static final String LAST_NOTIFICATION_MAX_ID = "last_notification_max_id"; + public static final int VIDEOS_PER_PAGE = 40; + public static final String SET_VIDEO_NSFW = "set_video_nsfw"; + /** * Returns the peertube URL depending of the academic domain name * @param acad String academic domain name @@ -48,4 +137,298 @@ public class Helper { public static String getPeertubeUrl(String acad) { return "https://tube-"+acad.replace("ac\\-|\\.fr","")+".beta.education.fr/"; } + + + /** + * Returns the instance of the authenticated user + * + * @param context Context + * @return String domain instance + */ + public static String getLiveInstance(Context context) { + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + return sharedpreferences.getString(Helper.PREF_INSTANCE, null); + } + + + public static String getLiveInstanceWithProtocol(Context context) { + return instanceWithProtocol(getLiveInstance(context)); + } + + + public static String instanceWithProtocol(String instance) { + if (instance == null) + return null; + return "https://" + instance; + } + + /** + * Convert String date from Mastodon + * + * @param date String + * @return Date + */ + public static Date mstStringToDate(String date) throws ParseException { + if (date == null) + return null; + + String STRING_DATE_FORMAT; + Locale local = Locale.getDefault(); + if (!date.contains("+")) { + STRING_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + } else { //GNU date format + STRING_DATE_FORMAT = "EEE MMM dd HH:mm:ss ZZZZZ yyyy"; + local = Locale.ENGLISH; + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(STRING_DATE_FORMAT, local); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("gmt")); + simpleDateFormat.setLenient(true); + try { + return simpleDateFormat.parse(date); + } catch (Exception e) { + String newdate = date.split("\\+")[0].trim(); + simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", local); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("gmt")); + simpleDateFormat.setLenient(true); + return simpleDateFormat.parse(newdate); + } + } + + /** + * Convert a date in String -> format yyyy-MM-dd HH:mm:ss + * + * @param date Date + * @return String + */ + public static String dateToString(Date date) { + if (date == null) + return null; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + return dateFormat.format(date); + } + + /** + * Convert String date from db to Date Object + * + * @param stringDate date to convert + * @return Date + */ + public static Date stringToDate(Context context, String stringDate) { + if (stringDate == null) + return null; + Locale userLocale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + userLocale = context.getResources().getConfiguration().getLocales().get(0); + } else { + userLocale = context.getResources().getConfiguration().locale; + } + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", userLocale); + Date date = null; + try { + date = dateFormat.parse(stringDate); + } catch (java.text.ParseException ignored) { + + } + return date; + } + + + public static String secondsToString(int pTime) { + + int hour = pTime / 3600; + int min = (pTime - (hour * 3600)) / 60; + int sec = pTime - (hour * 3600) - (min * 60); + String strHour = "0", strMin = "0", strSec; + + if (hour > 0) + strHour = String.format(Locale.getDefault(), "%02d", hour); + if (min > 0) + strMin = String.format(Locale.getDefault(), "%02d", min); + strSec = String.format(Locale.getDefault(), "%02d", sec); + if (hour > 0) + return String.format(Locale.getDefault(), "%s:%s:%s", strHour, strMin, strSec); + else + return String.format(Locale.getDefault(), "%s:%s", strMin, strSec); + } + + /*** + * Returns a String depending of the date + * @param context Context + * @param dateToot Date + * @return String + */ + public static String dateDiff(Context context, Date dateToot) { + Date now = new Date(); + long diff = now.getTime() - dateToot.getTime(); + long seconds = diff / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + long months = days / 30; + long years = days / 365; + + String format = DateFormat.getDateInstance(DateFormat.SHORT).format(dateToot); + if (years > 0) { + return format; + } else if (months > 0 || days > 7) { + //Removes the year depending of the locale from DateFormat.SHORT format + SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()); + df.applyPattern(df.toPattern().replaceAll("[^\\p{Alpha}]*y+[^\\p{Alpha}]*", "")); + return df.format(dateToot); + } else if (days > 0) + return context.getString(R.string.date_day, days); + else if (hours > 0) + return context.getResources().getString(R.string.date_hours, (int) hours); + else if (minutes > 0) + return context.getResources().getString(R.string.date_minutes, (int) minutes); + else { + if (seconds < 0) + seconds = 0; + return context.getResources().getString(R.string.date_seconds, (int) seconds); + } + } + + + public static String dateDiffFull(Date dateToot) { + SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault()); + try { + return df.format(dateToot); + } catch (Exception e) { + return ""; + } + } + + public static String withSuffix(long count) { + if (count < 1000) return "" + count; + int exp = (int) (Math.log(count) / Math.log(1000)); + Locale locale = null; + try { + locale = Locale.getDefault(); + } catch (Exception ignored) { + } + if (locale != null) + return String.format(locale, "%.1f %c", + count / Math.pow(1000, exp), + "kMGTPE".charAt(exp - 1)); + else + return String.format(Locale.getDefault(), "%.1f %c", + count / Math.pow(1000, exp), + "kMGTPE".charAt(exp - 1)); + } + + + public static void loadGiF(final Context context, Account account, final ImageView imageView) { + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + String url = account.getAvatar(); + if (url != null && url.startsWith("/")) { + url = Helper.getLiveInstanceWithProtocol(context) + url; + } + + + + try { + Glide.with(imageView.getContext()) + .asDrawable() + .load(url) + .thumbnail(0.1f) + .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) + .into(imageView); + } catch (Exception e) { + try { + Glide.with(imageView.getContext()) + .asDrawable() + .load(R.drawable.missing_peertube) + .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) + .into(imageView); + + } catch (Exception ignored) { + } + } + + } + + + + /** + * Manage URLs to open (built-in or external app) + * + * @param context Context + * @param url String url to open + */ + public static void openBrowser(Context context, String url) { + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true); + if (embedded_browser) { + Intent intent = new Intent(context, WebviewActivity.class); + Bundle b = new Bundle(); + String finalUrl = url; + if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) + finalUrl = "http://" + url; + b.putString("url", finalUrl); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } else { + boolean custom_tabs = sharedpreferences.getBoolean(Helper.SET_CUSTOM_TABS, true); + if (custom_tabs) { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + CustomTabsIntent customTabsIntent = builder.build(); + builder.setToolbarColor(ContextCompat.getColor(context, R.color.mastodonC1)); + try { + customTabsIntent.launchUrl(context, Uri.parse(url)); + } catch (Exception ignored) { + Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + } + } else { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setData(Uri.parse(url)); + try { + context.startActivity(intent); + } catch (Exception e) { + Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + } + + } + } + } + + + /** + * Manage downloads with URLs + * + * @param context Context + * @param url String download url + */ + public static void manageDownloads(final Context context, final String url) { + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + final DownloadManager.Request request; + try { + request = new DownloadManager.Request(Uri.parse(url.trim())); + } catch (Exception e) { + Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); + return; + } + final String fileName = URLUtil.guessFileName(url, null, null); + builder.setMessage(context.getResources().getString(R.string.download_file, fileName)); + builder.setCancelable(false) + .setPositiveButton(context.getString(R.string.yes), (dialog, id) -> { + request.allowScanningByMediaScanner(); + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + DownloadManager dm = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE); + assert dm != null; + dm.enqueue(request); + dialog.dismiss(); + }) + .setNegativeButton(context.getString(R.string.cancel), (dialog, id) -> dialog.cancel()); + AlertDialog alert = builder.create(); + if (alert.getWindow() != null) + alert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + alert.show(); + } + } diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnActionInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnActionInterface.java new file mode 100644 index 0000000..6ee1b60 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnActionInterface.java @@ -0,0 +1,9 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.client.APIResponse; + +public interface OnActionInterface { + void onActionDone(APIResponse apiResponse, int statusCode); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnDownloadInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnDownloadInterface.java new file mode 100644 index 0000000..9df0fca --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnDownloadInterface.java @@ -0,0 +1,9 @@ + +package app.fedilab.fedilabtube.interfaces; + + +public interface OnDownloadInterface { + void onDownloaded(String saveFilePath, String downloadUrl, Error error); + + void onUpdateProgress(int progress); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPlaylistActionInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPlaylistActionInterface.java new file mode 100644 index 0000000..b047224 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPlaylistActionInterface.java @@ -0,0 +1,10 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.asynctasks.ManagePlaylistsAsyncTask; +import app.fedilab.fedilabtube.client.APIResponse; + +public interface OnPlaylistActionInterface { + void onActionDone(ManagePlaylistsAsyncTask.action actionType, APIResponse apiResponse, int statusCode); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPostActionInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPostActionInterface.java new file mode 100644 index 0000000..abb678b --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnPostActionInterface.java @@ -0,0 +1,10 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Error; + +public interface OnPostActionInterface { + void onPostAction(int statusCode, PeertubeAPI.StatusAction statusAction, String userId, Error error); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrieveFeedsInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrieveFeedsInterface.java new file mode 100644 index 0000000..c3cb4a0 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrieveFeedsInterface.java @@ -0,0 +1,9 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.client.APIResponse; + +public interface OnRetrieveFeedsInterface { + void onRetrieveFeeds(APIResponse apiResponse); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeInterface.java new file mode 100644 index 0000000..67e6398 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeInterface.java @@ -0,0 +1,13 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.client.APIResponse; + +public interface OnRetrievePeertubeInterface { + void onRetrievePeertube(APIResponse apiResponse); + + void onRetrievePeertubeComments(APIResponse apiResponse); + + void onRetrievePeertubeChannels(APIResponse apiResponse); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeNotificationsInterface.java b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeNotificationsInterface.java new file mode 100644 index 0000000..c970254 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/interfaces/OnRetrievePeertubeNotificationsInterface.java @@ -0,0 +1,10 @@ + +package app.fedilab.fedilabtube.interfaces; + + +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.entities.Account; + +public interface OnRetrievePeertubeNotificationsInterface { + void onRetrievePeertubeNotifications(APIResponse apiResponse, Account account); +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java new file mode 100644 index 0000000..068bfd2 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/AccountDAO.java @@ -0,0 +1,372 @@ +package app.fedilab.fedilabtube.sqlite; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.helper.Helper; + + + +public class AccountDAO { + + public Context context; + private SQLiteDatabase db; + + + public AccountDAO(Context context, SQLiteDatabase db) { + //Creation of the DB with tables + this.context = context; + this.db = db; + } + + + /** + * Insert an Account in database + * + * @param account Account + * @return boolean + */ + public boolean insertAccount(Account account) { + ContentValues values = new ContentValues(); + if (account.getCreated_at() == null) + account.setCreated_at(new Date()); + if (account.getNote() == null) + account.setNote(""); + values.put(Sqlite.COL_USER_ID, account.getId()); + values.put(Sqlite.COL_USERNAME, account.getUsername()); + values.put(Sqlite.COL_ACCT, account.getAcct()); + values.put(Sqlite.COL_DISPLAYED_NAME, account.getDisplay_name()); + values.put(Sqlite.COL_LOCKED, account.isLocked()); + values.put(Sqlite.COL_FOLLOWERS_COUNT, account.getFollowers_count()); + values.put(Sqlite.COL_FOLLOWING_COUNT, account.getFollowing_count()); + values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); + values.put(Sqlite.COL_NOTE, account.getNote()); + values.put(Sqlite.COL_URL, account.getUrl()); + values.put(Sqlite.COL_AVATAR, account.getAvatar()); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); + values.put(Sqlite.COL_HEADER, account.getHeader()); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); + values.put(Sqlite.COL_INSTANCE, account.getInstance()); + if (account.getClient_id() != null && account.getClient_secret() != null && account.getRefresh_token() != null) { + values.put(Sqlite.COL_CLIENT_ID, account.getClient_id()); + values.put(Sqlite.COL_CLIENT_SECRET, account.getClient_secret()); + values.put(Sqlite.COL_REFRESH_TOKEN, account.getRefresh_token()); + } + if (account.getToken() != null) + values.put(Sqlite.COL_OAUTHTOKEN, account.getToken()); + + values.put(Sqlite.COL_SENSITIVE, account.isSensitive()); + values.put(Sqlite.COL_PRIVACY, account.getPrivacy()); + //Inserts account + try { + db.insertOrThrow(Sqlite.TABLE_USER_ACCOUNT, null, values); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Update an Account in database + * + * @param account Account + * @return boolean + */ + public int updateAccount(Account account) { + ContentValues values = new ContentValues(); + if (account.getNote() == null) + account.setNote(""); + if (account.getCreated_at() == null) + account.setCreated_at(new Date()); + values.put(Sqlite.COL_ACCT, account.getAcct()); + values.put(Sqlite.COL_DISPLAYED_NAME, account.getDisplay_name()); + values.put(Sqlite.COL_LOCKED, account.isLocked()); + values.put(Sqlite.COL_FOLLOWERS_COUNT, account.getFollowers_count()); + values.put(Sqlite.COL_FOLLOWING_COUNT, account.getFollowing_count()); + values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); + values.put(Sqlite.COL_NOTE, account.getNote()); + values.put(Sqlite.COL_URL, account.getUrl()); + values.put(Sqlite.COL_AVATAR, account.getAvatar()); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); + values.put(Sqlite.COL_HEADER, account.getHeader()); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); + if (account.getClient_id() != null && account.getClient_secret() != null && account.getRefresh_token() != null) { + values.put(Sqlite.COL_CLIENT_ID, account.getClient_id()); + values.put(Sqlite.COL_CLIENT_SECRET, account.getClient_secret()); + values.put(Sqlite.COL_REFRESH_TOKEN, account.getRefresh_token()); + } + if (account.getToken() != null) + values.put(Sqlite.COL_OAUTHTOKEN, account.getToken()); + return db.update(Sqlite.TABLE_USER_ACCOUNT, + values, Sqlite.COL_USER_ID + " = ? AND " + Sqlite.COL_INSTANCE + " =?", + new String[]{account.getId(), account.getInstance()}); + } + + + /** + * Update an Account in database + * + * @param account Account + * @return boolean + */ + public int updateAccountCredential(Account account) { + ContentValues values = new ContentValues(); + if (account.getNote() == null) + account.setNote(""); + if (account.getCreated_at() == null) + account.setCreated_at(new Date()); + values.put(Sqlite.COL_ACCT, account.getAcct()); + values.put(Sqlite.COL_DISPLAYED_NAME, account.getDisplay_name()); + values.put(Sqlite.COL_LOCKED, account.isLocked()); + values.put(Sqlite.COL_FOLLOWERS_COUNT, account.getFollowers_count()); + values.put(Sqlite.COL_FOLLOWING_COUNT, account.getFollowing_count()); + values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); + values.put(Sqlite.COL_NOTE, account.getNote()); + values.put(Sqlite.COL_URL, account.getUrl()); + values.put(Sqlite.COL_AVATAR, account.getAvatar()); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); + values.put(Sqlite.COL_HEADER, account.getHeader()); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); + + if (account.getClient_id() != null && account.getClient_secret() != null && account.getRefresh_token() != null) { + values.put(Sqlite.COL_CLIENT_ID, account.getClient_id()); + values.put(Sqlite.COL_CLIENT_SECRET, account.getClient_secret()); + values.put(Sqlite.COL_REFRESH_TOKEN, account.getRefresh_token()); + } + if (account.getToken() != null) + values.put(Sqlite.COL_OAUTHTOKEN, account.getToken()); + values.put(Sqlite.COL_SENSITIVE, account.isSensitive()); + values.put(Sqlite.COL_PRIVACY, account.getPrivacy()); + return db.update(Sqlite.TABLE_USER_ACCOUNT, + values, Sqlite.COL_USER_ID + " = ? AND " + Sqlite.COL_INSTANCE + " =?", + new String[]{account.getId(), account.getInstance()}); + } + + + public int removeUser(Account account) { + return db.delete(Sqlite.TABLE_USER_ACCOUNT, Sqlite.COL_USER_ID + " = '" + account.getId() + + "' AND " + Sqlite.COL_INSTANCE + " = '" + account.getInstance() + "'", null); + } + + + /** + * Returns last used account + * + * @return Account + */ + public Account getLastUsedAccount() { + + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_OAUTHTOKEN + " != 'null'", null, null, null, Sqlite.COL_UPDATED_AT + " DESC", "1"); + return cursorToUser(c); + } catch (Exception e) { + return null; + } + } + + /** + * Returns an Account by its id and acct + * + * @param accountId String + * @param accountAcct String + * @return Account + */ + public Account getAccountByIDAcct(String accountId, String accountAcct) { + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_USER_ID + " = '" + accountId + "' AND " + Sqlite.COL_ACCT + " = '" + accountAcct + "'", null, null, null, null, "1"); + return cursorToUser(c); + } catch (Exception e) { + return null; + } + } + + + /** + * Returns all Account in db + * + * @return Account List + */ + public List getAllAccount() { + + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, Sqlite.COL_INSTANCE + " ASC", null); + return cursorToListUser(c); + } catch (Exception e) { + return null; + } + } + + /** + * Returns all Account in db + * + * @return Account List + */ + public List getAllAccountActivated() { + + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_OAUTHTOKEN + " != 'null'", null, null, null, Sqlite.COL_INSTANCE + " ASC", null); + return cursorToListUser(c); + } catch (Exception e) { + return null; + } + } + + + + /** + * Returns an Account by token + * + * @param token String + * @return Account + */ + public Account getAccountByToken(String token) { + + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_OAUTHTOKEN + " = \"" + token + "\"", null, null, null, null, "1"); + return cursorToUser(c); + } catch (Exception e) { + return null; + } + } + + /** + * Returns an Account by token + * + * @param userId String + * @param instance String + * @return Account + */ + public Account getUniqAccount(String userId, String instance) { + + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_USER_ID + " = \"" + userId + "\" AND " + Sqlite.COL_INSTANCE + " = \"" + instance + "\"", null, null, null, null, "1"); + return cursorToUser(c); + } catch (Exception e) { + return null; + } + } + + + /** + * Test if the current user is already stored in data base + * + * @param account Account + * @return boolean + */ + public boolean userExist(Account account) { + Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_USER_ACCOUNT + + " where " + Sqlite.COL_USERNAME + " = '" + account.getUsername() + "' AND " + Sqlite.COL_INSTANCE + " = '" + account.getInstance() + "'", null); + mCount.moveToFirst(); + int count = mCount.getInt(0); + mCount.close(); + return (count > 0); + } + + + /*** + * Method to hydrate an Account from database + * @param c Cursor + * @return Account + */ + private Account cursorToUser(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + //Take the first element + c.moveToFirst(); + //New user + Account account = new Account(); + + account.setId(c.getString(c.getColumnIndex(Sqlite.COL_USER_ID))); + account.setUsername(c.getString(c.getColumnIndex(Sqlite.COL_USERNAME))); + account.setAcct(c.getString(c.getColumnIndex(Sqlite.COL_ACCT))); + account.setDisplay_name(c.getString(c.getColumnIndex(Sqlite.COL_DISPLAYED_NAME))); + account.setLocked(c.getInt(c.getColumnIndex(Sqlite.COL_LOCKED)) == 1); + account.setFollowers_count(c.getInt(c.getColumnIndex(Sqlite.COL_FOLLOWERS_COUNT))); + account.setFollowing_count(c.getInt(c.getColumnIndex(Sqlite.COL_FOLLOWING_COUNT))); + account.setStatuses_count(c.getInt(c.getColumnIndex(Sqlite.COL_STATUSES_COUNT))); + account.setNote(c.getString(c.getColumnIndex(Sqlite.COL_NOTE))); + account.setUrl(c.getString(c.getColumnIndex(Sqlite.COL_URL))); + account.setAvatar(c.getString(c.getColumnIndex(Sqlite.COL_AVATAR))); + account.setAvatar_static(c.getString(c.getColumnIndex(Sqlite.COL_AVATAR_STATIC))); + account.setHeader(c.getString(c.getColumnIndex(Sqlite.COL_HEADER))); + account.setHeader_static(c.getString(c.getColumnIndex(Sqlite.COL_HEADER_STATIC))); + account.setUpdated_at(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_UPDATED_AT)))); + account.setCreated_at(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_CREATED_AT)))); + account.setInstance(c.getString(c.getColumnIndex(Sqlite.COL_INSTANCE))); + account.setToken(c.getString(c.getColumnIndex(Sqlite.COL_OAUTHTOKEN))); + account.setClient_id(c.getString(c.getColumnIndex(Sqlite.COL_CLIENT_ID))); + account.setClient_secret(c.getString(c.getColumnIndex(Sqlite.COL_CLIENT_SECRET))); + account.setRefresh_token(c.getString(c.getColumnIndex(Sqlite.COL_REFRESH_TOKEN))); + account.setSensitive(c.getInt(c.getColumnIndex(Sqlite.COL_SENSITIVE)) == 1); + account.setPrivacy((c.getString(c.getColumnIndex(Sqlite.COL_PRIVACY)))); + //Close the cursor + c.close(); + + //User is returned + return account; + } + + /*** + * Method to hydrate an Accounts from database + * @param c Cursor + * @return List + */ + private List cursorToListUser(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + List accounts = new ArrayList<>(); + while (c.moveToNext()) { + //New user + Account account = new Account(); + + account.setId(c.getString(c.getColumnIndex(Sqlite.COL_USER_ID))); + account.setUsername(c.getString(c.getColumnIndex(Sqlite.COL_USERNAME))); + account.setAcct(c.getString(c.getColumnIndex(Sqlite.COL_ACCT))); + account.setDisplay_name(c.getString(c.getColumnIndex(Sqlite.COL_DISPLAYED_NAME))); + account.setLocked(c.getInt(c.getColumnIndex(Sqlite.COL_LOCKED)) == 1); + account.setFollowers_count(c.getInt(c.getColumnIndex(Sqlite.COL_FOLLOWERS_COUNT))); + account.setFollowing_count(c.getInt(c.getColumnIndex(Sqlite.COL_FOLLOWING_COUNT))); + account.setStatuses_count(c.getInt(c.getColumnIndex(Sqlite.COL_STATUSES_COUNT))); + account.setNote(c.getString(c.getColumnIndex(Sqlite.COL_NOTE))); + account.setUrl(c.getString(c.getColumnIndex(Sqlite.COL_URL))); + account.setAvatar(c.getString(c.getColumnIndex(Sqlite.COL_AVATAR))); + account.setAvatar_static(c.getString(c.getColumnIndex(Sqlite.COL_AVATAR_STATIC))); + account.setHeader(c.getString(c.getColumnIndex(Sqlite.COL_HEADER))); + account.setHeader_static(c.getString(c.getColumnIndex(Sqlite.COL_HEADER_STATIC))); + account.setUpdated_at(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_UPDATED_AT)))); + account.setCreated_at(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_CREATED_AT)))); + account.setInstance(c.getString(c.getColumnIndex(Sqlite.COL_INSTANCE))); + account.setToken(c.getString(c.getColumnIndex(Sqlite.COL_OAUTHTOKEN))); + account.setClient_id(c.getString(c.getColumnIndex(Sqlite.COL_CLIENT_ID))); + account.setClient_secret(c.getString(c.getColumnIndex(Sqlite.COL_CLIENT_SECRET))); + account.setRefresh_token(c.getString(c.getColumnIndex(Sqlite.COL_REFRESH_TOKEN))); + account.setSensitive(c.getInt(c.getColumnIndex(Sqlite.COL_SENSITIVE)) == 1); + account.setPrivacy((c.getString(c.getColumnIndex(Sqlite.COL_PRIVACY)))); + accounts.add(account); + } + //Close the cursor + c.close(); + //Users list is returned + return accounts; + } + + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/PeertubeFavoritesDAO.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/PeertubeFavoritesDAO.java new file mode 100644 index 0000000..f227155 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/PeertubeFavoritesDAO.java @@ -0,0 +1,130 @@ +package app.fedilab.fedilabtube.sqlite; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import app.fedilab.fedilabtube.client.PeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.helper.Helper; + +public class PeertubeFavoritesDAO { + + public Context context; + private SQLiteDatabase db; + + public PeertubeFavoritesDAO(Context context, SQLiteDatabase db) { + //Creation of the DB with tables + this.context = context; + this.db = db; + } + + + //------- INSERTIONS ------- + + /** + * Insert a status in database + * + * @param peertube Peertube + * @return boolean + */ + public long insert(Peertube peertube) { + ContentValues values = new ContentValues(); + values.put(Sqlite.COL_UUID, peertube.getUuid()); + values.put(Sqlite.COL_INSTANCE, peertube.getInstance()); + values.put(Sqlite.COL_DATE, Helper.dateToString(new Date())); + values.put(Sqlite.COL_CACHE, peertube.getCache().toString()); + //Inserts cached peertube + long last_id; + try { + last_id = db.insert(Sqlite.TABLE_PEERTUBE_FAVOURITES, null, values); + } catch (Exception e) { + last_id = -1; + } + return last_id; + } + //------- REMOVE ------- + + /*** + * Remove stored status + * @return int + */ + public int remove(Peertube peertube) { + return db.delete(Sqlite.TABLE_PEERTUBE_FAVOURITES, Sqlite.COL_UUID + " = \"" + peertube.getUuid() + "\" AND " + Sqlite.COL_INSTANCE + " = \"" + peertube.getInstance() + "\"", null); + } + + /*** + * Remove stored status + * @return int + */ + public int removeAll() { + return db.delete(Sqlite.TABLE_PEERTUBE_FAVOURITES, null, null); + } + + + //------- GETTERS ------- + + /** + * Returns all cached Peertube + * + * @return stored peertube List + */ + public List getAllPeertube() { + try { + Cursor c = db.query(Sqlite.TABLE_PEERTUBE_FAVOURITES, null, null, null, null, null, Sqlite.COL_DATE + " DESC"); + return cursorToListPeertube(c); + } catch (Exception e) { + return null; + } + } + + /** + * Returns a cached Peertube + * + * @return stored peertube List + */ + public List getSinglePeertube(Peertube peertube) { + try { + Cursor c = db.query(Sqlite.TABLE_PEERTUBE_FAVOURITES, null, Sqlite.COL_UUID + " = \"" + peertube.getUuid() + "\" AND " + Sqlite.COL_INSTANCE + " = \"" + peertube.getInstance() + "\"", null, null, null, Sqlite.COL_DATE + " DESC"); + return cursorToListPeertube(c); + } catch (Exception e) { + return null; + } + } + + /*** + * Method to hydrate cached statuses from database + * @param c Cursor + * @return List + */ + private List cursorToListPeertube(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + List peertubes = new ArrayList<>(); + while (c.moveToNext()) { + //Restore cached status + try { + Peertube peertube = PeertubeAPI.parsePeertube( new JSONObject(c.getString(c.getColumnIndex(Sqlite.COL_CACHE)))); + peertubes.add(peertube); + } catch (JSONException e) { + e.printStackTrace(); + } + } + //Close the cursor + c.close(); + //Peertubes list is returned + return peertubes; + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java new file mode 100644 index 0000000..7d01328 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java @@ -0,0 +1,125 @@ +package app.fedilab.fedilabtube.sqlite; + + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + + +public class Sqlite extends SQLiteOpenHelper { + + public static final int DB_VERSION = 1; + public static final String DB_NAME = "mastodon_etalab_db"; + /*** + * List of tables to manage users and data + */ + //Table of owned accounts + static final String TABLE_USER_ACCOUNT = "USER_ACCOUNT"; + static final String COL_USER_ID = "USER_ID"; + static final String COL_USERNAME = "USERNAME"; + static final String COL_ACCT = "ACCT"; + static final String COL_DISPLAYED_NAME = "DISPLAYED_NAME"; + static final String COL_LOCKED = "LOCKED"; + static final String COL_CREATED_AT = "CREATED_AT"; + static final String COL_FOLLOWERS_COUNT = "FOLLOWERS_COUNT"; + static final String COL_FOLLOWING_COUNT = "FOLLOWING_COUNT"; + static final String COL_STATUSES_COUNT = "STATUSES_COUNT"; + static final String COL_NOTE = "NOTE"; + static final String COL_URL = "URL"; + static final String COL_AVATAR = "AVATAR"; + static final String COL_AVATAR_STATIC = "AVATAR_STATIC"; + static final String COL_HEADER = "HEADER"; + static final String COL_HEADER_STATIC = "HEADER_STATIC"; + static final String COL_INSTANCE = "INSTANCE"; + static final String COL_OAUTHTOKEN = "OAUTH_TOKEN"; + static final String COL_CLIENT_ID = "CLIENT_ID"; + static final String COL_CLIENT_SECRET = "CLIENT_SECRET"; + static final String COL_REFRESH_TOKEN = "REFRESH_TOKEN"; + static final String COL_IS_MODERATOR = "IS_MODERATOR"; + static final String COL_IS_ADMIN = "IS_ADMIN"; + static final String COL_UPDATED_AT = "UPDATED_AT"; + static final String COL_PRIVACY = "PRIVACY"; + static final String COL_SENSITIVE = "SENSITIVE"; + + + private static final String CREATE_TABLE_USER_ACCOUNT = "CREATE TABLE " + TABLE_USER_ACCOUNT + " (" + + COL_USER_ID + " TEXT, " + COL_USERNAME + " TEXT NOT NULL, " + COL_ACCT + " TEXT NOT NULL, " + + COL_DISPLAYED_NAME + " TEXT NOT NULL, " + COL_LOCKED + " INTEGER NOT NULL, " + + COL_FOLLOWERS_COUNT + " INTEGER NOT NULL, " + COL_FOLLOWING_COUNT + " INTEGER NOT NULL, " + COL_STATUSES_COUNT + " INTEGER NOT NULL, " + + COL_NOTE + " TEXT NOT NULL, " + COL_URL + " TEXT NOT NULL, " + + COL_AVATAR + " TEXT NOT NULL, " + COL_AVATAR_STATIC + " TEXT NOT NULL, " + + COL_HEADER + " TEXT NOT NULL, " + COL_HEADER_STATIC + " TEXT NOT NULL, " + + COL_IS_MODERATOR + " INTEGER DEFAULT 0, " + + COL_IS_ADMIN + " INTEGER DEFAULT 0, " + + COL_CLIENT_ID + " TEXT, " + COL_CLIENT_SECRET + " TEXT, " + COL_REFRESH_TOKEN + " TEXT," + + COL_UPDATED_AT + " TEXT, " + + COL_PRIVACY + " TEXT, " + + COL_SENSITIVE + " INTEGER DEFAULT 0, " + + COL_INSTANCE + " TEXT NOT NULL, " + COL_OAUTHTOKEN + " TEXT NOT NULL, " + COL_CREATED_AT + " TEXT NOT NULL)"; + + + //Table for peertube favorites + static final String TABLE_PEERTUBE_FAVOURITES = "PEERTUBE_FAVOURITES"; + static final String COL_ID = "ID"; + static final String COL_UUID = "UUID"; + static final String COL_CACHE = "CACHE"; + static final String COL_DATE = "DATE"; + + private final String CREATE_TABLE_PEERTUBE_FAVOURITES = "CREATE TABLE " + + TABLE_PEERTUBE_FAVOURITES + "(" + + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COL_UUID + " TEXT NOT NULL, " + + COL_INSTANCE + " TEXT NOT NULL, " + + COL_CACHE + " TEXT NOT NULL, " + + COL_DATE + " TEXT NOT NULL)"; + + public static SQLiteDatabase db; + private static Sqlite sInstance; + + + public Sqlite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { + super(context, name, factory, version); + } + + + public static synchronized Sqlite getInstance(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { + if (sInstance == null) { + sInstance = new Sqlite(context, name, factory, version); + } + return sInstance; + } + + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE_USER_ACCOUNT); + db.execSQL(CREATE_TABLE_PEERTUBE_FAVOURITES); + } + + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + switch (oldVersion) { + case 1: + } + } + + public SQLiteDatabase open() { + //opened with write access + db = getWritableDatabase(); + return db; + } + + public void close() { + //Close the db + if (db != null && db.isOpen()) { + db.close(); + } + } + + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/ui/video/PeertubeAdapter.java b/app/src/main/java/app/fedilab/fedilabtube/ui/video/PeertubeAdapter.java new file mode 100644 index 0000000..5546f65 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/ui/video/PeertubeAdapter.java @@ -0,0 +1,160 @@ +package app.fedilab.fedilabtube.ui.video; + + + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; + +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import app.fedilab.fedilabtube.PeertubeActivity; +import app.fedilab.fedilabtube.PeertubeEditUploadActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.entities.Account; +import app.fedilab.fedilabtube.client.entities.Peertube; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.interfaces.OnActionInterface; + + +public class PeertubeAdapter extends RecyclerView.Adapter implements OnActionInterface { + + private List peertubes; + private Context context; + private String instance; + + + public PeertubeAdapter(String instance, List peertubes) { + this.peertubes = peertubes; + this.instance = instance; + + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + context = parent.getContext(); + LayoutInflater layoutInflater = LayoutInflater.from(context); + return new ViewHolder(layoutInflater.inflate(R.layout.drawer_peertube, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + + + final PeertubeAdapter.ViewHolder holder = (PeertubeAdapter.ViewHolder) viewHolder; + final Peertube peertube = peertubes.get(position); + if (peertube.getInstance() == null) + peertube.setInstance(Helper.getLiveInstance(context)); + Account account = peertube.getAccount(); + + holder.peertube_account_name.setText(account.getAcct()); + holder.peertube_title.setText(peertube.getName()); + holder.peertube_duration.setText(context.getString(R.string.duration_video, Helper.secondsToString(peertube.getDuration()))); + holder.peertube_date.setText(String.format(" - %s", Helper.dateDiff(context, peertube.getCreated_at()))); + holder.peertube_views.setText(context.getString(R.string.number_view_video, Helper.withSuffix(peertube.getView()))); + + + Glide.with(holder.peertube_video_image.getContext()) + .load("https://" + peertube.getInstance() + peertube.getThumbnailPath()) + .into(holder.peertube_video_image); + if (account.getAvatar() != null && !account.getAvatar().equals("null") && !account.getAvatar().startsWith("http")) { + account.setAvatar("https://" + peertube.getInstance() + account.getAvatar()); + account.setAvatar_static(account.getAvatar()); + } + Helper.loadGiF(context, account, holder.peertube_profile); + + + if (peertube.getHeaderType() != null && peertube.getHeaderTypeValue() != null) { + String type = peertube.getHeaderType(); + if ("tags".equals(type)) { + holder.header_title.setText(String.format("#%s", peertube.getHeaderTypeValue())); + } else { + holder.header_title.setText(peertube.getHeaderTypeValue()); + } + holder.header_title.setVisibility(View.VISIBLE); + } else { + holder.header_title.setVisibility(View.GONE); + } + + holder.bottom_container.setOnClickListener(v -> { + Intent intent = new Intent(context, PeertubeEditUploadActivity.class); + Bundle b = new Bundle(); + b.putString("video_id", peertube.getUuid()); + intent.putExtras(b); + context.startActivity(intent); + }); + holder.peertube_video_image.setOnClickListener(v -> { + Intent intent = new Intent(context, PeertubeActivity.class); + Bundle b = new Bundle(); + if ((instance == null || instance.trim().length() == 0) ) + instance = Helper.getLiveInstance(context); + String finalUrl = "https://" + instance + peertube.getEmbedPath(); + Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/embed/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$"); + Matcher matcherLink = link.matcher(finalUrl); + if (matcherLink.find()) { + String url = matcherLink.group(1) + "/videos/watch/" + matcherLink.group(2); + b.putString("peertubeLinkToFetch", url); + b.putString("peertube_instance", Objects.requireNonNull(matcherLink.group(1)).replace("https://", "").replace("http://", "")); + b.putString("video_id", matcherLink.group(2)); + } + intent.putExtras(b); + context.startActivity(intent); + }); + + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return peertubes.size(); + } + + + @Override + public void onActionDone(APIResponse apiResponse, int statusCode) { + + } + + static class ViewHolder extends RecyclerView.ViewHolder { + LinearLayout main_container, bottom_container; + ImageView peertube_profile, peertube_video_image; + TextView peertube_account_name, peertube_views, peertube_duration; + TextView peertube_title, peertube_date, header_title; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + peertube_account_name = itemView.findViewById(R.id.peertube_account_name); + peertube_title = itemView.findViewById(R.id.peertube_title); + peertube_video_image = itemView.findViewById(R.id.peertube_video_image); + peertube_profile = itemView.findViewById(R.id.peertube_profile); + peertube_date = itemView.findViewById(R.id.peertube_date); + peertube_views = itemView.findViewById(R.id.peertube_views); + peertube_duration = itemView.findViewById(R.id.peertube_duration); + main_container = itemView.findViewById(R.id.main_container); + header_title = itemView.findViewById(R.id.header_title); + bottom_container = itemView.findViewById(R.id.bottom_container); + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/ui/video/VideoListFragment.java b/app/src/main/java/app/fedilab/fedilabtube/ui/video/VideoListFragment.java index 298f4c3..eb7fb44 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/ui/video/VideoListFragment.java +++ b/app/src/main/java/app/fedilab/fedilabtube/ui/video/VideoListFragment.java @@ -1,9 +1,15 @@ package app.fedilab.fedilabtube.ui.video; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.AsyncTask; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -11,19 +17,65 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.asynctasks.RetrieveFeedsAsyncTask; +import app.fedilab.fedilabtube.client.entities.Peertube; public class VideoListFragment extends Fragment { private VideoListModel videoListModel; - + private LinearLayoutManager mLayoutManager; + private boolean flag_loading; + private Context context; + private AsyncTask asyncTask; + private PeertubeAdapter peertubeAdapater; + private String max_id; + private List peertubes; + private RetrieveFeedsAsyncTask.Type type; + private RelativeLayout mainLoader, nextElementLoader, textviewNoAction; + private boolean firstLoad; + private SwipeRefreshLayout swipeRefreshLayout; + private String targetedId; + private String tag; + private RecyclerView lv_status; + private boolean showMediaOnly, showPinned, showReply; + private Intent streamingFederatedIntent, streamingLocalIntent; + private boolean firstTootsLoaded; + private String userId, instance; + private SharedPreferences sharedpreferences; + private boolean isSwipped; + private String remoteInstance; + private String instanceType; + private String search_peertube, remote_channel_name; + private String initialBookMark; + private String updatedBookMark; + private String lastReadToot; + private TextView textviewNoActionText; + private boolean ischannel; + private boolean ownVideos; + private BroadcastReceiver receive_action; + private BroadcastReceiver receive_data; + private Date lastReadTootDate, initialBookMarkDate, updatedBookMarkDate; + private int timelineId; + private String currentfilter; + private View rootView; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { videoListModel = ViewModelProviders.of(this).get(VideoListModel.class); View root = inflater.inflate(R.layout.fragment_video, container, false); final TextView textView = root.findViewById(R.id.text_home); + + peertubes = new ArrayList<>(); + videoListModel.getText().observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(@Nullable String s) { diff --git a/app/src/main/java/app/fedilab/fedilabtube/webview/CustomWebview.java b/app/src/main/java/app/fedilab/fedilabtube/webview/CustomWebview.java new file mode 100644 index 0000000..d5d2d54 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/webview/CustomWebview.java @@ -0,0 +1,42 @@ +package app.fedilab.fedilabtube.webview; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; +import android.util.AttributeSet; +import android.webkit.WebView; + + + + +public class CustomWebview extends WebView { + + + public CustomWebview(Context context) { + super(getFixedContext(context)); + } + + public CustomWebview(Context context, AttributeSet attrs) { + super(getFixedContext(context), attrs); + } + + public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr) { + super(getFixedContext(context), attrs, defStyleAttr); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(getFixedContext(context), attrs, defStyleAttr, defStyleRes); + } + + @SuppressWarnings("deprecation") + public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) { + super(getFixedContext(context), attrs, defStyleAttr, privateBrowsing); + } + + public static Context getFixedContext(Context context) { + return context.createConfigurationContext(new Configuration()); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebChromeClient.java b/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebChromeClient.java new file mode 100644 index 0000000..5655a5a --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebChromeClient.java @@ -0,0 +1,217 @@ +package app.fedilab.fedilabtube.webview; + + +import android.app.Activity; +import android.graphics.Bitmap; +import android.media.MediaPlayer; +import android.view.LayoutInflater; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import app.fedilab.fedilabtube.R; + + +public class MastalabWebChromeClient extends WebChromeClient implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener { + + private FrameLayout videoViewContainer; + private CustomViewCallback videoViewCallback; + + private ToggledFullscreenCallback toggledFullscreenCallback; + + private CustomWebview webView; + private View activityNonVideoView; + private ViewGroup activityVideoView; + private ProgressBar pbar; + private boolean isVideoFullscreen; + private Activity activity; + + + public MastalabWebChromeClient(Activity activity, CustomWebview webView, FrameLayout activityNonVideoView, ViewGroup activityVideoView) { + this.activity = activity; + this.isVideoFullscreen = false; + this.webView = webView; + this.pbar = activity.findViewById(R.id.progress_bar); + this.activityNonVideoView = activityNonVideoView; + this.activityVideoView = activityVideoView; + } + + @Override + public void onProgressChanged(WebView view, int progress) { + if (pbar != null) { + if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) { + pbar.setVisibility(ProgressBar.VISIBLE); + } + pbar.setProgress(progress); + if (progress == 100) { + pbar.setVisibility(ProgressBar.GONE); + } + } + } + + @Override + public Bitmap getDefaultVideoPoster() { + return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888); + } + + @Override + public void onReceivedIcon(WebView view, Bitmap icon) { + super.onReceivedIcon(view, icon); + LayoutInflater mInflater = LayoutInflater.from(activity); + ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar(); + if (actionBar != null) { + View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, new LinearLayout(activity), false); + TextView webview_title = webview_actionbar.findViewById(R.id.webview_title); + webview_title.setText(view.getTitle()); + ImageView webview_favicon = webview_actionbar.findViewById(R.id.webview_favicon); + if (icon != null) + webview_favicon.setImageBitmap(icon); + actionBar.setCustomView(webview_actionbar); + actionBar.setDisplayShowCustomEnabled(true); + } else { + activity.setTitle(view.getTitle()); + } + + } + + /** + * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen) + * + * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback + */ + public void setOnToggledFullscreen(ToggledFullscreenCallback callback) { + this.toggledFullscreenCallback = callback; + } + + //FULLSCREEN VIDEO + //Code from https://stackoverflow.com/a/16179544/3197259 + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + if (view instanceof FrameLayout) { + if (((AppCompatActivity) activity).getSupportActionBar() != null) + //noinspection ConstantConditions + ((AppCompatActivity) activity).getSupportActionBar().hide(); + // A video wants to be shown + FrameLayout frameLayout = (FrameLayout) view; + View focusedChild = frameLayout.getFocusedChild(); + + // Save video related variables + isVideoFullscreen = true; + this.videoViewContainer = frameLayout; + this.videoViewCallback = callback; + + // Hide the non-video view, add the video view, and show it + activityNonVideoView.setVisibility(View.INVISIBLE); + activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + activityVideoView.setVisibility(View.VISIBLE); + if (focusedChild instanceof android.widget.VideoView) { + // android.widget.VideoView (typically API level <11) + android.widget.VideoView videoView = (android.widget.VideoView) focusedChild; + // Handle all the required events + videoView.setOnCompletionListener(this); + videoView.setOnErrorListener(this); + } else { + // Other classes, including: + // - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18) + // - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18) + // - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+) + + // Handle HTML5 video ended event only if the class is a SurfaceView + // Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below + if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView) { + // Run javascript code that detects the video end and notifies the Javascript interface + String js = "javascript:"; + js += "var _ytrp_html5_video_last;"; + js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];"; + js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {"; + { + js += "_ytrp_html5_video_last = _ytrp_html5_video;"; + js += "function _ytrp_html5_video_ended() {"; + { + js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView + } + js += "}"; + js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);"; + } + js += "}"; + webView.loadUrl(js); + } + } + // Notify full-screen change + if (toggledFullscreenCallback != null) { + toggledFullscreenCallback.toggledFullscreen(true); + } + } + } + + // Available in API level 14+, deprecated in API level 18+ + @Override + @SuppressWarnings("deprecation") + public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { + onShowCustomView(view, callback); + } + + @Override + public void onHideCustomView() { + if (((AppCompatActivity) activity).getSupportActionBar() != null) + //noinspection ConstantConditions + ((AppCompatActivity) activity).getSupportActionBar().show(); + // This method should be manually called on video end in all cases because it's not always called automatically. + // This method must be manually called on back key press (from this class' onBackPressed() method). + if (isVideoFullscreen) { + // Hide the video view, remove it, and show the non-video view + activityVideoView.setVisibility(View.INVISIBLE); + activityVideoView.removeView(videoViewContainer); + activityNonVideoView.setVisibility(View.VISIBLE); + // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes) + if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium.")) { + videoViewCallback.onCustomViewHidden(); + } + + // Reset video related variables + isVideoFullscreen = false; + videoViewContainer = null; + videoViewCallback = null; + + // Notify full-screen change + if (toggledFullscreenCallback != null) { + toggledFullscreenCallback.toggledFullscreen(false); + } + } + } + + // Video will start loading + @Override + public View getVideoLoadingProgressView() { + return super.getVideoLoadingProgressView(); + } + + // Video finished playing, only called in the case of android.widget.VideoView (typically API level <11) + @Override + public void onCompletion(MediaPlayer mp) { + onHideCustomView(); + } + + // Error while playing video, only called in the case of android.widget.VideoView (typically API level <11) + @Override + public boolean onError(MediaPlayer mp, int what, int extra) { + return false; // By returning false, onCompletion() will be called + } + + public interface ToggledFullscreenCallback { + void toggledFullscreen(boolean fullscreen); + } + +} + diff --git a/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebViewClient.java b/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebViewClient.java new file mode 100644 index 0000000..e7b7516 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/webview/MastalabWebViewClient.java @@ -0,0 +1,76 @@ +package app.fedilab.fedilabtube.webview; + + +import android.app.Activity; +import android.graphics.Bitmap; +import android.view.LayoutInflater; +import android.view.View; +import android.webkit.URLUtil; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.fedilabtube.R; + + + +public class MastalabWebViewClient extends WebViewClient { + + public List domains = new ArrayList<>(); + private Activity activity; + private int count = 0; + + public MastalabWebViewClient(Activity activity) { + this.activity = activity; + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + } + + + public List getDomains() { + return this.domains; + } + + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (URLUtil.isNetworkUrl(url)) { + return false; + } else { + view.stopLoading(); + view.goBack(); + } + return false; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + count = 0; + domains = new ArrayList<>(); + domains.clear(); + ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar(); + LayoutInflater mInflater = LayoutInflater.from(activity); + if (actionBar != null) { + View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, new LinearLayout(activity), false); + TextView webview_title = webview_actionbar.findViewById(R.id.webview_title); + webview_title.setText(url); + actionBar.setCustomView(webview_actionbar); + actionBar.setDisplayShowCustomEnabled(true); + } else { + activity.setTitle(url); + } + + } + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/webview/ProxyHelper.java b/app/src/main/java/app/fedilab/fedilabtube/webview/ProxyHelper.java new file mode 100644 index 0000000..43e2440 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/webview/ProxyHelper.java @@ -0,0 +1,153 @@ +package app.fedilab.fedilabtube.webview; + + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.Proxy; +import android.util.ArrayMap; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ProxyHelper { + + + public static void setProxy(Context context, CustomWebview webview, String host, int port, String applicationClassName) { + + setProxyKKPlus(context, webview, host, port, applicationClassName); + } + + + @SuppressWarnings("all") + private static boolean setProxyICS(CustomWebview webview, String host, int port) { + try { + Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge"); + Class params[] = new Class[1]; + params[0] = Class.forName("android.net.ProxyProperties"); + Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params); + + Class wv = Class.forName("android.webkit.WebView"); + Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore"); + Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview); + + Class wvc = Class.forName("android.webkit.WebViewCore"); + Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame"); + Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance); + + Class bf = Class.forName("android.webkit.BrowserFrame"); + Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge"); + Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame); + + Class ppclass = Class.forName("android.net.ProxyProperties"); + Class pparams[] = new Class[3]; + pparams[0] = String.class; + pparams[1] = int.class; + pparams[2] = String.class; + Constructor ppcont = ppclass.getConstructor(pparams); + + updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null)); + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * Set Proxy for Android 4.1 - 4.3. + */ + @SuppressWarnings("all") + private static boolean setProxyJB(CustomWebview webview, String host, int port) { + + try { + Class wvcClass = Class.forName("android.webkit.WebViewClassic"); + Class wvParams[] = new Class[1]; + wvParams[0] = Class.forName("android.webkit.WebView"); + Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams); + Object webViewClassic = fromWebView.invoke(null, webview); + + Class wv = Class.forName("android.webkit.WebViewClassic"); + Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore"); + Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic); + + Class wvc = Class.forName("android.webkit.WebViewCore"); + Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame"); + Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance); + + Class bf = Class.forName("android.webkit.BrowserFrame"); + Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge"); + Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame); + + Class ppclass = Class.forName("android.net.ProxyProperties"); + Class pparams[] = new Class[3]; + pparams[0] = String.class; + pparams[1] = int.class; + pparams[2] = String.class; + Constructor ppcont = ppclass.getConstructor(pparams); + + Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge"); + Class params[] = new Class[1]; + params[0] = Class.forName("android.net.ProxyProperties"); + Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params); + + updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null)); + } catch (Exception ex) { + return false; + } + return true; + } + + // from https://stackoverflow.com/questions/19979578/android-webview-set-proxy-programatically-kitkat + @SuppressLint("NewApi") + @SuppressWarnings("all") + private static void setProxyKKPlus(Context appContext, CustomWebview webView, String host, int port, String applicationClassName) { + + System.setProperty("http.proxyHost", host); + System.setProperty("http.proxyPort", port + ""); + System.setProperty("https.proxyHost", host); + System.setProperty("https.proxyPort", port + ""); + try { + Class applictionCls = Class.forName("android.app.Application"); + Field loadedApkField = applictionCls.getDeclaredField("mLoadedApk"); + loadedApkField.setAccessible(true); + Object loadedApk = loadedApkField.get(appContext); + Class loadedApkCls = Class.forName("android.app.LoadedApk"); + Field receiversField = loadedApkCls.getDeclaredField("mReceivers"); + receiversField.setAccessible(true); + ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); + for (Object receiverMap : receivers.values()) { + for (Object rec : ((ArrayMap) receiverMap).keySet()) { + Class clazz = rec.getClass(); + if (clazz.getName().contains("ProxyChangeListener")) { + Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class); + Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); + + onReceiveMethod.invoke(rec, appContext, intent); + } + } + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException { + boolean oldAccessibleValue = field.isAccessible(); + field.setAccessible(true); + Object result = field.get(classInstance); + field.setAccessible(oldAccessibleValue); + return result; + } +} diff --git a/app/src/main/res/drawable/missing_peertube.png b/app/src/main/res/drawable/missing_peertube.png new file mode 100644 index 0000000..df66422 Binary files /dev/null and b/app/src/main/res/drawable/missing_peertube.png differ diff --git a/app/src/main/res/layout/drawer_peertube.xml b/app/src/main/res/layout/drawer_peertube.xml new file mode 100644 index 0000000..5bbb1d7 --- /dev/null +++ b/app/src/main/res/layout/drawer_peertube.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_peertube_notification.xml b/app/src/main/res/layout/drawer_peertube_notification.xml new file mode 100644 index 0000000..081c243 --- /dev/null +++ b/app/src/main/res/layout/drawer_peertube_notification.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_peertube_favourites.xml b/app/src/main/res/layout/fragment_peertube_favourites.xml new file mode 100644 index 0000000..53394f9 --- /dev/null +++ b/app/src/main/res/layout/fragment_peertube_favourites.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_peertube_settings.xml b/app/src/main/res/layout/fragment_peertube_settings.xml new file mode 100644 index 0000000..3b0c846 --- /dev/null +++ b/app/src/main/res/layout/fragment_peertube_settings.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + 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 0000000..03f0241 --- /dev/null +++ b/app/src/main/res/layout/fragment_playlists.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_video.xml b/app/src/main/res/layout/fragment_video.xml index 82035df..3c745fc 100644 --- a/app/src/main/res/layout/fragment_video.xml +++ b/app/src/main/res/layout/fragment_video.xml @@ -1,22 +1,78 @@ - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index e00c2dd..98c6c2b 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,4 +2,5 @@ 16dp 16dp + 5dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3ea1404..4e1d715 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,4 +8,25 @@ Tendances Compte Plus aimées + Une erreur s\'est produite ! + Aucune vidéo ! + Favicon + + Fermer + Téléverser + Il n’y a aucune vidéo Peertube dans vos favoris ! + Aperçu de l\'image + Sélectionnez un fichier à transférer + Chaîne + Vidéos + Chaînes + Photo du profil + + %d s + %d min + %d h + %d j + + %s vues + Durée : %s \ No newline at end of file