mirror of
				https://framagit.org/tom79/fedilab-tube
				synced 2025-06-05 21:09:11 +02:00 
			
		
		
		
	Support Emoji and replies
This commit is contained in:
		| @@ -6,6 +6,7 @@ android { | ||||
|     compileSdkVersion 30 | ||||
|     buildToolsVersion "30.0.2" | ||||
|  | ||||
|  | ||||
|     defaultConfig { | ||||
|  | ||||
|         minSdkVersion 21 | ||||
| @@ -30,6 +31,10 @@ android { | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|  | ||||
|     buildFeatures { | ||||
|         viewBinding = true | ||||
|     } | ||||
|  | ||||
|     lintOptions { | ||||
|         disable 'MissingTranslation' | ||||
|         checkReleaseBuilds false | ||||
|   | ||||
| @@ -184,6 +184,7 @@ | ||||
|     <string name="peertube_video_unblacklist"><![CDATA[Votre vidéo <b>%1$s</b> n’est plus blacklisté]]></string> | ||||
|     <string name="peertube_video_abuse"><![CDATA[Nouveau signalement pour la vidéo : <b>%1$s</b>]]></string> | ||||
|     <string name="add_public_comment">Ajouter un commentaire public</string> | ||||
|     <string name="add_public_reply">Répondre publiquement</string> | ||||
|     <string name="send_comment">Envoyer un commentaire</string> | ||||
|     <string name="all">Tout</string> | ||||
|     <!-- end languages --> | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|     <string name="set_video_cache_choice" translatable="false">set_video_cache_choice</string> | ||||
|     <string name="set_theme_choice" translatable="false">set_theme_choice</string> | ||||
|  | ||||
|     <string name="add_public_reply">Add a public reply</string> | ||||
|  | ||||
|     <plurals name="number_of_replies"> | ||||
|         <item quantity="one">%d reply</item> | ||||
|   | ||||
							
								
								
									
										2684
									
								
								app/src/main/assets/emoji.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2684
									
								
								app/src/main/assets/emoji.csv
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -32,20 +32,21 @@ import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.support.v4.media.session.MediaSessionCompat; | ||||
| import android.text.Html; | ||||
| import android.text.Spanned; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.MotionEvent; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.WindowManager; | ||||
| import android.view.animation.Animation; | ||||
| import android.view.animation.TranslateAnimation; | ||||
| import android.view.inputmethod.InputMethodManager; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | ||||
| import android.widget.EditText; | ||||
| import android.widget.FrameLayout; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.RelativeLayout; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| @@ -53,13 +54,10 @@ import androidx.annotation.NonNull; | ||||
| import androidx.annotation.RequiresApi; | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.appcompat.widget.AppCompatImageView; | ||||
| import androidx.appcompat.widget.PopupMenu; | ||||
| import androidx.constraintlayout.widget.ConstraintLayout; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.content.ContextCompat; | ||||
| import androidx.core.graphics.drawable.DrawableCompat; | ||||
| import androidx.core.widget.NestedScrollView; | ||||
| import androidx.lifecycle.ViewModelProvider; | ||||
| import androidx.recyclerview.widget.LinearLayoutManager; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| @@ -79,7 +77,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; | ||||
| import com.google.android.exoplayer2.trackselection.TrackSelector; | ||||
| 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.DefaultHttpDataSourceFactory; | ||||
| @@ -98,6 +95,7 @@ import app.fedilab.fedilabtube.client.APIResponse; | ||||
| import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; | ||||
| import app.fedilab.fedilabtube.client.data.AccountData.Account; | ||||
| import app.fedilab.fedilabtube.client.data.CaptionData.Caption; | ||||
| import app.fedilab.fedilabtube.client.data.CommentData; | ||||
| import app.fedilab.fedilabtube.client.data.CommentData.Comment; | ||||
| import app.fedilab.fedilabtube.client.data.PlaylistData; | ||||
| import app.fedilab.fedilabtube.client.data.VideoData; | ||||
| @@ -105,6 +103,7 @@ import app.fedilab.fedilabtube.client.entities.File; | ||||
| import app.fedilab.fedilabtube.client.entities.ItemStr; | ||||
| import app.fedilab.fedilabtube.client.entities.PlaylistExist; | ||||
| import app.fedilab.fedilabtube.client.entities.Report; | ||||
| import app.fedilab.fedilabtube.databinding.ActivityPeertubeBinding; | ||||
| import app.fedilab.fedilabtube.drawer.CommentListAdapter; | ||||
| import app.fedilab.fedilabtube.helper.CacheDataSourceFactory; | ||||
| import app.fedilab.fedilabtube.helper.FullScreenMediaController; | ||||
| @@ -124,6 +123,7 @@ import es.dmoral.toasty.Toasty; | ||||
|  | ||||
| import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT; | ||||
| import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO; | ||||
| import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPLY; | ||||
| import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT; | ||||
| import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_VIDEO; | ||||
| import static app.fedilab.fedilabtube.helper.Helper.getAttColor; | ||||
| @@ -136,33 +136,23 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|     public static String video_id; | ||||
|     private String peertubeInstance, videoUuid; | ||||
|     private FullScreenMediaController.fullscreen fullscreen; | ||||
|     private RelativeLayout loader; | ||||
|     private TextView peertube_view_count, peertube_playlist, peertube_bookmark, peertube_like_count, peertube_dislike_count, peertube_description, peertube_title, more_actions; | ||||
|     private NestedScrollView peertube_information_container; | ||||
|     private VideoData.Video peertube; | ||||
|     private PlayerView playerView; | ||||
|     private ImageView fullScreenIcon; | ||||
|     private SimpleExoPlayer player; | ||||
|     private boolean fullScreenMode; | ||||
|     private Dialog fullScreenDialog; | ||||
|     private AppCompatImageView fullScreenIcon; | ||||
|     private TextView resolution; | ||||
|     private VideoData.Video peertube; | ||||
|     private int mode; | ||||
|     private ConstraintLayout write_comment_container; | ||||
|     private ImageView send; | ||||
|     private TextView add_comment_read; | ||||
|     private EditText add_comment_write; | ||||
|     private Map<String, List<PlaylistExist>> playlists; | ||||
|     private boolean playInMinimized; | ||||
|     private boolean onStopCalled; | ||||
|     private List<Caption> captions; | ||||
|     private TextView no_action_text; | ||||
|     private String max_id; | ||||
|     private RecyclerView lv_comments; | ||||
|     private boolean flag_loading; | ||||
|     private boolean isMyVideo; | ||||
|     private List<Comment> comments; | ||||
|     private CommentListAdapter commentListAdapter; | ||||
|     private CommentListAdapter commentListAdapter, commentReplyListAdapter; | ||||
|     private boolean sepiaSearch; | ||||
|     private ActivityPeertubeBinding binding; | ||||
|  | ||||
|     public static void hideKeyboard(Activity activity) { | ||||
|         if (activity != null && activity.getWindow() != null) { | ||||
| @@ -176,45 +166,32 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_peertube); | ||||
|  | ||||
|         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); | ||||
|         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); | ||||
|         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); | ||||
|         CustomWebview webview_video = findViewById(R.id.webview_video); | ||||
|         playerView = findViewById(R.id.media_video); | ||||
|         write_comment_container = findViewById(R.id.write_comment_container); | ||||
|         binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); | ||||
|         View view = binding.getRoot(); | ||||
|         setContentView(view); | ||||
|  | ||||
|         max_id = "0"; | ||||
|         loader = findViewById(R.id.loader); | ||||
|         ImageView my_pp = findViewById(R.id.my_pp); | ||||
|  | ||||
|         SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); | ||||
|         SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); | ||||
|         String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); | ||||
|         if (Helper.isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|             Account account = new AccountDAO(PeertubeActivity.this, db).getAccountByToken(token); | ||||
|             Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, my_pp); | ||||
|             Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, binding.myPp); | ||||
|             Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, binding.myPpReply); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         if (Helper.isTablet(PeertubeActivity.this)) { | ||||
|             RelativeLayout video_container = findViewById(R.id.video_container); | ||||
|  | ||||
|             LinearLayout.LayoutParams param = new LinearLayout.LayoutParams( | ||||
|                     LinearLayout.LayoutParams.MATCH_PARENT, | ||||
|                     0, | ||||
|                     2.0f | ||||
|             ); | ||||
|             video_container.setLayoutParams(param); | ||||
|             binding.videoContainer.setLayoutParams(param); | ||||
|         } | ||||
|  | ||||
|         if (getSupportActionBar() != null) | ||||
| @@ -239,23 +216,21 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         } | ||||
|  | ||||
|         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); | ||||
|             binding.webviewVideo.setVisibility(View.VISIBLE); | ||||
|             binding.mediaVideo.setVisibility(View.GONE); | ||||
|             CustomWebview webview_video = Helper.initializeWebview(PeertubeActivity.this, R.id.webview_video, null); | ||||
|  | ||||
|             MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, webview_container, videoLayout); | ||||
|             MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, binding.mainMediaFrame, binding.videoLayout); | ||||
|             mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> { | ||||
|  | ||||
|                 if (fullscreen) { | ||||
|                     videoLayout.setVisibility(View.VISIBLE); | ||||
|                     binding.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); | ||||
|                     binding.peertubeInformationContainer.setVisibility(View.GONE); | ||||
|                     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | ||||
|                 } else { | ||||
|                     WindowManager.LayoutParams attrs = getWindow().getAttributes(); | ||||
| @@ -263,39 +238,41 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                     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); | ||||
|                     binding.videoLayout.setVisibility(View.GONE); | ||||
|                     binding.peertubeInformationContainer.setVisibility(View.VISIBLE); | ||||
|                 } | ||||
|             }); | ||||
|             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/" + videoUuid); | ||||
|             binding.webviewVideo.getSettings().setAllowFileAccess(true); | ||||
|             binding.webviewVideo.setWebChromeClient(mastalabWebChromeClient); | ||||
|             binding.webviewVideo.getSettings().setDomStorageEnabled(true); | ||||
|             binding.webviewVideo.getSettings().setAppCacheEnabled(true); | ||||
|             binding.webviewVideo.getSettings().setMediaPlaybackRequiresUserGesture(false); | ||||
|             binding.webviewVideo.setWebViewClient(new MastalabWebViewClient(PeertubeActivity.this)); | ||||
|             binding.webviewVideo.loadUrl("https://" + peertubeInstance + "/videos/embed/" + videoUuid); | ||||
|         } else { | ||||
|             webview_video.setVisibility(View.GONE); | ||||
|             playerView.setVisibility(View.VISIBLE); | ||||
|             loader.setVisibility(View.VISIBLE); | ||||
|             binding.webviewVideo.setVisibility(View.GONE); | ||||
|             binding.mediaVideo.setVisibility(View.VISIBLE); | ||||
|             binding.loader.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|  | ||||
|         if (mode != Helper.VIDEO_MODE_WEBVIEW) { | ||||
|             playerView.setControllerShowTimeoutMs(1000); | ||||
|             playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); | ||||
|             binding.mediaVideo.setControllerShowTimeoutMs(1000); | ||||
|             binding.mediaVideo.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); | ||||
|             initFullscreenDialog(); | ||||
|             initFullscreenButton(); | ||||
|         } | ||||
|         flag_loading = true; | ||||
|         comments = new ArrayList<>(); | ||||
|         lv_comments = findViewById(R.id.peertube_comments); | ||||
|  | ||||
|         binding.closeReply.setOnClickListener(v->closeCommentThread()); | ||||
|  | ||||
|         commentListAdapter = new CommentListAdapter(comments, Helper.isVideoOwner(PeertubeActivity.this, peertube)); | ||||
|         commentListAdapter.allCommentRemoved = PeertubeActivity.this; | ||||
|         LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); | ||||
|         lv_comments.setLayoutManager(mLayoutManager); | ||||
|         lv_comments.setNestedScrollingEnabled(false); | ||||
|         lv_comments.setAdapter(commentListAdapter); | ||||
|         lv_comments.addOnScrollListener(new RecyclerView.OnScrollListener() { | ||||
|         binding.peertubeComments.setLayoutManager(mLayoutManager); | ||||
|         binding.peertubeComments.setNestedScrollingEnabled(false); | ||||
|         binding.peertubeComments.setAdapter(commentListAdapter); | ||||
|         binding.peertubeComments.addOnScrollListener(new RecyclerView.OnScrollListener() { | ||||
|             public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { | ||||
|                 if (dy > 0) { | ||||
|                     int visibleItemCount = mLayoutManager.getChildCount(); | ||||
| @@ -318,6 +295,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private void manageVIewVideos(APIResponse apiResponse) { | ||||
|         if( apiResponse == null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { | ||||
|             playVideo(); | ||||
| @@ -346,11 +325,46 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             } | ||||
|         } | ||||
|         if (comments.size() > 0) { | ||||
|             lv_comments.setVisibility(View.VISIBLE); | ||||
|             binding.peertubeComments.setVisibility(View.VISIBLE); | ||||
|             commentListAdapter.notifyItemRangeInserted(oldSize, newComments); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void manageVIewCommentReply(APIResponse apiResponse) { | ||||
|         if (apiResponse == null || apiResponse.getError() != null || apiResponse.getCommentThreadData() == null) { | ||||
|             if (apiResponse == null || apiResponse.getError() == 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<CommentData.CommentThreadData> commentThreadDataList = apiResponse.getCommentThreadData().getChildren(); | ||||
|         List<Comment> comments = generateCommentReply(commentThreadDataList, new ArrayList<>()); | ||||
|         if (comments.size() > 0) { | ||||
|             commentReplyListAdapter = new CommentListAdapter(comments, Helper.isVideoOwner(PeertubeActivity.this, peertube)); | ||||
|             LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); | ||||
|             binding.peertubeReply.setLayoutManager(mLayoutManager); | ||||
|             binding.peertubeReply.setNestedScrollingEnabled(false); | ||||
|             binding.peertubeReply.setAdapter(commentReplyListAdapter); | ||||
|             binding.peertubeReply.setVisibility(View.VISIBLE); | ||||
|             commentReplyListAdapter.notifyItemRangeInserted(0, comments.size()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private List<Comment> generateCommentReply(List<CommentData.CommentThreadData> commentThreadDataList, List<Comment> comments) { | ||||
|         for (CommentData.CommentThreadData commentThreadData : commentThreadDataList) { | ||||
|             if (commentThreadData.getComment().getText() != null && commentThreadData.getComment().getText().trim().length() > 0) { | ||||
|                 commentThreadData.getComment().setReply(true); | ||||
|                 comments.add(commentThreadData.getComment()); | ||||
|             } | ||||
|             if( commentThreadData.getChildren() != null && commentThreadData.getChildren().size() >0) { | ||||
|                 generateCommentReply(commentThreadData.getChildren(), comments); | ||||
|             } | ||||
|         } | ||||
|         return comments; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNewIntent(Intent intent) { | ||||
|         super.onNewIntent(intent); | ||||
| @@ -367,8 +381,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             player.setPlayWhenReady(false); | ||||
|             player.release(); | ||||
|             player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); | ||||
|             playerView.setPlayer(player); | ||||
|             loader.setVisibility(View.GONE); | ||||
|             binding.mediaVideo.setPlayer(player); | ||||
|             binding.loader.setVisibility(View.GONE); | ||||
|             player.setPlayWhenReady(true); | ||||
|             captions = null; | ||||
|         } | ||||
| @@ -376,8 +390,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         setFullscreen(FullScreenMediaController.fullscreen.OFF); | ||||
|         fullScreenMode = false; | ||||
|  | ||||
|         peertube_playlist.setVisibility(View.VISIBLE); | ||||
|         peertube_bookmark.setVisibility(View.GONE); | ||||
|         binding.peertubePlaylist.setVisibility(View.VISIBLE); | ||||
|         binding.peertubeBookmark.setVisibility(View.GONE); | ||||
|         TimelineVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class); | ||||
|         feedsViewModel.getVideo(sepiaSearch?peertubeInstance:null, videoUuid, isMyVideo).observe(PeertubeActivity.this, this::manageVIewVideo); | ||||
|         CaptionsVM captionsViewModel = new ViewModelProvider(PeertubeActivity.this).get(CaptionsVM.class); | ||||
| @@ -389,12 +403,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | | ||||
|                     WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||||
|             Objects.requireNonNull(getSupportActionBar()).hide(); | ||||
|             peertube_information_container.setVisibility(View.GONE); | ||||
|             binding.peertubeInformationContainer.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); | ||||
|             binding.peertubeInformationContainer.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -411,9 +425,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             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); | ||||
|                 binding.addCommentRead.setVisibility(View.VISIBLE); | ||||
|                 binding.addCommentWrite.setVisibility(View.GONE); | ||||
|                 binding.send.setVisibility(View.GONE); | ||||
|                 hideKeyboard(PeertubeActivity.this); | ||||
|             } | ||||
|         } | ||||
| @@ -439,8 +453,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         LayoutInflater inflater1 = getLayoutInflater(); | ||||
|         View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(PeertubeActivity.this), false); | ||||
|         dialogBuilder.setView(dialogView); | ||||
|         EditText report_content = dialogView.findViewById(R.id.report_content); | ||||
|         dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); | ||||
|         EditText report_content = dialogView.findViewById(R.id.report_content); | ||||
|         dialogBuilder.setPositiveButton(R.string.report, (dialog, id) -> { | ||||
|             if (report_content.getText().toString().trim().length() == 0) { | ||||
|                 Toasty.info(PeertubeActivity.this, getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show(); | ||||
| @@ -493,12 +507,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|  | ||||
|         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); | ||||
|             binding.loader.setVisibility(View.GONE); | ||||
|             return; | ||||
|         } | ||||
|         if (apiResponse.getPeertubes() == null || apiResponse.getPeertubes().get(0) == null || apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this) == null) { | ||||
|             Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); | ||||
|             loader.setVisibility(View.GONE); | ||||
|             binding.loader.setVisibility(View.GONE); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -513,13 +527,13 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         viewModel.videoExists(videoIds).observe(this, this::manageVIewPlaylist); | ||||
|  | ||||
|  | ||||
|         add_comment_read.setOnClickListener(v -> { | ||||
|         binding.writeCommentContainer.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 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()); | ||||
|                  binding.addCommentRead.setVisibility(View.GONE); | ||||
|                  binding.addCommentWrite.setVisibility(View.VISIBLE); | ||||
|                  binding.send.setVisibility(View.VISIBLE); | ||||
|                  binding.addCommentWrite.requestFocus(); | ||||
|                  binding.addCommentWrite.setSelection( binding.addCommentWrite.getText().length()); | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
|                     Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); | ||||
| @@ -529,17 +543,32 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             } | ||||
|  | ||||
|         }); | ||||
|         send.setOnClickListener(v -> { | ||||
|         binding.writeCommentContainerReply.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 String comment = add_comment_write.getText().toString(); | ||||
|                 binding.addCommentReadReply.setVisibility(View.GONE); | ||||
|                 binding.addCommentWriteReply.setVisibility(View.VISIBLE); | ||||
|                 binding.send.setVisibility(View.VISIBLE); | ||||
|                 binding.addCommentWriteReply.requestFocus(); | ||||
|                 binding.addCommentWriteReply.setSelection( binding.addCommentWriteReply.getText().length()); | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
|                     Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); | ||||
|                 }else { | ||||
|                     Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         binding.send.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 String comment =  binding.addCommentWrite.getText().toString(); | ||||
|                 if (comment.trim().length() > 0) { | ||||
|                     PostActionsVM viewModelComment = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); | ||||
|                     viewModelComment.comment(ADD_COMMENT, peertube.getId(), null, comment).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(ADD_COMMENT, apiResponse1)); | ||||
|                     add_comment_write.setText(""); | ||||
|                     add_comment_read.setVisibility(View.VISIBLE); | ||||
|                     add_comment_write.setVisibility(View.GONE); | ||||
|                     send.setVisibility(View.GONE); | ||||
|                     add_comment_read.requestFocus(); | ||||
|                      binding.addCommentWrite.setText(""); | ||||
|                      binding.addCommentRead.setVisibility(View.VISIBLE); | ||||
|                      binding.addCommentWrite.setVisibility(View.GONE); | ||||
|                      binding.sendReply.setVisibility(View.GONE); | ||||
|                      binding.addCommentRead.requestFocus(); | ||||
|                 } | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
| @@ -550,47 +579,45 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         peertube_playlist.setOnClickListener(v -> { | ||||
|         binding.peertubePlaylist.setOnClickListener(v -> { | ||||
|             PlaylistsVM viewModelOwnerPlaylist = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class); | ||||
|             viewModelOwnerPlaylist.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(PeertubeActivity.this, this::manageVIewPlaylists); | ||||
|         }); | ||||
|         no_action_text = findViewById(R.id.no_action_text); | ||||
|  | ||||
|         if (peertube.isCommentsEnabled()) { | ||||
|             CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); | ||||
|             commentViewModel.getThread(sepiaSearch?peertubeInstance:null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment); | ||||
|             write_comment_container.setVisibility(View.VISIBLE); | ||||
|             binding.writeCommentContainer.setVisibility(View.VISIBLE); | ||||
|  | ||||
|         } else { | ||||
|             RelativeLayout no_action = findViewById(R.id.no_action); | ||||
|  | ||||
|             no_action_text.setText(getString(R.string.comment_no_allowed_peertube)); | ||||
|             no_action.setVisibility(View.VISIBLE); | ||||
|             write_comment_container.setVisibility(View.GONE); | ||||
|  | ||||
|             binding.noActionText.setText(getString(R.string.comment_no_allowed_peertube)); | ||||
|             binding.noAction.setVisibility(View.VISIBLE); | ||||
|             binding.writeCommentContainer.setVisibility(View.GONE); | ||||
|         } | ||||
|         SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); | ||||
|         setTitle(peertube.getName()); | ||||
|  | ||||
|         peertube_description.setText(peertube.getDescription()); | ||||
|         binding.peertubeDescription.setText(peertube.getDescription()); | ||||
|  | ||||
|  | ||||
|         peertube_title.setText(peertube.getName()); | ||||
|         peertube_dislike_count.setText(String.valueOf(peertube.getDislikes())); | ||||
|         peertube_like_count.setText(String.valueOf(peertube.getLikes())); | ||||
|         peertube_view_count.setText(String.valueOf(peertube.getViews())); | ||||
|         binding.peertubeTitle.setText(peertube.getName()); | ||||
|         binding.peertubeDislikeCount.setText(String.valueOf(peertube.getDislikes())); | ||||
|         binding.peertubeLikeCount.setText(String.valueOf(peertube.getLikes())); | ||||
|         binding.peertubeViewCount.setText(String.valueOf(peertube.getViews())); | ||||
|         video_id = peertube.getId(); | ||||
|  | ||||
|         changeColor(); | ||||
|         initResolution(); | ||||
|  | ||||
|         peertube_like_count.setOnClickListener(v -> { | ||||
|         binding.peertubeLikeCount.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 String newState = peertube.getMyRating().equals("like") ? "none" : "like"; | ||||
|                 PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); | ||||
|                 viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, apiResponse1)); | ||||
|                 peertube.setMyRating(newState); | ||||
|                 int count = Integer.parseInt(peertube_like_count.getText().toString()); | ||||
|                 int count = Integer.parseInt(binding.peertubeLikeCount.getText().toString()); | ||||
|                 if (newState.compareTo("none") == 0) { | ||||
|                     count--; | ||||
|                     if (count - 1 < 0) { | ||||
| @@ -599,7 +626,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                 } else { | ||||
|                     count++; | ||||
|                 } | ||||
|                 peertube_like_count.setText(String.valueOf(count)); | ||||
|                 binding.peertubeLikeCount.setText(String.valueOf(count)); | ||||
|                 changeColor(); | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
| @@ -609,13 +636,13 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         peertube_dislike_count.setOnClickListener(v -> { | ||||
|         binding.peertubeDislikeCount.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 String newState = peertube.getMyRating().equals("dislike") ? "none" : "dislike"; | ||||
|                 PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); | ||||
|                 viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, apiResponse1)); | ||||
|                 peertube.setMyRating(newState); | ||||
|                 int count = Integer.parseInt(peertube_dislike_count.getText().toString()); | ||||
|                 int count = Integer.parseInt(binding.peertubeDislikeCount.getText().toString()); | ||||
|                 if (newState.compareTo("none") == 0) { | ||||
|                     count--; | ||||
|                     if (count - 1 < 0) { | ||||
| @@ -624,7 +651,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                 } else { | ||||
|                     count++; | ||||
|                 } | ||||
|                 peertube_dislike_count.setText(String.valueOf(count)); | ||||
|                 binding.peertubeDislikeCount.setText(String.valueOf(count)); | ||||
|                 changeColor(); | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
| @@ -642,8 +669,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             ProgressiveMediaSource videoSource; | ||||
|  | ||||
|             player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); | ||||
|             playerView.setPlayer(player); | ||||
|             loader.setVisibility(View.GONE); | ||||
|             binding.mediaVideo.setPlayer(player); | ||||
|             binding.loader.setVisibility(View.GONE); | ||||
|  | ||||
|             if (apiResponse.getPeertubes().get(0).getFiles() != null && apiResponse.getPeertubes().get(0).getFiles().size() > 0) { | ||||
|                 if (video_cache == 0) { | ||||
| @@ -666,8 +693,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         } | ||||
|  | ||||
|  | ||||
|         more_actions.setOnClickListener(view -> { | ||||
|             PopupMenu popup = new PopupMenu(PeertubeActivity.this, more_actions); | ||||
|         binding.moreActions.setOnClickListener(view -> { | ||||
|             PopupMenu popup = new PopupMenu(PeertubeActivity.this, binding.moreActions); | ||||
|             popup.getMenuInflater() | ||||
|                     .inflate(R.menu.main_video, popup.getMenu()); | ||||
|  | ||||
| @@ -778,7 +805,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                                 if (uri != null) | ||||
|                                     subtitleSource = new SingleSampleMediaSource.Factory(cacheDataSourceFactory).createMediaSource(uri, subtitleFormat, C.TIME_UNSET); | ||||
|                             } | ||||
|                             playerView.setPlayer(player); | ||||
|                             binding.mediaVideo.setPlayer(player); | ||||
|                             if (which > 0 && subtitleSource != null) { | ||||
|                                 MergingMediaSource mergedSource = | ||||
|                                         new MergingMediaSource(videoSource, subtitleSource); | ||||
| @@ -809,13 +836,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                     LayoutInflater inflater1 = getLayoutInflater(); | ||||
|                     View dialogView = inflater1.inflate(R.layout.popup_report_choice, new LinearLayout(PeertubeActivity.this), false); | ||||
|                     dialogBuilder.setView(dialogView); | ||||
|                     Button report_video = dialogView.findViewById(R.id.report_video); | ||||
|                     Button report_account = dialogView.findViewById(R.id.report_account); | ||||
|  | ||||
|                     dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); | ||||
|                     alertDialog = dialogBuilder.create(); | ||||
|                     alertDialog.show(); | ||||
|                     report_video.setOnClickListener(v -> reportAlert(REPORT_VIDEO, alertDialog)); | ||||
|                     report_account.setOnClickListener(v -> reportAlert(REPORT_ACCOUNT, alertDialog)); | ||||
|                     dialogView.findViewById(R.id.report_video).setOnClickListener(v -> reportAlert(REPORT_VIDEO, alertDialog)); | ||||
|                     dialogView.findViewById(R.id.report_account).setOnClickListener(v -> reportAlert(REPORT_ACCOUNT, alertDialog)); | ||||
|                 } | ||||
|                 return true; | ||||
|             }); | ||||
| @@ -846,6 +872,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         binding = null; | ||||
|         if (player != null) { | ||||
|             player.setPlayWhenReady(false); | ||||
|             player.release(); | ||||
| @@ -875,9 +902,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|             MediaSessionCompat mediaSession = new MediaSessionCompat(this, getPackageName()); | ||||
|             MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession); | ||||
|             mediaSessionConnector.setPlayer(player); | ||||
|             PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); | ||||
|             PlayerControlView controlView = binding.mediaVideo.findViewById(R.id.exo_controller); | ||||
|             controlView.hide(); | ||||
|             playerView.setControllerAutoShow(false); | ||||
|             binding.mediaVideo.setControllerAutoShow(false); | ||||
|             mediaSession.setActive(true); | ||||
|             enterPictureInPictureMode(); | ||||
|         } | ||||
| @@ -938,18 +965,18 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|         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); | ||||
|             if (binding.mediaVideo != null) { | ||||
|                 binding.loader.setVisibility(View.VISIBLE); | ||||
|                 long position = player.getCurrentPosition(); | ||||
|                 PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); | ||||
|                 resolution = controlView.findViewById(R.id.resolution); | ||||
|                 PlayerControlView controlView = binding.mediaVideo.findViewById(R.id.exo_controller); | ||||
|                 TextView resolution = controlView.findViewById(R.id.resolution); | ||||
|                 resolution.setText(String.format("%sp", res)); | ||||
|                 if (mode == Helper.VIDEO_MODE_NORMAL) { | ||||
|                     if (player != null) | ||||
|                         player.release(); | ||||
|                     player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); | ||||
|                     playerView.setPlayer(player); | ||||
|                     loader.setVisibility(View.GONE); | ||||
|                     binding.mediaVideo.setPlayer(player); | ||||
|                     binding.loader.setVisibility(View.GONE); | ||||
|                     int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); | ||||
|                     ProgressiveMediaSource videoSource; | ||||
|                     if (video_cache == 0) { | ||||
| @@ -1001,18 +1028,97 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|  | ||||
|     private void openFullscreenDialog() { | ||||
|  | ||||
|         ((ViewGroup) playerView.getParent()).removeView(playerView); | ||||
|         fullScreenDialog.addContentView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); | ||||
|         ((ViewGroup) binding.mediaVideo.getParent()).removeView(binding.mediaVideo); | ||||
|         fullScreenDialog.addContentView(binding.mediaVideo, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); | ||||
|         fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_exit_24)); | ||||
|         fullScreenMode = true; | ||||
|  | ||||
|         fullScreenDialog.show(); | ||||
|     } | ||||
|  | ||||
|     public void openCommentThread(Comment comment){ | ||||
|  | ||||
|         CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); | ||||
|         binding.peertubeReply.setVisibility(View.GONE); | ||||
|         commentViewModel.getRepliesComment(videoUuid, comment.getId()).observe(PeertubeActivity.this, this::manageVIewCommentReply); | ||||
|  | ||||
|         Account account = comment.getAccount(); | ||||
|         Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, binding.commentAccountProfile); | ||||
|         binding.commentAccountDisplayname.setText(account.getDisplayName()); | ||||
|         binding.commentAccountUsername.setText(account.getAcct()); | ||||
|         Spanned commentSpan; | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) | ||||
|             commentSpan = Html.fromHtml(comment.getText(), Html.FROM_HTML_MODE_COMPACT); | ||||
|         else | ||||
|             commentSpan = Html.fromHtml(comment.getText()); | ||||
|         binding.commentContent.setText(commentSpan); | ||||
|         binding.commentDate.setText(Helper.dateDiff(PeertubeActivity.this, comment.getCreatedAt())); | ||||
|         binding.replyThread.setVisibility(View.VISIBLE); | ||||
|         TranslateAnimation animate = new TranslateAnimation( | ||||
|                 binding.peertubeInformationContainer.getWidth(), | ||||
|                 0, | ||||
|                 0, | ||||
|                 0); | ||||
|         animate.setAnimationListener(new Animation.AnimationListener() { | ||||
|             @Override | ||||
|             public void onAnimationStart(Animation animation) {} | ||||
|             @Override | ||||
|             public void onAnimationEnd(Animation animation) { | ||||
|                 binding.peertubeInformationContainer.setVisibility(View.GONE); | ||||
|             } | ||||
|             @Override | ||||
|             public void onAnimationRepeat(Animation animation) {} | ||||
|         }); | ||||
|         animate.setDuration(500); | ||||
|         binding.replyThread.startAnimation(animate); | ||||
|         binding.sendReply.setOnClickListener(null); | ||||
|         binding.sendReply.setOnClickListener(v -> { | ||||
|             if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { | ||||
|                 String commentView =  binding.addCommentWriteReply.getText().toString(); | ||||
|                 if (commentView.trim().length() > 0) { | ||||
|                     PostActionsVM viewModelComment = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); | ||||
|                     viewModelComment.comment(REPLY, peertube.getId(), comment.getId(), commentView).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(ADD_COMMENT, apiResponse1)); | ||||
|                     binding.addCommentWriteReply.setText(""); | ||||
|                     binding.addCommentReadReply.setVisibility(View.VISIBLE); | ||||
|                     binding.addCommentWriteReply.setVisibility(View.GONE); | ||||
|                     binding.sendReply.setVisibility(View.GONE); | ||||
|                     binding.addCommentReadReply.requestFocus(); | ||||
|                 } | ||||
|             } else { | ||||
|                 if( sepiaSearch) { | ||||
|                     Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); | ||||
|                 }else { | ||||
|                     Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void closeCommentThread(){ | ||||
|         binding.peertubeInformationContainer.setVisibility(View.VISIBLE); | ||||
|         TranslateAnimation animate = new TranslateAnimation( | ||||
|                 0, | ||||
|                 binding.replyThread.getWidth(), | ||||
|                 0, | ||||
|                 0); | ||||
|         animate.setAnimationListener(new Animation.AnimationListener() { | ||||
|             @Override | ||||
|             public void onAnimationStart(Animation animation) {} | ||||
|             @Override | ||||
|             public void onAnimationEnd(Animation animation) { | ||||
|                 binding.replyThread.setVisibility(View.GONE); | ||||
|             } | ||||
|             @Override | ||||
|             public void onAnimationRepeat(Animation animation) {} | ||||
|         }); | ||||
|         animate.setDuration(500); | ||||
|         binding.replyThread.startAnimation(animate); | ||||
|     } | ||||
|  | ||||
|     private void closeFullscreenDialog() { | ||||
|  | ||||
|         ((ViewGroup) playerView.getParent()).removeView(playerView); | ||||
|         ((FrameLayout) findViewById(R.id.main_media_frame)).addView(playerView); | ||||
|         ((ViewGroup) binding.mediaVideo.getParent()).removeView(binding.mediaVideo); | ||||
|         ((FrameLayout) findViewById(R.id.main_media_frame)).addView(binding.mediaVideo); | ||||
|         fullScreenMode = false; | ||||
|         fullScreenDialog.dismiss(); | ||||
|         fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_24)); | ||||
| @@ -1020,7 +1126,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|  | ||||
|     private void initFullscreenButton() { | ||||
|  | ||||
|         PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); | ||||
|         PlayerControlView controlView = binding.mediaVideo.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 -> { | ||||
| @@ -1038,8 +1144,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|     } | ||||
|  | ||||
|     private void initResolution() { | ||||
|         PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); | ||||
|         resolution = controlView.findViewById(R.id.resolution); | ||||
|         PlayerControlView controlView = binding.mediaVideo.findViewById(R.id.exo_controller); | ||||
|         TextView resolution = controlView.findViewById(R.id.resolution); | ||||
|         if (peertube.getFiles() != null && peertube.getFiles().size() > 0) { | ||||
|             resolution.setText(String.format("%s", Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()).getResolutions().getLabel())); | ||||
|             resolution.setOnClickListener(v -> displayResolution()); | ||||
| @@ -1073,8 +1179,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|                 DrawableCompat.setTint(thumbDown, getResources().getColor(R.color.negative_thumbs)); | ||||
|             } | ||||
|         } | ||||
|         peertube_like_count.setCompoundDrawablesWithIntrinsicBounds(null, thumbUp, null, null); | ||||
|         peertube_dislike_count.setCompoundDrawablesWithIntrinsicBounds(null, thumbDown, null, null); | ||||
|         binding.peertubeLikeCount.setCompoundDrawablesWithIntrinsicBounds(null, thumbUp, null, null); | ||||
|         binding.peertubeDislikeCount.setCompoundDrawablesWithIntrinsicBounds(null, thumbDown, null, null); | ||||
|     } | ||||
|  | ||||
|     public void manageVIewPlaylists(APIResponse apiResponse) { | ||||
| @@ -1160,6 +1266,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd | ||||
|  | ||||
|     @Override | ||||
|     public void onAllCommentRemoved() { | ||||
|         no_action_text.setVisibility(View.VISIBLE); | ||||
|         binding.noActionText.setVisibility(View.VISIBLE); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -46,6 +46,7 @@ public class APIResponse { | ||||
|     private List<CommentData.Comment> comments = null; | ||||
|     private List<Block> muted; | ||||
|     private List<VideoPlaylist> videoPlaylist; | ||||
|     private CommentData.CommentThreadData commentThreadData; | ||||
|     private List<NotificationData.Notification> peertubeNotifications = null; | ||||
|     private List<PlaylistData.Playlist> playlists = null; | ||||
|     private List<String> domains = null; | ||||
| @@ -247,4 +248,12 @@ public class APIResponse { | ||||
|     public void setVideoExistPlaylist(Map<String, List<PlaylistExist>> videoExistPlaylist) { | ||||
|         this.videoExistPlaylist = videoExistPlaylist; | ||||
|     } | ||||
|  | ||||
|     public CommentData.CommentThreadData getCommentThreadData() { | ||||
|         return commentThreadData; | ||||
|     } | ||||
|  | ||||
|     public void setCommentThreadData(CommentData.CommentThreadData commentThreadData) { | ||||
|         this.commentThreadData = commentThreadData; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -328,7 +328,7 @@ public interface PeertubeService { | ||||
|     Call<CommentData> getComments(@Path("id") String id, @Query("start") String maxId, @Query("count") String count); | ||||
|  | ||||
|     @GET("videos/{id}/comment-threads/{threadId}") | ||||
|     Call<CommentData> getReplies(@Path("id") String id, @Path("threadId") String threadId); | ||||
|     Call<CommentData.CommentThreadData> getReplies(@Path("id") String id, @Path("threadId") String threadId); | ||||
|  | ||||
|     @FormUrlEncoded | ||||
|     @POST("videos/{id}/comment-threads") | ||||
|   | ||||
| @@ -1203,10 +1203,10 @@ public class RetrofitPeertubeAPI { | ||||
|                     setError(apiResponse, response.code(), response.errorBody()); | ||||
|                 } | ||||
|             } else if (type == CommentVM.action.GET_REPLIES) { | ||||
|                 Call<CommentData> commentsCall = peertubeService.getReplies(videoId, forCommentId); | ||||
|                 Response<CommentData> response = commentsCall.execute(); | ||||
|                 Call<CommentData.CommentThreadData> commentsCall = peertubeService.getReplies(videoId, forCommentId); | ||||
|                 Response<CommentData.CommentThreadData> response = commentsCall.execute(); | ||||
|                 if (response.isSuccessful() && response.body() != null) { | ||||
|                     apiResponse.setComments(response.body().data); | ||||
|                     apiResponse.setCommentThreadData(response.body()); | ||||
|                 } else { | ||||
|                     setError(apiResponse, response.code(), response.errorBody()); | ||||
|                 } | ||||
|   | ||||
| @@ -172,6 +172,31 @@ public class CommentData { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static class CommentThreadData { | ||||
|  | ||||
|         @SerializedName("comment") | ||||
|         public Comment comment; | ||||
|         @SerializedName("children") | ||||
|         public List<CommentThreadData> children; | ||||
|  | ||||
|         public Comment getComment() { | ||||
|             return comment; | ||||
|         } | ||||
|  | ||||
|         public void setComment(Comment comment) { | ||||
|             this.comment = comment; | ||||
|         } | ||||
|  | ||||
|         public List<CommentThreadData> getChildren() { | ||||
|             return children; | ||||
|         } | ||||
|  | ||||
|         public void setChildren(List<CommentThreadData> children) { | ||||
|             this.children = children; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class CommentPosted{ | ||||
|         @SerializedName("comment") | ||||
|         private Comment comment; | ||||
|   | ||||
| @@ -49,11 +49,13 @@ import java.util.List; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| import app.fedilab.fedilabtube.PeertubeActivity; | ||||
| import app.fedilab.fedilabtube.R; | ||||
| import app.fedilab.fedilabtube.client.APIResponse; | ||||
| import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; | ||||
| import app.fedilab.fedilabtube.client.data.CommentData.Comment; | ||||
| import app.fedilab.fedilabtube.client.entities.Report; | ||||
| import app.fedilab.fedilabtube.helper.EmojiHelper; | ||||
| import app.fedilab.fedilabtube.helper.Helper; | ||||
| import app.fedilab.fedilabtube.viewmodel.PostActionsVM; | ||||
| import es.dmoral.toasty.Toasty; | ||||
| @@ -206,15 +208,19 @@ public class CommentListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo | ||||
|  | ||||
|         Spanned commentSpan; | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) | ||||
|             commentSpan = Html.fromHtml(comment.getText(), Html.FROM_HTML_MODE_COMPACT); | ||||
|             commentSpan = Html.fromHtml(EmojiHelper.shortnameToUnicode(comment.getText()), Html.FROM_HTML_MODE_COMPACT); | ||||
|         else | ||||
|             commentSpan = Html.fromHtml(comment.getText()); | ||||
|             commentSpan = Html.fromHtml(EmojiHelper.shortnameToUnicode(comment.getText())); | ||||
|         holder.comment_content.setText(commentSpan, TextView.BufferType.SPANNABLE); | ||||
|  | ||||
|         holder.comment_content.setMovementMethod(LinkMovementMethod.getInstance()); | ||||
|  | ||||
|         holder.comment_account_displayname.setText(comment.getAccount().getDisplayName()); | ||||
|  | ||||
|         if( context instanceof PeertubeActivity && !comment.isReply()) { | ||||
|             holder.main_container.setOnClickListener(v -> ((PeertubeActivity) context).openCommentThread(comment)); | ||||
|             holder.comment_content.setOnClickListener(v -> ((PeertubeActivity) context).openCommentThread(comment)); | ||||
|         } | ||||
|         if( comment.getTotalReplies() > 0) { | ||||
|             holder.number_of_replies.setVisibility(View.VISIBLE); | ||||
|             holder.number_of_replies.setText(context.getResources().getQuantityString(R.plurals.number_of_replies, comment.getTotalReplies(), comment.getTotalReplies())); | ||||
|   | ||||
| @@ -0,0 +1,78 @@ | ||||
| package app.fedilab.fedilabtube.helper; | ||||
| /* 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.content.Context; | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStreamReader; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| public class EmojiHelper { | ||||
|  | ||||
|  | ||||
|     //Emoji manager | ||||
|     private static final Map<String, String> emoji = new HashMap<>(); | ||||
|     private static final Pattern SHORTNAME_PATTERN = Pattern.compile(":( |)([-+\\w]+):"); | ||||
|  | ||||
|     /** | ||||
|      * Converts emojis in input to unicode | ||||
|      * @param input String | ||||
|      * @return String | ||||
|      */ | ||||
|     public static String shortnameToUnicode(String input) { | ||||
|         Matcher matcher = SHORTNAME_PATTERN.matcher(input); | ||||
|  | ||||
|         while (matcher.find()) { | ||||
|             String unicode = emoji.get(matcher.group(2)); | ||||
|             if (unicode == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (matcher.group(1).equals(" ")) | ||||
|                 input = input.replace(": " + matcher.group(2) + ":", unicode); | ||||
|             else | ||||
|                 input = input.replace(":" + matcher.group(2) + ":", unicode); | ||||
|         } | ||||
|         return input; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public static void fillMapEmoji(Context context) { | ||||
|         try { | ||||
|             BufferedReader br = new BufferedReader(new InputStreamReader(context.getAssets().open("emoji.csv"))); | ||||
|             String line; | ||||
|             while( (line = br.readLine()) != null) { | ||||
|                 String[] str = line.split(","); | ||||
|                 String unicode = null; | ||||
|                 if(str.length == 2) | ||||
|                     unicode =  new String(new int[] {Integer.parseInt(str[1].replace("0x","").trim(), 16)}, 0, 1); | ||||
|                 else if(str.length == 3) | ||||
|                     unicode =  new String(new int[] {Integer.parseInt(str[1].replace("0x","").trim(), 16), Integer.parseInt(str[2].replace("0x","").trim(), 16)}, 0, 2); | ||||
|                 else if(str.length == 4) | ||||
|                     unicode =  new String(new int[] {Integer.parseInt(str[1].replace("0x","").trim(), 16), Integer.parseInt(str[2].replace("0x","").trim(), 16), Integer.parseInt(str[3].replace("0x","").trim(), 16)}, 0, 3); | ||||
|                 else if(str.length == 5) | ||||
|                     unicode =  new String(new int[] {Integer.parseInt(str[1].replace("0x","").trim(), 16), Integer.parseInt(str[2].replace("0x","").trim(), 16), Integer.parseInt(str[3].replace("0x","").trim(), 16), Integer.parseInt(str[4].replace("0x","").trim(), 16)}, 0, 4); | ||||
|                 if( unicode != null) | ||||
|                     emoji.put(str[0],unicode); | ||||
|             } | ||||
|             br.close(); | ||||
|         } catch (IOException ignored) {ignored.printStackTrace();} | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -33,6 +33,7 @@ import java.util.Objects; | ||||
| import app.fedilab.fedilabtube.R; | ||||
| import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; | ||||
| import app.fedilab.fedilabtube.client.entities.PeertubeInformation; | ||||
| import app.fedilab.fedilabtube.helper.EmojiHelper; | ||||
| import app.fedilab.fedilabtube.helper.NetworkStateReceiver; | ||||
|  | ||||
| import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; | ||||
| @@ -56,7 +57,6 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver | ||||
|     @Override | ||||
|     public int onStartCommand(Intent intent, int flags, int startId) { | ||||
|  | ||||
|  | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, | ||||
|                     getString(R.string.notification_channel_name), | ||||
| @@ -110,6 +110,7 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver | ||||
|  | ||||
|             @Override | ||||
|             public void run() { | ||||
|                 EmojiHelper.fillMapEmoji(getApplicationContext()); | ||||
|                 peertubeInformation = new PeertubeInformation(); | ||||
|                 peertubeInformation.setCategories(new LinkedHashMap<>()); | ||||
|                 peertubeInformation.setLanguages(new LinkedHashMap<>()); | ||||
|   | ||||
| @@ -74,7 +74,7 @@ public class CommentVM extends AndroidViewModel { | ||||
|         new Thread(() -> { | ||||
|             try { | ||||
|                 RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(_mContext); | ||||
|                 APIResponse apiResponse = api.getComments(action.GET_REPLIES, videoId, null, null); | ||||
|                 APIResponse apiResponse = api.getComments(action.GET_REPLIES, videoId, commentId, null); | ||||
|                 Handler mainHandler = new Handler(Looper.getMainLooper()); | ||||
|                 Runnable myRunnable = () -> apiResponseMutableLiveData.setValue(apiResponse); | ||||
|                 mainHandler.post(myRunnable); | ||||
|   | ||||
| @@ -223,10 +223,21 @@ | ||||
|                         android:layout_margin="10dp" | ||||
|                         android:layout_width="match_parent" | ||||
|                         android:layout_height="wrap_content"> | ||||
|                         <androidx.constraintlayout.widget.ConstraintLayout | ||||
|                         <View | ||||
|                             android:id="@+id/separator_top" | ||||
|                             android:layout_margin="5dp" | ||||
|                             app:layout_constraintTop_toTopOf="parent" | ||||
|                             app:layout_constraintStart_toStartOf="parent" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/separator" | ||||
|                             app:layout_constraintEnd_toEndOf="parent" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/write_container" | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="1px" | ||||
|                             android:background="@android:color/darker_gray"/> | ||||
|                         <androidx.constraintlayout.widget.ConstraintLayout | ||||
|                             app:layout_constraintTop_toBottomOf="@+id/separator_top" | ||||
|                             app:layout_constraintStart_toStartOf="parent" | ||||
|                             app:layout_constraintEnd_toEndOf="parent" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/separator_bottom" | ||||
|                             android:id="@+id/write_container" | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="wrap_content"> | ||||
| @@ -276,10 +287,9 @@ | ||||
|                         </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|                         <View | ||||
|                             android:id="@+id/separator" | ||||
|                             android:id="@+id/separator_bottom" | ||||
|                             android:layout_margin="5dp" | ||||
|                             app:layout_constraintTop_toBottomOf="@+id/write_container" | ||||
|                             app:layout_constraintBottom_toBottomOf="parent" | ||||
|                             app:layout_constraintStart_toStartOf="parent" | ||||
|                             app:layout_constraintEnd_toEndOf="parent" | ||||
|                             android:layout_width="match_parent" | ||||
| @@ -312,6 +322,7 @@ | ||||
|             </androidx.core.widget.NestedScrollView> | ||||
|  | ||||
|             <androidx.core.widget.NestedScrollView | ||||
|                 android:background="?android:colorBackground" | ||||
|                 android:visibility="gone" | ||||
|                 app:layout_constraintTop_toTopOf="parent" | ||||
|                 app:layout_constraintStart_toStartOf="parent" | ||||
| @@ -321,6 +332,10 @@ | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent"> | ||||
|                 <LinearLayout | ||||
|                     android:layout_marginStart="20dp" | ||||
|                     android:layout_marginLeft="20dp" | ||||
|                     android:layout_marginEnd="20dp" | ||||
|                     android:layout_marginRight="20dp" | ||||
|                     android:id="@+id/main_container" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content" | ||||
| @@ -397,10 +412,20 @@ | ||||
|                         android:layout_margin="10dp" | ||||
|                         android:layout_width="match_parent" | ||||
|                         android:layout_height="wrap_content"> | ||||
|                         <androidx.constraintlayout.widget.ConstraintLayout | ||||
|                         <View | ||||
|                             android:id="@+id/separator_top_reply" | ||||
|                             android:layout_margin="5dp" | ||||
|                             app:layout_constraintTop_toTopOf="parent" | ||||
|                             app:layout_constraintStart_toStartOf="parent" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/separator_reply" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/write_container_reply" | ||||
|                             app:layout_constraintEnd_toEndOf="parent" | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="1px" | ||||
|                             android:background="@android:color/darker_gray"/> | ||||
|                         <androidx.constraintlayout.widget.ConstraintLayout | ||||
|                             app:layout_constraintTop_toBottomOf="@+id/separator_top_reply" | ||||
|                             app:layout_constraintStart_toStartOf="parent" | ||||
|                             app:layout_constraintBottom_toTopOf="@+id/separator_bottom_reply" | ||||
|                             android:id="@+id/write_container_reply" | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="wrap_content"> | ||||
| @@ -414,7 +439,7 @@ | ||||
|                             <TextView | ||||
|                                 android:id="@+id/add_comment_read_reply" | ||||
|                                 android:gravity="center" | ||||
|                                 android:text="@string/add_public_comment" | ||||
|                                 android:text="@string/add_public_reply" | ||||
|                                 app:layout_constraintTop_toTopOf="parent" | ||||
|                                 app:layout_constraintStart_toEndOf="@+id/my_pp_reply" | ||||
|                                 app:layout_constraintEnd_toEndOf="parent" | ||||
| @@ -429,7 +454,7 @@ | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:focusable="true" | ||||
|                                 android:focusableInTouchMode="true" | ||||
|                                 android:hint="@string/add_public_comment" | ||||
|                                 android:hint="@string/add_public_reply" | ||||
|                                 android:importantForAutofill="no" | ||||
|                                 android:inputType="textMultiLine" | ||||
|                                 android:maxLines="4" | ||||
| @@ -450,7 +475,7 @@ | ||||
|                         </androidx.constraintlayout.widget.ConstraintLayout> | ||||
|  | ||||
|                         <View | ||||
|                             android:id="@+id/separator_reply" | ||||
|                             android:id="@+id/separator_bottom_reply" | ||||
|                             android:layout_margin="5dp" | ||||
|                             app:layout_constraintTop_toBottomOf="@+id/write_container_reply" | ||||
|                             app:layout_constraintBottom_toBottomOf="parent" | ||||
|   | ||||
| @@ -21,7 +21,9 @@ | ||||
|     android:layout_height="wrap_content" | ||||
|     android:divider="?android:dividerHorizontal" | ||||
|     android:orientation="vertical" | ||||
|     android:showDividers="end"> | ||||
|     android:clickable="true" | ||||
|     android:showDividers="end" | ||||
|     android:focusable="true"> | ||||
|  | ||||
|     <androidx.constraintlayout.widget.ConstraintLayout | ||||
|         android:layout_width="match_parent" | ||||
|   | ||||
| @@ -3,6 +3,7 @@ buildscript { | ||||
|     repositories { | ||||
|         google() | ||||
|         jcenter() | ||||
|         mavenCentral() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:4.1.0' | ||||
| @@ -17,6 +18,8 @@ allprojects { | ||||
|     repositories { | ||||
|         google() | ||||
|         jcenter() | ||||
|         mavenCentral() | ||||
|         mavenLocal() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user