replaced direct media access, added image caching, bug fix
This commit is contained in:
parent
b27d77b937
commit
33260ae84e
|
@ -21,8 +21,6 @@ import android.content.ActivityNotFoundException;
|
|||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
|
@ -31,7 +29,6 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -44,6 +41,7 @@ import org.nuclearfog.twidda.backend.ImageSaver;
|
|||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
|
@ -72,11 +70,6 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
*/
|
||||
private static final String[] TYPE_ALL = {MIME_IMAGE_READ, MIME_VIDEO_READ};
|
||||
|
||||
/**
|
||||
* Cursor mode to get the full path to the image
|
||||
*/
|
||||
private static final String[] GET_MEDIA = {MediaStore.MediaColumns.DATA};
|
||||
|
||||
/**
|
||||
* request code to check permissions
|
||||
*/
|
||||
|
@ -109,9 +102,9 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
|
||||
@Nullable
|
||||
private ImageSaver imageTask;
|
||||
private Bitmap image;
|
||||
private String filename;
|
||||
private boolean locationPending = false;
|
||||
private Uri selectedImage;
|
||||
private String imageName;
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -153,17 +146,7 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
protected final void onActivityResult(int reqCode, int returnCode, @Nullable Intent intent) {
|
||||
super.onActivityResult(reqCode, returnCode, intent);
|
||||
if (returnCode == RESULT_OK && intent != null && intent.getData() != null) {
|
||||
Cursor cursor = getContentResolver().query(intent.getData(), GET_MEDIA, null, null, null);
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
String path = cursor.getString(0);
|
||||
if (path != null) {
|
||||
onMediaFetched(reqCode, path);
|
||||
// todo add error handling if no media returned
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
onMediaFetched(reqCode, intent.getData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,22 +183,23 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
// store images directly
|
||||
File imageFolder = Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES);
|
||||
File imageFile = new File(imageFolder, filename);
|
||||
OutputStream fileStream = new FileOutputStream(imageFile);
|
||||
File imageFile = new File(imageFolder, imageName);
|
||||
InputStream src = getContentResolver().openInputStream(selectedImage);
|
||||
OutputStream dest = new FileOutputStream(imageFile);
|
||||
imageTask = new ImageSaver(this);
|
||||
imageTask.execute(image, fileStream);
|
||||
imageTask.execute(src, dest);
|
||||
} else {
|
||||
// use scoped storage
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DISPLAY_NAME, filename);
|
||||
values.put(DISPLAY_NAME, imageName);
|
||||
values.put(DATE_TAKEN, System.currentTimeMillis());
|
||||
values.put(RELATIVE_PATH, DIRECTORY_PICTURES);
|
||||
values.put(MIME_TYPE, MIME_IMAGE_WRITE);
|
||||
Uri imageUri = getContentResolver().insert(EXTERNAL_CONTENT_URI, values);
|
||||
if (imageUri != null) {
|
||||
OutputStream fileStream = getContentResolver().openOutputStream(imageUri);
|
||||
OutputStream dest = getContentResolver().openOutputStream(imageUri);
|
||||
imageTask = new ImageSaver(this);
|
||||
imageTask.execute(image, fileStream);
|
||||
imageTask.execute(selectedImage, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,8 +215,7 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
Toast.makeText(this, R.string.info_image_saved, LENGTH_SHORT).show();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
// start media scanner to scan for new image
|
||||
String[] fileName = {filename};
|
||||
MediaScannerConnection.scanFile(this, fileName, null, null);
|
||||
MediaScannerConnection.scanFile(this, new String[]{imageName}, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,15 +266,11 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
/**
|
||||
* store image bitmap into storage
|
||||
*
|
||||
* @param filename name of the file
|
||||
* @param image image bitmap to store
|
||||
* @param uri Uri of the image
|
||||
*/
|
||||
protected void storeImage(Bitmap image, String filename) {
|
||||
this.image = image;
|
||||
this.filename = filename;
|
||||
if (!filename.endsWith(".jpg")) {
|
||||
this.filename += ".jpg";
|
||||
}
|
||||
protected void storeImage(Uri uri) {
|
||||
selectedImage = uri;
|
||||
imageName = "shitter_" + uri.getLastPathSegment() + "jpg";
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
|| checkSelfPermission(PERMISSIONS[2][0]) == PERMISSION_GRANTED) {
|
||||
saveImage();
|
||||
|
@ -352,9 +331,8 @@ public abstract class MediaActivity extends AppCompatActivity implements Locatio
|
|||
|
||||
/**
|
||||
* called when a media file path was successfully fetched
|
||||
*
|
||||
* @param resultType type of media call
|
||||
* @param path local path to the media file
|
||||
* @param resultType type of media call
|
||||
* @param uri Uri of the file
|
||||
*/
|
||||
protected abstract void onMediaFetched(int resultType, @NonNull String path);
|
||||
protected abstract void onMediaFetched(int resultType, @NonNull Uri uri);
|
||||
}
|
|
@ -3,21 +3,14 @@ package org.nuclearfog.twidda.activities;
|
|||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.location.Location;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaPlayer.OnInfoListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ProgressBar;
|
||||
|
@ -37,26 +30,21 @@ import org.nuclearfog.twidda.adapter.ImageAdapter;
|
|||
import org.nuclearfog.twidda.adapter.ImageAdapter.OnImageClickListener;
|
||||
import org.nuclearfog.twidda.backend.ImageLoader;
|
||||
import org.nuclearfog.twidda.backend.SeekbarUpdater;
|
||||
import org.nuclearfog.twidda.backend.holder.ImageHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.backend.utils.StringTools;
|
||||
import org.nuclearfog.twidda.database.GlobalSettings;
|
||||
import org.nuclearfog.zoomview.ZoomView;
|
||||
|
||||
import static android.media.MediaPlayer.MEDIA_ERROR_UNKNOWN;
|
||||
import static android.media.MediaPlayer.MEDIA_INFO_BUFFERING_END;
|
||||
import static android.media.MediaPlayer.MEDIA_INFO_BUFFERING_START;
|
||||
import static android.media.MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START;
|
||||
import static android.os.AsyncTask.Status.RUNNING;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static android.media.MediaPlayer.*;
|
||||
import static android.os.AsyncTask.Status.*;
|
||||
import static android.view.MotionEvent.*;
|
||||
import static android.view.View.*;
|
||||
import static android.widget.Toast.*;
|
||||
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Media viewer activity for images and videos
|
||||
*
|
||||
|
@ -66,15 +54,25 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
OnPreparedListener, OnInfoListener, OnErrorListener, OnClickListener, OnTouchListener {
|
||||
|
||||
/**
|
||||
* Key for the media URL, local or online, required
|
||||
* Key for online media files
|
||||
*/
|
||||
public static final String KEY_MEDIA_LINK = "media_link";
|
||||
|
||||
/**
|
||||
* key for local media files
|
||||
*/
|
||||
public static final String KEY_MEDIA_URI = "media_uri";
|
||||
|
||||
/**
|
||||
* Key for the media type, required
|
||||
* {@link #MEDIAVIEWER_IMAGE}, {@link #MEDIAVIEWER_VIDEO} or {@link #MEDIAVIEWER_ANGIF}
|
||||
*/
|
||||
public static final String KEY_MEDIA_TYPE = "media_type";
|
||||
|
||||
/**
|
||||
* cache folder name
|
||||
*/
|
||||
public static final String CACHE_FOLDER = "imagecache";
|
||||
/**
|
||||
* setup media viewer for images from twitter
|
||||
*/
|
||||
|
@ -124,11 +122,12 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
private ZoomView zoomImage;
|
||||
private ViewGroup controlPanel;
|
||||
|
||||
private String[] mediaLinks;
|
||||
private Uri[] mediaLinks;
|
||||
private int type;
|
||||
|
||||
private PlayStat playStat = PlayStat.IDLE;
|
||||
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
super.attachBaseContext(AppStyles.setFontScale(newBase));
|
||||
|
@ -163,43 +162,55 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
GlobalSettings settings = GlobalSettings.getInstance(this);
|
||||
AppStyles.setProgressColor(loadingCircle, settings.getHighlightColor());
|
||||
AppStyles.setTheme(controlPanel, settings.getBackgroundColor());
|
||||
adapter = new ImageAdapter(settings, this);
|
||||
adapter = new ImageAdapter(getApplicationContext(), this);
|
||||
|
||||
// check if link is a local file
|
||||
Parcelable[] links = getIntent().getParcelableArrayExtra(KEY_MEDIA_URI);
|
||||
if (links != null) {
|
||||
mediaLinks = new Uri[links.length];
|
||||
for (int i = 0; i < mediaLinks.length ; i++) {
|
||||
mediaLinks[i] = (Uri) links[i];
|
||||
}
|
||||
}
|
||||
// check if links are http:// links
|
||||
else {
|
||||
String[] strLinks = getIntent().getStringArrayExtra(KEY_MEDIA_LINK);
|
||||
mediaLinks = new Uri[strLinks.length];
|
||||
for (int i = 0; i < mediaLinks.length ; i++) {
|
||||
mediaLinks[i] = Uri.parse(strLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// get intent data and type
|
||||
mediaLinks = getIntent().getStringArrayExtra(KEY_MEDIA_LINK);
|
||||
type = getIntent().getIntExtra(KEY_MEDIA_TYPE, 0);
|
||||
|
||||
if (mediaLinks != null && mediaLinks.length > 0) {
|
||||
switch (type) {
|
||||
case MEDIAVIEWER_IMAGE:
|
||||
zoomImage.setVisibility(VISIBLE);
|
||||
imageListContainer.setVisibility(VISIBLE);
|
||||
if (!mediaLinks[0].startsWith("http"))
|
||||
adapter.disableSaveButton();
|
||||
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
|
||||
imageList.setAdapter(adapter);
|
||||
switch (type) {
|
||||
case MEDIAVIEWER_IMAGE:
|
||||
zoomImage.setVisibility(VISIBLE);
|
||||
imageListContainer.setVisibility(VISIBLE);
|
||||
if (!mediaLinks[0].getScheme().startsWith("http")) {
|
||||
adapter.disableSaveButton();
|
||||
for (Uri uri : mediaLinks)
|
||||
setImage(uri);
|
||||
adapter.disableLoading();
|
||||
} else {
|
||||
imageAsync = new ImageLoader(this);
|
||||
imageAsync.execute(mediaLinks);
|
||||
break;
|
||||
}
|
||||
imageList.setLayoutManager(new LinearLayoutManager(this, HORIZONTAL, false));
|
||||
imageList.setAdapter(adapter);
|
||||
break;
|
||||
|
||||
case MEDIAVIEWER_VIDEO:
|
||||
controlPanel.setVisibility(VISIBLE);
|
||||
if (mediaLinks[0].startsWith("/"))
|
||||
share.setVisibility(GONE); // local image
|
||||
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && mediaLinks[0].startsWith("https://")) {
|
||||
// for any reason VideoView ignores TLS 1.2 setup, so we have to use http:// instead
|
||||
// todo find a solution to add TLS 1.2 support for pre lollipop devices
|
||||
mediaLinks[0] = "http://" + mediaLinks[0].substring(8);
|
||||
}
|
||||
seekUpdate = new SeekbarUpdater(this, PROGRESS_UPDATE);
|
||||
// fall through
|
||||
case MEDIAVIEWER_ANGIF:
|
||||
videoView.setVisibility(VISIBLE);
|
||||
videoView.setZOrderMediaOverlay(true); // disable black background
|
||||
videoView.getHolder().setFormat(PixelFormat.TRANSPARENT);
|
||||
videoView.setVideoURI(Uri.parse(mediaLinks[0]));
|
||||
break;
|
||||
}
|
||||
case MEDIAVIEWER_VIDEO:
|
||||
controlPanel.setVisibility(VISIBLE);
|
||||
if (mediaLinks[0].getScheme().startsWith("http"))
|
||||
share.setVisibility(GONE); // local image
|
||||
seekUpdate = new SeekbarUpdater(this, PROGRESS_UPDATE);
|
||||
// fall through
|
||||
case MEDIAVIEWER_ANGIF:
|
||||
videoView.setVisibility(VISIBLE);
|
||||
videoView.setZOrderMediaOverlay(true); // disable black background
|
||||
videoView.getHolder().setFormat(PixelFormat.TRANSPARENT);
|
||||
videoView.setVideoURI(mediaLinks[0]);
|
||||
break;
|
||||
}
|
||||
share.setOnClickListener(this);
|
||||
play.setOnClickListener(this);
|
||||
|
@ -231,6 +242,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
imageAsync.cancel(true);
|
||||
if (seekUpdate != null)
|
||||
seekUpdate.shutdown();
|
||||
clearCache();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
@ -254,7 +266,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
// open link with another app
|
||||
else if (v.getId() == R.id.controller_share) {
|
||||
if (mediaLinks != null && mediaLinks.length > 0) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mediaLinks[0]));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, mediaLinks[0]);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException err) {
|
||||
|
@ -320,24 +332,20 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
|
||||
|
||||
@Override
|
||||
protected void onMediaFetched(int resultType, @NonNull String path) {
|
||||
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onImageClick(Bitmap image) {
|
||||
public void onImageClick(Uri uri) {
|
||||
zoomImage.reset();
|
||||
zoomImage.setImageBitmap(image);
|
||||
zoomImage.setImageURI(uri);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onImageSave(Bitmap image, int pos) {
|
||||
if (mediaLinks != null && pos < mediaLinks.length && mediaLinks[pos] != null) {
|
||||
String link = mediaLinks[pos];
|
||||
String name = "shitter_" + link.substring(link.lastIndexOf('/') + 1);
|
||||
storeImage(image, name);
|
||||
}
|
||||
public void onImageSave(Uri uri) {
|
||||
storeImage(uri);
|
||||
}
|
||||
|
||||
|
||||
|
@ -439,15 +447,15 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
/**
|
||||
* set downloaded image into preview list
|
||||
*
|
||||
* @param image Image container
|
||||
* @param imageUri Image Uri
|
||||
*/
|
||||
public void setImage(ImageHolder image) {
|
||||
public void setImage(Uri imageUri) {
|
||||
if (adapter.isEmpty()) {
|
||||
zoomImage.reset();
|
||||
zoomImage.setImageBitmap(image.reducedImage);
|
||||
zoomImage.setImageURI(imageUri);
|
||||
loadingCircle.setVisibility(INVISIBLE);
|
||||
}
|
||||
adapter.addLast(image);
|
||||
adapter.addLast(imageUri);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -490,4 +498,15 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clear the image cache
|
||||
*/
|
||||
private void clearCache() {
|
||||
File cacheFolder = new File(getExternalCacheDir(), CACHE_FOLDER);
|
||||
File[] files = cacheFolder.listFiles();
|
||||
for (File file : files) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,17 @@
|
|||
package org.nuclearfog.twidda.activities;
|
||||
|
||||
import static android.os.AsyncTask.Status.RUNNING;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.View.*;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_LINK;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.*;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
|
@ -26,6 +23,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.MessageUpdater;
|
||||
import org.nuclearfog.twidda.backend.holder.DirectmessageHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.dialog.ConfirmDialog;
|
||||
|
@ -53,7 +51,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
|
|||
private Dialog loadingCircle;
|
||||
private ConfirmDialog errorDialog, leaveDialog;
|
||||
@Nullable
|
||||
private String mediaPath;
|
||||
private Uri mediaUri;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
|
@ -95,7 +93,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (receiver.getText().length() == 0 && message.getText().length() == 0 && mediaPath == null) {
|
||||
if (receiver.getText().length() == 0 && message.getText().length() == 0 && mediaUri == null) {
|
||||
super.onBackPressed();
|
||||
} else if (!leaveDialog.isShowing()) {
|
||||
leaveDialog.show();
|
||||
|
@ -117,11 +115,11 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
|
|||
|
||||
|
||||
@Override
|
||||
protected void onMediaFetched(int resultType, @NonNull String path) {
|
||||
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
|
||||
if (resultType == REQUEST_IMAGE) {
|
||||
preview.setVisibility(VISIBLE);
|
||||
media.setVisibility(GONE);
|
||||
mediaPath = path;
|
||||
mediaUri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,9 +138,8 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
|
|||
}
|
||||
// open media
|
||||
else if (v.getId() == R.id.dm_preview) {
|
||||
String[] link = {mediaPath};
|
||||
Intent image = new Intent(this, MediaViewer.class);
|
||||
image.putExtra(KEY_MEDIA_LINK, link);
|
||||
image.putExtra(KEY_MEDIA_URI, new Uri[]{mediaUri});
|
||||
image.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
|
||||
startActivity(image);
|
||||
}
|
||||
|
@ -199,9 +196,12 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
|
|||
private void sendMessage() {
|
||||
String username = receiver.getText().toString();
|
||||
String message = this.message.getText().toString();
|
||||
if (!username.trim().isEmpty() && (!message.trim().isEmpty() || mediaPath != null)) {
|
||||
messageAsync = new MessageUpdater(this);
|
||||
messageAsync.execute(username, message, mediaPath);
|
||||
if (!username.trim().isEmpty() && (!message.trim().isEmpty() || mediaUri != null)) {
|
||||
DirectmessageHolder holder = new DirectmessageHolder(username, message);
|
||||
if (mediaUri != null)
|
||||
holder.addMedia(getApplicationContext(), mediaUri);
|
||||
messageAsync = new MessageUpdater(this, holder);
|
||||
messageAsync.execute();
|
||||
if (!loadingCircle.isShowing()) {
|
||||
loadingCircle.show();
|
||||
}
|
||||
|
|
|
@ -12,10 +12,9 @@ import static org.nuclearfog.twidda.database.GlobalSettings.PROFILE_IMG_HIGH_RES
|
|||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Point;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
|
@ -38,6 +37,7 @@ import com.squareup.picasso.Picasso;
|
|||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.UserUpdater;
|
||||
import org.nuclearfog.twidda.backend.holder.ProfileHolder;
|
||||
import org.nuclearfog.twidda.model.User;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
|
@ -49,8 +49,6 @@ import org.nuclearfog.twidda.dialog.ConfirmDialog.OnConfirmListener;
|
|||
import org.nuclearfog.twidda.dialog.ProgressDialog;
|
||||
import org.nuclearfog.twidda.dialog.ProgressDialog.OnProgressStopListener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
|
||||
|
||||
/**
|
||||
|
@ -76,7 +74,8 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, OnP
|
|||
private ConfirmDialog errorDialog, closeDialog;
|
||||
|
||||
private User user;
|
||||
private String profileLink, bannerLink;
|
||||
private Uri profileLink, bannerLink;
|
||||
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
|
@ -182,22 +181,20 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, OnP
|
|||
|
||||
|
||||
@Override
|
||||
protected void onMediaFetched(int resultType, @NonNull String path) {
|
||||
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
|
||||
// Add image as profile image
|
||||
if (resultType == REQUEST_PROFILE) {
|
||||
Bitmap image = BitmapFactory.decodeFile(path);
|
||||
profile_image.setImageBitmap(image);
|
||||
profileLink = path;
|
||||
profile_image.setImageURI(uri);
|
||||
profileLink = uri;
|
||||
}
|
||||
// Add image as banner image
|
||||
else if (resultType == REQUEST_BANNER) {
|
||||
File img = new File(path);
|
||||
Point displaySize = new Point();
|
||||
getWindowManager().getDefaultDisplay().getSize(displaySize);
|
||||
picasso.load(img).resize(displaySize.x, displaySize.x / 3).centerCrop(Gravity.TOP).into(profile_banner, this);
|
||||
picasso.load(uri).resize(displaySize.x, displaySize.x / 3).centerCrop(Gravity.TOP).into(profile_banner, this);
|
||||
addBannerBtn.setVisibility(INVISIBLE);
|
||||
changeBannerBtn.setVisibility(VISIBLE);
|
||||
bannerLink = path;
|
||||
bannerLink = uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,8 +286,13 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, OnP
|
|||
String errMsg = getString(R.string.error_invalid_link);
|
||||
link.setError(errMsg);
|
||||
} else if (editorAsync == null || editorAsync.getStatus() != RUNNING) {
|
||||
editorAsync = new UserUpdater(this);
|
||||
editorAsync.execute(username, userLink, userLoc, userBio, profileLink, bannerLink);
|
||||
ProfileHolder holder = new ProfileHolder(username, userLink, userBio, userLoc);
|
||||
if (profileLink != null)
|
||||
holder.addImageUri(getApplicationContext(), profileLink);
|
||||
if (bannerLink != null)
|
||||
holder.addBannerUri(getApplicationContext(), bannerLink);
|
||||
editorAsync = new UserUpdater(this, holder);
|
||||
editorAsync.execute();
|
||||
if (!loadingCircle.isShowing()) {
|
||||
loadingCircle.show();
|
||||
}
|
||||
|
|
|
@ -6,15 +6,13 @@ import static android.view.View.INVISIBLE;
|
|||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_LINK;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_VIDEO;
|
||||
import static org.nuclearfog.twidda.activities.MediaViewer.*;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
@ -70,6 +68,10 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
*/
|
||||
public static final String KEY_TWEETPOPUP_TEXT = "tweet_text";
|
||||
|
||||
private static final String MIME_GIF = "image/gif";
|
||||
private static final String MIME_IMAGE_ALL = "image/";
|
||||
private static final String MIME_VIDEO_ALL = "video/";
|
||||
|
||||
/**
|
||||
* max amount of images (limited to 4 by twitter)
|
||||
*/
|
||||
|
@ -90,7 +92,7 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
private View locationPending;
|
||||
|
||||
private Location location;
|
||||
private List<String> mediaPath = new LinkedList<>();
|
||||
private List<Uri> mediaPath = new LinkedList<>();
|
||||
private MediaType selectedFormat = MediaType.NONE;
|
||||
|
||||
@Override
|
||||
|
@ -203,7 +205,8 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
// open media preview
|
||||
else if (v.getId() == R.id.tweet_prev_media) {
|
||||
Intent image = new Intent(this, MediaViewer.class);
|
||||
image.putExtra(KEY_MEDIA_LINK, mediaPath.toArray(new String[0]));
|
||||
Uri[] uris = mediaPath.toArray(new Uri[0]);
|
||||
image.putExtra(KEY_MEDIA_URI, uris);
|
||||
if (selectedFormat == MediaType.VIDEO) {
|
||||
image.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_VIDEO);
|
||||
startActivity(image);
|
||||
|
@ -235,29 +238,32 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
|
||||
|
||||
@Override
|
||||
protected void onMediaFetched(int resultType, @NonNull String path) {
|
||||
String mime = StringTools.getMimeType(path);
|
||||
protected void onMediaFetched(int resultType, @NonNull Uri uri) {
|
||||
String mime = getContentResolver().getType(uri);
|
||||
if (mime == null) {
|
||||
Toast.makeText(this, R.string.error_file_format, LENGTH_SHORT).show();
|
||||
}
|
||||
// check if file is a gif image
|
||||
if (mime.equals("image/gif")) {
|
||||
else if (mime.equals(MIME_GIF)) {
|
||||
if (selectedFormat == MediaType.NONE) {
|
||||
selectedFormat = MediaType.GIF;
|
||||
previewBtn.setImageResource(R.drawable.gif);
|
||||
AppStyles.setDrawableColor(previewBtn, settings.getIconColor());
|
||||
previewBtn.setVisibility(VISIBLE);
|
||||
mediaBtn.setVisibility(GONE);
|
||||
mediaPath.add(path);
|
||||
mediaPath.add(uri);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.info_cant_add_video, LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
// check if file is an image with supported extension
|
||||
else if (mime.startsWith("image")) {
|
||||
else if (mime.startsWith(MIME_IMAGE_ALL)) {
|
||||
if (selectedFormat == MediaType.NONE)
|
||||
selectedFormat = MediaType.IMAGE;
|
||||
if (selectedFormat == MediaType.IMAGE) {
|
||||
// add up to 4 images
|
||||
if (mediaPath.size() < MAX_IMAGES) {
|
||||
mediaPath.add(path);
|
||||
mediaPath.add(uri);
|
||||
previewBtn.setVisibility(VISIBLE);
|
||||
// if limit reached, remove mediaselect button
|
||||
if (mediaPath.size() == MAX_IMAGES) {
|
||||
|
@ -269,14 +275,14 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
}
|
||||
}
|
||||
// check if file is a video with supported extension
|
||||
else if (mime.startsWith("video")) {
|
||||
else if (mime.startsWith(MIME_VIDEO_ALL)) {
|
||||
if (selectedFormat == MediaType.NONE) {
|
||||
selectedFormat = MediaType.VIDEO;
|
||||
previewBtn.setImageResource(R.drawable.video);
|
||||
AppStyles.setDrawableColor(previewBtn, settings.getIconColor());
|
||||
previewBtn.setVisibility(VISIBLE);
|
||||
mediaBtn.setVisibility(GONE);
|
||||
mediaPath.add(path);
|
||||
mediaPath.add(uri);
|
||||
}
|
||||
}
|
||||
// file type is not supported
|
||||
|
@ -349,9 +355,9 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro
|
|||
TweetHolder tweet = new TweetHolder(tweetStr, inReplyId);
|
||||
// add media
|
||||
if (selectedFormat == MediaType.IMAGE || selectedFormat == MediaType.GIF)
|
||||
tweet.addMedia(mediaPath.toArray(new String[0]), TweetHolder.MediaType.IMAGE);
|
||||
tweet.addMedia(getApplicationContext(), mediaPath);
|
||||
else if (selectedFormat == MediaType.VIDEO)
|
||||
tweet.addMedia(mediaPath.toArray(new String[0]), TweetHolder.MediaType.VIDEO);
|
||||
tweet.addMedia(getApplicationContext(), mediaPath);
|
||||
// add location
|
||||
if (location != null)
|
||||
tweet.addLocation(location);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.nuclearfog.twidda.adapter;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -12,15 +13,17 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|||
|
||||
import org.nuclearfog.twidda.adapter.holder.Footer;
|
||||
import org.nuclearfog.twidda.adapter.holder.ImageItem;
|
||||
import org.nuclearfog.twidda.backend.holder.ImageHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
|
||||
import org.nuclearfog.twidda.database.GlobalSettings;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
/**
|
||||
* Adapter class for image previews
|
||||
*
|
||||
|
@ -42,29 +45,32 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
|
||||
private GlobalSettings settings;
|
||||
|
||||
private List<ImageHolder> images = new LinkedList<>();
|
||||
private Picasso picasso;
|
||||
|
||||
private List<Uri> imageUri = new ArrayList<>(5);
|
||||
private boolean loading = false;
|
||||
private boolean saveImg = true;
|
||||
|
||||
/**
|
||||
* @param itemClickListener click listener
|
||||
*/
|
||||
public ImageAdapter(GlobalSettings settings, OnImageClickListener itemClickListener) {
|
||||
public ImageAdapter(Context context, OnImageClickListener itemClickListener) {
|
||||
this.itemClickListener = itemClickListener;
|
||||
this.settings = settings;
|
||||
this.settings = GlobalSettings.getInstance(context);
|
||||
picasso = PicassoBuilder.get(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* add new image at the last position
|
||||
*
|
||||
* @param imageItem image to add
|
||||
* @param uri Uri of the image
|
||||
*/
|
||||
@MainThread
|
||||
public void addLast(@NonNull ImageHolder imageItem) {
|
||||
int imagePos = images.size();
|
||||
public void addLast(@NonNull Uri uri) {
|
||||
int imagePos = imageUri.size();
|
||||
if (imagePos == 0)
|
||||
loading = true;
|
||||
images.add(imageItem);
|
||||
imageUri.add(uri);
|
||||
notifyItemInserted(imagePos);
|
||||
}
|
||||
|
||||
|
@ -74,7 +80,7 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
@MainThread
|
||||
public void disableLoading() {
|
||||
loading = false;
|
||||
int circlePos = images.size();
|
||||
int circlePos = imageUri.size();
|
||||
notifyItemRemoved(circlePos);
|
||||
}
|
||||
|
||||
|
@ -91,13 +97,13 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
* @return true if there isn't any image
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return images.isEmpty();
|
||||
return imageUri.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (loading && position == images.size())
|
||||
if (loading && position == imageUri.size())
|
||||
return LOADING;
|
||||
return PICTURE;
|
||||
}
|
||||
|
@ -106,8 +112,8 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
@Override
|
||||
public int getItemCount() {
|
||||
if (loading)
|
||||
return images.size() + 1;
|
||||
return images.size();
|
||||
return imageUri.size() + 1;
|
||||
return imageUri.size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -121,8 +127,7 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
public void onClick(View v) {
|
||||
int pos = item.getLayoutPosition();
|
||||
if (pos != NO_POSITION) {
|
||||
Bitmap img = images.get(pos).reducedImage;
|
||||
itemClickListener.onImageClick(img);
|
||||
itemClickListener.onImageClick(imageUri.get(pos));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -133,8 +138,7 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
public void onClick(View v) {
|
||||
int pos = item.getLayoutPosition();
|
||||
if (pos != NO_POSITION) {
|
||||
Bitmap img = images.get(pos).fullImage;
|
||||
itemClickListener.onImageSave(img, pos);
|
||||
itemClickListener.onImageSave(imageUri.get(pos));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -150,8 +154,8 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
public void onBindViewHolder(@NonNull ViewHolder vh, int index) {
|
||||
if (vh instanceof ImageItem) {
|
||||
ImageItem item = (ImageItem) vh;
|
||||
Bitmap image = images.get(index).preview;
|
||||
item.preview.setImageBitmap(image);
|
||||
Uri uri = imageUri.get(index);
|
||||
picasso.load(uri).into(item.preview);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,16 +167,13 @@ public class ImageAdapter extends Adapter<ViewHolder> {
|
|||
/**
|
||||
* called to select an image
|
||||
*
|
||||
* @param image selected image bitmap
|
||||
* @param uri selected image uri
|
||||
*/
|
||||
void onImageClick(Bitmap image);
|
||||
void onImageClick(Uri uri);
|
||||
|
||||
/**
|
||||
* called to save image to storage
|
||||
*
|
||||
* @param image selected image bitmap
|
||||
* @param index current image index
|
||||
*/
|
||||
void onImageSave(Bitmap image, int index);
|
||||
void onImageSave(Uri uri);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package org.nuclearfog.twidda.backend;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -10,25 +9,27 @@ import androidx.annotation.Nullable;
|
|||
import org.nuclearfog.twidda.activities.MediaViewer;
|
||||
import org.nuclearfog.twidda.backend.api.Twitter;
|
||||
import org.nuclearfog.twidda.backend.api.TwitterException;
|
||||
import org.nuclearfog.twidda.backend.holder.ImageHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.backend.utils.StringTools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Background task to load images from twitter and storage
|
||||
* todo: cache files to prevent out of memory error
|
||||
* background async to download images to a cache folder
|
||||
*
|
||||
* @author nuclearfog
|
||||
* @see MediaViewer
|
||||
*/
|
||||
public class ImageLoader extends AsyncTask<String, ImageHolder, Boolean> {
|
||||
public class ImageLoader extends AsyncTask<Uri, Uri, Boolean> {
|
||||
|
||||
@Nullable
|
||||
private ErrorHandler.TwitterError err;
|
||||
private Twitter twitter;
|
||||
private WeakReference<MediaViewer> callback;
|
||||
|
||||
private File cache;
|
||||
|
||||
/**
|
||||
* initialize image loader
|
||||
|
@ -39,23 +40,32 @@ public class ImageLoader extends AsyncTask<String, ImageHolder, Boolean> {
|
|||
super();
|
||||
callback = new WeakReference<>(activity);
|
||||
twitter = Twitter.get(activity);
|
||||
cache = new File(activity.getExternalCacheDir(), MediaViewer.CACHE_FOLDER);
|
||||
cache.mkdirs();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(String[] links) {
|
||||
protected Boolean doInBackground(Uri[] links) {
|
||||
try {
|
||||
for (String link : links) {
|
||||
Bitmap image;
|
||||
if (link.startsWith("https://")) {
|
||||
image = twitter.downloadImage(link);
|
||||
} else {
|
||||
image = BitmapFactory.decodeFile(link);
|
||||
}
|
||||
if (image != null) {
|
||||
ImageHolder images = new ImageHolder(image);
|
||||
publishProgress(images);
|
||||
// create cache folder if not exists
|
||||
// download imaged to a local cache folder
|
||||
for (Uri link : links) {
|
||||
File file = new File(cache, StringTools.getRandomString());
|
||||
file.createNewFile();
|
||||
FileOutputStream os = new FileOutputStream(file);
|
||||
InputStream input = twitter.downloadImage(link.toString());
|
||||
|
||||
// copy image to cache folder
|
||||
int length;
|
||||
byte[] buffer = new byte[4096];
|
||||
while ((length = input.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
input.close();
|
||||
os.close();
|
||||
// create a new uri
|
||||
publishProgress(Uri.fromFile(file));
|
||||
}
|
||||
return true;
|
||||
} catch (TwitterException err) {
|
||||
|
@ -68,9 +78,9 @@ public class ImageLoader extends AsyncTask<String, ImageHolder, Boolean> {
|
|||
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(ImageHolder[] images) {
|
||||
protected void onProgressUpdate(Uri[] uris) {
|
||||
if (callback.get() != null) {
|
||||
callback.get().setImage(images[0]);
|
||||
callback.get().setImage(uris[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package org.nuclearfog.twidda.backend;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import org.nuclearfog.twidda.activities.MediaActivity;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
@ -16,10 +16,6 @@ import java.lang.ref.WeakReference;
|
|||
*/
|
||||
public class ImageSaver extends AsyncTask<Object, Void, Boolean> {
|
||||
|
||||
/**
|
||||
* Quality of the saved jpeg images
|
||||
*/
|
||||
private static final int JPEG_QUALITY = 90;
|
||||
|
||||
private WeakReference<MediaActivity> callback;
|
||||
|
||||
|
@ -34,12 +30,19 @@ public class ImageSaver extends AsyncTask<Object, Void, Boolean> {
|
|||
protected Boolean doInBackground(Object... data) {
|
||||
try {
|
||||
if (data != null && data.length == 2) {
|
||||
if (data[0] instanceof Bitmap && data[1] instanceof OutputStream) {
|
||||
Bitmap image = (Bitmap) data[0];
|
||||
OutputStream fileStream = (OutputStream) data[1];
|
||||
boolean imageSaved = image.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, fileStream);
|
||||
fileStream.close();
|
||||
return imageSaved;
|
||||
if (data[0] instanceof InputStream && data[1] instanceof OutputStream) {
|
||||
InputStream source = (InputStream) data[0];
|
||||
OutputStream destiny = (OutputStream) data[1];
|
||||
|
||||
// copy file from cache to the destiny folder
|
||||
int length;
|
||||
byte[] buffer = new byte[4096];
|
||||
while ((length = source.read(buffer)) > 0) {
|
||||
destiny.write(buffer, 0, length);
|
||||
}
|
||||
source.close();
|
||||
destiny.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception err) {
|
||||
|
|
|
@ -72,7 +72,7 @@ public class ListAction extends AsyncTask<Long, Void, UserList> {
|
|||
return twitter.unfollowUserlist(listId);
|
||||
|
||||
case DELETE:
|
||||
return twitter.destroyUserlist(listId);
|
||||
return twitter.deleteUserlist(listId);
|
||||
}
|
||||
} catch (TwitterException err) {
|
||||
this.err = err;
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
|
|||
import org.nuclearfog.twidda.activities.MessageEditor;
|
||||
import org.nuclearfog.twidda.backend.api.Twitter;
|
||||
import org.nuclearfog.twidda.backend.api.TwitterException;
|
||||
import org.nuclearfog.twidda.backend.holder.DirectmessageHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -17,38 +18,41 @@ import java.lang.ref.WeakReference;
|
|||
* @author nuclearfog
|
||||
* @see MessageEditor
|
||||
*/
|
||||
public class MessageUpdater extends AsyncTask<String, Void, Boolean> {
|
||||
public class MessageUpdater extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
private ErrorHandler.TwitterError twException;
|
||||
private WeakReference<MessageEditor> callback;
|
||||
private Twitter twitter;
|
||||
|
||||
private DirectmessageHolder message;
|
||||
|
||||
/**
|
||||
* send direct message
|
||||
*
|
||||
* @param activity Activity context
|
||||
*/
|
||||
public MessageUpdater(@NonNull MessageEditor activity) {
|
||||
public MessageUpdater(@NonNull MessageEditor activity, DirectmessageHolder message) {
|
||||
super();
|
||||
twitter = Twitter.get(activity);
|
||||
callback = new WeakReference<>(activity);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(String[] param) {
|
||||
protected Boolean doInBackground(Void[] v) {
|
||||
try {
|
||||
// first check if user exists
|
||||
long id = twitter.showUser(param[0]).getId();
|
||||
long id = twitter.showUser(message.getReceiver()).getId();
|
||||
// upload media if any
|
||||
long mediaId = -1;
|
||||
String mediaPath = param[2];
|
||||
if (mediaPath != null && !mediaPath.isEmpty()) {
|
||||
mediaId = twitter.uploadImage(param[2]);
|
||||
if (message.getMediaStream() != null) {
|
||||
mediaId = twitter.uploadMedia(message.getMediaStream(), message.getMimeType());
|
||||
message.getMediaStream().close();
|
||||
}
|
||||
// upload message and media ID if defined
|
||||
if (!isCancelled()) {
|
||||
twitter.sendDirectmessage(id, param[1], mediaId);
|
||||
twitter.sendDirectmessage(id, message.getText(), mediaId);
|
||||
}
|
||||
return true;
|
||||
} catch (TwitterException twException) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.nuclearfog.twidda.backend.api.TwitterException;
|
|||
import org.nuclearfog.twidda.backend.holder.TweetHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
|
@ -18,9 +19,8 @@ import java.lang.ref.WeakReference;
|
|||
*/
|
||||
public class TweetUpdater extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
|
||||
private ErrorHandler.TwitterError twException;
|
||||
private Twitter twitter;
|
||||
private ErrorHandler.TwitterError twException;
|
||||
private WeakReference<TweetEditor> callback;
|
||||
private TweetHolder tweet;
|
||||
|
||||
|
@ -40,25 +40,16 @@ public class TweetUpdater extends AsyncTask<Void, Void, Boolean> {
|
|||
@Override
|
||||
protected Boolean doInBackground(Void[] v) {
|
||||
try {
|
||||
long[] mediaIds = {};
|
||||
String[] mediaLinks = tweet.getMediaPaths();
|
||||
if (mediaLinks != null && mediaLinks.length > 0) {
|
||||
mediaIds = new long[mediaLinks.length];
|
||||
String[] mimeTypes = tweet.getMimeTypes();
|
||||
InputStream[] mediaStreams = tweet.getMediaStreams();
|
||||
|
||||
// upload image
|
||||
if (tweet.getMediaType() == TweetHolder.MediaType.IMAGE) {
|
||||
for (int i = 0; i < mediaLinks.length; i++) {
|
||||
mediaIds[i] = twitter.uploadImage(mediaLinks[i]);
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// upload video file
|
||||
else if (tweet.getMediaType() == TweetHolder.MediaType.VIDEO) {// fixme
|
||||
//mediaIds[0] = mTwitter.uploadVideo(mediaLinks[0]);
|
||||
}
|
||||
// upload media first
|
||||
long[] mediaIds = new long[mediaStreams.length];
|
||||
for (int pos = 0 ; pos < mediaStreams.length ; pos++) {
|
||||
mediaIds[pos] = twitter.uploadMedia(mediaStreams[pos], mimeTypes[pos]);
|
||||
mediaStreams[pos].close();
|
||||
}
|
||||
|
||||
// upload tweet
|
||||
if (!isCancelled()) {
|
||||
double[] coordinates = null;
|
||||
|
|
|
@ -7,10 +7,12 @@ import androidx.annotation.Nullable;
|
|||
import org.nuclearfog.twidda.activities.ProfileEditor;
|
||||
import org.nuclearfog.twidda.backend.api.Twitter;
|
||||
import org.nuclearfog.twidda.backend.api.TwitterException;
|
||||
import org.nuclearfog.twidda.backend.holder.ProfileHolder;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
|
||||
import org.nuclearfog.twidda.model.User;
|
||||
import org.nuclearfog.twidda.database.AppDatabase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +21,7 @@ import java.lang.ref.WeakReference;
|
|||
* @author nuclearfog
|
||||
* @see ProfileEditor
|
||||
*/
|
||||
public class UserUpdater extends AsyncTask<String, Void, User> {
|
||||
public class UserUpdater extends AsyncTask<Void, Void, User> {
|
||||
|
||||
@Nullable
|
||||
private ErrorHandler.TwitterError twException;
|
||||
|
@ -27,31 +29,36 @@ public class UserUpdater extends AsyncTask<String, Void, User> {
|
|||
private Twitter twitter;
|
||||
private AppDatabase db;
|
||||
|
||||
private ProfileHolder profile;
|
||||
|
||||
public UserUpdater(ProfileEditor activity) {
|
||||
|
||||
public UserUpdater(ProfileEditor activity, ProfileHolder profile) {
|
||||
super();
|
||||
callback = new WeakReference<>(activity);
|
||||
twitter = Twitter.get(activity);
|
||||
db = new AppDatabase(activity);
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected User doInBackground(String[] param) {
|
||||
protected User doInBackground(Void[] v) {
|
||||
try {
|
||||
String name = param[0];
|
||||
String link = param[1];
|
||||
String location = param[2];
|
||||
String bio = param[3];
|
||||
String profileImg = param[4];
|
||||
String bannerImg = param[5];
|
||||
twitter.updateProfileImage(profileImg);
|
||||
twitter.updateProfileBanner(bannerImg);
|
||||
User user = twitter.updateProfile(name, link, location, bio);
|
||||
if (profile.getProfileImageStream() != null) {
|
||||
twitter.updateProfileImage(profile.getProfileImageStream());
|
||||
profile.getProfileImageStream().close();
|
||||
}
|
||||
if (profile.getBannerImageStream() != null) {
|
||||
twitter.updateProfileBanner(profile.getBannerImageStream());
|
||||
profile.getBannerImageStream().close();
|
||||
}
|
||||
User user = twitter.updateProfile(profile.getName(), profile.getUrl(), profile.getLocation(), profile.getDescription());
|
||||
db.storeUser(user);
|
||||
return user;
|
||||
} catch (TwitterException twException) {
|
||||
this.twException = twException;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package org.nuclearfog.twidda.backend.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
|
@ -27,7 +25,6 @@ import org.nuclearfog.twidda.model.User;
|
|||
import org.nuclearfog.twidda.database.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.UserList;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
@ -50,6 +47,8 @@ import okhttp3.OkHttpClient;
|
|||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
|
||||
/**
|
||||
* new API implementation to replace twitter4j and add version 2.0 support
|
||||
|
@ -258,6 +257,8 @@ public class Twitter {
|
|||
*/
|
||||
public User showUser(String name) throws TwitterException {
|
||||
List<String> params = new ArrayList<>(3);
|
||||
if (name.startsWith("@"))
|
||||
name = name.substring(1);
|
||||
params.add("screen_name=" + name);
|
||||
return getUser1(USER_LOOKUP, params);
|
||||
}
|
||||
|
@ -940,7 +941,7 @@ public class Twitter {
|
|||
* @param listId ID of the list
|
||||
* @return removed userlist
|
||||
*/
|
||||
public UserList destroyUserlist(long listId) throws TwitterException {
|
||||
public UserList deleteUserlist(long listId) throws TwitterException {
|
||||
List<String> params = new ArrayList<>(2);
|
||||
params.add("list_id=" + listId);
|
||||
return getUserlist(USERLIST_DESTROY, params);
|
||||
|
@ -1064,7 +1065,7 @@ public class Twitter {
|
|||
params.add("id=" + messageId);
|
||||
try {
|
||||
Response response = delete(DIRECTMESSAGE_DELETE, params);
|
||||
if (response.code() != 200) {
|
||||
if (response.code() >= 300) {
|
||||
throw new TwitterException(response);
|
||||
}
|
||||
} catch (IOException err) {
|
||||
|
@ -1130,20 +1131,18 @@ public class Twitter {
|
|||
}
|
||||
|
||||
/**
|
||||
* upload image file to twitter and generate a media ID
|
||||
* upload medida file to twitter and generate a media ID
|
||||
*
|
||||
* @param filePath path to the local file
|
||||
* @param uploadStream path to the local file
|
||||
* @return media ID
|
||||
*/
|
||||
public long uploadImage(String filePath) throws TwitterException {
|
||||
File file = new File(filePath);
|
||||
String mime = StringTools.getMimeType(filePath);
|
||||
public long uploadMedia(InputStream uploadStream, String mime) throws TwitterException {
|
||||
List<String> params = new ArrayList<>(4);
|
||||
try {
|
||||
// step 1 INIT
|
||||
List<String> params = new ArrayList<>();
|
||||
params.add("command=INIT");
|
||||
params.add("media_type=" + mime);
|
||||
params.add("total_bytes=" + file.length());
|
||||
params.add("total_bytes=" + uploadStream.available());
|
||||
Response response = post(MEDIA_UPLOAD, params);
|
||||
if (response.code() < 200 || response.code() >= 300 || response.body() == null)
|
||||
throw new TwitterException(response);
|
||||
|
@ -1155,7 +1154,7 @@ public class Twitter {
|
|||
params.add("command=APPEND");
|
||||
params.add("segment_index=0");
|
||||
params.add("media_id=" + mediaId);
|
||||
response = post(MEDIA_UPLOAD, params, file, "media");
|
||||
response = post(MEDIA_UPLOAD, params, uploadStream, "media");
|
||||
if (response.code() < 200 || response.code() >= 300)
|
||||
throw new TwitterException(response);
|
||||
|
||||
|
@ -1181,14 +1180,13 @@ public class Twitter {
|
|||
* @param link link to the image
|
||||
* @return image bitmap
|
||||
*/
|
||||
public Bitmap downloadImage(String link) throws TwitterException {
|
||||
public InputStream downloadImage(String link) throws TwitterException {
|
||||
try {
|
||||
// this type of link requires authentication
|
||||
if (link.startsWith("https://ton.twitter.com/")) {
|
||||
Response response = get(link, new ArrayList<>(0));
|
||||
if (response.code() == 200) {
|
||||
InputStream is = response.body().byteStream();
|
||||
return BitmapFactory.decodeStream(is);
|
||||
return response.body().byteStream();
|
||||
} else {
|
||||
throw new TwitterException(response);
|
||||
}
|
||||
|
@ -1196,8 +1194,7 @@ public class Twitter {
|
|||
// public link, no authentication required
|
||||
else {
|
||||
URL imageUrl = new URL(link);
|
||||
InputStream is = imageUrl.openConnection().getInputStream();
|
||||
return BitmapFactory.decodeStream(is);
|
||||
return imageUrl.openConnection().getInputStream();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new TwitterException(e);
|
||||
|
@ -1225,19 +1222,19 @@ public class Twitter {
|
|||
/**
|
||||
* update current user's profile image
|
||||
*
|
||||
* @param path local path to the image
|
||||
* @param inputStream inputstream of the local image file
|
||||
*/
|
||||
public void updateProfileImage(String path) throws TwitterException {
|
||||
updateImage(PROFILE_UPDATE_IMAGE, path, "image");
|
||||
public void updateProfileImage(InputStream inputStream) throws TwitterException {
|
||||
updateImage(PROFILE_UPDATE_IMAGE, inputStream, "image");
|
||||
}
|
||||
|
||||
/**
|
||||
* update current user's profile banner image
|
||||
*
|
||||
* @param path local path to the image
|
||||
* @param inputStream inputstream of the local image file
|
||||
*/
|
||||
public void updateProfileBanner(String path) throws TwitterException {
|
||||
updateImage(PROFILE_UPDATE_BANNER, path, "banner");
|
||||
public void updateProfileBanner(InputStream inputStream) throws TwitterException {
|
||||
updateImage(PROFILE_UPDATE_BANNER, inputStream, "banner");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1552,15 +1549,15 @@ public class Twitter {
|
|||
* update profile images
|
||||
*
|
||||
* @param endpoint endpoint to use
|
||||
* @param imagePath path to the local image
|
||||
* @param input inputstream of the image file
|
||||
* @param key key name used to identify the type of image
|
||||
*/
|
||||
private void updateImage(String endpoint, String imagePath, String key) throws TwitterException {
|
||||
private void updateImage(String endpoint, InputStream input, String key) throws TwitterException {
|
||||
try {
|
||||
List<String> params = new ArrayList<>(3);
|
||||
params.add("skip_status=true");
|
||||
params.add("include_entities=false");
|
||||
Response response = post(endpoint, params, new File(imagePath), key);
|
||||
Response response = post(endpoint, params, input, key);
|
||||
if (response.body() != null) {
|
||||
JSONObject json = new JSONObject(response.body().string());
|
||||
if (response.code() != 200) {
|
||||
|
@ -1655,10 +1652,20 @@ public class Twitter {
|
|||
* @param params additional http parameters
|
||||
* @return http resonse
|
||||
*/
|
||||
private Response post(String endpoint, List<String> params, File file, String addToKey) throws IOException {
|
||||
RequestBody data = RequestBody.create(MediaType.parse("application/octet-stream"), file);
|
||||
private Response post(String endpoint, List<String> params, InputStream is, String addToKey) throws IOException {
|
||||
RequestBody data = new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse("application/octet-stream");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
sink.writeAll(Okio.buffer(Okio.source(is)));
|
||||
}
|
||||
};
|
||||
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
.addFormDataPart(addToKey, file.getName(), data).build();
|
||||
.addFormDataPart(addToKey, "", data).build();
|
||||
return post(endpoint, params, body);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package org.nuclearfog.twidda.backend.holder;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* this class holds information about a directmessage
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class DirectmessageHolder {
|
||||
|
||||
private String name;
|
||||
private String text;
|
||||
private String mimeType = "";
|
||||
private InputStream fileStream;
|
||||
|
||||
|
||||
public DirectmessageHolder(String name, String text) {
|
||||
this.name = name;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* add media uri and create input stream
|
||||
*
|
||||
* @param context context used to create inputstream and mime type
|
||||
* @param uri uri of a local media file
|
||||
*/
|
||||
public void addMedia(Context context, @NonNull Uri uri) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
try {
|
||||
fileStream = resolver.openInputStream(uri);
|
||||
mimeType = resolver.getType(uri);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get name of the receiver
|
||||
*
|
||||
* @return screen name
|
||||
*/
|
||||
public String getReceiver() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* get message text
|
||||
*
|
||||
* @return message text
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* get inputstream of the media file
|
||||
*
|
||||
* @return input stream
|
||||
*/
|
||||
public InputStream getMediaStream() {
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* get MIME type of the media file
|
||||
*
|
||||
* @return mime type string
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package org.nuclearfog.twidda.backend.holder;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Container class for Bitmap images and previews
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class ImageHolder {
|
||||
|
||||
/**
|
||||
* maximum height of the smallest preview in pixels
|
||||
*/
|
||||
private static final float PREVIEW_HEIGHT = 320.0f;
|
||||
|
||||
/**
|
||||
* maximum height of the image preview in pixels
|
||||
*/
|
||||
private static final float REDUCED_HEIGHT = 1200.0f;
|
||||
|
||||
/**
|
||||
* preview image bitmap
|
||||
*/
|
||||
public final Bitmap preview;
|
||||
|
||||
/**
|
||||
* downscaled image bitmap
|
||||
*/
|
||||
public final Bitmap reducedImage;
|
||||
|
||||
/**
|
||||
* full image bitmap
|
||||
*/
|
||||
public final Bitmap fullImage;
|
||||
|
||||
/**
|
||||
* @param fullImage Full size image
|
||||
*/
|
||||
public ImageHolder(@NonNull Bitmap fullImage) {
|
||||
this.fullImage = fullImage;
|
||||
|
||||
float reducedRatio = fullImage.getHeight() / REDUCED_HEIGHT;
|
||||
float previewRatio = fullImage.getHeight() / PREVIEW_HEIGHT;
|
||||
|
||||
if (reducedRatio > 1.0f) {
|
||||
int height = (int) REDUCED_HEIGHT;
|
||||
int width = (int) (fullImage.getWidth() / reducedRatio);
|
||||
reducedImage = Bitmap.createScaledBitmap(fullImage, width, height, false);
|
||||
} else {
|
||||
reducedImage = fullImage;
|
||||
}
|
||||
if (previewRatio > 1.0f) {
|
||||
int height = (int) PREVIEW_HEIGHT;
|
||||
int width = (int) (fullImage.getWidth() / previewRatio);
|
||||
preview = Bitmap.createScaledBitmap(fullImage, width, height, false);
|
||||
} else {
|
||||
preview = fullImage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package org.nuclearfog.twidda.backend.holder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* holder class for profile update information
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class ProfileHolder {
|
||||
|
||||
private String name;
|
||||
private String url;
|
||||
private String description;
|
||||
private String location;
|
||||
|
||||
private InputStream profileImgStream;
|
||||
private InputStream bannerImgStream;
|
||||
|
||||
|
||||
public ProfileHolder(String name, String url, String description, String location) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.description = description;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* add profile image Uri
|
||||
*
|
||||
* @param context context used to resolve Uri
|
||||
* @param profileImgUri Uri of the local image file
|
||||
*/
|
||||
public void addImageUri(Context context, @Nullable Uri profileImgUri) {
|
||||
try {
|
||||
profileImgStream = context.getContentResolver().openInputStream(profileImgUri);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add banner image Uri
|
||||
*
|
||||
* @param context context used to resolve Uri
|
||||
* @param bannerImgUri Uri of the local image file
|
||||
*/
|
||||
public void addBannerUri(Context context, @Nullable Uri bannerImgUri) {
|
||||
try {
|
||||
bannerImgStream = context.getContentResolver().openInputStream(bannerImgUri);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return screen name of the user
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return profile description (bio)
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return location name
|
||||
*/
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return profile url
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return filestream of the profile image
|
||||
*/
|
||||
public InputStream getProfileImageStream() {
|
||||
return profileImgStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return filestream of the banner image
|
||||
*/
|
||||
public InputStream getBannerImageStream() {
|
||||
return bannerImgStream;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
package org.nuclearfog.twidda.backend.holder;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TweetHolder keeps information about a written tweet such as text, media files and location
|
||||
*
|
||||
|
@ -11,19 +18,13 @@ import androidx.annotation.NonNull;
|
|||
*/
|
||||
public class TweetHolder {
|
||||
|
||||
public enum MediaType {
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
NONE
|
||||
}
|
||||
|
||||
private final String text;
|
||||
private final long replyId;
|
||||
private String[] mediaPaths;
|
||||
private double longitude;
|
||||
private double latitude;
|
||||
private InputStream[] mediaStreams;
|
||||
|
||||
private MediaType mType = MediaType.NONE;
|
||||
private String[] mimeTypes = {};
|
||||
private boolean hasLocation = false;
|
||||
|
||||
|
||||
|
@ -41,12 +42,25 @@ public class TweetHolder {
|
|||
/**
|
||||
* Add media paths to the holder
|
||||
*
|
||||
* @param mediaLinks array of media paths from storage
|
||||
* @param mType type of media
|
||||
* @param context context to resolve Uri links
|
||||
* @param mediaUri array of media paths from storage
|
||||
*/
|
||||
public void addMedia(String[] mediaLinks, MediaType mType) {
|
||||
this.mediaPaths = mediaLinks;
|
||||
this.mType = mType;
|
||||
public void addMedia(Context context, List<Uri> mediaUri) {
|
||||
if (!mediaUri.isEmpty()) {
|
||||
List<InputStream> iss = new ArrayList<>();
|
||||
List<String> mimeTypes = new ArrayList<>();
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
try {
|
||||
for (Uri uri : mediaUri) {
|
||||
iss.add(resolver.openInputStream(uri));
|
||||
mimeTypes.add(resolver.getType(uri));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.mediaStreams = iss.toArray(new InputStream[0]);
|
||||
this.mimeTypes = mimeTypes.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,8 +97,8 @@ public class TweetHolder {
|
|||
*
|
||||
* @return media type
|
||||
*/
|
||||
public MediaType getMediaType() {
|
||||
return mType;
|
||||
public String[] getMimeTypes() {
|
||||
return mimeTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,8 +106,8 @@ public class TweetHolder {
|
|||
*
|
||||
* @return array of media paths
|
||||
*/
|
||||
public String[] getMediaPaths() {
|
||||
return mediaPaths;
|
||||
public InputStream[] getMediaStreams() {
|
||||
return mediaStreams;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,15 +137,6 @@ public class TweetHolder {
|
|||
return hasLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* return if tweet is a reply
|
||||
*
|
||||
* @return true if tweet is a reply
|
||||
*/
|
||||
public boolean isReply() {
|
||||
return replyId > 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.nuclearfog.twidda.backend.proxy;
|
|||
|
||||
import org.nuclearfog.twidda.database.GlobalSettings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
|
||||
|
|
|
@ -108,36 +108,6 @@ public final class StringTools {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get MIME type of a media file
|
||||
* if file type is not supported, return {@link #MIME_ALL}
|
||||
*
|
||||
* @param filename file name or path with extension
|
||||
* @return MIME type
|
||||
*/
|
||||
public static String getMimeType(String filename) {
|
||||
int end = filename.lastIndexOf('.');
|
||||
if (end < 0)
|
||||
return MIME_ALL;
|
||||
|
||||
String extension = filename.substring(end + 1).toLowerCase();
|
||||
switch (extension) {
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "webp":
|
||||
case "png":
|
||||
case "gif":
|
||||
return "image/" + extension;
|
||||
|
||||
case "mp4":
|
||||
case "3gp":
|
||||
return "video/" + extension;
|
||||
|
||||
default:
|
||||
return MIME_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert Twitter ISO 8601 date time to long format
|
||||
*
|
||||
|
|
|
@ -5,7 +5,11 @@ import android.database.Cursor;
|
|||
import org.nuclearfog.twidda.model.DirectMessage;
|
||||
import org.nuclearfog.twidda.model.User;
|
||||
|
||||
|
||||
/**
|
||||
* database implementation of a directmessage
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
class DirectMessageImpl implements DirectMessage {
|
||||
|
||||
private long id;
|
||||
|
|
Loading…
Reference in New Issue