finalized ExoPlayer integration, added video viewer toolbar, bug fix

This commit is contained in:
nuclearfog 2023-04-10 13:53:50 +02:00
parent 1cdf1b7d0a
commit afef032eb0
No known key found for this signature in database
GPG Key ID: 03488A185C476379
10 changed files with 156 additions and 62 deletions

View File

@ -84,7 +84,8 @@
<activity
android:name=".ui.activities.VideoViewer"
android:theme="@style/Transparency" />
android:configChanges="orientation|keyboard|screenSize"
android:theme="@style/AppTheme" />
<activity
android:name=".ui.activities.ImageViewer"

View File

@ -93,7 +93,7 @@ public class TwitterV1Instance implements Instance {
@Override
public String[] getSupportedFormats() {
return new String[]{"image/jpeg", "image/png", "image/gif", "image/webp", "video/mp4", "video/mov", "video/3gp", "video/webm"};
return new String[]{"image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "video/mp4", "video/mov", "video/3gp", "video/webm"};
}

View File

@ -8,8 +8,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import org.nuclearfog.twidda.model.Instance;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
/**
* This class is used to upload a message
@ -23,6 +28,11 @@ public class MessageUpdate {
private String name = "";
private String text = "";
@Nullable
private Instance instance;
private Set<String> supportedFormats = new TreeSet<>();
/**
* @param name screen name of the user
*/
@ -82,14 +92,19 @@ public class MessageUpdate {
* @return true if file is valid
*/
public boolean addMedia(Context context, @NonNull Uri uri) {
// check if file is valid
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
if (file != null && file.length() > 0) {
String mime = context.getContentResolver().getType(uri);
// check if file is valid
if (file == null || file.length() == 0) {
return false;
}
// check if file format is supported
if (mime == null || !supportedFormats.contains(mime)) {
return false;
}
this.uri = uri;
return true;
}
return false;
}
/**
* initialize inputstream of the file to upload
@ -97,8 +112,10 @@ public class MessageUpdate {
* @return true if initialization succeded
*/
public boolean prepare(ContentResolver resolver) {
if (uri == null)
if (uri == null) {
// no need to check media files if not attached
return true;
}
try {
String mimeType = resolver.getType(uri);
InputStream fileStream = resolver.openInputStream(uri);
@ -112,6 +129,24 @@ public class MessageUpdate {
return false;
}
/**
* set instance imformation such as status limitations
*
* @param instance instance imformation
*/
public void setInstanceInformation(Instance instance) {
supportedFormats.addAll(Arrays.asList(instance.getSupportedFormats()));
this.instance = instance;
}
/**
* get instance information
*/
@Nullable
public Instance getInstance() {
return instance;
}
/**
* close inputstream of media file
*/

View File

@ -13,7 +13,10 @@ import org.nuclearfog.twidda.model.Instance;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* This class is used to upload status information
@ -77,6 +80,7 @@ public class StatusUpdate {
private int attachment = EMPTY;
private List<Uri> mediaUris = new ArrayList<>(5);
private Set<String> supportedFormats = new TreeSet<>();
private MediaStatus[] mediaUpdates = {};
private boolean attachmentLimitReached = false;
private boolean sensitive = false;
@ -108,7 +112,7 @@ public class StatusUpdate {
*/
public int addMedia(Context context, Uri mediaUri) {
String mime = context.getContentResolver().getType(mediaUri);
if (mime == null || instance == null) {
if (mime == null || instance == null || !supportedFormats.contains(mime)) {
return MEDIA_ERROR;
}
// check if file is a 'gif' image
@ -223,6 +227,7 @@ public class StatusUpdate {
* @param instance instance imformation
*/
public void setInstanceInformation(Instance instance) {
supportedFormats.addAll(Arrays.asList(instance.getSupportedFormats()));
this.instance = instance;
}
@ -325,6 +330,14 @@ public class StatusUpdate {
return attachmentLimitReached;
}
/**
* get instance information
*/
@Nullable
public Instance getInstance() {
return instance;
}
/**
* check if media information is attached
*

View File

@ -22,11 +22,13 @@ import androidx.annotation.Nullable;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.InstanceLoader;
import org.nuclearfog.twidda.backend.async.MessageUpdater;
import org.nuclearfog.twidda.backend.async.MessageUpdater.MessageUpdateResult;
import org.nuclearfog.twidda.backend.helper.MessageUpdate;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.model.Instance;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
@ -37,7 +39,7 @@ import org.nuclearfog.twidda.ui.dialogs.ProgressDialog.OnProgressStopListener;
*
* @author nuclearfog
*/
public class MessageEditor extends MediaActivity implements OnClickListener, OnConfirmListener, OnProgressStopListener, AsyncCallback<MessageUpdateResult> {
public class MessageEditor extends MediaActivity implements OnClickListener, OnConfirmListener, OnProgressStopListener {
/**
* key for the screenname if any
@ -45,7 +47,11 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
*/
public static final String KEY_DM_PREFIX = "dm_prefix";
private MessageUpdater messageAsync;
private AsyncCallback<Instance> instanceResult = this::onInstanceResult;
private AsyncCallback<MessageUpdateResult> messageResult = this::onMessageResult;
private InstanceLoader instanceLoader;
private MessageUpdater messageUpdater;
private ProgressDialog loadingCircle;
private ConfirmDialog confirmDialog;
@ -53,7 +59,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
private EditText receiver, message;
private ImageButton media, preview;
private MessageUpdate holder = new MessageUpdate();
private MessageUpdate messageUpdate = new MessageUpdate();
@Override
protected void attachBaseContext(Context newBase) {
@ -74,7 +80,8 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
message = findViewById(R.id.popup_message_text);
AppStyles.setEditorTheme(root, background);
messageAsync = new MessageUpdater(this);
messageUpdater = new MessageUpdater(this);
instanceLoader = new InstanceLoader(this);
loadingCircle = new ProgressDialog(this);
confirmDialog = new ConfirmDialog(this);
@ -87,12 +94,23 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
preview.setOnClickListener(this);
loadingCircle.addOnProgressStopListener(this);
confirmDialog.setConfirmListener(this);
}
@Override
protected void onResume() {
if (messageUpdate.getInstance() == null) {
instanceLoader.execute(null, instanceResult);
}
super.onResume();
}
@Override
public void onBackPressed() {
if (receiver.getText().length() == 0 && message.getText().length() == 0 && holder.getMediaUri() == null) {
if (receiver.getText().length() == 0 && message.getText().length() == 0 && messageUpdate.getMediaUri() == null) {
loadingCircle.dismiss();
super.onBackPressed();
} else {
@ -103,9 +121,9 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
@Override
protected void onDestroy() {
messageAsync.cancel();
if (holder != null)
holder.close();
messageUpdater.cancel();
if (messageUpdate != null)
messageUpdate.close();
super.onDestroy();
}
@ -118,7 +136,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
@Override
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
if (resultType == REQUEST_IMAGE) {
if (holder.addMedia(this, uri)) {
if (messageUpdate.addMedia(this, uri)) {
preview.setVisibility(VISIBLE);
media.setVisibility(GONE);
} else {
@ -132,7 +150,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
public void onClick(View v) {
// send direct message
if (v.getId() == R.id.popup_message_send) {
if (messageAsync.isIdle()) {
if (messageUpdater.isIdle()) {
sendMessage();
}
}
@ -142,9 +160,9 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
}
// open media
else if (v.getId() == R.id.popup_message_preview) {
if (holder.getMediaUri() != null) {
if (messageUpdate.getMediaUri() != null) {
Intent image = new Intent(this, ImageViewer.class);
image.putExtra(ImageViewer.IMAGE_URI, holder.getMediaUri());
image.putExtra(ImageViewer.IMAGE_URI, messageUpdate.getMediaUri());
startActivity(image);
}
}
@ -153,7 +171,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
@Override
public void stopProgress() {
messageAsync.cancel();
messageUpdater.cancel();
}
@ -169,9 +187,30 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
}
}
/**
* check inputs and send message
*/
private void sendMessage() {
String username = receiver.getText().toString();
String message = this.message.getText().toString();
if (!username.trim().isEmpty() && (!message.trim().isEmpty() || messageUpdate.getMediaUri() != null)) {
if (messageUpdate.prepare(getContentResolver())) {
messageUpdate.setReceiver(username);
messageUpdate.setText(message);
messageUpdater.execute(messageUpdate, messageResult);
loadingCircle.show();
} else {
Toast.makeText(getApplicationContext(), R.string.error_media_init, LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), R.string.error_dm, LENGTH_SHORT).show();
}
}
@Override
public void onResult(@NonNull MessageUpdateResult result) {
/**
*
*/
private void onMessageResult(@NonNull MessageUpdateResult result) {
if (result.success) {
Toast.makeText(getApplicationContext(), R.string.info_dm_send, Toast.LENGTH_SHORT).show();
finish();
@ -183,22 +222,9 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
}
/**
* check inputs and send message
*
*/
private void sendMessage() {
String username = receiver.getText().toString();
String message = this.message.getText().toString();
if (!username.trim().isEmpty() && (!message.trim().isEmpty() || holder.getMediaUri() != null)) {
if (holder.prepare(getContentResolver())) {
holder.setReceiver(username);
holder.setText(message);
messageAsync.execute(holder, this);
loadingCircle.show();
} else {
Toast.makeText(getApplicationContext(), R.string.error_media_init, LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), R.string.error_dm, LENGTH_SHORT).show();
}
private void onInstanceResult(Instance instance) {
messageUpdate.setInstanceInformation(instance);
}
}

View File

@ -127,8 +127,6 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr
iconList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true));
iconList.setAdapter(adapter);
instanceLoader.execute(null, instanceResult);
statusText.addTextChangedListener(this);
closeButton.setOnClickListener(this);
preference.setOnClickListener(this);
@ -139,12 +137,17 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr
confirmDialog.setConfirmListener(this);
loadingCircle.addOnProgressStopListener(this);
adapter.addOnMediaClickListener(this);
}
@Override
protected void onResume() {
super.onResume();
if (statusUpdate.getInstance() == null) {
instanceLoader.execute(null, instanceResult);
}
if (settings.getLogin().getConfiguration().locationSupported()) {
if (isLocating()) {
locationPending.setVisibility(View.VISIBLE);

View File

@ -3,11 +3,13 @@ package org.nuclearfog.twidda.ui.activities;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -23,11 +25,11 @@ import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.StyledPlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ConnectionBuilder;
import org.nuclearfog.twidda.config.GlobalSettings;
import okhttp3.Call;
@ -50,8 +52,8 @@ public class VideoViewer extends AppCompatActivity {
*/
public static final String ENABLE_VIDEO_CONTROLS = "enable_controls";
private GlobalSettings settings;
private ExoPlayer player;
private Toolbar toolbar;
private Uri data;
@ -65,19 +67,13 @@ public class VideoViewer extends AppCompatActivity {
protected void onCreate(@Nullable Bundle b) {
super.onCreate(b);
setContentView(R.layout.page_video);
ViewGroup root = findViewById(R.id.page_video_root);
StyledPlayerView playerView = findViewById(R.id.page_video_player);
Toolbar toolbar = findViewById(R.id.page_video_toolbar);
toolbar = findViewById(R.id.page_video_toolbar);
player = new ExoPlayer.Builder(this).build();
settings = GlobalSettings.getInstance(this);
AppStyles.setTheme(root);
if (toolbar != null) {
setSupportActionBar(toolbar);
toolbar.setTitle("");
}
setSupportActionBar(toolbar);
data = getIntent().getParcelableExtra(VIDEO_URI);
boolean enableControls = getIntent().getBooleanExtra(ENABLE_VIDEO_CONTROLS, true);
@ -86,15 +82,20 @@ public class VideoViewer extends AppCompatActivity {
player.setRepeatMode(Player.REPEAT_MODE_ONE);
}
DataSource.Factory dataSourceFactory;
MediaItem mediaItem = MediaItem.fromUri(data);
DataSource.Factory dataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) ConnectionBuilder.create(this, 128000));
if (data.getScheme().startsWith("http")) {
dataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) ConnectionBuilder.create(this, 128000));
} else {
dataSourceFactory = new DefaultDataSource.Factory(this);
toolbar.setVisibility(View.GONE);
}
MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem);
player.setMediaSource(mediaSource);
playerView.setPlayer(player);
player.prepare();
player.play();
player.setPlayWhenReady(true);
}
@ -105,11 +106,21 @@ public class VideoViewer extends AppCompatActivity {
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
toolbar.setVisibility(View.GONE);
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
toolbar.setVisibility(View.VISIBLE);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.video, menu);
AppStyles.setMenuIconColor(menu, settings.getIconColor());
menu.findItem(R.id.menu_video_link).setVisible(data.getScheme().startsWith("http"));
AppStyles.setMenuIconColor(menu, Color.WHITE);
return super.onCreateOptionsMenu(menu);
}

View File

@ -45,26 +45,30 @@
<Button
android:id="@+id/confirm_no"
style="@style/FeedbackButton"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="@dimen/confirm_button_height"
android:layout_margin="@dimen/confirm_button_margin"
android:padding="@dimen/confirm_button_padding"
android:lines="1"
android:text="@android:string/cancel"
android:textSize="@dimen/confirm_button_fontsize"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/confirm_yes" />
<Button
android:id="@+id/confirm_yes"
style="@style/FeedbackButton"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="@dimen/confirm_button_height"
android:layout_margin="@dimen/confirm_button_margin"
android:padding="@dimen/confirm_button_padding"
android:lines="1"
android:text="@android:string/ok"
android:textSize="@dimen/confirm_button_fontsize"
app:layout_constraintStart_toEndOf="@id/confirm_no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View File

@ -6,5 +6,6 @@
android:id="@+id/menu_video_link"
android:icon="@drawable/share"
android:title="@string/button_share"
android:visible="true"
app:showAsAction="always" />
</menu>

View File

@ -2,7 +2,7 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorAccent">@android:color/white</item>
<item name="android:colorAccent">@android:color/white</item>
<item name="android:colorBackground">@color/background</item>
<item name="android:navigationBarColor">@android:color/black</item>
<item name="android:windowAnimationStyle">@style/TransactionPending</item>