Prepares views

This commit is contained in:
Thomas 2020-09-20 11:07:28 +02:00
parent 9f444d6d88
commit 0c6c5c813f
11 changed files with 344 additions and 69 deletions

View File

@ -205,6 +205,9 @@
<string name="action_unmute">Réactiver le compte</string>
<string name="muted_done">Le compte a été mis en sourdine !</string>
<string name="title_channel">Chaînes</string>
<string name="captions">Sous-titres</string>
<string name="none">Aucun</string>
<string name="pickup_captions">Sélectionner des sous-titres</string>
<string name="name">Nom</string>
<string name="action_channel_create">Créer une chaîne</string>
<string name="action_channel_edit">Modifier une chaîne</string>

View File

@ -136,7 +136,9 @@
<string name="set_video_mode_choice" translatable="false">set_video_mode_choice</string>
<string name="set_video_minimize_choice" translatable="false">set_video_minimize_choice</string>
<string name="captions">Captions</string>
<string name="pickup_captions">Pickup captions</string>
<string name="none">None</string>
<string name="set_video_mode_description">Allows to change mode for playing videos (default, streaming or via a browser).</string>

View File

@ -16,7 +16,6 @@ package app.fedilab.fedilabtube;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
@ -50,6 +49,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.appcompat.widget.PopupMenu;
@ -76,7 +76,9 @@ import org.jetbrains.annotations.NotNull;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.net.ssl.HttpsURLConnection;
@ -85,6 +87,7 @@ 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.Caption;
import app.fedilab.fedilabtube.client.entities.Peertube;
import app.fedilab.fedilabtube.client.entities.Playlist;
import app.fedilab.fedilabtube.client.entities.PlaylistElement;
@ -97,6 +100,7 @@ import app.fedilab.fedilabtube.helper.Helper;
import app.fedilab.fedilabtube.sqlite.AccountDAO;
import app.fedilab.fedilabtube.sqlite.PeertubeFavoritesDAO;
import app.fedilab.fedilabtube.sqlite.Sqlite;
import app.fedilab.fedilabtube.viewmodel.CaptionsVM;
import app.fedilab.fedilabtube.viewmodel.CommentVM;
import app.fedilab.fedilabtube.viewmodel.FeedsVM;
import app.fedilab.fedilabtube.viewmodel.PlaylistsVM;
@ -118,7 +122,7 @@ public class PeertubeActivity extends AppCompatActivity {
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 TextView peertube_view_count, peertube_playlist, peertube_bookmark, peertube_like_count, peertube_dislike_count, peertube_description, peertube_title, more_actions;
private ScrollView peertube_information_container;
private Peertube peertube;
private PlayerView playerView;
@ -137,6 +141,8 @@ public class PeertubeActivity extends AppCompatActivity {
private PlaylistsVM playlistsViewModel;
private boolean playInMinimized;
private boolean onStopCalled;
private List<Caption> captions;
private String captionValue;
public static void hideKeyboard(Activity activity) {
if (activity != null && activity.getWindow() != null) {
@ -156,8 +162,8 @@ public class PeertubeActivity extends AppCompatActivity {
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);
more_actions = findViewById(R.id.more_actions);
peertube_description = findViewById(R.id.peertube_description);
peertube_title = findViewById(R.id.peertube_title);
peertube_information_container = findViewById(R.id.peertube_information_container);
@ -282,7 +288,7 @@ public class PeertubeActivity extends AppCompatActivity {
playerView.setPlayer(player);
loader.setVisibility(View.GONE);
player.setPlayWhenReady(true);
captions = null;
}
fullscreen = FullScreenMediaController.fullscreen.OFF;
setFullscreen(FullScreenMediaController.fullscreen.OFF);
@ -294,6 +300,9 @@ public class PeertubeActivity extends AppCompatActivity {
FeedsVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(FeedsVM.class);
feedsViewModel.getVideo(peertubeInstance, videoId).observe(PeertubeActivity.this, this::manageVIewVideo);
CaptionsVM captionsViewModel = new ViewModelProvider(PeertubeActivity.this).get(CaptionsVM.class);
captionsViewModel.getCaptions(videoId).observe(PeertubeActivity.this, this::manageCaptions);
}
public void change() {
@ -396,6 +405,15 @@ public class PeertubeActivity extends AppCompatActivity {
this.fullscreen = fullscreen;
}
public void manageCaptions(APIResponse apiResponse) {
if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getCaptions() == null || apiResponse.getCaptions().size() == 0) {
return;
}
captions = apiResponse.getCaptions();
}
public void manageVIewVideo(APIResponse apiResponse) {
if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) {
@ -599,7 +617,17 @@ public class PeertubeActivity extends AppCompatActivity {
}
peertube_download.setOnClickListener(v -> {
more_actions.setOnClickListener(view -> {
PopupMenu popup = new PopupMenu(PeertubeActivity.this, more_actions);
popup.getMenuInflater()
.inflate(R.menu.main_video, popup.getMenu());
if (captions == null) {
popup.getMenu().findItem(R.id.action_captions).setEnabled(false);
}
popup.setOnMenuItemClickListener(item -> {
switch (item.getItemId()) {
case R.id.action_download:
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);
@ -609,7 +637,72 @@ public class PeertubeActivity extends AppCompatActivity {
} else {
Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null, PeertubeActivity.this));
}
break;
case R.id.action_share:
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)));
break;
case R.id.action_captions:
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this);
if (captions == null) {
return true;
}
String[] itemsKeyLanguage = new String[captions.size() + 1];
String[] itemsLabelLanguage = new String[captions.size() + 1];
itemsLabelLanguage[0] = getString(R.string.none);
itemsKeyLanguage[0] = "null";
int i = 1;
if (captions.size() > 0) {
for (Caption caption : captions) {
Iterator<Map.Entry<String, String>> it = caption.getLanguage().entrySet().iterator();
Map.Entry<String, String> pair = it.next();
itemsLabelLanguage[i] = pair.getValue();
itemsKeyLanguage[i] = pair.getKey();
i++;
}
}
dialogBuilder.setSingleChoiceItems(itemsLabelLanguage, i, (dialog, which) -> {
captionValue = itemsKeyLanguage[which];
});
dialogBuilder.setOnDismissListener(dialogInterface -> {
});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> dialog.dismiss());
androidx.appcompat.app.AlertDialog alertDialog = dialogBuilder.create();
alertDialog.setTitle(getString(R.string.pickup_captions));
alertDialog.show();
break;
}
return true;
});
popup.show();
});
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
List<Peertube> peertubes = new PeertubeFavoritesDAO(PeertubeActivity.this, db).getSinglePeertube(peertube);
@ -636,31 +729,6 @@ public class PeertubeActivity extends AppCompatActivity {
peertube_bookmark.setCompoundDrawablesWithIntrinsicBounds(null, ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_bookmark_24), 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

View File

@ -17,6 +17,7 @@ package app.fedilab.fedilabtube.client;
import java.util.List;
import app.fedilab.fedilabtube.client.entities.Account;
import app.fedilab.fedilabtube.client.entities.Caption;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.Instance;
import app.fedilab.fedilabtube.client.entities.Peertube;
@ -37,6 +38,7 @@ public class APIResponse {
private List<Playlist> playlists = null;
private List<String> domains = null;
private List<Relationship> relationships = null;
private List<Caption> captions = null;
private Error error = null;
private String since_id, max_id;
private List<PlaylistElement> playlistForVideos;
@ -170,4 +172,12 @@ public class APIResponse {
public void setInstances(List<Instance> instances) {
this.instances = instances;
}
public List<Caption> getCaptions() {
return captions;
}
public void setCaptions(List<Caption> captions) {
this.captions = captions;
}
}

View File

@ -21,6 +21,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.text.Html;
import android.text.SpannableString;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
@ -46,6 +47,7 @@ import app.fedilab.fedilabtube.BuildConfig;
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.Caption;
import app.fedilab.fedilabtube.client.entities.ChannelCreation;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.Instance;
@ -515,14 +517,16 @@ public class PeertubeAPI {
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.setId(comment.getString("id"));
status.setUri(comment.getString("url"));
status.setUrl(comment.getString("url"));
status.setSensitive(false);
status.setContent(comment.get("text").toString());
status.setIn_reply_to_id(comment.get("inReplyToCommentId").toString());
status.setContent(comment.getString("text"));
status.setIn_reply_to_id(comment.getString("inReplyToCommentId"));
if (comment.has("account") && !comment.isNull("account")) {
status.setAccount(parseAccountResponsePeertube(instance, comment.getJSONObject("account")));
status.setCreated_at(Helper.mstStringToDate(comment.get("createdAt").toString()));
}
status.setCreated_at(Helper.mstStringToDate(comment.getString("createdAt")));
status.setVisibility("public");
SpannableString spannableString;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
@ -548,11 +552,11 @@ public class PeertubeAPI {
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.setId(resobj.getString("id"));
account.setUsername(resobj.getString("name"));
account.setAcct(resobj.getString("name") + "@" + resobj.getString("host"));
account.setDisplay_name(resobj.get("displayName").toString());
account.setHost(resobj.get("host").toString());
account.setHost(resobj.getString("host"));
if (resobj.has("createdAt"))
account.setCreated_at(Helper.mstStringToDate(resobj.getString("createdAt")));
else
@ -994,6 +998,32 @@ public class PeertubeAPI {
return newValues;
}
/**
* Find captions for a video
*
* @param videoId String id of the video
* @return APIResponse
*/
public APIResponse getCaptions(String videoId) {
apiResponse = new APIResponse();
try {
String response = new HttpsConnection(context).get(getAbsoluteUrl(String.format("/videos/%s/captions", videoId)), 60, null, prefKeyOauthTokenT);
Log.v(Helper.TAG, "response: " + response);
JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
List<Caption> captions = parseCaption(jsonArray);
apiResponse.setCaptions(captions);
} catch (HttpsConnection.HttpsConnectionException e) {
e.printStackTrace();
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
e.printStackTrace();
}
return apiResponse;
}
/**
* Returns an account
*
@ -2053,6 +2083,46 @@ public class PeertubeAPI {
}
/**
* Parse json response for captions
*
* @param jsonArray JSONArray
* @return List<Caption>
*/
private List<Caption> parseCaption(JSONArray jsonArray) {
List<Caption> captions = new ArrayList<>();
try {
int i = 0;
while (i < jsonArray.length()) {
JSONObject resobj = jsonArray.getJSONObject(i);
Caption caption = parseCaption(resobj);
captions.add(caption);
i++;
}
} catch (JSONException e) {
setDefaultError(e);
}
return captions;
}
private Caption parseCaption(JSONObject resobj) {
Caption caption = new Caption();
try {
caption.setCaptionPath(resobj.getString("captionPath"));
if (resobj.has("language")) {
JSONObject resobjLang = resobj.getJSONObject("language");
HashMap<String, String> language = new HashMap<>();
language.put(resobjLang.getString("id"), resobjLang.getString("label"));
caption.setLanguage(language);
}
} catch (JSONException e) {
e.printStackTrace();
}
return caption;
}
/**
* Parse json array response for instances
*

View File

@ -0,0 +1,39 @@
package app.fedilab.fedilabtube.client.entities;
/* Copyright 2020 Thomas Schneider
*
* This file is a part of TubeLab
*
* 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.
*
* TubeLab 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 TubeLab; if not,
* see <http://www.gnu.org/licenses>. */
import java.util.HashMap;
public class Caption {
private String captionPath;
private HashMap<String, String> language;
public String getCaptionPath() {
return captionPath;
}
public void setCaptionPath(String captionPath) {
this.captionPath = captionPath;
}
public HashMap<String, String> getLanguage() {
return language;
}
public void setLanguage(HashMap<String, String> language) {
this.language = language;
}
}

View File

@ -0,0 +1,58 @@
package app.fedilab.fedilabtube.viewmodel;
/* Copyright 2020 Thomas Schneider
*
* This file is a part of TubeLab
*
* 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.
*
* TubeLab 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 TubeLab; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import app.fedilab.fedilabtube.client.APIResponse;
import app.fedilab.fedilabtube.client.PeertubeAPI;
public class CaptionsVM extends AndroidViewModel {
private MutableLiveData<APIResponse> apiResponseMutableLiveData;
public CaptionsVM(@NonNull Application application) {
super(application);
}
public LiveData<APIResponse> getCaptions(String videoId) {
apiResponseMutableLiveData = new MutableLiveData<>();
loadCaptions(videoId);
return apiResponseMutableLiveData;
}
private void loadCaptions(String videoId) {
Context _mContext = getApplication().getApplicationContext();
new Thread(() -> {
try {
PeertubeAPI peertubeAPI = new PeertubeAPI(_mContext);
APIResponse apiResponse = peertubeAPI.getCaptions(videoId);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> apiResponseMutableLiveData.setValue(apiResponse);
mainHandler.post(myRunnable);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2L4,14v-2zM14,18L4,18v-2h10v2zM20,18h-4v-2h4v2zM20,14L10,14v-2h10v2z" />
</vector>

View File

@ -183,23 +183,9 @@
android:layout_weight="1"
tools:ignore="UselessLeaf" />
<TextView
android:id="@+id/peertube_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:drawablePadding="5dp"
android:gravity="center_horizontal"
android:text="@string/share"
android:textSize="12sp"
app:drawableTopCompat="@drawable/ic_baseline_share_24" />
<TextView
android:id="@+id/peertube_download"
android:id="@+id/more_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
@ -209,9 +195,9 @@
android:layout_marginRight="10dp"
android:drawablePadding="5dp"
android:gravity="center_horizontal"
android:text="@string/download"
android:textSize="12sp"
app:drawableTopCompat="@drawable/ic_baseline_cloud_upload_24" />
android:text=""
app:drawableTopCompat="@drawable/ic_baseline_more_vert_24" />
</LinearLayout>
<TextView

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_baseline_share_24"
android:title="@string/share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_download"
android:icon="@drawable/ic_baseline_cloud_download_24"
android:title="@string/download"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_captions"
android:icon="@drawable/ic_baseline_subtitles_24"
android:title="@string/captions"
app:showAsAction="ifRoom" />
</menu>