parent
bd03437c71
commit
faf8e980be
|
@ -8,7 +8,7 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
|
||||
classpath 'com.android.tools.build:gradle:2.2.2'
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
classpath 'com.google.gms:google-services:3.0.0'
|
||||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
classpath('fr.avianey.androidsvgdrawable:gradle-plugin:3.0.0') {
|
||||
|
|
|
@ -2,6 +2,9 @@ package org.mariotaku.twidere.annotation;
|
|||
|
||||
import android.support.annotation.StringDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/1/28.
|
||||
*/
|
||||
|
@ -15,6 +18,7 @@ import android.support.annotation.StringDef;
|
|||
CustomTabType.SEARCH_STATUSES,
|
||||
CustomTabType.LIST_TIMELINE,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CustomTabType {
|
||||
String HOME_TIMELINE = "home_timeline";
|
||||
String NOTIFICATIONS_TIMELINE = "notifications_timeline";
|
||||
|
|
|
@ -30,7 +30,7 @@ import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
|||
import org.mariotaku.commons.objectcursor.LoganSquareCursorFieldConverter;
|
||||
import org.mariotaku.library.objectcursor.annotation.CursorField;
|
||||
import org.mariotaku.library.objectcursor.annotation.CursorObject;
|
||||
import org.mariotaku.twidere.model.draft.ActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.ActionExtras;
|
||||
import org.mariotaku.twidere.model.util.DraftExtrasConverter;
|
||||
import org.mariotaku.twidere.model.util.UserKeysCursorFieldConverter;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||
|
@ -69,7 +69,7 @@ public class Draft implements Parcelable {
|
|||
@Nullable
|
||||
@ParcelableThisPlease
|
||||
@CursorField(value = Drafts.ACTION_EXTRAS, converter = DraftExtrasConverter.class)
|
||||
public ActionExtra action_extras;
|
||||
public ActionExtras action_extras;
|
||||
|
||||
|
||||
public Draft() {
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.content.ContentValues;
|
|||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
|
@ -85,7 +86,7 @@ public class ParcelableLocation implements Parcelable {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
public static ParcelableLocation valueOf(@Nullable final String locationString) {
|
||||
public static ParcelableLocation valueOf(@NonNull final String locationString) {
|
||||
if (locationString == null) return null;
|
||||
final String[] longlat = locationString.split(",");
|
||||
if (longlat.length != 2) {
|
||||
|
@ -106,7 +107,9 @@ public class ParcelableLocation implements Parcelable {
|
|||
public static class Converter implements CursorFieldConverter<ParcelableLocation> {
|
||||
@Override
|
||||
public ParcelableLocation parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) {
|
||||
return valueOf(cursor.getString(columnIndex));
|
||||
final String locationString = cursor.getString(columnIndex);
|
||||
if (locationString == null) return null;
|
||||
return valueOf(locationString);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,5 +5,5 @@ import android.os.Parcelable;
|
|||
/**
|
||||
* Created by mariotaku on 16/2/21.
|
||||
*/
|
||||
public interface ActionExtra extends Parcelable {
|
||||
public interface ActionExtras extends Parcelable {
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package org.mariotaku.twidere.model.draft;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/21.
|
||||
*/
|
||||
@ParcelablePlease
|
||||
@JsonObject
|
||||
public class SendDirectMessageActionExtra implements ActionExtra {
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "recipient_id")
|
||||
String recipientId;
|
||||
|
||||
public String getRecipientId() {
|
||||
return recipientId;
|
||||
}
|
||||
|
||||
public void setRecipientId(String recipientId) {
|
||||
this.recipientId = recipientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
SendDirectMessageActionExtraParcelablePlease.writeToParcel(this, dest, flags);
|
||||
}
|
||||
|
||||
public static final Creator<SendDirectMessageActionExtra> CREATOR = new Creator<SendDirectMessageActionExtra>() {
|
||||
@Override
|
||||
public SendDirectMessageActionExtra createFromParcel(Parcel source) {
|
||||
SendDirectMessageActionExtra target = new SendDirectMessageActionExtra();
|
||||
SendDirectMessageActionExtraParcelablePlease.readFromParcel(target, source);
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendDirectMessageActionExtra[] newArray(int size) {
|
||||
return new SendDirectMessageActionExtra[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.mariotaku.twidere.model.draft;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/21.
|
||||
*/
|
||||
@ParcelablePlease
|
||||
@JsonObject
|
||||
public class SendDirectMessageActionExtras implements ActionExtras {
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "recipient_id")
|
||||
String recipientId;
|
||||
|
||||
public String getRecipientId() {
|
||||
return recipientId;
|
||||
}
|
||||
|
||||
public void setRecipientId(String recipientId) {
|
||||
this.recipientId = recipientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
SendDirectMessageActionExtrasParcelablePlease.writeToParcel(this, dest, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SendDirectMessageActionExtras that = (SendDirectMessageActionExtras) o;
|
||||
|
||||
return recipientId != null ? recipientId.equals(that.recipientId) : that.recipientId == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return recipientId != null ? recipientId.hashCode() : 0;
|
||||
}
|
||||
|
||||
public static final Creator<SendDirectMessageActionExtras> CREATOR = new Creator<SendDirectMessageActionExtras>() {
|
||||
@Override
|
||||
public SendDirectMessageActionExtras createFromParcel(Parcel source) {
|
||||
SendDirectMessageActionExtras target = new SendDirectMessageActionExtras();
|
||||
SendDirectMessageActionExtrasParcelablePlease.readFromParcel(target, source);
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendDirectMessageActionExtras[] newArray(int size) {
|
||||
return new SendDirectMessageActionExtras[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -14,7 +14,7 @@ import org.mariotaku.twidere.model.ParcelableStatus;
|
|||
*/
|
||||
@ParcelablePlease
|
||||
@JsonObject
|
||||
public class UpdateStatusActionExtra implements ActionExtra {
|
||||
public class UpdateStatusActionExtras implements ActionExtras {
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "in_reply_to_status")
|
||||
ParcelableStatus inReplyToStatus;
|
||||
|
@ -78,20 +78,47 @@ public class UpdateStatusActionExtra implements ActionExtra {
|
|||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
UpdateStatusActionExtraParcelablePlease.writeToParcel(this, dest, flags);
|
||||
UpdateStatusActionExtrasParcelablePlease.writeToParcel(this, dest, flags);
|
||||
}
|
||||
|
||||
public static final Creator<UpdateStatusActionExtra> CREATOR = new Creator<UpdateStatusActionExtra>() {
|
||||
@Override
|
||||
public UpdateStatusActionExtra createFromParcel(Parcel source) {
|
||||
UpdateStatusActionExtra target = new UpdateStatusActionExtra();
|
||||
UpdateStatusActionExtraParcelablePlease.readFromParcel(target, source);
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
UpdateStatusActionExtras that = (UpdateStatusActionExtras) o;
|
||||
|
||||
if (possiblySensitive != that.possiblySensitive) return false;
|
||||
if (displayCoordinates != that.displayCoordinates) return false;
|
||||
if (inReplyToStatus != null ? !inReplyToStatus.equals(that.inReplyToStatus) : that.inReplyToStatus != null)
|
||||
return false;
|
||||
if (repostStatusId != null ? !repostStatusId.equals(that.repostStatusId) : that.repostStatusId != null)
|
||||
return false;
|
||||
return attachmentUrl != null ? attachmentUrl.equals(that.attachmentUrl) : that.attachmentUrl == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = inReplyToStatus != null ? inReplyToStatus.hashCode() : 0;
|
||||
result = 31 * result + (possiblySensitive ? 1 : 0);
|
||||
result = 31 * result + (repostStatusId != null ? repostStatusId.hashCode() : 0);
|
||||
result = 31 * result + (displayCoordinates ? 1 : 0);
|
||||
result = 31 * result + (attachmentUrl != null ? attachmentUrl.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final Creator<UpdateStatusActionExtras> CREATOR = new Creator<UpdateStatusActionExtras>() {
|
||||
@Override
|
||||
public UpdateStatusActionExtras createFromParcel(Parcel source) {
|
||||
UpdateStatusActionExtras target = new UpdateStatusActionExtras();
|
||||
UpdateStatusActionExtrasParcelablePlease.readFromParcel(target, source);
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateStatusActionExtra[] newArray(int size) {
|
||||
return new UpdateStatusActionExtra[size];
|
||||
public UpdateStatusActionExtras[] newArray(int size) {
|
||||
return new UpdateStatusActionExtras[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -8,9 +8,9 @@ import com.bluelinelabs.logansquare.LoganSquare;
|
|||
|
||||
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
|
||||
import org.mariotaku.twidere.model.Draft;
|
||||
import org.mariotaku.twidere.model.draft.ActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.ActionExtras;
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras;
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -19,33 +19,30 @@ import java.lang.reflect.ParameterizedType;
|
|||
/**
|
||||
* Created by mariotaku on 16/2/20.
|
||||
*/
|
||||
public class DraftExtrasConverter implements CursorFieldConverter<ActionExtra> {
|
||||
public class DraftExtrasConverter implements CursorFieldConverter<ActionExtras> {
|
||||
@Override
|
||||
public ActionExtra parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException {
|
||||
public ActionExtras parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException {
|
||||
final String actionType = cursor.getString(cursor.getColumnIndex(Drafts.ACTION_TYPE));
|
||||
if (TextUtils.isEmpty(actionType)) return null;
|
||||
final String json = cursor.getString(columnIndex);
|
||||
if (TextUtils.isEmpty(actionType) || TextUtils.isEmpty(json)) return null;
|
||||
switch (actionType) {
|
||||
case "0":
|
||||
case "1":
|
||||
case Draft.Action.UPDATE_STATUS:
|
||||
case Draft.Action.REPLY:
|
||||
case Draft.Action.QUOTE: {
|
||||
final String string = cursor.getString(columnIndex);
|
||||
if (TextUtils.isEmpty(string)) return null;
|
||||
return LoganSquare.parse(string, UpdateStatusActionExtra.class);
|
||||
return LoganSquare.parse(json, UpdateStatusActionExtras.class);
|
||||
}
|
||||
case "2":
|
||||
case Draft.Action.SEND_DIRECT_MESSAGE: {
|
||||
final String string = cursor.getString(columnIndex);
|
||||
if (TextUtils.isEmpty(string)) return null;
|
||||
return LoganSquare.parse(string, SendDirectMessageActionExtra.class);
|
||||
return LoganSquare.parse(json, SendDirectMessageActionExtras.class);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeField(ContentValues values, ActionExtra object, String columnName, ParameterizedType fieldType) throws IOException {
|
||||
public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException {
|
||||
if (object == null) return;
|
||||
values.put(columnName, LoganSquare.serialize(object));
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ dependencies {
|
|||
androidTestCompile "com.android.support:support-annotations:$android_support_lib_version"
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||
androidTestCompile 'commons-io:commons-io:2.5'
|
||||
|
||||
compile 'com.android.support:multidex:1.0.1'
|
||||
compile "com.android.support:support-v4:$android_support_lib_version"
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package org.mariotaku.twidere
|
||||
|
||||
import android.net.Uri
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mariotaku.twidere.extension.readMimeMessageFrom
|
||||
import org.mariotaku.twidere.extension.writeMimeMessageTo
|
||||
import org.mariotaku.twidere.model.Draft
|
||||
import org.mariotaku.twidere.model.ParcelableMedia
|
||||
import org.mariotaku.twidere.model.ParcelableMediaUpdate
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.*
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -24,16 +23,24 @@ class DraftExtensionsTest {
|
|||
fun testMimeMessageProcessing() {
|
||||
val context = InstrumentationRegistry.getTargetContext()
|
||||
val draft = Draft()
|
||||
draft.action_type = Draft.Action.UPDATE_STATUS
|
||||
draft.timestamp = System.currentTimeMillis()
|
||||
draft.account_keys = arrayOf(UserKey("user1", "twitter.com"), UserKey("user2", "twitter.com"))
|
||||
draft.text = "Hello world"
|
||||
draft.media = arrayOf(ParcelableMediaUpdate().apply {
|
||||
this.uri = "file:///system/media/audio/ringtones/Atria.ogg"
|
||||
draft.text = "Hello world 测试"
|
||||
draft.location = ParcelableLocation(-11.956, 99.625) // Randomly generated
|
||||
draft.media = arrayOf(
|
||||
"file:///system/media/audio/ringtones/Atria.ogg",
|
||||
"file:///system/media/audio/ringtones/Callisto.ogg",
|
||||
"file:///system/media/audio/ringtones/Dione.ogg"
|
||||
).map { uri ->
|
||||
ParcelableMediaUpdate().apply {
|
||||
this.uri = uri
|
||||
this.type = ParcelableMedia.Type.VIDEO
|
||||
this.alt_text = String(CharArray(420).apply {
|
||||
fill('A')
|
||||
})
|
||||
})
|
||||
}
|
||||
}.toTypedArray()
|
||||
val output = ByteArrayOutputStream()
|
||||
draft.writeMimeMessageTo(context, output)
|
||||
val input = ByteArrayInputStream(output.toByteArray())
|
||||
|
@ -44,10 +51,16 @@ class DraftExtensionsTest {
|
|||
Assert.assertArrayEquals(draft.account_keys?.sortedArray(), newDraft.account_keys?.sortedArray())
|
||||
Assert.assertEquals(TimeUnit.MILLISECONDS.toSeconds(draft.timestamp), TimeUnit.MILLISECONDS.toSeconds(newDraft.timestamp))
|
||||
Assert.assertEquals(draft.text, newDraft.text)
|
||||
Assert.assertEquals(draft.location, newDraft.location)
|
||||
Assert.assertEquals(draft.action_type, newDraft.action_type)
|
||||
Assert.assertEquals(draft.action_extras, newDraft.action_extras)
|
||||
draft.media?.forEachIndexed { idx, expected ->
|
||||
val actual = newDraft.media!![idx]
|
||||
Assert.assertEquals(expected.alt_text, actual.alt_text)
|
||||
Assert.assertEquals(expected.type, actual.type)
|
||||
val stl = context.contentResolver.openInputStream(Uri.parse(expected.uri))
|
||||
val str = context.contentResolver.openInputStream(Uri.parse(actual.uri))
|
||||
Assert.assertTrue(IOUtils.contentEquals(stl, str))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,9 @@
|
|||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.mariotaku.twidere.activity.DropboxAuthStarterActivity"
|
||||
android:theme="@style/Theme.Twidere.NoDisplay"/>
|
||||
<activity
|
||||
android:name="com.dropbox.core.android.AuthActivity"
|
||||
android:configChanges="orientation|keyboard"
|
||||
|
@ -42,5 +45,7 @@
|
|||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="org.mariotaku.twidere.service.DropboxDataSyncService"/>
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,16 @@
|
|||
package org.mariotaku.twidere.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import com.dropbox.core.android.Auth
|
||||
import org.mariotaku.twidere.Constants.DROPBOX_APP_KEY
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/7.
|
||||
*/
|
||||
class DropboxAuthStarterActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Auth.startOAuth2Authentication(this, DROPBOX_APP_KEY)
|
||||
finish()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.mariotaku.twidere.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.SyncResult
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import org.mariotaku.twidere.IDataSyncService
|
||||
import org.mariotaku.twidere.activity.DropboxAuthStarterActivity
|
||||
import org.mariotaku.twidere.model.SyncAuthInfo
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/7.
|
||||
*/
|
||||
|
||||
class DropboxDataSyncService : Service() {
|
||||
private val serviceInterface: ServiceInterface
|
||||
|
||||
init {
|
||||
serviceInterface = ServiceInterface(WeakReference(this))
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
return serviceInterface.asBinder()
|
||||
}
|
||||
|
||||
private fun getAuthInfo(): SyncAuthInfo? {
|
||||
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
private fun getAuthRequestIntent(info: SyncAuthInfo?): Intent {
|
||||
return Intent(this, DropboxAuthStarterActivity::class.java)
|
||||
}
|
||||
|
||||
private fun onPerformSync(info: SyncAuthInfo, extras: Bundle?, syncResult: SyncResult) {
|
||||
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
internal class ServiceInterface(val service: WeakReference<DropboxDataSyncService>) : IDataSyncService.Stub() {
|
||||
|
||||
override fun getAuthInfo(): SyncAuthInfo? {
|
||||
return service.get().getAuthInfo()
|
||||
}
|
||||
|
||||
override fun getAuthRequestIntent(info: SyncAuthInfo?): Intent {
|
||||
return service.get().getAuthRequestIntent(info)
|
||||
}
|
||||
|
||||
override fun onPerformSync(info: SyncAuthInfo, extras: Bundle?, syncResult: SyncResult) {
|
||||
service.get().onPerformSync(info, extras, syncResult)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ import android.content.Context;
|
|||
import org.mariotaku.twidere.model.AccountDetails;
|
||||
import org.mariotaku.twidere.model.Draft;
|
||||
import org.mariotaku.twidere.model.ParcelableStatusUpdate;
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/12.
|
||||
|
@ -25,8 +25,8 @@ public class ParcelableStatusUpdateUtils {
|
|||
statusUpdate.text = draft.text;
|
||||
statusUpdate.location = draft.location;
|
||||
statusUpdate.media = draft.media;
|
||||
if (draft.action_extras instanceof UpdateStatusActionExtra) {
|
||||
final UpdateStatusActionExtra extra = (UpdateStatusActionExtra) draft.action_extras;
|
||||
if (draft.action_extras instanceof UpdateStatusActionExtras) {
|
||||
final UpdateStatusActionExtras extra = (UpdateStatusActionExtras) draft.action_extras;
|
||||
statusUpdate.in_reply_to_status = extra.getInReplyToStatus();
|
||||
statusUpdate.is_possibly_sensitive = extra.isPossiblySensitive();
|
||||
statusUpdate.display_coordinates = extra.getDisplayCoordinates();
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.mariotaku.twidere.model.ParcelableUser;
|
|||
import org.mariotaku.twidere.model.ParcelableUserMention;
|
||||
import org.mariotaku.twidere.model.ParcelableUserValuesCreator;
|
||||
import org.mariotaku.twidere.model.UserKey;
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra;
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras;
|
||||
import org.mariotaku.twidere.model.util.ParcelableActivityExtensionsKt;
|
||||
import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils;
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
|
||||
|
@ -121,7 +121,7 @@ public final class ContentValuesCreator implements TwidereConstants {
|
|||
values.put(Drafts.MEDIA, JsonSerializer.serialize(Arrays.asList(mediaArray),
|
||||
ParcelableMediaUpdate.class));
|
||||
}
|
||||
final SendDirectMessageActionExtra extra = new SendDirectMessageActionExtra();
|
||||
final SendDirectMessageActionExtras extra = new SendDirectMessageActionExtras();
|
||||
extra.setRecipientId(recipientId);
|
||||
values.put(Drafts.ACTION_EXTRAS, JsonSerializer.serialize(extra));
|
||||
return values;
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.os.RemoteException;
|
|||
import android.util.Log;
|
||||
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.IStatusShortener;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.model.ParcelableStatusUpdate;
|
||||
|
@ -40,8 +39,10 @@ import org.mariotaku.twidere.model.UserKey;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public final class StatusShortenerInterface extends AbsServiceInterface<IStatusShortener>
|
||||
implements Constants {
|
||||
import static org.mariotaku.twidere.TwidereConstants.LOGTAG;
|
||||
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_EXTENSION_SHORTEN_STATUS;
|
||||
|
||||
public final class StatusShortenerInterface extends AbsServiceInterface<IStatusShortener> {
|
||||
|
||||
protected StatusShortenerInterface(Context context, String shortenerName, Bundle metaData) {
|
||||
super(context, shortenerName, metaData);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.mariotaku.twidere.util.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/8.
|
||||
*/
|
||||
|
||||
public final class CountOnlyOutputStream extends OutputStream {
|
||||
private int count;
|
||||
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
count++;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ import org.mariotaku.twidere.constant.KeyboardShortcutConstants
|
|||
import org.mariotaku.twidere.fragment.BaseDialogFragment
|
||||
import org.mariotaku.twidere.fragment.ProgressDialogFragment
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
|
||||
import org.mariotaku.twidere.preference.ServicePickerPreference
|
||||
|
@ -178,8 +178,11 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
|
||||
override fun onBackPressed() {
|
||||
if (currentTask != null && currentTask!!.status == AsyncTask.Status.RUNNING) return
|
||||
if (hasComposingStatus()) {
|
||||
shouldSkipDraft = false
|
||||
if (!shouldSkipDraft && hasComposingStatus() ) {
|
||||
saveToDrafts()
|
||||
Toast.makeText(this, R.string.status_saved_to_draft, Toast.LENGTH_SHORT).show()
|
||||
shouldSkipDraft = true
|
||||
finish()
|
||||
} else {
|
||||
shouldSkipDraft = true
|
||||
discardTweet()
|
||||
|
@ -429,7 +432,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
draft.text = text
|
||||
draft.media = media
|
||||
draft.location = recentLocation
|
||||
draft.action_extras = UpdateStatusActionExtra().apply {
|
||||
draft.timestamp = System.currentTimeMillis()
|
||||
draft.action_extras = UpdateStatusActionExtras().apply {
|
||||
this.inReplyToStatus = this@ComposeActivity.inReplyToStatus
|
||||
this.isPossiblySensitive = this@ComposeActivity.possiblySensitive
|
||||
}
|
||||
|
@ -837,8 +841,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
addMedia(Arrays.asList(*draft.media))
|
||||
}
|
||||
recentLocation = draft.location
|
||||
if (draft.action_extras is UpdateStatusActionExtra) {
|
||||
val extra = draft.action_extras as UpdateStatusActionExtra?
|
||||
if (draft.action_extras is UpdateStatusActionExtras) {
|
||||
val extra = draft.action_extras as UpdateStatusActionExtras?
|
||||
possiblySensitive = extra!!.isPossiblySensitive
|
||||
inReplyToStatus = extra.inReplyToStatus
|
||||
}
|
||||
|
@ -871,16 +875,16 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
when (draft.action_type) {
|
||||
Draft.Action.REPLY -> {
|
||||
if (draft.action_extras is UpdateStatusActionExtra) {
|
||||
showReplyLabel((draft.action_extras as UpdateStatusActionExtra).inReplyToStatus)
|
||||
if (draft.action_extras is UpdateStatusActionExtras) {
|
||||
showReplyLabel((draft.action_extras as UpdateStatusActionExtras).inReplyToStatus)
|
||||
} else {
|
||||
hideLabel()
|
||||
return false
|
||||
}
|
||||
}
|
||||
Draft.Action.QUOTE -> {
|
||||
if (draft.action_extras is UpdateStatusActionExtra) {
|
||||
showQuoteLabel((draft.action_extras as UpdateStatusActionExtra).inReplyToStatus)
|
||||
if (draft.action_extras is UpdateStatusActionExtras) {
|
||||
showQuoteLabel((draft.action_extras as UpdateStatusActionExtras).inReplyToStatus)
|
||||
} else {
|
||||
hideLabel()
|
||||
return false
|
||||
|
@ -1280,7 +1284,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
update.media = media
|
||||
update.in_reply_to_status = inReplyToStatus
|
||||
update.is_possibly_sensitive = isPossiblySensitive
|
||||
update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtra)?.attachmentUrl
|
||||
update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtras)?.attachmentUrl
|
||||
BackgroundOperationService.updateStatusesAsync(this, action, update)
|
||||
if (preferences.getBoolean(KEY_NO_CLOSE_AFTER_TWEET_SENT, false) && inReplyToStatus == null) {
|
||||
possiblySensitive = false
|
||||
|
|
|
@ -48,7 +48,7 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li
|
|||
private val mediaPreviewStyle: Int
|
||||
|
||||
private var mTextSize: Float = 0.toFloat()
|
||||
private var mIndices: DraftCursorIndices? = null
|
||||
private var indices: DraftCursorIndices? = null
|
||||
|
||||
init {
|
||||
GeneralComponentHelper.build(context).inject(this)
|
||||
|
@ -58,7 +58,7 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li
|
|||
|
||||
override fun bindView(view: View, context: Context, cursor: Cursor) {
|
||||
val holder = view.tag as DraftViewHolder
|
||||
val indices = mIndices!!
|
||||
val indices = indices!!
|
||||
val accountKeys = UserKey.arrayOf(cursor.getString(indices.account_keys))
|
||||
val text = cursor.getString(indices.text)
|
||||
val mediaUpdates = JsonSerializer.parseArray(cursor.getString(indices.media), ParcelableMediaUpdate::class.java)
|
||||
|
@ -115,11 +115,16 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li
|
|||
override fun swapCursor(c: Cursor?): Cursor? {
|
||||
val old = super.swapCursor(c)
|
||||
if (c != null) {
|
||||
mIndices = DraftCursorIndices(c)
|
||||
indices = DraftCursorIndices(c)
|
||||
}
|
||||
return old
|
||||
}
|
||||
|
||||
fun getDraft(position: Int): Draft {
|
||||
cursor.moveToPosition(position)
|
||||
return indices!!.newObject(cursor)
|
||||
}
|
||||
|
||||
private fun getActionName(context: Context, actionType: String): String? {
|
||||
if (TextUtils.isEmpty(actionType)) return context.getString(R.string.update_status)
|
||||
when (actionType) {
|
||||
|
|
|
@ -2,27 +2,30 @@ package org.mariotaku.twidere.extension
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nostra13.universalimageloader.utils.IoUtils
|
||||
import org.apache.james.mime4j.dom.Header
|
||||
import org.apache.james.mime4j.dom.MessageServiceFactory
|
||||
import org.apache.james.mime4j.dom.address.Mailbox
|
||||
import org.apache.james.mime4j.dom.field.*
|
||||
import org.apache.james.mime4j.message.AbstractMessage
|
||||
import org.apache.james.mime4j.message.BodyPart
|
||||
import org.apache.james.mime4j.message.MultipartImpl
|
||||
import org.apache.james.mime4j.message.SimpleContentHandler
|
||||
import org.apache.james.mime4j.message.*
|
||||
import org.apache.james.mime4j.parser.MimeStreamParser
|
||||
import org.apache.james.mime4j.storage.StorageBodyFactory
|
||||
import org.apache.james.mime4j.stream.BodyDescriptor
|
||||
import org.apache.james.mime4j.stream.MimeConfig
|
||||
import org.apache.james.mime4j.stream.RawField
|
||||
import org.apache.james.mime4j.util.MimeUtil
|
||||
import org.mariotaku.ktextension.convert
|
||||
import org.mariotaku.ktextension.toInt
|
||||
import org.mariotaku.ktextension.toString
|
||||
import org.mariotaku.twidere.extension.model.getMimeType
|
||||
import org.mariotaku.twidere.model.Draft
|
||||
import org.mariotaku.twidere.model.ParcelableMedia
|
||||
import org.mariotaku.twidere.model.ParcelableMediaUpdate
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.Draft.Action
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
|
||||
import org.mariotaku.twidere.util.collection.NonEmptyHashMap
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.charset.Charset
|
||||
|
@ -45,44 +48,64 @@ fun Draft.writeMimeMessageTo(context: Context, st: OutputStream) {
|
|||
|
||||
message.date = Date(this.timestamp)
|
||||
message.setFrom(this.account_keys?.map { Mailbox(it.id, it.host) })
|
||||
if (message.header == null) {
|
||||
message.header = HeaderImpl()
|
||||
}
|
||||
|
||||
this.location?.let { location ->
|
||||
message.header.addField(RawField("X-GeoLocation", location.toString()))
|
||||
}
|
||||
this.action_type?.let { type ->
|
||||
message.header.addField(RawField("X-Action-Type", type))
|
||||
}
|
||||
|
||||
val multipart = MultipartImpl("mixed")
|
||||
multipart.addBodyPart(BodyPart().apply {
|
||||
setText(bodyFactory.textBody(this@writeMimeMessageTo.text))
|
||||
setText(bodyFactory.textBody(this@writeMimeMessageTo.text, Charsets.UTF_8.name()))
|
||||
})
|
||||
|
||||
this.action_extras?.let { extras ->
|
||||
multipart.addBodyPart(BodyPart().apply {
|
||||
setText(bodyFactory.textBody(LoganSquare.serialize(extras)), "json")
|
||||
this.filename = "twidere.action.extras.json"
|
||||
})
|
||||
}
|
||||
this.media?.forEach { mediaItem ->
|
||||
multipart.addBodyPart(BodyPart().apply {
|
||||
val uri = Uri.parse(mediaItem.uri)
|
||||
val storage = storageProvider.store(contentResolver.openInputStream(uri))
|
||||
val mimeType = mediaItem.getMimeType(contentResolver) ?: "application/octet-stream"
|
||||
val parameters = NonEmptyHashMap<String, String?>()
|
||||
parameters["alt_text"] = mediaItem.alt_text
|
||||
parameters["media_type"] = mediaItem.type.toString()
|
||||
this.setBody(bodyFactory.binaryBody(storage), mimeType, parameters)
|
||||
val storage = storageProvider.store(contentResolver.openInputStream(uri))
|
||||
this.filename = uri.lastPathSegment
|
||||
this.contentTransferEncoding = MimeUtil.ENC_BASE64
|
||||
this.setBody(bodyFactory.binaryBody(storage), mimeType, parameters)
|
||||
})
|
||||
}
|
||||
|
||||
message.setMultipart(multipart)
|
||||
writer.writeMessage(message, st)
|
||||
st.flush()
|
||||
}
|
||||
|
||||
fun Draft.readMimeMessageFrom(context: Context, st: InputStream) {
|
||||
val config = MimeConfig()
|
||||
val parser = MimeStreamParser(config)
|
||||
parser.setContentHandler(DraftContentHandler(this))
|
||||
parser.isContentDecoding = true
|
||||
parser.setContentHandler(DraftContentHandler(context, this))
|
||||
parser.parse(st)
|
||||
}
|
||||
|
||||
private class DraftContentHandler(private val draft: Draft) : SimpleContentHandler() {
|
||||
private class DraftContentHandler(private val context: Context, private val draft: Draft) : SimpleContentHandler() {
|
||||
private val processingStack = Stack<SimpleContentHandler>()
|
||||
private val mediaList: MutableList<ParcelableMediaUpdate> = ArrayList()
|
||||
override fun headers(header: Header) {
|
||||
if (processingStack.isEmpty()) {
|
||||
draft.timestamp = header.getField("Date").convert {
|
||||
draft.timestamp = header.getField("Date")?.convert {
|
||||
(it as DateTimeField).date.time
|
||||
}
|
||||
draft.account_keys = header.getField("From").convert { field ->
|
||||
} ?: 0
|
||||
draft.account_keys = header.getField("From")?.convert { field ->
|
||||
when (field) {
|
||||
is MailboxField -> {
|
||||
return@convert arrayOf(field.mailbox.convert { UserKey(it.localPart, it.domain) })
|
||||
|
@ -95,6 +118,8 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl
|
|||
}
|
||||
}
|
||||
}
|
||||
draft.location = header.getField("X-GeoLocation")?.body?.convert(ParcelableLocation::valueOf)
|
||||
draft.action_type = header.getField("X-Action-Type")?.body
|
||||
} else {
|
||||
processingStack.peek().headers(header)
|
||||
}
|
||||
|
@ -108,7 +133,7 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl
|
|||
}
|
||||
|
||||
override fun startBodyPart() {
|
||||
processingStack.push(BodyPartHandler(draft))
|
||||
processingStack.push(BodyPartHandler(context, draft))
|
||||
}
|
||||
|
||||
override fun body(bd: BodyDescriptor?, `is`: InputStream?) {
|
||||
|
@ -131,7 +156,7 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl
|
|||
}
|
||||
}
|
||||
|
||||
private class BodyPartHandler(private val draft: Draft) : SimpleContentHandler() {
|
||||
private class BodyPartHandler(private val context: Context, private val draft: Draft) : SimpleContentHandler() {
|
||||
internal lateinit var header: Header
|
||||
internal var media: ParcelableMediaUpdate? = null
|
||||
|
||||
|
@ -147,11 +172,32 @@ private class BodyPartHandler(private val draft: Draft) : SimpleContentHandler()
|
|||
val contentDisposition = header.getField("Content-Disposition") as? ContentDispositionField
|
||||
if (contentDisposition != null && contentDisposition.isAttachment) {
|
||||
when (contentDisposition.filename) {
|
||||
"twidere.action.extras.json" -> {
|
||||
draft.action_extras = when (draft.action_type) {
|
||||
"0", "1", Action.UPDATE_STATUS, Action.REPLY, Action.QUOTE -> {
|
||||
LoganSquare.parse(st, UpdateStatusActionExtras::class.java)
|
||||
}
|
||||
"2", Action.SEND_DIRECT_MESSAGE -> {
|
||||
LoganSquare.parse(st, SendDirectMessageActionExtras::class.java)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val contentType = header.getField("Content-Type") as? ContentTypeField
|
||||
val filename = contentDisposition.filename ?: return
|
||||
val mediaFile = File(context.filesDir, filename)
|
||||
media = ParcelableMediaUpdate().apply {
|
||||
bd.transferEncoding
|
||||
this.type = contentType?.getParameter("media_type").toInt(ParcelableMedia.Type.UNKNOWN)
|
||||
this.alt_text = contentType?.getParameter("alt_text")
|
||||
FileOutputStream(mediaFile).use {
|
||||
IoUtils.copyStream(st, it, null)
|
||||
it.flush()
|
||||
}
|
||||
this.uri = Uri.fromFile(mediaFile).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,11 @@ import android.content.DialogInterface
|
|||
import android.content.DialogInterface.OnClickListener
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.support.v4.app.DialogFragment
|
||||
import android.support.v4.app.FragmentActivity
|
||||
|
@ -43,7 +45,10 @@ import android.widget.AbsListView.MultiChoiceModeListener
|
|||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ListView
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.fragment_drafts.*
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.sqliteqb.library.Columns.Column
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.sqliteqb.library.RawItemArray
|
||||
|
@ -52,10 +57,10 @@ import org.mariotaku.twidere.TwidereConstants.*
|
|||
import org.mariotaku.twidere.activity.iface.IExtendedActivity
|
||||
import org.mariotaku.twidere.adapter.DraftsAdapter
|
||||
import org.mariotaku.twidere.constant.IntentConstants
|
||||
import org.mariotaku.twidere.extension.writeMimeMessageTo
|
||||
import org.mariotaku.twidere.model.Draft
|
||||
import org.mariotaku.twidere.model.DraftCursorIndices
|
||||
import org.mariotaku.twidere.model.ParcelableMediaUpdate
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
||||
import org.mariotaku.twidere.service.BackgroundOperationService
|
||||
|
@ -63,6 +68,7 @@ import org.mariotaku.twidere.util.AsyncTaskUtils
|
|||
import org.mariotaku.twidere.util.JsonSerializer
|
||||
import org.mariotaku.twidere.util.Utils.getDefaultTextSize
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
|
||||
class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemClickListener, MultiChoiceModeListener {
|
||||
|
@ -85,27 +91,52 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
|
|||
R.id.delete -> {
|
||||
val f = DeleteDraftsConfirmDialogFragment()
|
||||
val args = Bundle()
|
||||
args.putLongArray(IntentConstants.EXTRA_IDS, listView!!.checkedItemIds)
|
||||
args.putLongArray(IntentConstants.EXTRA_IDS, listView.checkedItemIds)
|
||||
f.arguments = args
|
||||
f.show(childFragmentManager, "delete_drafts_confirm")
|
||||
}
|
||||
R.id.send -> {
|
||||
val c = adapter!!.cursor
|
||||
if (c == null || c.isClosed) return false
|
||||
val checked = listView!!.checkedItemPositions
|
||||
val checked = listView.checkedItemPositions
|
||||
val list = ArrayList<Draft>()
|
||||
val indices = DraftCursorIndices(c)
|
||||
for (i in 0 until checked.size()) {
|
||||
if (checked.valueAt(i) && c.moveToPosition(checked.keyAt(i))) {
|
||||
list.add(indices.newObject(c))
|
||||
val position = checked.keyAt(i)
|
||||
if (checked.valueAt(i)) {
|
||||
list.add(adapter!!.getDraft(position))
|
||||
}
|
||||
}
|
||||
if (sendDrafts(list)) {
|
||||
val where = Expression.`in`(Column(Drafts._ID),
|
||||
RawItemArray(listView!!.checkedItemIds))
|
||||
RawItemArray(listView.checkedItemIds))
|
||||
contentResolver.delete(Drafts.CONTENT_URI, where.sql, null)
|
||||
}
|
||||
}
|
||||
R.id.save -> {
|
||||
val checked = listView.checkedItemPositions
|
||||
val drafts = ArrayList<Draft>()
|
||||
for (i in 0 until checked.size()) {
|
||||
val position = checked.keyAt(i)
|
||||
if (checked.valueAt(i)) {
|
||||
drafts.add(adapter!!.getDraft(position))
|
||||
}
|
||||
}
|
||||
task {
|
||||
val pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
|
||||
val paths = drafts.map { draft ->
|
||||
val name = "${draft.timestamp}.eml"
|
||||
val destFile = File(pubDir, name)
|
||||
FileOutputStream(destFile).use {
|
||||
draft.writeMimeMessageTo(context, it)
|
||||
it.flush()
|
||||
}
|
||||
return@map destFile.absolutePath
|
||||
}
|
||||
MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null)
|
||||
}.successUi {
|
||||
Toast.makeText(context, R.string.draft_saved, Toast.LENGTH_SHORT).show()
|
||||
}.fail { ex ->
|
||||
Log.w(LOGTAG, ex)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
|
@ -140,9 +171,7 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
|
|||
}
|
||||
|
||||
override fun onItemClick(view: AdapterView<*>, child: View, position: Int, id: Long) {
|
||||
val c = adapter!!.cursor
|
||||
if (c == null || c.isClosed || !c.moveToPosition(position)) return
|
||||
val item = DraftCursorIndices.fromCursor(c)
|
||||
val item = adapter!!.getDraft(position)
|
||||
if (TextUtils.isEmpty(item.action_type)) {
|
||||
editDraft(item)
|
||||
return
|
||||
|
@ -162,13 +191,13 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
|
|||
super.onActivityCreated(savedInstanceState)
|
||||
adapter = DraftsAdapter(activity)
|
||||
adapter!!.setTextSize(preferences.getInt(KEY_TEXT_SIZE, getDefaultTextSize(activity)).toFloat())
|
||||
listView!!.adapter = adapter
|
||||
listView!!.emptyView = emptyView
|
||||
listView!!.onItemClickListener = this
|
||||
listView!!.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
|
||||
listView!!.setMultiChoiceModeListener(this)
|
||||
emptyIcon!!.setImageResource(R.drawable.ic_info_draft)
|
||||
emptyText!!.setText(R.string.drafts_hint_messages)
|
||||
listView.adapter = adapter
|
||||
listView.emptyView = emptyView
|
||||
listView.onItemClickListener = this
|
||||
listView.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
|
||||
listView.setMultiChoiceModeListener(this)
|
||||
emptyIcon.setImageResource(R.drawable.ic_info_draft)
|
||||
emptyText.setText(R.string.drafts_hint_messages)
|
||||
loaderManager.initLoader(0, null, this)
|
||||
setListShown(false)
|
||||
}
|
||||
|
@ -207,8 +236,8 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
|
|||
}
|
||||
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 (item.action_extras is SendDirectMessageActionExtras) {
|
||||
recipientId = (item.action_extras as SendDirectMessageActionExtras).recipientId
|
||||
}
|
||||
if (item.account_keys?.isEmpty() ?: true || recipientId == null) {
|
||||
continue@loop
|
||||
|
@ -224,7 +253,7 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
|
|||
|
||||
private fun updateTitle(mode: ActionMode?) {
|
||||
if (listView == null || mode == null) return
|
||||
val count = listView!!.checkedItemCount
|
||||
val count = listView.checkedItemCount
|
||||
mode.title = resources.getQuantityString(R.plurals.Nitems_selected, count, count)
|
||||
}
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ object ParcelableStatusUtils {
|
|||
private fun getPlaceFullName(status: Status): String? {
|
||||
val place = status.place
|
||||
if (place != null) return place.fullName
|
||||
val location = status.location
|
||||
val location = status.location ?: return null
|
||||
if (ParcelableLocation.valueOf(location) == null) {
|
||||
return location
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ object ParcelableStatusUtils {
|
|||
if (geoLocation != null) {
|
||||
return ParcelableLocationUtils.fromGeoLocation(geoLocation)
|
||||
}
|
||||
val locationString = status.location
|
||||
val locationString = status.location ?: return null
|
||||
val location = ParcelableLocation.valueOf(locationString)
|
||||
if (location != null) {
|
||||
return location
|
||||
|
|
|
@ -31,7 +31,6 @@ class AccountAuthenticatorService : Service() {
|
|||
|
||||
internal class TwidereAccountAuthenticator(val context: Context) : AbstractAccountAuthenticator(context) {
|
||||
|
||||
// TODO: Make SignInActivity comply with AccountAuthenticatorActivity
|
||||
override fun addAccount(response: AccountAuthenticatorResponse, accountType: String,
|
||||
authTokenType: String?, requiredFeatures: Array<String>?,
|
||||
options: Bundle?): Bundle {
|
||||
|
|
|
@ -62,7 +62,7 @@ import org.mariotaku.twidere.TwidereConstants.*
|
|||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.extension.newMicroBlogInstance
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra
|
||||
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
|
||||
|
@ -173,7 +173,7 @@ class BackgroundOperationService : IntentService("background_operation"), Consta
|
|||
updateStatuses(item.action_type, ParcelableStatusUpdateUtils.fromDraftItem(this, item))
|
||||
}
|
||||
Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> {
|
||||
val recipientId = (item.action_extras as? SendDirectMessageActionExtra)?.recipientId ?: return
|
||||
val recipientId = (item.action_extras as? SendDirectMessageActionExtras)?.recipientId ?: return
|
||||
if (item.account_keys?.isEmpty() ?: true) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.mariotaku.twidere.TwidereConstants.*
|
|||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.app.TwidereApplication
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra
|
||||
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
|
||||
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.preference.ServicePickerPreference
|
||||
|
@ -519,7 +519,8 @@ class UpdateStatusTask(
|
|||
draft.text = statusUpdate.text
|
||||
draft.location = statusUpdate.location
|
||||
draft.media = statusUpdate.media
|
||||
draft.action_extras = UpdateStatusActionExtra().apply {
|
||||
draft.timestamp = System.currentTimeMillis()
|
||||
draft.action_extras = UpdateStatusActionExtras().apply {
|
||||
inReplyToStatus = statusUpdate.in_reply_to_status
|
||||
isPossiblySensitive = statusUpdate.is_possibly_sensitive
|
||||
isRepostStatusId = statusUpdate.repost_status_id
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
<item
|
||||
android:id="@id/delete"
|
||||
android:icon="@drawable/ic_action_delete"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
android:title="@string/delete"/>
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom|withText"/>
|
||||
<item
|
||||
android:id="@id/send"
|
||||
android:icon="@drawable/ic_action_send"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
android:title="@string/send"/>
|
||||
|
||||
android:title="@string/send"
|
||||
app:showAsAction="ifRoom|withText"/>
|
||||
<item
|
||||
android:id="@id/save"
|
||||
android:enabled="@bool/debug"
|
||||
android:icon="@drawable/ic_action_save"
|
||||
android:title="@string/save"
|
||||
android:visible="@bool/debug"/>
|
||||
</menu>
|
Loading…
Reference in New Issue