mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-02 17:56:56 +01:00
updated rest library
This commit is contained in:
parent
f22dd7cdf3
commit
6463922d0e
@ -41,8 +41,8 @@ dependencies {
|
||||
apt 'com.github.mariotaku.ObjectCursor:processor:0.9.9'
|
||||
compile 'com.android.support:support-annotations:24.0.0'
|
||||
compile 'com.bluelinelabs:logansquare:1.3.7'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.30'
|
||||
compile 'com.github.mariotaku.RestFu:oauth:0.9.30'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.31'
|
||||
compile 'com.github.mariotaku.RestFu:oauth:0.9.31'
|
||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
|
||||
compile 'com.github.mariotaku.ObjectCursor:core:0.9.9'
|
||||
compile 'com.github.mariotaku.CommonsLibrary:objectcursor:0.9.8'
|
||||
|
@ -151,8 +151,8 @@ dependencies {
|
||||
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
|
||||
compile 'com.github.mariotaku:PickNCrop:0.9.4'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.29'
|
||||
compile 'com.github.mariotaku.RestFu:okhttp3:0.9.29'
|
||||
compile 'com.github.mariotaku.RestFu:library:0.9.31'
|
||||
compile 'com.github.mariotaku.RestFu:okhttp3:0.9.31'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.2.0'
|
||||
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
||||
compile 'com.google.dagger:dagger:2.1'
|
||||
|
@ -157,7 +157,7 @@ public abstract class UserStreamCallback implements RawCallback {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void error(final Throwable cause) {
|
||||
public final void error(final Exception cause) {
|
||||
onException(cause);
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,8 @@ public class BrowserSignInActivity extends BaseActivity {
|
||||
private String readOAuthPin(final String html) {
|
||||
try {
|
||||
OAuthPasswordAuthenticator.OAuthPinData data = new OAuthPasswordAuthenticator.OAuthPinData();
|
||||
OAuthPasswordAuthenticator.readOAuthPINFromHtml(new StringReader(html), data);
|
||||
return data.oauthPin;
|
||||
OAuthPasswordAuthenticator.Companion.readOAuthPINFromHtml(new StringReader(html), data);
|
||||
return data.getOauthPin();
|
||||
} catch (final AttoParseException | IOException e) {
|
||||
Log.w(LOGTAG, e);
|
||||
}
|
||||
|
@ -363,10 +363,13 @@ public class BackgroundOperationService extends IntentService implements Constan
|
||||
@Override
|
||||
public void afterExecute(Context handler, UpdateStatusTask.UpdateStatusResult result) {
|
||||
boolean failed = false;
|
||||
if (result.exception != null) {
|
||||
Toast.makeText(context, result.exception.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
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;
|
||||
} else for (MicroBlogException e : result.exceptions) {
|
||||
Log.w(LOGTAG, exception);
|
||||
} else for (MicroBlogException e : exceptions) {
|
||||
if (e != null) {
|
||||
// Show error
|
||||
String errorMessage = Utils.getErrorMessage(context, e);
|
||||
@ -407,9 +410,11 @@ public class BackgroundOperationService extends IntentService implements Constan
|
||||
}
|
||||
});
|
||||
|
||||
if (result.exception != null) {
|
||||
Log.w(LOGTAG, result.exception);
|
||||
} else for (ParcelableStatus status : result.statuses) {
|
||||
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);
|
||||
@ -457,7 +462,7 @@ public class BackgroundOperationService extends IntentService implements Constan
|
||||
final Uri mediaUri = Uri.parse(imageUri);
|
||||
FileBody body = null;
|
||||
try {
|
||||
body = UpdateStatusTask.getBodyFromMedia(getContentResolver(), mediaUri,
|
||||
body = UpdateStatusTask.Companion.getBodyFromMedia(getContentResolver(), mediaUri,
|
||||
new MessageMediaUploadListener(this, mNotificationManager,
|
||||
builder, text));
|
||||
final MediaUploadResponse uploadResp = uploadMedia(twitterUpload, body);
|
||||
|
@ -1,749 +0,0 @@
|
||||
package org.mariotaku.twidere.task.twitter;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.mariotaku.abstask.library.AbstractTask;
|
||||
import org.mariotaku.microblog.library.MicroBlog;
|
||||
import org.mariotaku.microblog.library.MicroBlogException;
|
||||
import org.mariotaku.microblog.library.fanfou.model.PhotoStatusUpdate;
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUpload;
|
||||
import org.mariotaku.microblog.library.twitter.model.ErrorInfo;
|
||||
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse;
|
||||
import org.mariotaku.microblog.library.twitter.model.Status;
|
||||
import org.mariotaku.microblog.library.twitter.model.StatusUpdate;
|
||||
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.app.TwidereApplication;
|
||||
import org.mariotaku.twidere.model.Draft;
|
||||
import org.mariotaku.twidere.model.DraftValuesCreator;
|
||||
import org.mariotaku.twidere.model.MediaUploadResult;
|
||||
import org.mariotaku.twidere.model.ParcelableAccount;
|
||||
import org.mariotaku.twidere.model.ParcelableMediaUpdate;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.model.ParcelableStatusUpdate;
|
||||
import org.mariotaku.twidere.model.StatusShortenResult;
|
||||
import org.mariotaku.twidere.model.UploaderMediaItem;
|
||||
import org.mariotaku.twidere.model.UserKey;
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra;
|
||||
import org.mariotaku.twidere.model.util.ParcelableAccountUtils;
|
||||
import org.mariotaku.twidere.model.util.ParcelableLocationUtils;
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
|
||||
import org.mariotaku.twidere.preference.ServicePickerPreference;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
|
||||
import org.mariotaku.twidere.util.AbsServiceInterface;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.CollectionUtils;
|
||||
import org.mariotaku.twidere.util.MediaUploaderInterface;
|
||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.StatusShortenerInterface;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||
import org.mariotaku.twidere.util.io.ContentLengthInputStream;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/5/22.
|
||||
*/
|
||||
public class UpdateStatusTask extends AbstractTask<Pair<String, ParcelableStatusUpdate>,
|
||||
UpdateStatusTask.UpdateStatusResult, Context> implements Constants {
|
||||
|
||||
private static final int BULK_SIZE = 256 * 1024;// 128 Kib
|
||||
|
||||
final Context context;
|
||||
final StateCallback stateCallback;
|
||||
|
||||
@Inject
|
||||
@NonNull
|
||||
AsyncTwitterWrapper twitterWrapper;
|
||||
@Inject
|
||||
@NonNull
|
||||
SharedPreferencesWrapper preferences;
|
||||
|
||||
public UpdateStatusTask(Context context, StateCallback stateCallback) {
|
||||
GeneralComponentHelper.build(context).inject(this);
|
||||
this.context = context;
|
||||
this.stateCallback = stateCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpdateStatusResult doLongOperation(Pair<String, ParcelableStatusUpdate> params) {
|
||||
final long draftId = saveDraft(params.first, params.second);
|
||||
twitterWrapper.addSendingDraftId(draftId);
|
||||
try {
|
||||
final UpdateStatusResult result = doUpdateStatus(params.second);
|
||||
deleteOrUpdateDraft(params.second, result, draftId);
|
||||
return result;
|
||||
} catch (UpdateStatusException e) {
|
||||
return new UpdateStatusResult(e);
|
||||
} finally {
|
||||
twitterWrapper.removeSendingDraftId(draftId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExecute() {
|
||||
stateCallback.beforeExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Context handler, UpdateStatusResult result) {
|
||||
stateCallback.afterExecute(handler, result);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private UpdateStatusResult doUpdateStatus(ParcelableStatusUpdate update) throws UpdateStatusException {
|
||||
final TwidereApplication app = TwidereApplication.Companion.getInstance(context);
|
||||
final MediaUploaderInterface uploader = getMediaUploader(app);
|
||||
final StatusShortenerInterface shortener = getStatusShortener(app);
|
||||
|
||||
final PendingStatusUpdate pendingUpdate = PendingStatusUpdate.from(update);
|
||||
|
||||
|
||||
uploadMedia(uploader, update, pendingUpdate);
|
||||
shortenStatus(shortener, update, pendingUpdate);
|
||||
|
||||
final UpdateStatusResult result;
|
||||
try {
|
||||
result = requestUpdateStatus(update, pendingUpdate);
|
||||
} catch (IOException e) {
|
||||
return new UpdateStatusResult(new UpdateStatusException(e));
|
||||
}
|
||||
|
||||
mediaUploadCallback(uploader, pendingUpdate, result);
|
||||
statusShortenCallback(shortener, pendingUpdate, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void deleteOrUpdateDraft(ParcelableStatusUpdate update, UpdateStatusResult result, long draftId) {
|
||||
final String where = Expression.equalsArgs(Drafts._ID).getSQL();
|
||||
final String[] whereArgs = {String.valueOf(draftId)};
|
||||
boolean hasError = false;
|
||||
List<UserKey> failedAccounts = new ArrayList<>();
|
||||
for (int i = 0; i < update.accounts.length; i++) {
|
||||
Exception exception = result.exceptions[i];
|
||||
if (exception != null && !isDuplicate(exception)) {
|
||||
hasError = true;
|
||||
failedAccounts.add(update.accounts[i].account_key);
|
||||
}
|
||||
}
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
if (hasError) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Drafts.ACCOUNT_KEYS, CollectionUtils.toString(failedAccounts, ',', false));
|
||||
cr.update(Drafts.CONTENT_URI, values, where, whereArgs);
|
||||
// TODO show error message
|
||||
} else {
|
||||
cr.delete(Drafts.CONTENT_URI, where, whereArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadMedia(@Nullable MediaUploaderInterface uploader,
|
||||
@NonNull ParcelableStatusUpdate update,
|
||||
@NonNull PendingStatusUpdate pendingUpdate) throws UploadException {
|
||||
stateCallback.onStartUploadingMedia();
|
||||
if (uploader == null) {
|
||||
uploadMediaWithDefaultProvider(update, pendingUpdate);
|
||||
} else {
|
||||
uploadMediaWithExtension(uploader, update, pendingUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadMediaWithExtension(@NonNull MediaUploaderInterface uploader,
|
||||
@NonNull ParcelableStatusUpdate update,
|
||||
@NonNull PendingStatusUpdate pending) throws UploadException {
|
||||
final UploaderMediaItem[] media;
|
||||
try {
|
||||
media = UploaderMediaItem.getFromStatusUpdate(context, update);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new UploadException(e);
|
||||
}
|
||||
Map<UserKey, MediaUploadResult> sharedMedia = new HashMap<>();
|
||||
for (int i = 0; i < pending.length; i++) {
|
||||
ParcelableAccount account = update.accounts[i];
|
||||
// Skip upload if shared media found
|
||||
final UserKey accountKey = account.account_key;
|
||||
MediaUploadResult uploadResult = sharedMedia.get(accountKey);
|
||||
if (uploadResult == null) {
|
||||
uploadResult = uploader.upload(update, accountKey, media);
|
||||
if (uploadResult == null) {
|
||||
// TODO error handling
|
||||
continue;
|
||||
}
|
||||
pending.mediaUploadResults[i] = uploadResult;
|
||||
if (uploadResult.shared_owners != null) {
|
||||
for (UserKey sharedOwner : uploadResult.shared_owners) {
|
||||
sharedMedia.put(sharedOwner, uploadResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Override status text
|
||||
pending.overrideTexts[i] = Utils.getMediaUploadStatus(context,
|
||||
uploadResult.media_uris, pending.overrideTexts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void shortenStatus(@Nullable StatusShortenerInterface shortener,
|
||||
ParcelableStatusUpdate update,
|
||||
PendingStatusUpdate pending) {
|
||||
if (shortener == null) return;
|
||||
stateCallback.onShorteningStatus();
|
||||
Map<UserKey, StatusShortenResult> sharedShortened = new HashMap<>();
|
||||
for (int i = 0; i < pending.length; i++) {
|
||||
ParcelableAccount account = update.accounts[i];
|
||||
// Skip upload if this shared media found
|
||||
final UserKey accountKey = account.account_key;
|
||||
StatusShortenResult shortenResult = sharedShortened.get(accountKey);
|
||||
if (shortenResult == null) {
|
||||
shortenResult = shortener.shorten(update, accountKey, pending.overrideTexts[i]);
|
||||
if (shortenResult == null) {
|
||||
// TODO error handling
|
||||
continue;
|
||||
}
|
||||
pending.statusShortenResults[i] = shortenResult;
|
||||
if (shortenResult.shared_owners != null) {
|
||||
for (UserKey sharedOwner : shortenResult.shared_owners) {
|
||||
sharedShortened.put(sharedOwner, shortenResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Override status text
|
||||
pending.overrideTexts[i] = shortenResult.shortened;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private UpdateStatusResult requestUpdateStatus(ParcelableStatusUpdate statusUpdate, PendingStatusUpdate pendingUpdate) throws IOException {
|
||||
|
||||
stateCallback.onUpdatingStatus();
|
||||
|
||||
UpdateStatusResult result = new UpdateStatusResult(new ParcelableStatus[pendingUpdate.length],
|
||||
new MicroBlogException[pendingUpdate.length]);
|
||||
|
||||
for (int i = 0; i < pendingUpdate.length; i++) {
|
||||
final ParcelableAccount account = statusUpdate.accounts[i];
|
||||
MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, account.account_key, true);
|
||||
Body body = null;
|
||||
try {
|
||||
switch (ParcelableAccountUtils.getAccountType(account)) {
|
||||
case ParcelableAccount.Type.FANFOU: {
|
||||
// Call uploadPhoto if media present
|
||||
if (!ArrayUtils.isEmpty(statusUpdate.media)) {
|
||||
// Fanfou only allow one photo
|
||||
if (statusUpdate.media.length > 1) {
|
||||
result.exceptions[i] = new MicroBlogException(
|
||||
context.getString(R.string.error_too_many_photos_fanfou));
|
||||
break;
|
||||
}
|
||||
body = getBodyFromMedia(context.getContentResolver(),
|
||||
Uri.parse(statusUpdate.media[0].uri), new ContentLengthInputStream.ReadListener() {
|
||||
@Override
|
||||
public void onRead(long length, long position) {
|
||||
stateCallback.onUploadingProgressChanged(-1, position, length);
|
||||
}
|
||||
});
|
||||
PhotoStatusUpdate photoUpdate = new PhotoStatusUpdate(body,
|
||||
pendingUpdate.overrideTexts[i]);
|
||||
final Status requestResult = microBlog.uploadPhoto(photoUpdate);
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false);
|
||||
} else {
|
||||
final Status requestResult = twitterUpdateStatus(microBlog,
|
||||
statusUpdate, pendingUpdate, pendingUpdate.overrideTexts[i], i);
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
final Status requestResult = twitterUpdateStatus(microBlog, statusUpdate,
|
||||
pendingUpdate, pendingUpdate.overrideTexts[i], i);
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (MicroBlogException e) {
|
||||
result.exceptions[i] = e;
|
||||
} finally {
|
||||
Utils.closeSilently(body);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling Twitter's upload method. This method sets multiple owner for bandwidth saving
|
||||
*/
|
||||
private void uploadMediaWithDefaultProvider(ParcelableStatusUpdate update, PendingStatusUpdate pendingUpdate)
|
||||
throws UploadException {
|
||||
// Return empty array if no media attached
|
||||
if (ArrayUtils.isEmpty(update.media)) return;
|
||||
List<UserKey> ownersList = new ArrayList<>();
|
||||
List<String> ownerIdsList = new ArrayList<>();
|
||||
for (ParcelableAccount item : update.accounts) {
|
||||
if (ParcelableAccount.Type.TWITTER.equals(ParcelableAccountUtils.getAccountType(item))) {
|
||||
// Add to owners list
|
||||
ownersList.add(item.account_key);
|
||||
ownerIdsList.add(item.account_key.getId());
|
||||
}
|
||||
}
|
||||
String[] ownerIds = ownerIdsList.toArray(new String[ownerIdsList.size()]);
|
||||
for (int i = 0; i < pendingUpdate.length; i++) {
|
||||
final ParcelableAccount account = update.accounts[i];
|
||||
String[] mediaIds;
|
||||
switch (ParcelableAccountUtils.getAccountType(account)) {
|
||||
case ParcelableAccount.Type.TWITTER: {
|
||||
final TwitterUpload upload = MicroBlogAPIFactory.getInstance(context,
|
||||
account.account_key, true, true, TwitterUpload.class);
|
||||
if (pendingUpdate.sharedMediaIds != null) {
|
||||
mediaIds = pendingUpdate.sharedMediaIds;
|
||||
} else {
|
||||
mediaIds = uploadAllMediaShared(upload, update, ownerIds, true);
|
||||
pendingUpdate.sharedMediaIds = mediaIds;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ParcelableAccount.Type.FANFOU: {
|
||||
// Nope, fanfou uses photo uploading API
|
||||
mediaIds = null;
|
||||
break;
|
||||
}
|
||||
case ParcelableAccount.Type.STATUSNET: {
|
||||
// TODO use their native API
|
||||
final TwitterUpload upload = MicroBlogAPIFactory.getInstance(context,
|
||||
account.account_key, true, true, TwitterUpload.class);
|
||||
mediaIds = uploadAllMediaShared(upload, update, ownerIds, false);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
mediaIds = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pendingUpdate.mediaIds[i] = mediaIds;
|
||||
}
|
||||
pendingUpdate.sharedMediaOwners = ownersList.toArray(new UserKey[ownersList.size()]);
|
||||
}
|
||||
|
||||
private Status twitterUpdateStatus(MicroBlog microBlog, ParcelableStatusUpdate statusUpdate,
|
||||
PendingStatusUpdate pendingUpdate, String overrideText,
|
||||
int index) throws MicroBlogException {
|
||||
final StatusUpdate status = new StatusUpdate(overrideText);
|
||||
if (statusUpdate.in_reply_to_status != null) {
|
||||
status.inReplyToStatusId(statusUpdate.in_reply_to_status.id);
|
||||
}
|
||||
if (statusUpdate.repost_status_id != null) {
|
||||
status.setRepostStatusId(statusUpdate.repost_status_id);
|
||||
}
|
||||
if (statusUpdate.attachment_url != null) {
|
||||
status.setAttachmentUrl(statusUpdate.attachment_url);
|
||||
}
|
||||
if (statusUpdate.location != null) {
|
||||
status.location(ParcelableLocationUtils.toGeoLocation(statusUpdate.location));
|
||||
status.displayCoordinates(statusUpdate.display_coordinates);
|
||||
}
|
||||
final String[] mediaIds = pendingUpdate.mediaIds[index];
|
||||
if (mediaIds != null) {
|
||||
status.mediaIds(mediaIds);
|
||||
}
|
||||
status.possiblySensitive(statusUpdate.is_possibly_sensitive);
|
||||
return microBlog.updateStatus(status);
|
||||
}
|
||||
|
||||
private void statusShortenCallback(StatusShortenerInterface shortener, PendingStatusUpdate pendingUpdate, UpdateStatusResult updateResult) {
|
||||
for (int i = 0; i < pendingUpdate.length; i++) {
|
||||
final StatusShortenResult shortenResult = pendingUpdate.statusShortenResults[i];
|
||||
final ParcelableStatus status = updateResult.statuses[i];
|
||||
if (shortenResult == null || status == null) continue;
|
||||
shortener.callback(shortenResult, status);
|
||||
}
|
||||
}
|
||||
|
||||
private void mediaUploadCallback(MediaUploaderInterface uploader, PendingStatusUpdate pendingUpdate, UpdateStatusResult updateResult) {
|
||||
for (int i = 0; i < pendingUpdate.length; i++) {
|
||||
final MediaUploadResult uploadResult = pendingUpdate.mediaUploadResults[i];
|
||||
final ParcelableStatus status = updateResult.statuses[i];
|
||||
if (uploadResult == null || status == null) continue;
|
||||
uploader.callback(uploadResult, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private StatusShortenerInterface getStatusShortener(TwidereApplication app) throws UploaderNotFoundException, UploadException, ShortenerNotFoundException, ShortenException {
|
||||
final String shortenerComponent = preferences.getString(KEY_STATUS_SHORTENER, null);
|
||||
if (ServicePickerPreference.isNoneValue(shortenerComponent)) return null;
|
||||
|
||||
final StatusShortenerInterface shortener = StatusShortenerInterface.getInstance(app, shortenerComponent);
|
||||
if (shortener == null) throw new ShortenerNotFoundException();
|
||||
try {
|
||||
shortener.checkService(new AbsServiceInterface.CheckServiceAction() {
|
||||
@Override
|
||||
public void check(@Nullable Bundle metaData) throws AbsServiceInterface.CheckServiceException {
|
||||
if (metaData == null) throw new ExtensionVersionMismatchException();
|
||||
final String extensionVersion = metaData.getString(METADATA_KEY_EXTENSION_VERSION_STATUS_SHORTENER);
|
||||
if (!TextUtils.equals(extensionVersion, context.getString(R.string.status_shortener_service_interface_version))) {
|
||||
throw new ExtensionVersionMismatchException();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (AbsServiceInterface.CheckServiceException e) {
|
||||
if (e instanceof ExtensionVersionMismatchException) {
|
||||
throw new ShortenException(context.getString(R.string.shortener_version_incompatible));
|
||||
}
|
||||
throw new ShortenException(e);
|
||||
}
|
||||
return shortener;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MediaUploaderInterface getMediaUploader(TwidereApplication app) throws UploaderNotFoundException, UploadException {
|
||||
final String uploaderComponent = preferences.getString(KEY_MEDIA_UPLOADER, null);
|
||||
if (ServicePickerPreference.isNoneValue(uploaderComponent)) return null;
|
||||
final MediaUploaderInterface uploader = MediaUploaderInterface.getInstance(app, uploaderComponent);
|
||||
if (uploader == null) {
|
||||
throw new UploaderNotFoundException(context.getString(R.string.error_message_media_uploader_not_found));
|
||||
}
|
||||
try {
|
||||
uploader.checkService(new AbsServiceInterface.CheckServiceAction() {
|
||||
@Override
|
||||
public void check(@Nullable Bundle metaData) throws AbsServiceInterface.CheckServiceException {
|
||||
if (metaData == null) throw new ExtensionVersionMismatchException();
|
||||
final String extensionVersion = metaData.getString(METADATA_KEY_EXTENSION_VERSION_MEDIA_UPLOADER);
|
||||
if (!TextUtils.equals(extensionVersion, context.getString(R.string.media_uploader_service_interface_version))) {
|
||||
throw new ExtensionVersionMismatchException();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (AbsServiceInterface.CheckServiceException e) {
|
||||
if (e instanceof ExtensionVersionMismatchException) {
|
||||
throw new UploadException(context.getString(R.string.uploader_version_incompatible));
|
||||
}
|
||||
throw new UploadException(e);
|
||||
}
|
||||
return uploader;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String[] uploadAllMediaShared(TwitterUpload upload, ParcelableStatusUpdate update,
|
||||
String[] ownerIds, boolean chucked) throws UploadException {
|
||||
String[] mediaIds = new String[update.media.length];
|
||||
for (int i = 0; i < update.media.length; i++) {
|
||||
ParcelableMediaUpdate media = update.media[i];
|
||||
final MediaUploadResponse resp;
|
||||
//noinspection TryWithIdenticalCatches
|
||||
Body body = null;
|
||||
try {
|
||||
final int index = i;
|
||||
body = getBodyFromMedia(context.getContentResolver(), Uri.parse(media.uri), new ContentLengthInputStream.ReadListener() {
|
||||
@Override
|
||||
public void onRead(long length, long position) {
|
||||
stateCallback.onUploadingProgressChanged(index, position, length);
|
||||
}
|
||||
});
|
||||
if (chucked) {
|
||||
resp = uploadMediaChucked(upload, body, ownerIds);
|
||||
} else {
|
||||
resp = upload.uploadMedia(body, ownerIds);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UploadException(e);
|
||||
} catch (MicroBlogException e) {
|
||||
throw new UploadException(e);
|
||||
} finally {
|
||||
Utils.closeSilently(body);
|
||||
}
|
||||
mediaIds[i] = resp.getId();
|
||||
}
|
||||
return mediaIds;
|
||||
}
|
||||
|
||||
|
||||
private MediaUploadResponse uploadMediaChucked(final TwitterUpload upload, Body body,
|
||||
String[] ownerIds) 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, ownerIds);
|
||||
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 (MediaUploadResponse.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());
|
||||
}
|
||||
MediaUploadResponse.ProcessingInfo info = response.getProcessingInfo();
|
||||
if (info != null && MediaUploadResponse.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;
|
||||
}
|
||||
|
||||
|
||||
public static FileBody getBodyFromMedia(@NonNull final ContentResolver resolver,
|
||||
@NonNull final Uri mediaUri,
|
||||
@NonNull final ContentLengthInputStream.ReadListener
|
||||
readListener) throws IOException {
|
||||
final String mediaType = resolver.getType(mediaUri);
|
||||
final InputStream is = resolver.openInputStream(mediaUri);
|
||||
if (is == null) {
|
||||
throw new FileNotFoundException(mediaUri.toString());
|
||||
}
|
||||
final long length = is.available();
|
||||
final ContentLengthInputStream cis = new ContentLengthInputStream(is, length);
|
||||
cis.setReadListener(readListener);
|
||||
final ContentType contentType;
|
||||
if (TextUtils.isEmpty(mediaType)) {
|
||||
contentType = ContentType.parse("application/octet-stream");
|
||||
} else {
|
||||
contentType = ContentType.parse(mediaType);
|
||||
}
|
||||
return new FileBody(cis, "attachment", length, contentType);
|
||||
}
|
||||
|
||||
private boolean isDuplicate(Exception exception) {
|
||||
return exception instanceof MicroBlogException
|
||||
&& ((MicroBlogException) exception).getErrorCode() == ErrorInfo.STATUS_IS_DUPLICATE;
|
||||
}
|
||||
|
||||
private boolean shouldWaitForProcess(MediaUploadResponse.ProcessingInfo info) {
|
||||
if (info == null) return false;
|
||||
switch (info.getState()) {
|
||||
case MediaUploadResponse.ProcessingInfo.State.PENDING:
|
||||
case MediaUploadResponse.ProcessingInfo.State.IN_PROGRESS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private long saveDraft(String draftAction, ParcelableStatusUpdate statusUpdate) {
|
||||
final Draft draft = new Draft();
|
||||
draft.account_keys = ParcelableAccountUtils.getAccountKeys(statusUpdate.accounts);
|
||||
if (draftAction != null) {
|
||||
draft.action_type = draftAction;
|
||||
} else {
|
||||
draft.action_type = Draft.Action.UPDATE_STATUS;
|
||||
}
|
||||
draft.text = statusUpdate.text;
|
||||
draft.location = statusUpdate.location;
|
||||
draft.media = statusUpdate.media;
|
||||
final UpdateStatusActionExtra extra = new UpdateStatusActionExtra();
|
||||
extra.setInReplyToStatus(statusUpdate.in_reply_to_status);
|
||||
extra.setIsPossiblySensitive(statusUpdate.is_possibly_sensitive);
|
||||
extra.setRepostStatusId(statusUpdate.repost_status_id);
|
||||
extra.setDisplayCoordinates(statusUpdate.display_coordinates);
|
||||
draft.action_extras = extra;
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final Uri draftUri = resolver.insert(Drafts.CONTENT_URI, DraftValuesCreator.create(draft));
|
||||
if (draftUri == null) return -1;
|
||||
return NumberUtils.toLong(draftUri.getLastPathSegment(), -1);
|
||||
}
|
||||
|
||||
static class PendingStatusUpdate {
|
||||
|
||||
@Nullable
|
||||
String[] sharedMediaIds;
|
||||
UserKey[] sharedMediaOwners;
|
||||
|
||||
final int length;
|
||||
|
||||
@NonNull
|
||||
final String[] overrideTexts;
|
||||
@NonNull
|
||||
final String[][] mediaIds;
|
||||
|
||||
@NonNull
|
||||
final MediaUploadResult[] mediaUploadResults;
|
||||
@NonNull
|
||||
final StatusShortenResult[] statusShortenResults;
|
||||
|
||||
PendingStatusUpdate(int length, String defaultText) {
|
||||
this.length = length;
|
||||
overrideTexts = new String[length];
|
||||
mediaUploadResults = new MediaUploadResult[length];
|
||||
statusShortenResults = new StatusShortenResult[length];
|
||||
mediaIds = new String[length][];
|
||||
Arrays.fill(overrideTexts, defaultText);
|
||||
}
|
||||
|
||||
static PendingStatusUpdate from(ParcelableStatusUpdate statusUpdate) {
|
||||
return new PendingStatusUpdate(statusUpdate.accounts.length,
|
||||
statusUpdate.text);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UpdateStatusResult {
|
||||
@NonNull
|
||||
public final ParcelableStatus[] statuses;
|
||||
@NonNull
|
||||
public final MicroBlogException[] exceptions;
|
||||
|
||||
public final UpdateStatusException exception;
|
||||
|
||||
public UpdateStatusResult(@NonNull ParcelableStatus[] statuses, @NonNull MicroBlogException[] exceptions) {
|
||||
this.statuses = statuses;
|
||||
this.exceptions = exceptions;
|
||||
this.exception = null;
|
||||
}
|
||||
|
||||
public UpdateStatusResult(UpdateStatusException exception) {
|
||||
this.exception = exception;
|
||||
this.statuses = new ParcelableStatus[0];
|
||||
this.exceptions = new MicroBlogException[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class UpdateStatusException extends Exception {
|
||||
public UpdateStatusException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UpdateStatusException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
public UpdateStatusException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public UpdateStatusException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UploaderNotFoundException extends UpdateStatusException {
|
||||
|
||||
public UploaderNotFoundException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UploaderNotFoundException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
public UploaderNotFoundException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public UploaderNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UploadException extends UpdateStatusException {
|
||||
|
||||
public UploadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UploadException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
public UploadException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public UploadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExtensionVersionMismatchException extends AbsServiceInterface.CheckServiceException {
|
||||
|
||||
}
|
||||
|
||||
public static class ShortenerNotFoundException extends UpdateStatusException {
|
||||
}
|
||||
|
||||
public static class ShortenException extends UpdateStatusException {
|
||||
|
||||
public ShortenException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ShortenException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
public ShortenException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public ShortenException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public interface StateCallback {
|
||||
@WorkerThread
|
||||
void onStartUploadingMedia();
|
||||
|
||||
@WorkerThread
|
||||
void onUploadingProgressChanged(int index, long current, long total);
|
||||
|
||||
@WorkerThread
|
||||
void onShorteningStatus();
|
||||
|
||||
@WorkerThread
|
||||
void onUpdatingStatus();
|
||||
|
||||
@UiThread
|
||||
void afterExecute(Context handler, UpdateStatusResult result);
|
||||
|
||||
@UiThread
|
||||
void beforeExecute();
|
||||
}
|
||||
}
|
@ -0,0 +1,672 @@
|
||||
package org.mariotaku.twidere.task.twitter
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.support.annotation.UiThread
|
||||
import android.support.annotation.WorkerThread
|
||||
import android.text.TextUtils
|
||||
import android.util.Pair
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import org.apache.commons.lang3.math.NumberUtils
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.fanfou.model.PhotoStatusUpdate
|
||||
import org.mariotaku.microblog.library.twitter.TwitterUpload
|
||||
import org.mariotaku.microblog.library.twitter.model.ErrorInfo
|
||||
import org.mariotaku.microblog.library.twitter.model.MediaUploadResponse
|
||||
import org.mariotaku.microblog.library.twitter.model.Status
|
||||
import org.mariotaku.microblog.library.twitter.model.StatusUpdate
|
||||
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.TwidereConstants.*
|
||||
import org.mariotaku.twidere.app.TwidereApplication
|
||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants
|
||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra
|
||||
import org.mariotaku.twidere.model.util.ParcelableAccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.preference.ServicePickerPreference
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||
import org.mariotaku.twidere.util.io.ContentLengthInputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/5/22.
|
||||
*/
|
||||
class UpdateStatusTask(internal val context: Context, internal val stateCallback: UpdateStatusTask.StateCallback) : AbstractTask<Pair<String, ParcelableStatusUpdate>, UpdateStatusTask.UpdateStatusResult, Context>(), Constants {
|
||||
|
||||
@Inject
|
||||
lateinit var twitterWrapper: AsyncTwitterWrapper
|
||||
@Inject
|
||||
lateinit var preferences: SharedPreferencesWrapper
|
||||
|
||||
init {
|
||||
GeneralComponentHelper.build(context).inject(this)
|
||||
}
|
||||
|
||||
override fun doLongOperation(params: Pair<String, ParcelableStatusUpdate>): UpdateStatusResult {
|
||||
val draftId = saveDraft(params.first, params.second)
|
||||
twitterWrapper.addSendingDraftId(draftId)
|
||||
try {
|
||||
val result = doUpdateStatus(params.second)
|
||||
deleteOrUpdateDraft(params.second, result, draftId)
|
||||
return result
|
||||
} catch (e: UpdateStatusException) {
|
||||
return UpdateStatusResult(e)
|
||||
} finally {
|
||||
twitterWrapper.removeSendingDraftId(draftId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeExecute() {
|
||||
stateCallback.beforeExecute()
|
||||
}
|
||||
|
||||
override fun afterExecute(handler: Context?, result: UpdateStatusResult?) {
|
||||
stateCallback.afterExecute(handler, result)
|
||||
}
|
||||
|
||||
@Throws(UpdateStatusException::class)
|
||||
private fun doUpdateStatus(update: ParcelableStatusUpdate): UpdateStatusResult {
|
||||
val app = TwidereApplication.getInstance(context)
|
||||
val uploader = getMediaUploader(app)
|
||||
val shortener = getStatusShortener(app)
|
||||
|
||||
val pendingUpdate = PendingStatusUpdate.from(update)
|
||||
|
||||
|
||||
uploadMedia(uploader, update, pendingUpdate)
|
||||
shortenStatus(shortener, update, pendingUpdate)
|
||||
|
||||
val result: UpdateStatusResult
|
||||
try {
|
||||
result = requestUpdateStatus(update, pendingUpdate)
|
||||
} catch (e: IOException) {
|
||||
return UpdateStatusResult(UpdateStatusException(e))
|
||||
}
|
||||
|
||||
mediaUploadCallback(uploader, pendingUpdate, result)
|
||||
statusShortenCallback(shortener, pendingUpdate, result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun deleteOrUpdateDraft(update: ParcelableStatusUpdate, result: UpdateStatusResult, draftId: Long) {
|
||||
val where = Expression.equalsArgs(Drafts._ID).sql
|
||||
val whereArgs = arrayOf(draftId.toString())
|
||||
var hasError = false
|
||||
val failedAccounts = ArrayList<UserKey>()
|
||||
for (i in update.accounts.indices) {
|
||||
val exception = result.exceptions[i]
|
||||
if (exception != null && !isDuplicate(exception)) {
|
||||
hasError = true
|
||||
failedAccounts.add(update.accounts[i].account_key)
|
||||
}
|
||||
}
|
||||
val cr = context.contentResolver
|
||||
if (hasError) {
|
||||
val values = ContentValues()
|
||||
values.put(Drafts.ACCOUNT_KEYS, CollectionUtils.toString(failedAccounts, ',', false))
|
||||
cr.update(Drafts.CONTENT_URI, values, where, whereArgs)
|
||||
// TODO show error message
|
||||
} else {
|
||||
cr.delete(Drafts.CONTENT_URI, where, whereArgs)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadMedia(uploader: MediaUploaderInterface?,
|
||||
update: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate) {
|
||||
stateCallback.onStartUploadingMedia()
|
||||
if (uploader == null) {
|
||||
uploadMediaWithDefaultProvider(update, pendingUpdate)
|
||||
} else {
|
||||
uploadMediaWithExtension(uploader, update, pendingUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadMediaWithExtension(uploader: MediaUploaderInterface,
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
val media: Array<UploaderMediaItem>
|
||||
try {
|
||||
media = UploaderMediaItem.getFromStatusUpdate(context, update)
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw UploadException(e)
|
||||
}
|
||||
|
||||
val sharedMedia = HashMap<UserKey, MediaUploadResult>()
|
||||
for (i in 0..pending.length - 1) {
|
||||
val account = update.accounts[i]
|
||||
// Skip upload if shared media found
|
||||
val accountKey = account.account_key
|
||||
var uploadResult: MediaUploadResult? = sharedMedia[accountKey]
|
||||
if (uploadResult == null) {
|
||||
uploadResult = uploader.upload(update, accountKey, media)
|
||||
if (uploadResult == null) {
|
||||
// TODO error handling
|
||||
continue
|
||||
}
|
||||
pending.mediaUploadResults[i] = uploadResult
|
||||
if (uploadResult.shared_owners != null) {
|
||||
for (sharedOwner in uploadResult.shared_owners) {
|
||||
sharedMedia.put(sharedOwner, uploadResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Override status text
|
||||
pending.overrideTexts[i] = Utils.getMediaUploadStatus(context,
|
||||
uploadResult.media_uris, pending.overrideTexts[i])
|
||||
}
|
||||
}
|
||||
|
||||
private fun shortenStatus(shortener: StatusShortenerInterface?,
|
||||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
if (shortener == null) return
|
||||
stateCallback.onShorteningStatus()
|
||||
val sharedShortened = HashMap<UserKey, StatusShortenResult>()
|
||||
for (i in 0..pending.length - 1) {
|
||||
val account = update.accounts[i]
|
||||
// Skip upload if this shared media found
|
||||
val accountKey = account.account_key
|
||||
var shortenResult: StatusShortenResult? = sharedShortened[accountKey]
|
||||
if (shortenResult == null) {
|
||||
shortenResult = shortener.shorten(update, accountKey, pending.overrideTexts[i])
|
||||
if (shortenResult == null) {
|
||||
// TODO error handling
|
||||
continue
|
||||
}
|
||||
pending.statusShortenResults[i] = shortenResult
|
||||
if (shortenResult.shared_owners != null) {
|
||||
for (sharedOwner in shortenResult.shared_owners) {
|
||||
sharedShortened.put(sharedOwner, shortenResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Override status text
|
||||
pending.overrideTexts[i] = shortenResult.shortened
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun requestUpdateStatus(statusUpdate: ParcelableStatusUpdate, pendingUpdate: PendingStatusUpdate): UpdateStatusResult {
|
||||
|
||||
stateCallback.onUpdatingStatus()
|
||||
|
||||
val result = UpdateStatusResult(arrayOfNulls<ParcelableStatus>(pendingUpdate.length),
|
||||
arrayOfNulls<MicroBlogException>(pendingUpdate.length))
|
||||
|
||||
for (i in 0..pendingUpdate.length - 1) {
|
||||
val account = statusUpdate.accounts[i]
|
||||
val microBlog = MicroBlogAPIFactory.getInstance(context, account.account_key, true)
|
||||
var body: Body? = null
|
||||
try {
|
||||
when (ParcelableAccountUtils.getAccountType(account)) {
|
||||
ParcelableAccount.Type.FANFOU -> {
|
||||
// Call uploadPhoto if media present
|
||||
if (!ArrayUtils.isEmpty(statusUpdate.media)) {
|
||||
// Fanfou only allow one photo
|
||||
if (statusUpdate.media.size > 1) {
|
||||
result.exceptions[i] = MicroBlogException(
|
||||
context.getString(R.string.error_too_many_photos_fanfou))
|
||||
} else {
|
||||
body = getBodyFromMedia(context.contentResolver,
|
||||
Uri.parse(statusUpdate.media[0].uri), ContentLengthInputStream.ReadListener { length, position -> stateCallback.onUploadingProgressChanged(-1, position, length) })
|
||||
val photoUpdate = PhotoStatusUpdate(body,
|
||||
pendingUpdate.overrideTexts[i])
|
||||
val requestResult = microBlog.uploadPhoto(photoUpdate)
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false)
|
||||
}
|
||||
} else {
|
||||
val requestResult = twitterUpdateStatus(microBlog,
|
||||
statusUpdate, pendingUpdate, pendingUpdate.overrideTexts[i], i)
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val requestResult = twitterUpdateStatus(microBlog, statusUpdate,
|
||||
pendingUpdate, pendingUpdate.overrideTexts[i], i)
|
||||
|
||||
result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult,
|
||||
account.account_key, false)
|
||||
}
|
||||
}
|
||||
} catch (e: MicroBlogException) {
|
||||
result.exceptions[i] = e
|
||||
} finally {
|
||||
Utils.closeSilently(body)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling Twitter's upload method. This method sets multiple owner for bandwidth saving
|
||||
*/
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadMediaWithDefaultProvider(update: ParcelableStatusUpdate, pendingUpdate: PendingStatusUpdate) {
|
||||
// Return empty array if no media attached
|
||||
if (ArrayUtils.isEmpty(update.media)) return
|
||||
val ownersList = ArrayList<UserKey>()
|
||||
val ownerIdsList = ArrayList<String>()
|
||||
for (item in update.accounts) {
|
||||
if (ParcelableAccount.Type.TWITTER == ParcelableAccountUtils.getAccountType(item)) {
|
||||
// Add to owners list
|
||||
ownersList.add(item.account_key)
|
||||
ownerIdsList.add(item.account_key.id)
|
||||
}
|
||||
}
|
||||
val ownerIds = ownerIdsList.toTypedArray()
|
||||
for (i in 0..pendingUpdate.length - 1) {
|
||||
val account = update.accounts[i]
|
||||
val mediaIds: Array<String>?
|
||||
when (ParcelableAccountUtils.getAccountType(account)) {
|
||||
ParcelableAccount.Type.TWITTER -> {
|
||||
val upload = MicroBlogAPIFactory.getInstance(context,
|
||||
account.account_key, true, true, TwitterUpload::class.java)!!
|
||||
if (pendingUpdate.sharedMediaIds != null) {
|
||||
mediaIds = pendingUpdate.sharedMediaIds
|
||||
} else {
|
||||
mediaIds = uploadAllMediaShared(upload, update, ownerIds, true)
|
||||
pendingUpdate.sharedMediaIds = mediaIds
|
||||
}
|
||||
}
|
||||
ParcelableAccount.Type.FANFOU -> {
|
||||
// Nope, fanfou uses photo uploading API
|
||||
mediaIds = null
|
||||
}
|
||||
ParcelableAccount.Type.STATUSNET -> {
|
||||
// TODO use their native API
|
||||
val upload = MicroBlogAPIFactory.getInstance(context,
|
||||
account.account_key, true, true, TwitterUpload::class.java)!!
|
||||
mediaIds = uploadAllMediaShared(upload, update, ownerIds, false)
|
||||
}
|
||||
else -> {
|
||||
mediaIds = null
|
||||
}
|
||||
}
|
||||
pendingUpdate.mediaIds[i] = mediaIds
|
||||
}
|
||||
pendingUpdate.sharedMediaOwners = ownersList.toTypedArray()
|
||||
}
|
||||
|
||||
@Throws(MicroBlogException::class)
|
||||
private fun twitterUpdateStatus(microBlog: MicroBlog, statusUpdate: ParcelableStatusUpdate,
|
||||
pendingUpdate: PendingStatusUpdate, overrideText: String,
|
||||
index: Int): Status {
|
||||
val status = StatusUpdate(overrideText)
|
||||
if (statusUpdate.in_reply_to_status != null) {
|
||||
status.inReplyToStatusId(statusUpdate.in_reply_to_status.id)
|
||||
}
|
||||
if (statusUpdate.repost_status_id != null) {
|
||||
status.setRepostStatusId(statusUpdate.repost_status_id)
|
||||
}
|
||||
if (statusUpdate.attachment_url != null) {
|
||||
status.setAttachmentUrl(statusUpdate.attachment_url)
|
||||
}
|
||||
if (statusUpdate.location != null) {
|
||||
status.location(ParcelableLocationUtils.toGeoLocation(statusUpdate.location))
|
||||
status.displayCoordinates(statusUpdate.display_coordinates)
|
||||
}
|
||||
val mediaIds = pendingUpdate.mediaIds[index]
|
||||
if (mediaIds != null) {
|
||||
status.mediaIds(*mediaIds)
|
||||
}
|
||||
status.possiblySensitive(statusUpdate.is_possibly_sensitive)
|
||||
return microBlog.updateStatus(status)
|
||||
}
|
||||
|
||||
private fun statusShortenCallback(shortener: StatusShortenerInterface?, pendingUpdate: PendingStatusUpdate, updateResult: UpdateStatusResult) {
|
||||
if (shortener == null) return
|
||||
for (i in 0..pendingUpdate.length - 1) {
|
||||
val shortenResult = pendingUpdate.statusShortenResults[i]
|
||||
val status = updateResult.statuses[i]
|
||||
if (shortenResult == null || status == null) continue
|
||||
shortener.callback(shortenResult, status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mediaUploadCallback(uploader: MediaUploaderInterface?, pendingUpdate: PendingStatusUpdate, updateResult: UpdateStatusResult) {
|
||||
if (uploader == null) return
|
||||
for (i in 0..pendingUpdate.length - 1) {
|
||||
val uploadResult = pendingUpdate.mediaUploadResults[i]
|
||||
val status = updateResult.statuses[i]
|
||||
if (uploadResult == null || status == null) continue
|
||||
uploader.callback(uploadResult, status)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(UploaderNotFoundException::class, UploadException::class, ShortenerNotFoundException::class, ShortenException::class)
|
||||
private fun getStatusShortener(app: TwidereApplication): StatusShortenerInterface? {
|
||||
val shortenerComponent = preferences.getString(KEY_STATUS_SHORTENER, null)
|
||||
if (ServicePickerPreference.isNoneValue(shortenerComponent)) return null
|
||||
|
||||
val shortener = StatusShortenerInterface.getInstance(app, shortenerComponent) ?: throw ShortenerNotFoundException()
|
||||
try {
|
||||
shortener.checkService { metaData ->
|
||||
if (metaData == null) throw ExtensionVersionMismatchException()
|
||||
val extensionVersion = metaData.getString(METADATA_KEY_EXTENSION_VERSION_STATUS_SHORTENER)
|
||||
if (!TextUtils.equals(extensionVersion, context.getString(R.string.status_shortener_service_interface_version))) {
|
||||
throw ExtensionVersionMismatchException()
|
||||
}
|
||||
}
|
||||
} catch (e: AbsServiceInterface.CheckServiceException) {
|
||||
if (e is ExtensionVersionMismatchException) {
|
||||
throw ShortenException(context.getString(R.string.shortener_version_incompatible))
|
||||
}
|
||||
throw ShortenException(e)
|
||||
}
|
||||
|
||||
return shortener
|
||||
}
|
||||
|
||||
@Throws(UploaderNotFoundException::class, UploadException::class)
|
||||
private fun getMediaUploader(app: TwidereApplication): MediaUploaderInterface? {
|
||||
val uploaderComponent = preferences.getString(KEY_MEDIA_UPLOADER, null)
|
||||
if (ServicePickerPreference.isNoneValue(uploaderComponent)) return null
|
||||
val uploader = MediaUploaderInterface.getInstance(app, uploaderComponent) ?: throw UploaderNotFoundException(context.getString(R.string.error_message_media_uploader_not_found))
|
||||
try {
|
||||
uploader.checkService { metaData ->
|
||||
if (metaData == null) throw ExtensionVersionMismatchException()
|
||||
val extensionVersion = metaData.getString(METADATA_KEY_EXTENSION_VERSION_MEDIA_UPLOADER)
|
||||
if (!TextUtils.equals(extensionVersion, context.getString(R.string.media_uploader_service_interface_version))) {
|
||||
throw ExtensionVersionMismatchException()
|
||||
}
|
||||
}
|
||||
} catch (e: AbsServiceInterface.CheckServiceException) {
|
||||
if (e is ExtensionVersionMismatchException) {
|
||||
throw UploadException(context.getString(R.string.uploader_version_incompatible))
|
||||
}
|
||||
throw UploadException(e)
|
||||
}
|
||||
|
||||
return uploader
|
||||
}
|
||||
|
||||
@Throws(UploadException::class)
|
||||
private fun uploadAllMediaShared(upload: TwitterUpload, update: ParcelableStatusUpdate,
|
||||
ownerIds: Array<String>, chucked: Boolean): Array<String> {
|
||||
val mediaIds = update.media.mapIndexed { index, media ->
|
||||
val resp: MediaUploadResponse
|
||||
//noinspection TryWithIdenticalCatches
|
||||
var body: Body? = null
|
||||
try {
|
||||
body = getBodyFromMedia(context.contentResolver, Uri.parse(media.uri), ContentLengthInputStream.ReadListener { length, position ->
|
||||
stateCallback.onUploadingProgressChanged(index, position, length)
|
||||
})
|
||||
if (chucked) {
|
||||
resp = uploadMediaChucked(upload, body, ownerIds)
|
||||
} else {
|
||||
resp = upload.uploadMedia(body, ownerIds)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw UploadException(e)
|
||||
} catch (e: MicroBlogException) {
|
||||
throw UploadException(e)
|
||||
} finally {
|
||||
Utils.closeSilently(body)
|
||||
}
|
||||
resp.id
|
||||
}
|
||||
return mediaIds.toTypedArray()
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class, MicroBlogException::class)
|
||||
private fun uploadMediaChucked(upload: TwitterUpload, body: Body,
|
||||
ownerIds: Array<String>): MediaUploadResponse {
|
||||
val mediaType = body.contentType().contentType
|
||||
val length = body.length()
|
||||
val stream = body.stream()
|
||||
var response = upload.initUploadMedia(mediaType, length, ownerIds)
|
||||
val segments = if (length == 0L) 0 else (length / BULK_SIZE + 1).toInt()
|
||||
for (segmentIndex in 0..segments - 1) {
|
||||
val currentBulkSize = Math.min(BULK_SIZE.toLong(), 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: MediaUploadResponse.ProcessingInfo = response.processingInfo
|
||||
while (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 && MediaUploadResponse.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 isDuplicate(exception: Exception): Boolean {
|
||||
return exception is MicroBlogException && exception.errorCode == ErrorInfo.STATUS_IS_DUPLICATE
|
||||
}
|
||||
|
||||
private fun shouldWaitForProcess(info: MediaUploadResponse.ProcessingInfo?): Boolean {
|
||||
if (info == null) return false
|
||||
when (info.state) {
|
||||
MediaUploadResponse.ProcessingInfo.State.PENDING, MediaUploadResponse.ProcessingInfo.State.IN_PROGRESS -> return true
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun saveDraft(draftAction: String?, statusUpdate: ParcelableStatusUpdate): Long {
|
||||
val draft = Draft()
|
||||
draft.account_keys = ParcelableAccountUtils.getAccountKeys(statusUpdate.accounts)
|
||||
if (draftAction != null) {
|
||||
draft.action_type = draftAction
|
||||
} else {
|
||||
draft.action_type = Draft.Action.UPDATE_STATUS
|
||||
}
|
||||
draft.text = statusUpdate.text
|
||||
draft.location = statusUpdate.location
|
||||
draft.media = statusUpdate.media
|
||||
val extra = UpdateStatusActionExtra()
|
||||
extra.inReplyToStatus = statusUpdate.in_reply_to_status
|
||||
extra.setIsPossiblySensitive(statusUpdate.is_possibly_sensitive)
|
||||
extra.isRepostStatusId = statusUpdate.repost_status_id
|
||||
extra.displayCoordinates = statusUpdate.display_coordinates
|
||||
draft.action_extras = extra
|
||||
val resolver = context.contentResolver
|
||||
val draftUri = resolver.insert(Drafts.CONTENT_URI, DraftValuesCreator.create(draft)) ?: return -1
|
||||
return NumberUtils.toLong(draftUri.lastPathSegment, -1)
|
||||
}
|
||||
|
||||
internal class PendingStatusUpdate(val length: Int, defaultText: String) {
|
||||
|
||||
var sharedMediaIds: Array<String>? = null
|
||||
var sharedMediaOwners: Array<UserKey>? = null
|
||||
|
||||
val overrideTexts: Array<String>
|
||||
val mediaIds: Array<Array<String>?>
|
||||
|
||||
val mediaUploadResults: Array<MediaUploadResult?>
|
||||
val statusShortenResults: Array<StatusShortenResult?>
|
||||
|
||||
init {
|
||||
overrideTexts = Array(length) { idx ->
|
||||
defaultText
|
||||
}
|
||||
mediaUploadResults = arrayOfNulls<MediaUploadResult>(length)
|
||||
statusShortenResults = arrayOfNulls<StatusShortenResult>(length)
|
||||
mediaIds = arrayOfNulls<Array<String>>(length)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(statusUpdate: ParcelableStatusUpdate): PendingStatusUpdate {
|
||||
return PendingStatusUpdate(statusUpdate.accounts.size,
|
||||
statusUpdate.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateStatusResult {
|
||||
val statuses: Array<ParcelableStatus?>
|
||||
val exceptions: Array<MicroBlogException?>
|
||||
|
||||
val exception: UpdateStatusException?
|
||||
|
||||
constructor(statuses: Array<ParcelableStatus?>, exceptions: Array<MicroBlogException?>) {
|
||||
this.statuses = statuses
|
||||
this.exceptions = exceptions
|
||||
this.exception = null
|
||||
}
|
||||
|
||||
constructor(exception: UpdateStatusException) {
|
||||
this.exception = exception
|
||||
this.statuses = arrayOfNulls<ParcelableStatus>(0)
|
||||
this.exceptions = arrayOfNulls<MicroBlogException>(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open class UpdateStatusException : Exception {
|
||||
constructor() : super() {
|
||||
}
|
||||
|
||||
constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) {
|
||||
}
|
||||
|
||||
constructor(throwable: Throwable) : super(throwable) {
|
||||
}
|
||||
|
||||
constructor(message: String) : super(message) {
|
||||
}
|
||||
}
|
||||
|
||||
class UploaderNotFoundException : UpdateStatusException {
|
||||
|
||||
constructor() : super() {
|
||||
}
|
||||
|
||||
constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) {
|
||||
}
|
||||
|
||||
constructor(throwable: Throwable) : super(throwable) {
|
||||
}
|
||||
|
||||
constructor(message: String) : super(message) {
|
||||
}
|
||||
}
|
||||
|
||||
class UploadException : UpdateStatusException {
|
||||
|
||||
constructor() : super() {
|
||||
}
|
||||
|
||||
constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) {
|
||||
}
|
||||
|
||||
constructor(throwable: Throwable) : super(throwable) {
|
||||
}
|
||||
|
||||
constructor(message: String) : super(message) {
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionVersionMismatchException : AbsServiceInterface.CheckServiceException()
|
||||
|
||||
class ShortenerNotFoundException : UpdateStatusException()
|
||||
|
||||
class ShortenException : UpdateStatusException {
|
||||
|
||||
constructor() : super() {
|
||||
}
|
||||
|
||||
constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) {
|
||||
}
|
||||
|
||||
constructor(throwable: Throwable) : super(throwable) {
|
||||
}
|
||||
|
||||
constructor(message: String) : super(message) {
|
||||
}
|
||||
}
|
||||
|
||||
interface StateCallback {
|
||||
@WorkerThread
|
||||
fun onStartUploadingMedia()
|
||||
|
||||
@WorkerThread
|
||||
fun onUploadingProgressChanged(index: Int, current: Long, total: Long)
|
||||
|
||||
@WorkerThread
|
||||
fun onShorteningStatus()
|
||||
|
||||
@WorkerThread
|
||||
fun onUpdatingStatus()
|
||||
|
||||
@UiThread
|
||||
fun afterExecute(handler: Context?, result: UpdateStatusResult?)
|
||||
|
||||
@UiThread
|
||||
fun beforeExecute()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val BULK_SIZE = 256 * 1024// 128 Kib
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getBodyFromMedia(resolver: ContentResolver,
|
||||
mediaUri: Uri,
|
||||
readListener: ContentLengthInputStream.ReadListener): FileBody {
|
||||
val mediaType = resolver.getType(mediaUri)
|
||||
val `is` = resolver.openInputStream(mediaUri) ?: throw FileNotFoundException(mediaUri.toString())
|
||||
val length = `is`.available().toLong()
|
||||
val cis = ContentLengthInputStream(`is`, length)
|
||||
cis.setReadListener(readListener)
|
||||
val contentType: ContentType
|
||||
if (TextUtils.isEmpty(mediaType)) {
|
||||
contentType = ContentType.parse("application/octet-stream")
|
||||
} else {
|
||||
contentType = ContentType.parse(mediaType!!)
|
||||
}
|
||||
return FileBody(cis, "attachment", length, contentType)
|
||||
}
|
||||
}
|
||||
}
|
@ -935,7 +935,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
||||
field = value
|
||||
}
|
||||
|
||||
override fun getLoginVerification(challengeType: String?): String? {
|
||||
override fun getLoginVerification(challengeType: String): String? {
|
||||
// Dismiss current progress dialog
|
||||
publishProgress(Runnable {
|
||||
val activity = activityRef.get() ?: return@Runnable
|
||||
|
@ -90,10 +90,8 @@ open class BaseSupportWebViewFragment : BaseSupportFragment() {
|
||||
* Called when the fragment is no longer in use. Destroys the internal state of the WebView.
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
if (internalWebView != null) {
|
||||
internalWebView!!.destroy()
|
||||
internalWebView = null
|
||||
}
|
||||
internalWebView?.destroy()
|
||||
internalWebView = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ class ExternalBrowserPageFragment : MediaViewerFragment() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
webView.destroy()
|
||||
webView?.destroy()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
@ -510,7 +510,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
|
||||
return false
|
||||
}
|
||||
|
||||
fun getUserInfo(accountKey: UserKey?, userKey: UserKey?, screenName: String?,
|
||||
fun getUserInfo(accountKey: UserKey, userKey: UserKey?, screenName: String?,
|
||||
omitIntentExtra: Boolean) {
|
||||
val lm = loaderManager
|
||||
lm.destroyLoader(LOADER_ID_USER)
|
||||
@ -614,16 +614,9 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
|
||||
ThemeUtils.getUserThemeBackgroundAlpha(activity))
|
||||
mActionBarShadowColor = 0xA0000000.toInt()
|
||||
val args = arguments
|
||||
var accountId: UserKey? = null
|
||||
var userId: UserKey? = null
|
||||
var screenName: String? = null
|
||||
if (savedInstanceState != null) {
|
||||
args.putAll(savedInstanceState)
|
||||
} else {
|
||||
accountId = args.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||
userId = args.getParcelable<UserKey>(EXTRA_USER_KEY)
|
||||
screenName = args.getString(EXTRA_SCREEN_NAME)
|
||||
}
|
||||
val accountId: UserKey = args.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
|
||||
val userId: UserKey? = args.getParcelable<UserKey>(EXTRA_USER_KEY)
|
||||
val screenName: String? = args.getString(EXTRA_SCREEN_NAME)
|
||||
|
||||
Utils.setNdefPushMessageCallback(activity, CreateNdefMessageCallback {
|
||||
val user = user ?: return@CreateNdefMessageCallback null
|
||||
|
Loading…
x
Reference in New Issue
Block a user