Update source code with grid layout and channel management

Major changes:
- Add ChannelAdapter for grid display functionality
- Add ChannelRepository for data management
- Add PlayerActivity for dedicated video playback
- Add StreamChannel model for channel representation
- Update MainActivity to support grid layout
- Update activity_main.xml with grid UI components
- Add channel item layouts and drawables
- Update AndroidManifest.xml with new activity
- Update build.gradle dependencies

This implements the requested "grilla arreglada" functionality with proper channel grid display and management.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 18:50:54 +00:00
parent d861eca52f
commit e6b4d0825b
13 changed files with 537 additions and 168 deletions

View File

@@ -0,0 +1,203 @@
package com.streamplayer;
import android.content.Intent;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ui.PlayerView;
public class PlayerActivity extends AppCompatActivity {
public static final String EXTRA_CHANNEL_NAME = "extra_channel_name";
public static final String EXTRA_CHANNEL_URL = "extra_channel_url";
private PlayerView playerView;
private ProgressBar loadingIndicator;
private TextView errorMessage;
private TextView channelLabel;
private Button closeButton;
private View playerToolbar;
private ExoPlayer player;
private String channelName;
private String channelUrl;
private boolean overlayVisible = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder().permitAll().build()
);
setContentView(R.layout.activity_player);
Intent intent = getIntent();
if (intent == null) {
finish();
return;
}
channelName = intent.getStringExtra(EXTRA_CHANNEL_NAME);
channelUrl = intent.getStringExtra(EXTRA_CHANNEL_URL);
if (channelName == null || channelUrl == null) {
finish();
return;
}
initViews();
channelLabel.setText(channelName);
DNSSetter.configureDNSToGoogle(this);
loadChannel();
}
private void initViews() {
playerView = findViewById(R.id.player_view);
loadingIndicator = findViewById(R.id.loading_indicator);
errorMessage = findViewById(R.id.error_message);
channelLabel = findViewById(R.id.player_channel_label);
closeButton = findViewById(R.id.close_button);
playerToolbar = findViewById(R.id.player_toolbar);
closeButton.setOnClickListener(v -> finish());
playerView.setOnClickListener(v -> toggleOverlay());
}
private void loadChannel() {
showLoading(true);
new Thread(() -> {
try {
String resolvedUrl = StreamUrlResolver.resolve(channelUrl);
runOnUiThread(() -> startPlayback(resolvedUrl));
} catch (Exception e) {
runOnUiThread(() -> showError("Error al obtener stream: " + e.getMessage()));
}
}).start();
}
private void startPlayback(String streamUrl) {
try {
releasePlayer();
player = new ExoPlayer.Builder(this).build();
playerView.setPlayer(player);
player.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_READY) {
showLoading(false);
} else if (playbackState == Player.STATE_BUFFERING) {
showLoading(true);
}
}
@Override
public void onPlayerError(PlaybackException error) {
showError("Error al reproducir: " + error.getMessage());
}
});
MediaItem mediaItem = MediaItem.fromUri(streamUrl);
player.setMediaItem(mediaItem);
player.prepare();
player.setPlayWhenReady(true);
setOverlayVisible(false);
} catch (Exception e) {
showError("Error al inicializar reproductor: " + e.getMessage());
}
}
private void showLoading(boolean show) {
loadingIndicator.setVisibility(show ? View.VISIBLE : View.GONE);
errorMessage.setVisibility(View.GONE);
playerView.setVisibility(show ? View.GONE : View.VISIBLE);
if (show) {
setOverlayVisible(true);
}
}
private void showError(String message) {
loadingIndicator.setVisibility(View.GONE);
playerView.setVisibility(View.GONE);
errorMessage.setVisibility(View.VISIBLE);
errorMessage.setText(message);
setOverlayVisible(true);
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
@Override
protected void onStart() {
super.onStart();
if (player != null) {
playerView.onResume();
}
}
@Override
protected void onResume() {
super.onResume();
if (player != null) {
playerView.onResume();
}
}
@Override
protected void onPause() {
super.onPause();
if (player != null) {
playerView.onPause();
}
}
@Override
protected void onStop() {
super.onStop();
if (player != null) {
playerView.onPause();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
releasePlayer();
}
private void toggleOverlay() {
setOverlayVisible(!overlayVisible);
}
private void setOverlayVisible(boolean visible) {
overlayVisible = visible;
playerToolbar.setVisibility(visible ? View.VISIBLE : View.GONE);
}
@Override
public void onBackPressed() {
if (!overlayVisible) {
setOverlayVisible(true);
} else {
super.onBackPressed();
}
}
}