Unfinished keyboard GIF picking stuff? Not accessible by the user, yet.
This commit is contained in:
parent
9e49da64bf
commit
91ad3acc79
@ -27,6 +27,7 @@ dependencies {
|
|||||||
})
|
})
|
||||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
compile 'com.android.support:appcompat-v7:25.1.0'
|
||||||
compile 'com.android.support:recyclerview-v7:25.1.0'
|
compile 'com.android.support:recyclerview-v7:25.1.0'
|
||||||
|
compile 'com.android.support:support-v13:25.1.0'
|
||||||
compile 'com.android.volley:volley:1.0.0'
|
compile 'com.android.volley:volley:1.0.0'
|
||||||
compile 'com.android.support:design:25.1.0'
|
compile 'com.android.support:design:25.1.0'
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
/** Android Studio complains about built-in assertions so here's this is an alternative. */
|
/** Android Studio complains about built-in assertions so this is an alternative. */
|
||||||
class Assert {
|
class Assert {
|
||||||
private static boolean ENABLED = BuildConfig.DEBUG;
|
private static boolean ENABLED = BuildConfig.DEBUG;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import android.content.DialogInterface;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -42,20 +43,29 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v13.view.inputmethod.EditorInfoCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.InputType;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -74,6 +84,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -87,6 +98,7 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB
|
private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB
|
||||||
private static final int MEDIA_PICK_RESULT = 1;
|
private static final int MEDIA_PICK_RESULT = 1;
|
||||||
private static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
|
private static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
|
||||||
|
private static final int MEDIA_SIZE_UNKNOWN = -1;
|
||||||
|
|
||||||
private String inReplyToId;
|
private String inReplyToId;
|
||||||
private String domain;
|
private String domain;
|
||||||
@ -102,6 +114,9 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
private boolean statusHideText; //
|
private boolean statusHideText; //
|
||||||
private View contentWarningBar;
|
private View contentWarningBar;
|
||||||
private boolean statusAlreadyInFlight; // to prevent duplicate sends by mashing the send button
|
private boolean statusAlreadyInFlight; // to prevent duplicate sends by mashing the send button
|
||||||
|
private InputContentInfoCompat currentInputContentInfo;
|
||||||
|
private int currentFlags;
|
||||||
|
private ProgressDialog finishingUploadDialog;
|
||||||
|
|
||||||
private static class QueuedMedia {
|
private static class QueuedMedia {
|
||||||
enum Type {
|
enum Type {
|
||||||
@ -312,6 +327,13 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
statusHideText = savedInstanceState.getBoolean("statusHideText");
|
statusHideText = savedInstanceState.getBoolean("statusHideText");
|
||||||
// Keep these until everything needed to put them in the queue is finished initializing.
|
// Keep these until everything needed to put them in the queue is finished initializing.
|
||||||
savedMediaQueued = savedInstanceState.getParcelableArrayList("savedMediaQueued");
|
savedMediaQueued = savedInstanceState.getParcelableArrayList("savedMediaQueued");
|
||||||
|
// These are for restoring an in-progress commit content operation.
|
||||||
|
InputContentInfoCompat previousInputContentInfo = InputContentInfoCompat.wrap(
|
||||||
|
savedInstanceState.getParcelable("commitContentInputContentInfo"));
|
||||||
|
int previousFlags = savedInstanceState.getInt("commitContentFlags");
|
||||||
|
if (previousInputContentInfo != null) {
|
||||||
|
onCommitContentInternal(previousInputContentInfo, previousFlags);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showMarkSensitive = false;
|
showMarkSensitive = false;
|
||||||
statusVisibility = preferences.getString("rememberedVisibility", "public");
|
statusVisibility = preferences.getString("rememberedVisibility", "public");
|
||||||
@ -329,7 +351,12 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
domain = preferences.getString("domain", null);
|
domain = preferences.getString("domain", null);
|
||||||
accessToken = preferences.getString("accessToken", null);
|
accessToken = preferences.getString("accessToken", null);
|
||||||
|
|
||||||
textEditor = (EditText) findViewById(R.id.field_status);
|
textEditor = createEditText(null); // new String[] { "image/gif", "image/webp" }
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
textEditor.onRestoreInstanceState(savedInstanceState.getParcelable("textEditorState"));
|
||||||
|
}
|
||||||
|
RelativeLayout editArea = (RelativeLayout) findViewById(R.id.compose_edit_area);
|
||||||
|
editArea.addView(textEditor);
|
||||||
final TextView charactersLeft = (TextView) findViewById(R.id.characters_left);
|
final TextView charactersLeft = (TextView) findViewById(R.id.characters_left);
|
||||||
final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
|
final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
|
||||||
TextWatcher textEditorWatcher = new TextWatcher() {
|
TextWatcher textEditorWatcher = new TextWatcher() {
|
||||||
@ -457,6 +484,14 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
outState.putString("statusVisibility", statusVisibility);
|
outState.putString("statusVisibility", statusVisibility);
|
||||||
outState.putBoolean("statusMarkSensitive", statusMarkSensitive);
|
outState.putBoolean("statusMarkSensitive", statusMarkSensitive);
|
||||||
outState.putBoolean("statusHideText", statusHideText);
|
outState.putBoolean("statusHideText", statusHideText);
|
||||||
|
outState.putParcelable("textEditorState", textEditor.onSaveInstanceState());
|
||||||
|
if (currentInputContentInfo != null) {
|
||||||
|
outState.putParcelable("commitContentInputContentInfo",
|
||||||
|
(Parcelable) currentInputContentInfo.unwrap());
|
||||||
|
outState.putInt("commitContentFlags", currentFlags);
|
||||||
|
}
|
||||||
|
currentInputContentInfo = null;
|
||||||
|
currentFlags = 0;
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,6 +511,101 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
VolleySingleton.getInstance(this).cancelAll(TAG);
|
VolleySingleton.getInstance(this).cancelAll(TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EditText createEditText(String[] contentMimeTypes) {
|
||||||
|
final String[] mimeTypes;
|
||||||
|
if (contentMimeTypes == null || contentMimeTypes.length == 0) {
|
||||||
|
mimeTypes = new String[0];
|
||||||
|
} else {
|
||||||
|
mimeTypes = Arrays.copyOf(contentMimeTypes, contentMimeTypes.length);
|
||||||
|
}
|
||||||
|
EditText editText = new EditText(this) {
|
||||||
|
@Override
|
||||||
|
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
||||||
|
final InputConnection ic = super.onCreateInputConnection(editorInfo);
|
||||||
|
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
|
||||||
|
final InputConnectionCompat.OnCommitContentListener callback =
|
||||||
|
new InputConnectionCompat.OnCommitContentListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
|
||||||
|
int flags, Bundle opts) {
|
||||||
|
return ComposeActivity.this.onCommitContent(inputContentInfo, flags,
|
||||||
|
mimeTypes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return InputConnectionCompat.createWrapper(ic, editorInfo, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
|
editText.setLayoutParams(layoutParams);
|
||||||
|
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||||
|
editText.setEms(10);
|
||||||
|
editText.setGravity(Gravity.START | Gravity.TOP);
|
||||||
|
editText.setHint(R.string.hint_compose);
|
||||||
|
return editText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
||||||
|
String[] mimeTypes) {
|
||||||
|
try {
|
||||||
|
if (currentInputContentInfo != null) {
|
||||||
|
currentInputContentInfo.releasePermission();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "InputContentInfoCompat#releasePermission() failed." + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
currentInputContentInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the returned content's type is actually in the list of MIME types requested.
|
||||||
|
boolean supported = false;
|
||||||
|
for (final String mimeType : mimeTypes) {
|
||||||
|
if (inputContentInfo.getDescription().hasMimeType(mimeType)) {
|
||||||
|
supported = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return supported && onCommitContentInternal(inputContentInfo, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onCommitContentInternal(InputContentInfoCompat inputContentInfo, int flags) {
|
||||||
|
if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
|
||||||
|
try {
|
||||||
|
inputContentInfo.requestPermission();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "InputContentInfoCompat#requestPermission() failed." + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the file size before putting handing it off to be put in the queue.
|
||||||
|
Uri uri = inputContentInfo.getContentUri();
|
||||||
|
long mediaSize;
|
||||||
|
AssetFileDescriptor descriptor = null;
|
||||||
|
try {
|
||||||
|
descriptor = getContentResolver().openAssetFileDescriptor(uri, "r");
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// Eat this exception, having the descriptor be null is sufficient.
|
||||||
|
}
|
||||||
|
if (descriptor != null) {
|
||||||
|
mediaSize = descriptor.getLength();
|
||||||
|
try {
|
||||||
|
descriptor.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Just eat this exception.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mediaSize = MEDIA_SIZE_UNKNOWN;
|
||||||
|
}
|
||||||
|
pickMedia(uri, mediaSize);
|
||||||
|
|
||||||
|
currentInputContentInfo = inputContentInfo;
|
||||||
|
currentFlags = flags;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void sendStatus(String content, String visibility, boolean sensitive,
|
private void sendStatus(String content, String visibility, boolean sensitive,
|
||||||
String spoilerText) {
|
String spoilerText) {
|
||||||
String endpoint = getString(R.string.endpoint_status);
|
String endpoint = getString(R.string.endpoint_status);
|
||||||
@ -535,7 +665,7 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
|
|
||||||
private void readyStatus(final String content, final String visibility, final boolean sensitive,
|
private void readyStatus(final String content, final String visibility, final boolean sensitive,
|
||||||
final String spoilerText) {
|
final String spoilerText) {
|
||||||
final ProgressDialog dialog = ProgressDialog.show(
|
finishingUploadDialog = ProgressDialog.show(
|
||||||
this, getString(R.string.dialog_title_finishing_media_upload),
|
this, getString(R.string.dialog_title_finishing_media_upload),
|
||||||
getString(R.string.dialog_message_uploading_media), true, true);
|
getString(R.string.dialog_message_uploading_media), true, true);
|
||||||
final AsyncTask<Void, Void, Boolean> waitForMediaTask =
|
final AsyncTask<Void, Void, Boolean> waitForMediaTask =
|
||||||
@ -553,7 +683,8 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean successful) {
|
protected void onPostExecute(Boolean successful) {
|
||||||
super.onPostExecute(successful);
|
super.onPostExecute(successful);
|
||||||
dialog.dismiss();
|
finishingUploadDialog.dismiss();
|
||||||
|
finishingUploadDialog = null;
|
||||||
if (successful) {
|
if (successful) {
|
||||||
sendStatus(content, visibility, sensitive, spoilerText);
|
sendStatus(content, visibility, sensitive, spoilerText);
|
||||||
} else {
|
} else {
|
||||||
@ -568,7 +699,7 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
super.onCancelled();
|
super.onCancelled();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
finishingUploadDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
/* Generating an interrupt by passing true here is important because an interrupt
|
/* Generating an interrupt by passing true here is important because an interrupt
|
||||||
@ -848,6 +979,9 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
|
|
||||||
private void onUploadFailure(QueuedMedia item) {
|
private void onUploadFailure(QueuedMedia item) {
|
||||||
displayTransientError(R.string.error_media_upload_sending);
|
displayTransientError(R.string.error_media_upload_sending);
|
||||||
|
if (finishingUploadDialog != null) {
|
||||||
|
finishingUploadDialog.cancel();
|
||||||
|
}
|
||||||
removeMediaFromQueue(item);
|
removeMediaFromQueue(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,69 +1001,78 @@ public class ComposeActivity extends BaseActivity {
|
|||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if (requestCode == MEDIA_PICK_RESULT && resultCode == RESULT_OK && data != null) {
|
if (requestCode == MEDIA_PICK_RESULT && resultCode == RESULT_OK && data != null) {
|
||||||
Uri uri = data.getData();
|
Uri uri = data.getData();
|
||||||
ContentResolver contentResolver = getContentResolver();
|
long mediaSize;
|
||||||
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
||||||
if (cursor == null) {
|
if (cursor != null) {
|
||||||
displayTransientError(R.string.error_media_upload_opening);
|
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
||||||
return;
|
cursor.moveToFirst();
|
||||||
}
|
mediaSize = cursor.getLong(sizeIndex);
|
||||||
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
cursor.close();
|
||||||
cursor.moveToFirst();
|
|
||||||
long mediaSize = cursor.getLong(sizeIndex);
|
|
||||||
cursor.close();
|
|
||||||
String mimeType = contentResolver.getType(uri);
|
|
||||||
if (mimeType != null) {
|
|
||||||
String topLevelType = mimeType.substring(0, mimeType.indexOf('/'));
|
|
||||||
switch (topLevelType) {
|
|
||||||
case "video": {
|
|
||||||
if (mediaSize > STATUS_MEDIA_SIZE_LIMIT) {
|
|
||||||
displayTransientError(R.string.error_media_upload_size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mediaQueued.size() > 0
|
|
||||||
&& mediaQueued.get(0).type == QueuedMedia.Type.IMAGE) {
|
|
||||||
displayTransientError(R.string.error_media_upload_image_or_video);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
|
||||||
retriever.setDataSource(this, uri);
|
|
||||||
Bitmap source = retriever.getFrameAtTime();
|
|
||||||
Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96);
|
|
||||||
source.recycle();
|
|
||||||
addMediaToQueue(QueuedMedia.Type.VIDEO, bitmap, uri, mediaSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "image": {
|
|
||||||
InputStream stream;
|
|
||||||
try {
|
|
||||||
stream = contentResolver.openInputStream(uri);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
displayTransientError(R.string.error_media_upload_opening);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bitmap source = BitmapFactory.decodeStream(stream);
|
|
||||||
Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96);
|
|
||||||
source.recycle();
|
|
||||||
try {
|
|
||||||
if (stream != null) {
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
bitmap.recycle();
|
|
||||||
displayTransientError(R.string.error_media_upload_opening);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addMediaToQueue(QueuedMedia.Type.IMAGE, bitmap, uri, mediaSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
displayTransientError(R.string.error_media_upload_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
displayTransientError(R.string.error_media_upload_type);
|
mediaSize = MEDIA_SIZE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
pickMedia(uri, mediaSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickMedia(Uri uri, long mediaSize) {
|
||||||
|
ContentResolver contentResolver = getContentResolver();
|
||||||
|
if (mediaSize == MEDIA_SIZE_UNKNOWN) {
|
||||||
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String mimeType = contentResolver.getType(uri);
|
||||||
|
if (mimeType != null) {
|
||||||
|
String topLevelType = mimeType.substring(0, mimeType.indexOf('/'));
|
||||||
|
switch (topLevelType) {
|
||||||
|
case "video": {
|
||||||
|
if (mediaSize > STATUS_MEDIA_SIZE_LIMIT) {
|
||||||
|
displayTransientError(R.string.error_media_upload_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mediaQueued.size() > 0
|
||||||
|
&& mediaQueued.get(0).type == QueuedMedia.Type.IMAGE) {
|
||||||
|
displayTransientError(R.string.error_media_upload_image_or_video);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||||
|
retriever.setDataSource(this, uri);
|
||||||
|
Bitmap source = retriever.getFrameAtTime();
|
||||||
|
Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96);
|
||||||
|
source.recycle();
|
||||||
|
addMediaToQueue(QueuedMedia.Type.VIDEO, bitmap, uri, mediaSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "image": {
|
||||||
|
InputStream stream;
|
||||||
|
try {
|
||||||
|
stream = contentResolver.openInputStream(uri);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bitmap source = BitmapFactory.decodeStream(stream);
|
||||||
|
Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, 96, 96);
|
||||||
|
source.recycle();
|
||||||
|
try {
|
||||||
|
if (stream != null) {
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
bitmap.recycle();
|
||||||
|
displayTransientError(R.string.error_media_upload_opening);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addMediaToQueue(QueuedMedia.Type.IMAGE, bitmap, uri, mediaSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
displayTransientError(R.string.error_media_upload_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
displayTransientError(R.string.error_media_upload_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class HtmlUtils {
|
|||||||
return s.subSequence(0, i + 1);
|
return s.subSequence(0, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
static Spanned fromHtml(String html) {
|
static Spanned fromHtml(String html) {
|
||||||
Spanned result;
|
Spanned result;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
@ -40,6 +41,7 @@ class HtmlUtils {
|
|||||||
return (Spanned) trimTrailingWhitespace(result);
|
return (Spanned) trimTrailingWhitespace(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
static String toHtml(Spanned text) {
|
static String toHtml(Spanned text) {
|
||||||
String result;
|
String result;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
@ -295,6 +295,10 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupButtons(final StatusActionListener listener, final String accountId) {
|
private void setupButtons(final StatusActionListener listener, final String accountId) {
|
||||||
|
/* Originally position was passed through to all these listeners, but it caused several
|
||||||
|
* bugs where other statuses in the list would be removed or added and cause the position
|
||||||
|
* here to become outdated. So, getting the adapter position at the time the listener is
|
||||||
|
* actually called is the appropriate solution. */
|
||||||
avatar.setOnClickListener(new View.OnClickListener() {
|
avatar.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -50,23 +50,18 @@
|
|||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1">
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/compose_edit_area">
|
||||||
|
|
||||||
<EditText
|
<!--An special EditText is created at runtime here, because it has to be a modified
|
||||||
android:layout_width="match_parent"
|
* anonymous class to support image/GIF picking from the soft keyboard.-->
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:inputType="textMultiLine"
|
|
||||||
android:ems="10"
|
|
||||||
android:gravity="top|start"
|
|
||||||
android:id="@+id/field_status"
|
|
||||||
android:hint="@string/hint_compose" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:id="@+id/compose_media_preview_bar"
|
android:id="@+id/compose_media_preview_bar"
|
||||||
android:layout_alignBottom="@id/field_status">
|
android:layout_alignParentBottom="true">
|
||||||
|
|
||||||
<!--This is filled at runtime with ImageView's for each preview in the upload queue.-->
|
<!--This is filled at runtime with ImageView's for each preview in the upload queue.-->
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user