added downscale image
This commit is contained in:
parent
1a997992ca
commit
830ffb7c7a
|
@ -10,17 +10,20 @@ import org.mariotaku.twidere.model.ParcelableStatus
|
||||||
*/
|
*/
|
||||||
fun ParcelableActivity.getActivityStatus(): ParcelableStatus? {
|
fun ParcelableActivity.getActivityStatus(): ParcelableStatus? {
|
||||||
val status: ParcelableStatus
|
val status: ParcelableStatus
|
||||||
if (Activity.Action.MENTION == action) {
|
when (action) {
|
||||||
if (ArrayUtils.isEmpty(target_object_statuses)) return null
|
Activity.Action.MENTION -> {
|
||||||
status = target_object_statuses[0]
|
if (ArrayUtils.isEmpty(target_object_statuses)) return null
|
||||||
} else if (Activity.Action.REPLY == action) {
|
status = target_object_statuses[0]
|
||||||
if (ArrayUtils.isEmpty(target_statuses)) return null
|
}
|
||||||
status = target_statuses[0]
|
Activity.Action.REPLY -> {
|
||||||
} else if (Activity.Action.QUOTE == action) {
|
if (ArrayUtils.isEmpty(target_statuses)) return null
|
||||||
if (ArrayUtils.isEmpty(target_statuses)) return null
|
status = target_statuses[0]
|
||||||
status = target_statuses[0]
|
}
|
||||||
} else {
|
Activity.Action.QUOTE -> {
|
||||||
return null
|
if (ArrayUtils.isEmpty(target_statuses)) return null
|
||||||
|
status = target_statuses[0]
|
||||||
|
}
|
||||||
|
else -> return null
|
||||||
}
|
}
|
||||||
status.account_color = account_color
|
status.account_color = account_color
|
||||||
status.user_color = status_user_color
|
status.user_color = status_user_color
|
||||||
|
|
|
@ -1,615 +0,0 @@
|
||||||
/*
|
|
||||||
* Twidere - Twitter client for Android
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mariotaku.twidere.service;
|
|
||||||
|
|
||||||
import android.app.IntentService;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.WorkerThread;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.support.v4.app.NotificationCompat.Builder;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.twitter.Extractor;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
|
||||||
import org.mariotaku.abstask.library.ManualTaskStarter;
|
|
||||||
import org.mariotaku.microblog.library.MicroBlog;
|
|
||||||
import org.mariotaku.microblog.library.MicroBlogException;
|
|
||||||
import org.mariotaku.microblog.library.twitter.TwitterUpload;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.ErrorInfo;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse.ProcessingInfo;
|
|
||||||
import org.mariotaku.restfu.http.ContentType;
|
|
||||||
import org.mariotaku.restfu.http.mime.Body;
|
|
||||||
import org.mariotaku.restfu.http.mime.FileBody;
|
|
||||||
import org.mariotaku.restfu.http.mime.SimpleBody;
|
|
||||||
import org.mariotaku.sqliteqb.library.Expression;
|
|
||||||
import org.mariotaku.twidere.Constants;
|
|
||||||
import org.mariotaku.twidere.R;
|
|
||||||
import org.mariotaku.twidere.model.Draft;
|
|
||||||
import org.mariotaku.twidere.model.DraftCursorIndices;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableAccount;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableCredentials;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableDirectMessage;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableStatusUpdate;
|
|
||||||
import org.mariotaku.twidere.model.SingleResponse;
|
|
||||||
import org.mariotaku.twidere.model.UserKey;
|
|
||||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra;
|
|
||||||
import org.mariotaku.twidere.model.util.ParcelableAccountUtils;
|
|
||||||
import org.mariotaku.twidere.model.util.ParcelableCredentialsUtils;
|
|
||||||
import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils;
|
|
||||||
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
|
|
||||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask;
|
|
||||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
|
||||||
import org.mariotaku.twidere.util.ContentValuesCreator;
|
|
||||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
|
|
||||||
import org.mariotaku.twidere.util.NotificationManagerWrapper;
|
|
||||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
|
||||||
import org.mariotaku.twidere.util.TwidereValidator;
|
|
||||||
import org.mariotaku.twidere.util.Utils;
|
|
||||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
|
||||||
import org.mariotaku.twidere.util.io.ContentLengthInputStream.ReadListener;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
|
||||||
import edu.tsinghua.hotmobi.model.TimelineType;
|
|
||||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
|
||||||
|
|
||||||
public class BackgroundOperationService extends IntentService implements Constants {
|
|
||||||
|
|
||||||
|
|
||||||
private Handler mHandler;
|
|
||||||
@Inject
|
|
||||||
SharedPreferencesWrapper mPreferences;
|
|
||||||
@Inject
|
|
||||||
AsyncTwitterWrapper mTwitter;
|
|
||||||
@Inject
|
|
||||||
NotificationManagerWrapper mNotificationManager;
|
|
||||||
@Inject
|
|
||||||
TwidereValidator mValidator;
|
|
||||||
@Inject
|
|
||||||
Extractor mExtractor;
|
|
||||||
private static final long BULK_SIZE = 128 * 1024; // 128KiB
|
|
||||||
|
|
||||||
|
|
||||||
public BackgroundOperationService() {
|
|
||||||
super("background_operation");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
GeneralComponentHelper.build(this).inject(this);
|
|
||||||
mHandler = new Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
|
||||||
super.onStartCommand(intent, flags, startId);
|
|
||||||
return START_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showErrorMessage(final CharSequence message, final boolean longMessage) {
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Utils.showErrorMessage(BackgroundOperationService.this, message, longMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showErrorMessage(final int actionRes, final Exception e, final boolean longMessage) {
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Utils.showErrorMessage(BackgroundOperationService.this, actionRes, e, longMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showErrorMessage(final int actionRes, final String message, final boolean longMessage) {
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Utils.showErrorMessage(BackgroundOperationService.this, actionRes, message, longMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showOkMessage(final int messageRes, final boolean longMessage) {
|
|
||||||
showToast(getString(messageRes), longMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showToast(final CharSequence message, final boolean longMessage) {
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(BackgroundOperationService.this, message, longMessage ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHandleIntent(final Intent intent) {
|
|
||||||
if (intent == null) return;
|
|
||||||
final String action = intent.getAction();
|
|
||||||
if (action == null) return;
|
|
||||||
switch (action) {
|
|
||||||
case INTENT_ACTION_UPDATE_STATUS:
|
|
||||||
handleUpdateStatusIntent(intent);
|
|
||||||
break;
|
|
||||||
case INTENT_ACTION_SEND_DIRECT_MESSAGE:
|
|
||||||
handleSendDirectMessageIntent(intent);
|
|
||||||
break;
|
|
||||||
case INTENT_ACTION_DISCARD_DRAFT:
|
|
||||||
handleDiscardDraftIntent(intent);
|
|
||||||
break;
|
|
||||||
case INTENT_ACTION_SEND_DRAFT: {
|
|
||||||
handleSendDraftIntent(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSendDraftIntent(Intent intent) {
|
|
||||||
final Uri uri = intent.getData();
|
|
||||||
if (uri == null) return;
|
|
||||||
mNotificationManager.cancel(uri.toString(), NOTIFICATION_ID_DRAFTS);
|
|
||||||
final long def = -1;
|
|
||||||
final long draftId = NumberUtils.toLong(uri.getLastPathSegment(), def);
|
|
||||||
if (draftId == -1) return;
|
|
||||||
final Expression where = Expression.equals(Drafts._ID, draftId);
|
|
||||||
final ContentResolver cr = getContentResolver();
|
|
||||||
final Cursor c = cr.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.getSQL(), null, null);
|
|
||||||
if (c == null) return;
|
|
||||||
final DraftCursorIndices i = new DraftCursorIndices(c);
|
|
||||||
final Draft item;
|
|
||||||
try {
|
|
||||||
if (!c.moveToFirst()) return;
|
|
||||||
item = i.newObject(c);
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
cr.delete(Drafts.CONTENT_URI, where.getSQL(), null);
|
|
||||||
if (TextUtils.isEmpty(item.action_type)) {
|
|
||||||
item.action_type = Draft.Action.UPDATE_STATUS;
|
|
||||||
}
|
|
||||||
switch (item.action_type) {
|
|
||||||
case Draft.Action.UPDATE_STATUS_COMPAT_1:
|
|
||||||
case Draft.Action.UPDATE_STATUS_COMPAT_2:
|
|
||||||
case Draft.Action.UPDATE_STATUS:
|
|
||||||
case Draft.Action.REPLY:
|
|
||||||
case Draft.Action.QUOTE: {
|
|
||||||
updateStatuses(item.action_type, ParcelableStatusUpdateUtils.fromDraftItem(this, item));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Draft.Action.SEND_DIRECT_MESSAGE_COMPAT:
|
|
||||||
case Draft.Action.SEND_DIRECT_MESSAGE: {
|
|
||||||
String recipientId = null;
|
|
||||||
if (item.action_extras instanceof SendDirectMessageActionExtra) {
|
|
||||||
recipientId = ((SendDirectMessageActionExtra) item.action_extras).getRecipientId();
|
|
||||||
}
|
|
||||||
if (ArrayUtils.isEmpty(item.account_keys) || recipientId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final UserKey accountKey = item.account_keys[0];
|
|
||||||
final String imageUri = ArrayUtils.isEmpty(item.media) ? null : item.media[0].uri;
|
|
||||||
sendMessage(accountKey, recipientId, item.text, imageUri);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDiscardDraftIntent(Intent intent) {
|
|
||||||
final Uri data = intent.getData();
|
|
||||||
if (data == null) return;
|
|
||||||
mNotificationManager.cancel(data.toString(), NOTIFICATION_ID_DRAFTS);
|
|
||||||
final ContentResolver cr = getContentResolver();
|
|
||||||
final long def = -1;
|
|
||||||
final long id = NumberUtils.toLong(data.getLastPathSegment(), def);
|
|
||||||
final Expression where = Expression.equals(Drafts._ID, id);
|
|
||||||
cr.delete(Drafts.CONTENT_URI, where.getSQL(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSendDirectMessageIntent(final Intent intent) {
|
|
||||||
final UserKey accountId = intent.getParcelableExtra(EXTRA_ACCOUNT_KEY);
|
|
||||||
final String recipientId = intent.getStringExtra(EXTRA_RECIPIENT_ID);
|
|
||||||
final String text = intent.getStringExtra(EXTRA_TEXT);
|
|
||||||
final String imageUri = intent.getStringExtra(EXTRA_IMAGE_URI);
|
|
||||||
if (accountId == null || recipientId == null || text == null) return;
|
|
||||||
sendMessage(accountId, recipientId, text, imageUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMessage(@NonNull UserKey accountId, @NonNull String recipientId,
|
|
||||||
@NonNull String text, @Nullable String imageUri) {
|
|
||||||
final String title = getString(R.string.sending_direct_message);
|
|
||||||
final Builder builder = new Builder(this);
|
|
||||||
builder.setSmallIcon(R.drawable.ic_stat_send);
|
|
||||||
builder.setProgress(100, 0, true);
|
|
||||||
builder.setTicker(title);
|
|
||||||
builder.setContentTitle(title);
|
|
||||||
builder.setContentText(text);
|
|
||||||
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
|
|
||||||
builder.setOngoing(true);
|
|
||||||
final Notification notification = builder.build();
|
|
||||||
startForeground(NOTIFICATION_ID_SEND_DIRECT_MESSAGE, notification);
|
|
||||||
final SingleResponse<ParcelableDirectMessage> result = sendDirectMessage(builder, accountId,
|
|
||||||
recipientId, text, imageUri);
|
|
||||||
|
|
||||||
final ContentResolver resolver = getContentResolver();
|
|
||||||
if (result.hasData()) {
|
|
||||||
final ParcelableDirectMessage message = result.getData();
|
|
||||||
final ContentValues values = ContentValuesCreator.createDirectMessage(message);
|
|
||||||
final String deleteWhere = Expression.and(Expression.equalsArgs(DirectMessages.ACCOUNT_KEY),
|
|
||||||
Expression.equalsArgs(DirectMessages.MESSAGE_ID)).getSQL();
|
|
||||||
String[] deleteWhereArgs = {message.account_key.toString(), message.id};
|
|
||||||
resolver.delete(DirectMessages.Outbox.CONTENT_URI, deleteWhere, deleteWhereArgs);
|
|
||||||
resolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
|
|
||||||
showOkMessage(R.string.direct_message_sent, false);
|
|
||||||
} else {
|
|
||||||
final ContentValues values = ContentValuesCreator.createMessageDraft(accountId, recipientId, text, imageUri);
|
|
||||||
resolver.insert(Drafts.CONTENT_URI, values);
|
|
||||||
showErrorMessage(R.string.action_sending_direct_message, result.getException(), true);
|
|
||||||
}
|
|
||||||
stopForeground(false);
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID_SEND_DIRECT_MESSAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleUpdateStatusIntent(final Intent intent) {
|
|
||||||
final ParcelableStatusUpdate status = intent.getParcelableExtra(EXTRA_STATUS);
|
|
||||||
final Parcelable[] status_parcelables = intent.getParcelableArrayExtra(EXTRA_STATUSES);
|
|
||||||
final ParcelableStatusUpdate[] statuses;
|
|
||||||
if (status_parcelables != null) {
|
|
||||||
statuses = new ParcelableStatusUpdate[status_parcelables.length];
|
|
||||||
for (int i = 0, j = status_parcelables.length; i < j; i++) {
|
|
||||||
statuses[i] = (ParcelableStatusUpdate) status_parcelables[i];
|
|
||||||
}
|
|
||||||
} else if (status != null) {
|
|
||||||
statuses = new ParcelableStatusUpdate[1];
|
|
||||||
statuses[0] = status;
|
|
||||||
} else
|
|
||||||
return;
|
|
||||||
@Draft.Action
|
|
||||||
final String actionType = intent.getStringExtra(EXTRA_ACTION);
|
|
||||||
updateStatuses(actionType, statuses);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateStatuses(@Draft.Action final String actionType, final ParcelableStatusUpdate... statuses) {
|
|
||||||
final BackgroundOperationService context = this;
|
|
||||||
final Builder builder = new Builder(context);
|
|
||||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
|
||||||
builder, 0, null));
|
|
||||||
for (final ParcelableStatusUpdate item : statuses) {
|
|
||||||
final UpdateStatusTask task = new UpdateStatusTask(context, new UpdateStatusTask.StateCallback() {
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
@Override
|
|
||||||
public void onStartUploadingMedia() {
|
|
||||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
|
||||||
builder, 0, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
@Override
|
|
||||||
public void onUploadingProgressChanged(int index, long current, long total) {
|
|
||||||
int progress = (int) (current * 100 / total);
|
|
||||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
|
||||||
builder, progress, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
@Override
|
|
||||||
public void onShorteningStatus() {
|
|
||||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
|
||||||
builder, 0, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
@Override
|
|
||||||
public void onUpdatingStatus() {
|
|
||||||
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
|
||||||
builder, 0, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterExecute(Context handler, UpdateStatusTask.UpdateStatusResult result) {
|
|
||||||
boolean failed = false;
|
|
||||||
final UpdateStatusTask.UpdateStatusException exception = result.getException();
|
|
||||||
final MicroBlogException[] exceptions = result.getExceptions();
|
|
||||||
if (exception != null) {
|
|
||||||
Toast.makeText(context, exception.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
failed = true;
|
|
||||||
Log.w(LOGTAG, exception);
|
|
||||||
} else for (MicroBlogException e : exceptions) {
|
|
||||||
if (e != null) {
|
|
||||||
// Show error
|
|
||||||
String errorMessage = Utils.getErrorMessage(context, e);
|
|
||||||
if (TextUtils.isEmpty(errorMessage)) {
|
|
||||||
errorMessage = context.getString(R.string.status_not_updated);
|
|
||||||
}
|
|
||||||
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (failed) {
|
|
||||||
// TODO show draft notification
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, R.string.status_updated, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeExecute() {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
task.setCallback(this);
|
|
||||||
task.setParams(Pair.create(actionType, item));
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ManualTaskStarter.invokeBeforeExecute(task);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final UpdateStatusTask.UpdateStatusResult result = ManualTaskStarter.invokeExecute(task);
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ManualTaskStarter.invokeAfterExecute(task, result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final UpdateStatusTask.UpdateStatusException exception = result.getException();
|
|
||||||
final ParcelableStatus[] updatedStatuses = result.getStatuses();
|
|
||||||
if (exception != null) {
|
|
||||||
Log.w(LOGTAG, exception);
|
|
||||||
} else for (ParcelableStatus status : updatedStatuses) {
|
|
||||||
if (status == null) continue;
|
|
||||||
final TweetEvent event = TweetEvent.create(context, status, TimelineType.OTHER);
|
|
||||||
event.setAction(TweetEvent.Action.TWEET);
|
|
||||||
HotMobiLogger.getInstance(context).log(status.account_key, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mPreferences.getBoolean(KEY_REFRESH_AFTER_TWEET)) {
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mTwitter.refreshAll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
stopForeground(false);
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID_UPDATE_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private SingleResponse<ParcelableDirectMessage> sendDirectMessage(final NotificationCompat.Builder builder,
|
|
||||||
final UserKey accountKey,
|
|
||||||
final String recipientId,
|
|
||||||
final String text,
|
|
||||||
final String imageUri) {
|
|
||||||
final ParcelableCredentials credentials = ParcelableCredentialsUtils.getCredentials(this,
|
|
||||||
accountKey);
|
|
||||||
if (credentials == null) return SingleResponse.Companion.getInstance();
|
|
||||||
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(this, credentials, true, true);
|
|
||||||
final TwitterUpload twitterUpload = MicroBlogAPIFactory.getInstance(this, credentials,
|
|
||||||
true, true, TwitterUpload.class);
|
|
||||||
if (twitter == null || twitterUpload == null) return SingleResponse.Companion.getInstance();
|
|
||||||
try {
|
|
||||||
final ParcelableDirectMessage directMessage;
|
|
||||||
switch (ParcelableAccountUtils.getAccountType(credentials)) {
|
|
||||||
case ParcelableAccount.Type.FANFOU: {
|
|
||||||
if (imageUri != null) {
|
|
||||||
throw new MicroBlogException("Can't send image DM on Fanfou");
|
|
||||||
}
|
|
||||||
final DirectMessage dm = twitter.sendFanfouDirectMessage(recipientId, text);
|
|
||||||
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(dm, accountKey, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (imageUri != null) {
|
|
||||||
final Uri mediaUri = Uri.parse(imageUri);
|
|
||||||
FileBody body = null;
|
|
||||||
try {
|
|
||||||
body = UpdateStatusTask.Companion.getBodyFromMedia(getContentResolver(), mediaUri,
|
|
||||||
new MessageMediaUploadListener(this, mNotificationManager,
|
|
||||||
builder, text), null);
|
|
||||||
final MediaUploadResponse uploadResp = uploadMedia(twitterUpload, body);
|
|
||||||
final DirectMessage response = twitter.sendDirectMessage(recipientId,
|
|
||||||
text, uploadResp.getId());
|
|
||||||
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(response,
|
|
||||||
accountKey, true);
|
|
||||||
} finally {
|
|
||||||
Utils.closeSilently(body);
|
|
||||||
}
|
|
||||||
final String path = Utils.getImagePathFromUri(this, mediaUri);
|
|
||||||
if (path != null) {
|
|
||||||
final File file = new File(path);
|
|
||||||
if (!file.delete()) {
|
|
||||||
Log.d(LOGTAG, String.format("unable to delete %s", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final DirectMessage response = twitter.sendDirectMessage(recipientId, text);
|
|
||||||
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(response,
|
|
||||||
accountKey, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Utils.setLastSeen(this, new UserKey(recipientId, accountKey.getHost()),
|
|
||||||
System.currentTimeMillis());
|
|
||||||
|
|
||||||
return SingleResponse.Companion.getInstance(directMessage);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
return SingleResponse.Companion.getInstance(e);
|
|
||||||
} catch (final MicroBlogException e) {
|
|
||||||
return SingleResponse.Companion.getInstance(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private MediaUploadResponse uploadMedia(final TwitterUpload upload, Body body) throws IOException, MicroBlogException {
|
|
||||||
final String mediaType = body.contentType().getContentType();
|
|
||||||
final long length = body.length();
|
|
||||||
final InputStream stream = body.stream();
|
|
||||||
MediaUploadResponse response = upload.initUploadMedia(mediaType, length, null);
|
|
||||||
final int segments = length == 0 ? 0 : (int) (length / BULK_SIZE + 1);
|
|
||||||
for (int segmentIndex = 0; segmentIndex < segments; segmentIndex++) {
|
|
||||||
final int currentBulkSize = (int) Math.min(BULK_SIZE, length - segmentIndex * BULK_SIZE);
|
|
||||||
final SimpleBody bulk = new SimpleBody(ContentType.OCTET_STREAM, null, currentBulkSize,
|
|
||||||
stream);
|
|
||||||
upload.appendUploadMedia(response.getId(), segmentIndex, bulk);
|
|
||||||
}
|
|
||||||
response = upload.finalizeUploadMedia(response.getId());
|
|
||||||
for (ProcessingInfo info = response.getProcessingInfo(); shouldWaitForProcess(info); info = response.getProcessingInfo()) {
|
|
||||||
final long checkAfterSecs = info.getCheckAfterSecs();
|
|
||||||
if (checkAfterSecs <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Thread.sleep(TimeUnit.SECONDS.toMillis(checkAfterSecs));
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
response = upload.getUploadMediaStatus(response.getId());
|
|
||||||
}
|
|
||||||
ProcessingInfo info = response.getProcessingInfo();
|
|
||||||
if (info != null && ProcessingInfo.State.FAILED.equals(info.getState())) {
|
|
||||||
final MicroBlogException exception = new MicroBlogException();
|
|
||||||
ErrorInfo errorInfo = info.getError();
|
|
||||||
if (errorInfo != null) {
|
|
||||||
exception.setErrors(new ErrorInfo[]{errorInfo});
|
|
||||||
}
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldWaitForProcess(ProcessingInfo info) {
|
|
||||||
if (info == null) return false;
|
|
||||||
switch (info.getState()) {
|
|
||||||
case ProcessingInfo.State.PENDING:
|
|
||||||
case ProcessingInfo.State.IN_PROGRESS:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Notification updateSendDirectMessageNotification(final Context context,
|
|
||||||
final NotificationCompat.Builder builder,
|
|
||||||
final int progress, final String message) {
|
|
||||||
builder.setContentTitle(context.getString(R.string.sending_direct_message));
|
|
||||||
if (message != null) {
|
|
||||||
builder.setContentText(message);
|
|
||||||
}
|
|
||||||
builder.setSmallIcon(R.drawable.ic_stat_send);
|
|
||||||
builder.setProgress(100, progress, progress >= 100 || progress <= 0);
|
|
||||||
builder.setOngoing(true);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Notification updateUpdateStatusNotification(final Context context,
|
|
||||||
final NotificationCompat.Builder builder,
|
|
||||||
final int progress,
|
|
||||||
final ParcelableStatusUpdate status) {
|
|
||||||
builder.setContentTitle(context.getString(R.string.updating_status_notification));
|
|
||||||
if (status != null) {
|
|
||||||
builder.setContentText(status.text);
|
|
||||||
}
|
|
||||||
builder.setSmallIcon(R.drawable.ic_stat_send);
|
|
||||||
builder.setProgress(100, progress, progress >= 100 || progress <= 0);
|
|
||||||
builder.setOngoing(true);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateStatusesAsync(Context context, @Draft.Action final String action,
|
|
||||||
final ParcelableStatusUpdate... statuses) {
|
|
||||||
final Intent intent = new Intent(context, BackgroundOperationService.class);
|
|
||||||
intent.setAction(INTENT_ACTION_UPDATE_STATUS);
|
|
||||||
intent.putExtra(EXTRA_STATUSES, statuses);
|
|
||||||
intent.putExtra(EXTRA_ACTION, action);
|
|
||||||
context.startService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MessageMediaUploadListener implements ReadListener {
|
|
||||||
private final Context context;
|
|
||||||
private final NotificationManagerWrapper manager;
|
|
||||||
|
|
||||||
int percent;
|
|
||||||
|
|
||||||
private final Builder builder;
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
MessageMediaUploadListener(final Context context, final NotificationManagerWrapper manager,
|
|
||||||
final NotificationCompat.Builder builder, final String message) {
|
|
||||||
this.context = context;
|
|
||||||
this.manager = manager;
|
|
||||||
this.builder = builder;
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRead(final long length, final long position) {
|
|
||||||
final int percent = length > 0 ? (int) (position * 100 / length) : 0;
|
|
||||||
if (this.percent != percent) {
|
|
||||||
manager.notify(NOTIFICATION_ID_SEND_DIRECT_MESSAGE,
|
|
||||||
updateSendDirectMessageNotification(context, builder, percent, message));
|
|
||||||
}
|
|
||||||
this.percent = percent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,12 +5,12 @@ import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Point
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.annotation.UiThread
|
import android.support.annotation.UiThread
|
||||||
import android.support.annotation.WorkerThread
|
import android.support.annotation.WorkerThread
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import android.util.Size
|
|
||||||
import org.apache.commons.lang3.ArrayUtils
|
import org.apache.commons.lang3.ArrayUtils
|
||||||
import org.apache.commons.lang3.math.NumberUtils
|
import org.apache.commons.lang3.math.NumberUtils
|
||||||
import org.mariotaku.abstask.library.AbstractTask
|
import org.mariotaku.abstask.library.AbstractTask
|
||||||
|
@ -228,8 +228,13 @@ class UpdateStatusTask(internal val context: Context, internal val stateCallback
|
||||||
result.exceptions[i] = MicroBlogException(
|
result.exceptions[i] = MicroBlogException(
|
||||||
context.getString(R.string.error_too_many_photos_fanfou))
|
context.getString(R.string.error_too_many_photos_fanfou))
|
||||||
} else {
|
} else {
|
||||||
|
val sizeLimit = Point(2048, 1536)
|
||||||
body = getBodyFromMedia(context.contentResolver,
|
body = getBodyFromMedia(context.contentResolver,
|
||||||
Uri.parse(statusUpdate.media[0].uri), ContentLengthInputStream.ReadListener { length, position -> stateCallback.onUploadingProgressChanged(-1, position, length) })
|
Uri.parse(statusUpdate.media[0].uri),
|
||||||
|
sizeLimit,
|
||||||
|
ContentLengthInputStream.ReadListener { length, position ->
|
||||||
|
stateCallback.onUploadingProgressChanged(-1, position, length)
|
||||||
|
})
|
||||||
val photoUpdate = PhotoStatusUpdate(body,
|
val photoUpdate = PhotoStatusUpdate(body,
|
||||||
pendingUpdate.overrideTexts[i])
|
pendingUpdate.overrideTexts[i])
|
||||||
val requestResult = microBlog.uploadPhoto(photoUpdate)
|
val requestResult = microBlog.uploadPhoto(photoUpdate)
|
||||||
|
@ -413,9 +418,11 @@ class UpdateStatusTask(internal val context: Context, internal val stateCallback
|
||||||
//noinspection TryWithIdenticalCatches
|
//noinspection TryWithIdenticalCatches
|
||||||
var body: Body? = null
|
var body: Body? = null
|
||||||
try {
|
try {
|
||||||
body = getBodyFromMedia(context.contentResolver, Uri.parse(media.uri), ContentLengthInputStream.ReadListener { length, position ->
|
val sizeLimit = Point(2048, 1536)
|
||||||
stateCallback.onUploadingProgressChanged(index, position, length)
|
body = getBodyFromMedia(context.contentResolver, Uri.parse(media.uri), sizeLimit,
|
||||||
})
|
ContentLengthInputStream.ReadListener { length, position ->
|
||||||
|
stateCallback.onUploadingProgressChanged(index, position, length)
|
||||||
|
})
|
||||||
if (chucked) {
|
if (chucked) {
|
||||||
resp = uploadMediaChucked(upload, body, ownerIds)
|
resp = uploadMediaChucked(upload, body, ownerIds)
|
||||||
} else {
|
} else {
|
||||||
|
@ -652,8 +659,8 @@ class UpdateStatusTask(internal val context: Context, internal val stateCallback
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getBodyFromMedia(resolver: ContentResolver,
|
fun getBodyFromMedia(resolver: ContentResolver,
|
||||||
mediaUri: Uri,
|
mediaUri: Uri,
|
||||||
readListener: ContentLengthInputStream.ReadListener,
|
sizeLimit: Point? = null,
|
||||||
sizeLimit: Size? = null): FileBody {
|
readListener: ContentLengthInputStream.ReadListener): FileBody {
|
||||||
val mediaType = resolver.getType(mediaUri)
|
val mediaType = resolver.getType(mediaUri)
|
||||||
val st = resolver.openInputStream(mediaUri) ?: throw FileNotFoundException(mediaUri.toString())
|
val st = resolver.openInputStream(mediaUri) ?: throw FileNotFoundException(mediaUri.toString())
|
||||||
val cis: ContentLengthInputStream
|
val cis: ContentLengthInputStream
|
||||||
|
@ -662,6 +669,8 @@ class UpdateStatusTask(internal val context: Context, internal val stateCallback
|
||||||
val o = BitmapFactory.Options()
|
val o = BitmapFactory.Options()
|
||||||
o.inJustDecodeBounds = true
|
o.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeStream(st, null, o)
|
BitmapFactory.decodeStream(st, null, o)
|
||||||
|
o.inSampleSize = Utils.calculateInSampleSize(o.outWidth, o.outHeight,
|
||||||
|
sizeLimit.x, sizeLimit.y)
|
||||||
o.inJustDecodeBounds = false
|
o.inJustDecodeBounds = false
|
||||||
val bitmap = BitmapFactory.decodeStream(st, null, o)
|
val bitmap = BitmapFactory.decodeStream(st, null, o)
|
||||||
val os = DirectByteArrayOutputStream()
|
val os = DirectByteArrayOutputStream()
|
||||||
|
|
|
@ -164,7 +164,8 @@ public final class ContentValuesCreator implements TwidereConstants {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static ContentValues createActivity(final ParcelableActivity activity,
|
public static ContentValues createActivity(final ParcelableActivity activity,
|
||||||
ParcelableCredentials credentials, UserColorNameManager manager) {
|
final ParcelableCredentials credentials,
|
||||||
|
final UserColorNameManager manager) {
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
final ParcelableStatus status = ParcelableActivityExtensionsKt.getActivityStatus(activity);
|
final ParcelableStatus status = ParcelableActivityExtensionsKt.getActivityStatus(activity);
|
||||||
|
|
||||||
|
|
|
@ -1155,7 +1155,7 @@ public final class Utils implements Constants {
|
||||||
return context.getString(R.string.error_message_with_action, action, message);
|
return context.getString(R.string.error_message_with_action, action, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getErrorMessage(final Context context, final CharSequence action, final Throwable t) {
|
public static String getErrorMessage(final Context context, final CharSequence action, @Nullable final Throwable t) {
|
||||||
if (context == null) return null;
|
if (context == null) return null;
|
||||||
if (t instanceof MicroBlogException)
|
if (t instanceof MicroBlogException)
|
||||||
return getTwitterErrorMessage(context, action, (MicroBlogException) t);
|
return getTwitterErrorMessage(context, action, (MicroBlogException) t);
|
||||||
|
@ -1786,7 +1786,7 @@ public final class Utils implements Constants {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showErrorMessage(final Context context, final CharSequence action,
|
public static void showErrorMessage(final Context context, final CharSequence action,
|
||||||
final Throwable t, final boolean longMessage) {
|
@Nullable final Throwable t, final boolean longMessage) {
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
if (t instanceof MicroBlogException) {
|
if (t instanceof MicroBlogException) {
|
||||||
showTwitterErrorMessage(context, action, (MicroBlogException) t, longMessage);
|
showTwitterErrorMessage(context, action, (MicroBlogException) t, longMessage);
|
||||||
|
@ -1801,7 +1801,8 @@ public final class Utils implements Constants {
|
||||||
showErrorMessage(context, context.getString(actionRes), desc, longMessage);
|
showErrorMessage(context, context.getString(actionRes), desc, longMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showErrorMessage(final Context context, final int action, final Throwable t,
|
public static void showErrorMessage(final Context context, final int action,
|
||||||
|
@Nullable final Throwable t,
|
||||||
final boolean long_message) {
|
final boolean long_message) {
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
showErrorMessage(context, context.getString(action), t, long_message);
|
showErrorMessage(context, context.getString(action), t, long_message);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.mariotaku.ktextension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 16/7/30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun String.toLong(def: Long): Long {
|
||||||
|
try {
|
||||||
|
return toLong()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
}
|
|
@ -283,7 +283,12 @@ class ParcelableActivitiesAdapter(
|
||||||
if (followingOnly && !activity.status_user_following) return ITEM_VIEW_TYPE_EMPTY
|
if (followingOnly && !activity.status_user_following) return ITEM_VIEW_TYPE_EMPTY
|
||||||
return ITEM_VIEW_TYPE_STATUS
|
return ITEM_VIEW_TYPE_STATUS
|
||||||
}
|
}
|
||||||
Activity.Action.FOLLOW, Activity.Action.FAVORITE, Activity.Action.RETWEET, Activity.Action.FAVORITED_RETWEET, Activity.Action.RETWEETED_RETWEET, Activity.Action.RETWEETED_MENTION, Activity.Action.FAVORITED_MENTION, Activity.Action.LIST_CREATED, Activity.Action.LIST_MEMBER_ADDED, Activity.Action.MEDIA_TAGGED, Activity.Action.RETWEETED_MEDIA_TAGGED, Activity.Action.FAVORITED_MEDIA_TAGGED, Activity.Action.JOINED_TWITTER -> {
|
Activity.Action.FOLLOW, Activity.Action.FAVORITE, Activity.Action.RETWEET,
|
||||||
|
Activity.Action.FAVORITED_RETWEET, Activity.Action.RETWEETED_RETWEET,
|
||||||
|
Activity.Action.RETWEETED_MENTION, Activity.Action.FAVORITED_MENTION,
|
||||||
|
Activity.Action.LIST_CREATED, Activity.Action.LIST_MEMBER_ADDED,
|
||||||
|
Activity.Action.MEDIA_TAGGED, Activity.Action.RETWEETED_MEDIA_TAGGED,
|
||||||
|
Activity.Action.FAVORITED_MEDIA_TAGGED, Activity.Action.JOINED_TWITTER -> {
|
||||||
if (mentionsOnly) return ITEM_VIEW_TYPE_EMPTY
|
if (mentionsOnly) return ITEM_VIEW_TYPE_EMPTY
|
||||||
ParcelableActivityUtils.initAfterFilteredSourceIds(activity, filteredUserIds, followingOnly)
|
ParcelableActivityUtils.initAfterFilteredSourceIds(activity, filteredUserIds, followingOnly)
|
||||||
if (ArrayUtils.isEmpty(activity.after_filtered_source_ids)) {
|
if (ArrayUtils.isEmpty(activity.after_filtered_source_ids)) {
|
||||||
|
|
|
@ -0,0 +1,518 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.service
|
||||||
|
|
||||||
|
import android.app.IntentService
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Handler
|
||||||
|
import android.support.annotation.WorkerThread
|
||||||
|
import android.support.v4.app.NotificationCompat
|
||||||
|
import android.support.v4.app.NotificationCompat.Builder
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.Pair
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.twitter.Extractor
|
||||||
|
import edu.tsinghua.hotmobi.HotMobiLogger
|
||||||
|
import edu.tsinghua.hotmobi.model.TimelineType
|
||||||
|
import edu.tsinghua.hotmobi.model.TweetEvent
|
||||||
|
import org.apache.commons.lang3.ArrayUtils
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils
|
||||||
|
import org.mariotaku.abstask.library.ManualTaskStarter
|
||||||
|
import org.mariotaku.ktextension.asTypedArray
|
||||||
|
import org.mariotaku.ktextension.toLong
|
||||||
|
import org.mariotaku.microblog.library.MicroBlogException
|
||||||
|
import org.mariotaku.microblog.library.twitter.TwitterUpload
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse.ProcessingInfo
|
||||||
|
import org.mariotaku.restfu.http.ContentType
|
||||||
|
import org.mariotaku.restfu.http.mime.Body
|
||||||
|
import org.mariotaku.restfu.http.mime.FileBody
|
||||||
|
import org.mariotaku.restfu.http.mime.SimpleBody
|
||||||
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
|
import org.mariotaku.twidere.Constants
|
||||||
|
import org.mariotaku.twidere.R
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.*
|
||||||
|
import org.mariotaku.twidere.model.*
|
||||||
|
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableAccountUtils
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableCredentialsUtils
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
||||||
|
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||||
|
import org.mariotaku.twidere.util.*
|
||||||
|
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||||
|
import org.mariotaku.twidere.util.io.ContentLengthInputStream.ReadListener
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class BackgroundOperationService : IntentService("background_operation"), Constants {
|
||||||
|
|
||||||
|
|
||||||
|
private var handler: Handler? = null
|
||||||
|
@Inject
|
||||||
|
lateinit var preferences: SharedPreferencesWrapper
|
||||||
|
@Inject
|
||||||
|
lateinit var twitterWrapper: AsyncTwitterWrapper
|
||||||
|
@Inject
|
||||||
|
lateinit var notificationManager: NotificationManagerWrapper
|
||||||
|
@Inject
|
||||||
|
lateinit var validator: TwidereValidator
|
||||||
|
@Inject
|
||||||
|
lateinit var extractor: Extractor
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
GeneralComponentHelper.build(this).inject(this)
|
||||||
|
handler = Handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
|
super.onStartCommand(intent, flags, startId)
|
||||||
|
return Service.START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showErrorMessage(message: CharSequence, longMessage: Boolean) {
|
||||||
|
handler!!.post { Utils.showErrorMessage(this@BackgroundOperationService, message, longMessage) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showErrorMessage(actionRes: Int, e: Exception?, longMessage: Boolean) {
|
||||||
|
handler!!.post { Utils.showErrorMessage(this@BackgroundOperationService, actionRes, e, longMessage) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showErrorMessage(actionRes: Int, message: String, longMessage: Boolean) {
|
||||||
|
handler!!.post { Utils.showErrorMessage(this@BackgroundOperationService, actionRes, message, longMessage) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showOkMessage(messageRes: Int, longMessage: Boolean) {
|
||||||
|
showToast(getString(messageRes), longMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showToast(message: CharSequence, longMessage: Boolean) {
|
||||||
|
handler!!.post { Toast.makeText(this@BackgroundOperationService, message, if (longMessage) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHandleIntent(intent: Intent?) {
|
||||||
|
if (intent == null) return
|
||||||
|
val action = intent.action ?: return
|
||||||
|
when (action) {
|
||||||
|
INTENT_ACTION_UPDATE_STATUS -> {
|
||||||
|
handleUpdateStatusIntent(intent)
|
||||||
|
}
|
||||||
|
INTENT_ACTION_SEND_DIRECT_MESSAGE -> {
|
||||||
|
handleSendDirectMessageIntent(intent)
|
||||||
|
}
|
||||||
|
INTENT_ACTION_DISCARD_DRAFT -> {
|
||||||
|
handleDiscardDraftIntent(intent)
|
||||||
|
}
|
||||||
|
INTENT_ACTION_SEND_DRAFT -> {
|
||||||
|
handleSendDraftIntent(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSendDraftIntent(intent: Intent) {
|
||||||
|
val uri = intent.data ?: return
|
||||||
|
notificationManager.cancel(uri.toString(), NOTIFICATION_ID_DRAFTS)
|
||||||
|
val draftId = uri.lastPathSegment.toLong(-1)
|
||||||
|
if (draftId == -1L) return
|
||||||
|
val where = Expression.equals(Drafts._ID, draftId)
|
||||||
|
val cr = contentResolver
|
||||||
|
val c = cr.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.sql, null, null) ?: return
|
||||||
|
val i = DraftCursorIndices(c)
|
||||||
|
val item: Draft
|
||||||
|
try {
|
||||||
|
if (!c.moveToFirst()) return
|
||||||
|
item = i.newObject(c)
|
||||||
|
} finally {
|
||||||
|
c.close()
|
||||||
|
}
|
||||||
|
cr.delete(Drafts.CONTENT_URI, where.sql, null)
|
||||||
|
if (TextUtils.isEmpty(item.action_type)) {
|
||||||
|
item.action_type = Draft.Action.UPDATE_STATUS
|
||||||
|
}
|
||||||
|
when (item.action_type) {
|
||||||
|
Draft.Action.UPDATE_STATUS_COMPAT_1, Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.UPDATE_STATUS, Draft.Action.REPLY, Draft.Action.QUOTE -> {
|
||||||
|
updateStatuses(item.action_type, ParcelableStatusUpdateUtils.fromDraftItem(this, item))
|
||||||
|
}
|
||||||
|
Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> {
|
||||||
|
var recipientId: String? = null
|
||||||
|
if (item.action_extras is SendDirectMessageActionExtra) {
|
||||||
|
recipientId = (item.action_extras as SendDirectMessageActionExtra).recipientId
|
||||||
|
}
|
||||||
|
if (ArrayUtils.isEmpty(item.account_keys) || recipientId == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val accountKey = item.account_keys!![0]
|
||||||
|
val imageUri = if (ArrayUtils.isEmpty(item.media)) null else item.media[0].uri
|
||||||
|
sendMessage(accountKey, recipientId, item.text, imageUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDiscardDraftIntent(intent: Intent) {
|
||||||
|
val data = intent.data ?: return
|
||||||
|
notificationManager.cancel(data.toString(), NOTIFICATION_ID_DRAFTS)
|
||||||
|
val cr = contentResolver
|
||||||
|
val def: Long = -1
|
||||||
|
val id = NumberUtils.toLong(data.lastPathSegment, def)
|
||||||
|
val where = Expression.equals(Drafts._ID, id)
|
||||||
|
cr.delete(Drafts.CONTENT_URI, where.sql, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSendDirectMessageIntent(intent: Intent) {
|
||||||
|
val accountId = intent.getParcelableExtra<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||||
|
val recipientId = intent.getStringExtra(EXTRA_RECIPIENT_ID)
|
||||||
|
val text = intent.getStringExtra(EXTRA_TEXT)
|
||||||
|
val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
|
||||||
|
if (accountId == null || recipientId == null || text == null) return
|
||||||
|
sendMessage(accountId, recipientId, text, imageUri)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendMessage(accountId: UserKey, recipientId: String,
|
||||||
|
text: String, imageUri: String?) {
|
||||||
|
val title = getString(R.string.sending_direct_message)
|
||||||
|
val builder = Builder(this)
|
||||||
|
builder.setSmallIcon(R.drawable.ic_stat_send)
|
||||||
|
builder.setProgress(100, 0, true)
|
||||||
|
builder.setTicker(title)
|
||||||
|
builder.setContentTitle(title)
|
||||||
|
builder.setContentText(text)
|
||||||
|
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||||
|
builder.setOngoing(true)
|
||||||
|
val notification = builder.build()
|
||||||
|
startForeground(NOTIFICATION_ID_SEND_DIRECT_MESSAGE, notification)
|
||||||
|
val result = sendDirectMessage(builder, accountId,
|
||||||
|
recipientId, text, imageUri)
|
||||||
|
|
||||||
|
val resolver = contentResolver
|
||||||
|
if (result.hasData()) {
|
||||||
|
val message = result.data
|
||||||
|
val values = ContentValuesCreator.createDirectMessage(message)
|
||||||
|
val deleteWhere = Expression.and(Expression.equalsArgs(DirectMessages.ACCOUNT_KEY),
|
||||||
|
Expression.equalsArgs(DirectMessages.MESSAGE_ID)).sql
|
||||||
|
val deleteWhereArgs = arrayOf(message!!.account_key.toString(), message.id)
|
||||||
|
resolver.delete(DirectMessages.Outbox.CONTENT_URI, deleteWhere, deleteWhereArgs)
|
||||||
|
resolver.insert(DirectMessages.Outbox.CONTENT_URI, values)
|
||||||
|
showOkMessage(R.string.direct_message_sent, false)
|
||||||
|
} else {
|
||||||
|
val values = ContentValuesCreator.createMessageDraft(accountId, recipientId, text, imageUri)
|
||||||
|
resolver.insert(Drafts.CONTENT_URI, values)
|
||||||
|
showErrorMessage(R.string.action_sending_direct_message, result.exception, true)
|
||||||
|
}
|
||||||
|
stopForeground(false)
|
||||||
|
notificationManager.cancel(NOTIFICATION_ID_SEND_DIRECT_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleUpdateStatusIntent(intent: Intent) {
|
||||||
|
val status = intent.getParcelableExtra<ParcelableStatusUpdate>(EXTRA_STATUS)
|
||||||
|
val statusParcelables = intent.getParcelableArrayExtra(EXTRA_STATUSES)
|
||||||
|
val statuses: Array<ParcelableStatusUpdate>
|
||||||
|
if (statusParcelables != null) {
|
||||||
|
statuses = statusParcelables.asTypedArray(ParcelableStatusUpdate.CREATOR)
|
||||||
|
} else if (status != null) {
|
||||||
|
statuses = arrayOf(status)
|
||||||
|
} else
|
||||||
|
return
|
||||||
|
@Draft.Action
|
||||||
|
val actionType = intent.getStringExtra(EXTRA_ACTION)
|
||||||
|
updateStatuses(actionType, *statuses)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateStatuses(@Draft.Action actionType: String, vararg statuses: ParcelableStatusUpdate) {
|
||||||
|
val context = this
|
||||||
|
val builder = Builder(context)
|
||||||
|
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||||
|
builder, 0, null))
|
||||||
|
for (item in statuses) {
|
||||||
|
val task = UpdateStatusTask(context, object : UpdateStatusTask.StateCallback {
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
override fun onStartUploadingMedia() {
|
||||||
|
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||||
|
builder, 0, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
override fun onUploadingProgressChanged(index: Int, current: Long, total: Long) {
|
||||||
|
val progress = (current * 100 / total).toInt()
|
||||||
|
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||||
|
builder, progress, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
override fun onShorteningStatus() {
|
||||||
|
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||||
|
builder, 0, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
override fun onUpdatingStatus() {
|
||||||
|
startForeground(NOTIFICATION_ID_UPDATE_STATUS, updateUpdateStatusNotification(context,
|
||||||
|
builder, 0, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterExecute(handler: Context?, result: UpdateStatusTask.UpdateStatusResult?) {
|
||||||
|
var failed = false
|
||||||
|
val exception = result!!.exception
|
||||||
|
val exceptions = result.exceptions
|
||||||
|
if (exception != null) {
|
||||||
|
Toast.makeText(context, exception.message, Toast.LENGTH_SHORT).show()
|
||||||
|
failed = true
|
||||||
|
Log.w(LOGTAG, exception)
|
||||||
|
} else for (e in exceptions) {
|
||||||
|
if (e != null) {
|
||||||
|
// Show error
|
||||||
|
var errorMessage = Utils.getErrorMessage(context, e)
|
||||||
|
if (TextUtils.isEmpty(errorMessage)) {
|
||||||
|
errorMessage = context.getString(R.string.status_not_updated)
|
||||||
|
}
|
||||||
|
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
|
||||||
|
failed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
// TODO show draft notification
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.status_updated, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeExecute() {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
task.setCallback(this)
|
||||||
|
task.params = Pair.create(actionType, item)
|
||||||
|
handler!!.post { ManualTaskStarter.invokeBeforeExecute(task) }
|
||||||
|
|
||||||
|
val result = ManualTaskStarter.invokeExecute(task)
|
||||||
|
handler!!.post { ManualTaskStarter.invokeAfterExecute(task, result) }
|
||||||
|
|
||||||
|
val exception = result.exception
|
||||||
|
val updatedStatuses = result.statuses
|
||||||
|
if (exception != null) {
|
||||||
|
Log.w(LOGTAG, exception)
|
||||||
|
} else
|
||||||
|
for (status in updatedStatuses) {
|
||||||
|
if (status == null) continue
|
||||||
|
val event = TweetEvent.create(context, status, TimelineType.OTHER)
|
||||||
|
event.setAction(TweetEvent.Action.TWEET)
|
||||||
|
HotMobiLogger.getInstance(context).log(status.account_key, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (preferences.getBoolean(KEY_REFRESH_AFTER_TWEET)) {
|
||||||
|
handler!!.post { twitterWrapper.refreshAll() }
|
||||||
|
}
|
||||||
|
stopForeground(false)
|
||||||
|
notificationManager.cancel(NOTIFICATION_ID_UPDATE_STATUS)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun sendDirectMessage(builder: NotificationCompat.Builder,
|
||||||
|
accountKey: UserKey,
|
||||||
|
recipientId: String,
|
||||||
|
text: String,
|
||||||
|
imageUri: String?): SingleResponse<ParcelableDirectMessage> {
|
||||||
|
val credentials = ParcelableCredentialsUtils.getCredentials(this,
|
||||||
|
accountKey) ?: return SingleResponse.getInstance<ParcelableDirectMessage>()
|
||||||
|
val twitter = MicroBlogAPIFactory.getInstance(this, credentials, true, true)
|
||||||
|
val twitterUpload = MicroBlogAPIFactory.getInstance(this, credentials,
|
||||||
|
true, true, TwitterUpload::class.java)
|
||||||
|
if (twitter == null || twitterUpload == null) return SingleResponse.getInstance<ParcelableDirectMessage>()
|
||||||
|
try {
|
||||||
|
val directMessage: ParcelableDirectMessage
|
||||||
|
when (ParcelableAccountUtils.getAccountType(credentials)) {
|
||||||
|
ParcelableAccount.Type.FANFOU -> {
|
||||||
|
if (imageUri != null) {
|
||||||
|
throw MicroBlogException("Can't send image DM on Fanfou")
|
||||||
|
}
|
||||||
|
val dm = twitter.sendFanfouDirectMessage(recipientId, text)
|
||||||
|
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(dm, accountKey, true)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (imageUri != null) {
|
||||||
|
val mediaUri = Uri.parse(imageUri)
|
||||||
|
var body: FileBody? = null
|
||||||
|
try {
|
||||||
|
body = UpdateStatusTask.getBodyFromMedia(contentResolver,
|
||||||
|
mediaUri, null, MessageMediaUploadListener(this,
|
||||||
|
notificationManager, builder, text))
|
||||||
|
val uploadResp = uploadMedia(twitterUpload, body)
|
||||||
|
val response = twitter.sendDirectMessage(recipientId,
|
||||||
|
text, uploadResp.id)
|
||||||
|
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(response,
|
||||||
|
accountKey, true)
|
||||||
|
} finally {
|
||||||
|
Utils.closeSilently(body)
|
||||||
|
}
|
||||||
|
val path = Utils.getImagePathFromUri(this, mediaUri)
|
||||||
|
if (path != null) {
|
||||||
|
val file = File(path)
|
||||||
|
if (!file.delete()) {
|
||||||
|
Log.d(LOGTAG, String.format("unable to delete %s", path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val response = twitter.sendDirectMessage(recipientId, text)
|
||||||
|
directMessage = ParcelableDirectMessageUtils.fromDirectMessage(response,
|
||||||
|
accountKey, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Utils.setLastSeen(this, UserKey(recipientId, accountKey.host),
|
||||||
|
System.currentTimeMillis())
|
||||||
|
|
||||||
|
return SingleResponse.getInstance(directMessage)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
return SingleResponse.getInstance<ParcelableDirectMessage>(e)
|
||||||
|
} catch (e: MicroBlogException) {
|
||||||
|
return SingleResponse.getInstance<ParcelableDirectMessage>(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(IOException::class, MicroBlogException::class)
|
||||||
|
private fun uploadMedia(upload: TwitterUpload, body: Body): MediaUploadResponse {
|
||||||
|
val mediaType = body.contentType().contentType
|
||||||
|
val length = body.length()
|
||||||
|
val stream = body.stream()
|
||||||
|
var response = upload.initUploadMedia(mediaType, length, null)
|
||||||
|
val segments = if (length == 0L) 0 else (length / BULK_SIZE + 1).toInt()
|
||||||
|
for (segmentIndex in 0..segments - 1) {
|
||||||
|
val currentBulkSize = Math.min(BULK_SIZE, length - segmentIndex * BULK_SIZE).toInt()
|
||||||
|
val bulk = SimpleBody(ContentType.OCTET_STREAM, null, currentBulkSize.toLong(),
|
||||||
|
stream)
|
||||||
|
upload.appendUploadMedia(response.id, segmentIndex, bulk)
|
||||||
|
}
|
||||||
|
response = upload.finalizeUploadMedia(response.id)
|
||||||
|
run {
|
||||||
|
var info: ProcessingInfo? = response.processingInfo
|
||||||
|
while (info != null && shouldWaitForProcess(info)) {
|
||||||
|
val checkAfterSecs = info.checkAfterSecs
|
||||||
|
if (checkAfterSecs <= 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(TimeUnit.SECONDS.toMillis(checkAfterSecs))
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
response = upload.getUploadMediaStatus(response.id)
|
||||||
|
info = response.processingInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val info = response.processingInfo
|
||||||
|
if (info != null && ProcessingInfo.State.FAILED == info.state) {
|
||||||
|
val exception = MicroBlogException()
|
||||||
|
val errorInfo = info.error
|
||||||
|
if (errorInfo != null) {
|
||||||
|
exception.errors = arrayOf(errorInfo)
|
||||||
|
}
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldWaitForProcess(info: ProcessingInfo): Boolean {
|
||||||
|
when (info.state) {
|
||||||
|
ProcessingInfo.State.PENDING, ProcessingInfo.State.IN_PROGRESS -> return true
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageMediaUploadListener(private val context: Context, private val manager: NotificationManagerWrapper,
|
||||||
|
builder: NotificationCompat.Builder, private val message: String) : ReadListener {
|
||||||
|
|
||||||
|
var percent: Int = 0
|
||||||
|
|
||||||
|
private val builder: Builder
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.builder = builder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRead(length: Long, position: Long) {
|
||||||
|
val percent = if (length > 0) (position * 100 / length).toInt() else 0
|
||||||
|
if (this.percent != percent) {
|
||||||
|
manager.notify(NOTIFICATION_ID_SEND_DIRECT_MESSAGE,
|
||||||
|
updateSendDirectMessageNotification(context, builder, percent, message))
|
||||||
|
}
|
||||||
|
this.percent = percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val BULK_SIZE = (128 * 1024).toLong() // 128KiB
|
||||||
|
|
||||||
|
private fun updateSendDirectMessageNotification(context: Context,
|
||||||
|
builder: NotificationCompat.Builder,
|
||||||
|
progress: Int, message: String?): Notification {
|
||||||
|
builder.setContentTitle(context.getString(R.string.sending_direct_message))
|
||||||
|
if (message != null) {
|
||||||
|
builder.setContentText(message)
|
||||||
|
}
|
||||||
|
builder.setSmallIcon(R.drawable.ic_stat_send)
|
||||||
|
builder.setProgress(100, progress, progress >= 100 || progress <= 0)
|
||||||
|
builder.setOngoing(true)
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUpdateStatusNotification(context: Context,
|
||||||
|
builder: NotificationCompat.Builder,
|
||||||
|
progress: Int,
|
||||||
|
status: ParcelableStatusUpdate?): Notification {
|
||||||
|
builder.setContentTitle(context.getString(R.string.updating_status_notification))
|
||||||
|
if (status != null) {
|
||||||
|
builder.setContentText(status.text)
|
||||||
|
}
|
||||||
|
builder.setSmallIcon(R.drawable.ic_stat_send)
|
||||||
|
builder.setProgress(100, progress, progress >= 100 || progress <= 0)
|
||||||
|
builder.setOngoing(true)
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateStatusesAsync(context: Context, @Draft.Action action: String,
|
||||||
|
vararg statuses: ParcelableStatusUpdate) {
|
||||||
|
val intent = Intent(context, BackgroundOperationService::class.java)
|
||||||
|
intent.action = INTENT_ACTION_UPDATE_STATUS
|
||||||
|
intent.putExtra(EXTRA_STATUSES, statuses)
|
||||||
|
intent.putExtra(EXTRA_ACTION, action)
|
||||||
|
context.startService(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue