Apply monocles in app media preview

This commit is contained in:
Arne 2024-09-06 18:44:59 +02:00
parent ea3d3deb7b
commit 900c38512f
12 changed files with 758 additions and 14 deletions

View file

@ -125,6 +125,10 @@ dependencies {
implementation 'net.fellbaum:jemoji:1.4.1'
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1'
implementation 'com.google.android.exoplayer:extension-mediasession:2.19.1'
}
ext {

View file

@ -1150,7 +1150,7 @@ public class FileBackend {
}
}
private static int getRotation(final InputStream inputStream) throws IOException {
public static int getRotation(final InputStream inputStream) throws IOException {
final ExifInterface exif = new ExifInterface(inputStream);
final int orientation =
exif.getAttributeInt(
@ -2287,6 +2287,10 @@ public class FileBackend {
}
}
public boolean deleteFile(File file) {
return file.delete();
}
private static class Dimensions {
public final int width;
public final int height;

View file

@ -12,10 +12,12 @@ import android.widget.Toast;
import java.io.File;
import java.util.List;
import de.monocles.chat.MediaViewerActivity;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
import me.drakeet.support.toast.ToastCompat;
public class ViewUtil {
@ -37,24 +39,46 @@ public class ViewUtil {
view(context, file, mime);
}
private static void view(Context context, File file, String mime) {
Log.d(Config.LOGTAG,"viewing "+file.getAbsolutePath()+" "+mime);
final Intent openIntent = new Intent(Intent.ACTION_VIEW);
public static void view(Context context, File file, String mime) {
Log.d(Config.LOGTAG, "viewing " + file.getAbsolutePath() + " " + mime);
final Uri uri;
try {
uri = FileBackend.getUriForFile(context, file);
} catch (SecurityException e) {
Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
Toast.makeText(context, context.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
ToastCompat.makeText(context, context.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), ToastCompat.LENGTH_SHORT).show();
return;
}
openIntent.setDataAndType(uri, mime);
openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(openIntent);
} catch (final ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
// use internal viewer for images and videos
if (mime.startsWith("image/")) {
final Intent intent = new Intent(context, MediaViewerActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("image", Uri.fromFile(file));
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.cant_open_file, Toast.LENGTH_LONG).show();
}
} else if (mime.startsWith("video/")) {
final Intent intent = new Intent(context, MediaViewerActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("video", Uri.fromFile(file));
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.cant_open_file, Toast.LENGTH_LONG).show();
}
} else {
final Intent openIntent = new Intent(Intent.ACTION_VIEW);
openIntent.setDataAndType(uri, mime);
openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(openIntent);
} catch (final ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
}
}
}
}

View file

@ -35,7 +35,7 @@ public class Compatibility {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
}
private static boolean runsTwentyFour() {
public static boolean runsTwentyFour() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M160,720Q143,720 131.5,708.5Q120,697 120,680Q120,663 131.5,651.5Q143,640 160,640L800,640Q817,640 828.5,651.5Q840,663 840,680Q840,697 828.5,708.5Q817,720 800,720L160,720ZM160,520Q143,520 131.5,508.5Q120,497 120,480Q120,463 131.5,451.5Q143,440 160,440L800,440Q817,440 828.5,451.5Q840,463 840,480Q840,497 828.5,508.5Q817,520 800,520L160,520ZM160,320Q143,320 131.5,308.5Q120,297 120,280Q120,263 131.5,251.5Q143,240 160,240L800,240Q817,240 828.5,251.5Q840,263 840,280Q840,297 828.5,308.5Q817,320 800,320L160,320Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M440,726L440,600Q440,583 451.5,571.5Q463,560 480,560Q497,560 508.5,571.5Q520,583 520,600L520,725L563,681Q575,669 592,669Q609,669 621,681Q633,693 633,710Q633,727 621,739L508,852Q502,858 495,860.5Q488,863 480,863Q472,863 465,860.5Q458,858 452,852L338,738Q326,726 326.5,709.5Q327,693 339,681Q351,669 367.5,669Q384,669 396,681L440,726ZM235,520L279,563Q291,575 291,592Q291,609 279,621Q267,633 250,633Q233,633 221,621L108,508Q102,502 99.5,495Q97,488 97,480Q97,472 99.5,465Q102,458 108,452L221,339Q233,327 249.5,327Q266,327 278,339Q290,351 290,367.5Q290,384 278,396L234,440L360,440Q377,440 388.5,451.5Q400,463 400,480Q400,497 388.5,508.5Q377,520 360,520L235,520ZM726,520L600,520Q583,520 571.5,508.5Q560,497 560,480Q560,463 571.5,451.5Q583,440 600,440L725,440L681,397Q669,385 669,368Q669,351 681,339Q693,327 710,327Q727,327 739,339L852,452Q858,458 860.5,465Q863,472 863,480Q863,488 860.5,495Q858,502 852,508L738,622Q726,634 710,633.5Q694,633 682,621Q670,609 670,592.5Q670,576 682,564L726,520ZM440,234L395,279Q383,291 367,291Q351,291 339,279Q327,267 327,250.5Q327,234 339,222L452,108Q458,102 465,99.5Q472,97 480,97Q488,97 495,99.5Q502,102 508,108L622,222Q634,234 634,250Q634,266 622,278Q610,290 593.5,290Q577,290 565,278L520,234L520,360Q520,377 508.5,388.5Q497,400 480,400Q463,400 451.5,388.5Q440,377 440,360L440,234Z"/>
</vector>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:orientation="vertical">
<com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
android:id="@+id/messageImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="gone" />
<ImageView
android:id="@+id/messageGifView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="gone" />
<com.google.android.exoplayer2.ui.StyledPlayerView
android:id="@+id/messageVideoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="gone"
app:animation_enabled="true"
app:auto_show="false"
app:buffered_color="@color/gray_700"
app:played_color="?attr/colorAccent"
app:scrubber_color="?attr/colorAccent"
app:show_shuffle_button="false"
app:show_subtitle_button="false"
app:show_vr_button="false"
app:unplayed_color="@color/gray_700" />
<com.leinardi.android.speeddial.SpeedDialOverlayLayout
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black87" />
<com.leinardi.android.speeddial.SpeedDialView
android:id="@+id/speed_dial"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
app:backgroundTint="?attr/colorAccent"
app:sdExpansionMode="top"
app:sdMainFabClosedBackgroundColor="?attr/colorAccent"
app:sdMainFabClosedSrc="@drawable/ic_menu_white_24dp"
app:sdMainFabOpenedBackgroundColor="?attr/colorAccent"
app:sdMainFabOpenedSrc="@drawable/ic_action_cancel_launchersize"
app:sdOverlayLayout="@id/overlay"
app:tint="@color/white" />
</RelativeLayout>
</layout>

View file

@ -0,0 +1,18 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share_24dp"
android:orderInCategory="10"
android:title="@string/share" />
<item
android:id="@+id/action_open"
android:icon="@drawable/ic_open_in_new_white_24dp"
android:orderInCategory="20"
android:title="@string/action_open" />
<item
android:id="@+id/action_delete"
android:icon="@drawable/ic_delete_24dp"
android:orderInCategory="20"
android:title="@string/delete" />
</menu>

View file

@ -1082,4 +1082,5 @@
<string name="pref_show_less_avatars">Show less avatars</string>
<string name="pref_show_less_avatars_summary">Show only needed avatars in the chats</string>
<string name="store_media_only_in_cache">Store media only in cache</string>
<string name="cant_open_file">Can\'t open file</string>
</resources>

View file

@ -19,7 +19,14 @@
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
<activity
android:name="de.monocles.chat.MediaViewerActivity"
android:autoRemoveFromRecents="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleInstance"
android:supportsPictureInPicture="true"
android:exported="false"
android:theme="@style/Theme.Conversations3.FullScreen" />
<activity
android:name=".ui.ManageAccountActivity"
android:label="@string/title_activity_manage_accounts"

View file

@ -0,0 +1,612 @@
package de.monocles.chat;
import android.app.PictureInPictureParams;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import android.util.Rational;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import com.davemorrissey.labs.subscaleview.ImageSource;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
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.ext.mediasession.MediaSessionConnector;
import com.leinardi.android.speeddial.SpeedDialActionItem;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityMediaViewerBinding;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Rationals;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.MimeUtils;
import me.drakeet.support.toast.ToastCompat;
public class MediaViewerActivity extends XmppActivity implements AudioManager.OnAudioFocusChangeListener {
Integer oldOrientation;
ExoPlayer player;
Uri mFileUri;
File mFile;
int height = 0;
int width = 0;
Rational aspect;
int rotation = 0;
boolean isImage = false;
boolean isVideo = false;
private ActivityMediaViewerBinding binding;
private GestureDetector gestureDetector;
public static String getMimeType(String path) {
try {
String type = null;
String extension = path.substring(path.lastIndexOf(".") + 1, path.length());
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_media_viewer);
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
if (isImage) {
if (binding.speedDial.isShown()) {
hideFAB();
} else {
showFAB();
}
}
return super.onDown(e);
}
});
ActionBar actionBar = getSupportActionBar();
if (actionBar != null && actionBar.isShowing()) {
actionBar.hide();
}
oldOrientation = getRequestedOrientation();
WindowManager.LayoutParams layout = getWindow().getAttributes();
if (useMaxBrightness()) {
layout.screenBrightness = 1;
}
getWindow().setAttributes(layout);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//binding.speedDial.inflate(R.menu.media_viewer);
}
private void share() {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType(getMimeType(mFile.toString()));
share.putExtra(Intent.EXTRA_STREAM, FileBackend.getUriForFile(this, mFile));
try {
startActivity(Intent.createChooser(share, getText(R.string.share_with)));
} catch (ActivityNotFoundException e) {
//This should happen only on faulty androids because normally chooser is always available
ToastCompat.makeText(this, R.string.no_application_found_to_open_file, ToastCompat.LENGTH_SHORT).show();
}
}
private void deleteFile() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_file_dialog);
builder.setMessage(R.string.delete_file_dialog_msg);
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
if (this.xmppConnectionService.getFileBackend().deleteFile(mFile)) {
finish();
}
});
builder.create().show();
}
private void open() {
Uri uri;
try {
uri = FileBackend.getUriForFile(this, mFile);
} catch (SecurityException e) {
Log.d(Config.LOGTAG, "No permission to access " + mFile.getAbsolutePath(), e);
ToastCompat.makeText(this, this.getString(R.string.no_permission_to_access_x, mFile.getAbsolutePath()), ToastCompat.LENGTH_SHORT).show();
return;
}
String mime = MimeUtils.guessMimeTypeFromUri(this, uri);
Intent openIntent = new Intent(Intent.ACTION_VIEW);
openIntent.setDataAndType(uri, mime);
openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
PackageManager manager = this.getPackageManager();
List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
if (info.size() == 0) {
openIntent.setDataAndType(uri, "*/*");
}
if (player != null && isVideo) {
openIntent.putExtra("position", player.getCurrentPosition());
}
try {
this.startActivity(openIntent);
} catch (ActivityNotFoundException e) {
ToastCompat.makeText(this, R.string.no_application_found_to_open_file, ToastCompat.LENGTH_SHORT).show();
}
}
@Override
protected void refreshUiReal() {
}
@Override
public void onStart() {
super.onStart();
Intent intent = getIntent();
if (intent != null) {
if (intent.hasExtra("image")) {
mFileUri = intent.getParcelableExtra("image");
mFile = new File(mFileUri.getPath());
if (mFileUri != null && mFile.exists() && mFile.length() > 0) {
try {
isImage = true;
DisplayImage(mFile, mFileUri);
} catch (Exception e) {
isImage = false;
Log.d(Config.LOGTAG, "Illegal exeption :" + e);
ToastCompat.makeText(MediaViewerActivity.this, getString(R.string.error_file_not_found), ToastCompat.LENGTH_SHORT).show();
finish();
}
} else {
ToastCompat.makeText(MediaViewerActivity.this, getString(R.string.file_deleted), ToastCompat.LENGTH_SHORT).show();
}
} else if (intent.hasExtra("video")) {
mFileUri = intent.getParcelableExtra("video");
mFile = new File(mFileUri.getPath());
if (mFileUri != null && mFile.exists() && mFile.length() > 0) {
try {
isVideo = true;
DisplayVideo(mFileUri);
} catch (Exception e) {
isVideo = false;
Log.d(Config.LOGTAG, "Illegal exeption :" + e);
ToastCompat.makeText(MediaViewerActivity.this, getString(R.string.error_file_not_found), ToastCompat.LENGTH_SHORT).show();
finish();
}
} else {
ToastCompat.makeText(MediaViewerActivity.this, getString(R.string.file_deleted), ToastCompat.LENGTH_SHORT).show();
}
}
}
if (isDeletableFile(mFile)) {
binding.speedDial.addActionItem(new SpeedDialActionItem.Builder(R.id.action_delete, R.drawable.ic_delete_24dp)
.setLabel(R.string.delete)
.setFabImageTintColor(ContextCompat.getColor(this, R.color.white))
.create()
);
}
binding.speedDial.addActionItem(new SpeedDialActionItem.Builder(R.id.action_open, R.drawable.ic_open_in_new_white_24dp)
.setLabel(R.string.open_with)
.setFabImageTintColor(ContextCompat.getColor(this, R.color.white))
.create()
);
binding.speedDial.addActionItem(new SpeedDialActionItem.Builder(R.id.action_share, R.drawable.ic_share_24dp)
.setLabel(R.string.share)
.setFabImageTintColor(ContextCompat.getColor(this, R.color.white))
.create()
);
if (isDeletableFile(mFile)) {
binding.speedDial.setOnActionSelectedListener(actionItem -> {
switch (actionItem.getId()) {
case R.id.action_share:
share();
break;
case R.id.action_open:
open();
break;
case R.id.action_delete:
deleteFile();
break;
default:
return false;
}
return false;
});
} else {
binding.speedDial.setOnActionSelectedListener(actionItem -> {
switch (actionItem.getId()) {
case R.id.action_share:
share();
break;
case R.id.action_open:
open();
break;
default:
return false;
}
return false;
});
}
binding.speedDial.getMainFab().setSupportImageTintList(ColorStateList.valueOf(getResources().getColor(R.color.white)));
}
private void DisplayImage(final File file, final Uri uri) {
final boolean gif = "image/gif".equalsIgnoreCase(getMimeType(file.toString()));
final boolean bmp = "image/bmp".equalsIgnoreCase(getMimeType(file.toString())) || "image/x-ms-bmp".equalsIgnoreCase(getMimeType(file.toString()));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(new File(file.getPath()).getAbsolutePath(), options);
height = options.outHeight;
width = options.outWidth;
aspect = new Rational(width, height);
rotation = getRotation(Uri.parse("file://" + file.getAbsolutePath()));
Log.d(Config.LOGTAG, "Image height: " + height + ", width: " + width + ", rotation: " + rotation + " aspect: " + aspect);
if (useAutoRotateScreen()) {
rotateScreen(width, height, rotation);
}
try {
if (gif) {
binding.messageGifView.setVisibility(View.VISIBLE);
binding.messageGifView.setImageURI(uri);
binding.messageGifView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
} else {
binding.messageImageView.setVisibility(View.VISIBLE);
binding.messageImageView.setImage(ImageSource.uri(uri).tiling(!bmp));
binding.messageImageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_USE_EXIF);
binding.messageImageView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
}
} catch (Exception e) {
ToastCompat.makeText(this, getString(R.string.error_file_not_found), ToastCompat.LENGTH_LONG).show();
e.printStackTrace();
}
}
private void DisplayVideo(final Uri uri) {
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(uri.getPath());
Bitmap bitmap = null;
try {
bitmap = retriever.getFrameAtTime(0);
height = bitmap.getHeight();
width = bitmap.getWidth();
} catch (Exception e) {
height = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
width = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
} finally {
if (bitmap != null) {
bitmap.recycle();
}
}
try {
rotation = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
} catch (Exception e) {
rotation = 0;
}
aspect = new Rational(width, height);
Log.d(Config.LOGTAG, "Video height: " + height + ", width: " + width + ", rotation: " + rotation + ", aspect: " + aspect);
if (useAutoRotateScreen()) {
rotateScreen(width, height, rotation);
}
binding.messageVideoView.setVisibility(View.VISIBLE);
player = new ExoPlayer.Builder(this).build();
player.addListener(new Player.Listener() {
@Override
public void onIsPlayingChanged(boolean isPlaying) {
Player.Listener.super.onIsPlayingChanged(isPlaying);
if (isPlaying) {
hideFAB();
} else {
if (Compatibility.runsTwentyFour() && isInPictureInPictureMode()) {
hideFAB();
} else {
showFAB();
}
}
}
@Override
public void onPlayerError(PlaybackException error) {
open();
}
});
player.setRepeatMode(Player.REPEAT_MODE_OFF);
binding.messageVideoView.setPlayer(player);
player.setMediaItem(MediaItem.fromUri(uri));
player.prepare();
player.setPlayWhenReady(true);
final MediaSessionCompat session = new MediaSessionCompat(this, getPackageName());
final MediaSessionConnector connector = new MediaSessionConnector(session);
connector.setPlayer(player);
session.setActive(true);
requestAudioFocus();
setVolumeControlStream(AudioManager.STREAM_MUSIC);
// binding.messageVideoView.setOnTouchListener((view, motionEvent) -> gestureDetector.onTouchEvent(motionEvent));
} catch (Exception e) {
e.printStackTrace();
open();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void PIPVideo() {
try {
binding.messageVideoView.hideController();
binding.speedDial.setVisibility(View.GONE);
if (supportsPIP()) {
if (Compatibility.runsTwentySix()) {
final Rational rational = new Rational(width, height);
final Rational clippedRational = Rationals.clip(rational);
final PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(clippedRational)
.build();
this.enterPictureInPictureMode(params);
} else {
this.enterPictureInPictureMode();
}
}
} catch (final IllegalStateException e) {
// this sometimes happens on Samsung phones (possibly when Knox is enabled)
Log.w(Config.LOGTAG, "unable to enter picture in picture mode", e);
}
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
if (isInPictureInPictureMode) {
startPlayer();
hideFAB();
} else {
showFAB();
}
}
private void releaseAudiFocus() {
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
if (am != null) {
am.abandonAudioFocus(this);
}
}
private void requestAudioFocus() {
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
if (am != null) {
am.requestAudioFocus(this,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
}
@Override
public void onBackPressed() {
if (isVideo && isPlaying() && supportsPIP()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
PIPVideo();
}
} else {
super.onBackPressed();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
if (isVideo) {
PIPVideo();
}
}
private int getRotation(Uri image) {
try (final InputStream is = this.getContentResolver().openInputStream(image)) {
return is == null ? 0 : FileBackend.getRotation(is);
} catch (final Exception e) {
return 0;
}
}
private void rotateScreen(final int width, final int height, final int rotation) {
if (width > height) {
if (rotation == 0 || rotation == 180) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
} else {
if (rotation == 90 || rotation == 270) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
}
}
private void pausePlayer() {
if (player != null && isVideo && isPlaying()) {
player.setPlayWhenReady(false);
player.getPlaybackState();
if (Compatibility.runsTwentyFour() && isInPictureInPictureMode()) {
hideFAB();
} else {
showFAB();
}
}
}
private void startPlayer() {
if (player != null && isVideo && !isPlaying()) {
player.setPlayWhenReady(true);
player.getPlaybackState();
hideFAB();
}
}
private void stopPlayer() {
if (player != null && isVideo) {
if (supportsPIP()) {
finishAndRemoveTask();
}
if (isPlaying()) {
player.stop();
}
player.release();
if (Compatibility.runsTwentyFour() && isInPictureInPictureMode()) {
hideFAB();
} else {
showFAB();
}
}
}
private boolean isPlaying() {
return player != null
&& player.getPlaybackState() != Player.STATE_ENDED
&& player.getPlaybackState() != Player.STATE_IDLE
&& player.getPlayWhenReady();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void onResume() {
WindowManager.LayoutParams layout = getWindow().getAttributes();
if (useMaxBrightness()) {
layout.screenBrightness = 1;
}
getWindow().setAttributes(layout);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (!isPlaying()) {
showFAB();
} else {
hideFAB();
}
if (!isPlaying()) {
startPlayer();
}
super.onResume();
}
@Override
public void onPause() {
if (Compatibility.runsTwentyFour() && isInPictureInPictureMode()) {
startPlayer();
} else {
pausePlayer();
}
WindowManager.LayoutParams layout = getWindow().getAttributes();
if (useMaxBrightness()) {
layout.screenBrightness = -1;
}
getWindow().setAttributes(layout);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setRequestedOrientation(oldOrientation);
super.onPause();
}
@Override
public void onStop() {
stopPlayer();
releaseAudiFocus();
WindowManager.LayoutParams layout = getWindow().getAttributes();
if (useMaxBrightness()) {
layout.screenBrightness = -1;
}
getWindow().setAttributes(layout);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setRequestedOrientation(oldOrientation);
super.onStop();
}
@Override
protected void onBackendConnected() {
}
public boolean useMaxBrightness() {
// return getPreferences().getBoolean("use_max_brightness", getResources().getBoolean(R.bool.use_max_brightness));
return false;
}
public boolean useAutoRotateScreen() {
// return getPreferences().getBoolean("use_auto_rotate", getResources().getBoolean(R.bool.auto_rotate));
return false;
}
public SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
@Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.i(Config.LOGTAG, "Audio focus granted.");
} else if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.i(Config.LOGTAG, "Audio focus failed.");
}
}
private boolean isDeletableFile(File file) {
return (file == null || !file.toString().startsWith("/") || file.canWrite());
}
private void showFAB() {
binding.speedDial.show();
}
private void hideFAB() {
binding.speedDial.hide();
}
private boolean supportsPIP() {
if (Compatibility.runsTwentyFour()) {
return this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
} else {
return false;
}
}
}

View file

@ -76,4 +76,5 @@
<string name="one_participant">One participant</string>
<string name="is_typing">is typing …</string>
<string name="more_participants">%d participants</string>
<string name="action_open">Open</string>
</resources>