This commit is contained in:
Mariotaku Lee 2017-04-08 22:06:04 +08:00
parent 8ca86a00f9
commit 32fd8f9d87
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
33 changed files with 642 additions and 229 deletions

View File

@ -27,6 +27,7 @@ import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
@ -44,7 +45,6 @@ import org.mariotaku.twidere.model.account.AccountExtras;
import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.model.util.RGBHexColorConverter;
import org.mariotaku.twidere.model.util.UserKeyConverter;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.model.AccountDetailsUtils;
import java.io.IOException;
@ -140,10 +140,10 @@ public class AccountDetails implements Parcelable, Comparable<AccountDetails> {
@OnPreJsonSerialize
void onPreJsonSerialize() throws IOException {
if (credentials != null) {
credentials_json = JsonSerializer.serialize(credentials);
credentials_json = LoganSquare.serialize(credentials);
}
if (extras != null) {
extras_json = JsonSerializer.serialize(extras);
extras_json = LoganSquare.serialize(extras);
}
}

View File

@ -35,7 +35,7 @@ import org.mariotaku.library.objectcursor.annotation.AfterCursorObjectCreated;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
import org.mariotaku.twidere.model.draft.ActionExtras;
import org.mariotaku.twidere.model.util.DraftExtrasConverter;
import org.mariotaku.twidere.model.util.DraftExtrasFieldConverter;
import org.mariotaku.twidere.model.util.UserKeysCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
@ -73,7 +73,7 @@ public class Draft implements Parcelable {
public String action_type;
@Nullable
@ParcelableThisPlease
@CursorField(value = Drafts.ACTION_EXTRAS, converter = DraftExtrasConverter.class)
@CursorField(value = Drafts.ACTION_EXTRAS, converter = DraftExtrasFieldConverter.class)
public ActionExtras action_extras;
@Nullable
@ParcelableThisPlease

View File

@ -24,12 +24,20 @@ package org.mariotaku.twidere.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
import com.bluelinelabs.logansquare.annotation.OnPreJsonSerialize;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import org.mariotaku.commons.logansquare.JsonStringConverter;
import org.mariotaku.twidere.model.draft.ActionExtras;
import org.mariotaku.twidere.model.util.DraftExtrasFieldConverter;
import java.io.IOException;
import java.util.Arrays;
@ParcelablePlease
@ -72,6 +80,13 @@ public class ParcelableStatusUpdate implements Parcelable {
@ParcelableThisPlease
@Draft.Action
public String draft_action;
@JsonField(name = "draft_extras")
@ParcelableThisPlease
@Nullable
public ActionExtras draft_extras;
@JsonField(name = "draft_extras", typeConverter = JsonStringConverter.class)
String rawDraftExtras;
public ParcelableStatusUpdate() {
}
@ -100,6 +115,16 @@ public class ParcelableStatusUpdate implements Parcelable {
ParcelableStatusUpdateParcelablePlease.writeToParcel(this, dest, flags);
}
@OnJsonParseComplete
void onJsonParseComplete() throws IOException {
draft_extras = DraftExtrasFieldConverter.parseExtras(draft_action, rawDraftExtras);
}
@OnPreJsonSerialize
void onPreJsonSerialize() throws IOException {
rawDraftExtras = DraftExtrasFieldConverter.serializeExtras(draft_extras);
}
public static final Creator<ParcelableStatusUpdate> CREATOR = new Creator<ParcelableStatusUpdate>() {
@Override
public ParcelableStatusUpdate createFromParcel(Parcel source) {

View File

@ -0,0 +1,83 @@
/*
* Twidere - Twitter client for Android
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.mariotaku.twidere.model.draft;
import android.os.Parcel;
import android.os.Parcelable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import org.mariotaku.twidere.model.ParcelableStatus;
/**
* Created by mariotaku on 2017/2/7.
*/
@ParcelablePlease
@JsonObject
public class QuoteStatusActionExtras implements ActionExtras, Parcelable {
@JsonField(name = "status")
ParcelableStatus status;
@JsonField(name = "quote_original_status")
boolean quoteOriginalStatus;
public ParcelableStatus getStatus() {
return status;
}
public void setStatus(final ParcelableStatus status) {
this.status = status;
}
public boolean isQuoteOriginalStatus() {
return quoteOriginalStatus;
}
public void setQuoteOriginalStatus(final boolean quoteOriginalStatus) {
this.quoteOriginalStatus = quoteOriginalStatus;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
QuoteStatusActionExtrasParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<QuoteStatusActionExtras> CREATOR = new Creator<QuoteStatusActionExtras>() {
public QuoteStatusActionExtras createFromParcel(Parcel source) {
QuoteStatusActionExtras target = new QuoteStatusActionExtras();
QuoteStatusActionExtrasParcelablePlease.readFromParcel(target, source);
return target;
}
public QuoteStatusActionExtras[] newArray(int size) {
return new QuoteStatusActionExtras[size];
}
};
}

View File

@ -36,9 +36,17 @@ import org.mariotaku.twidere.model.ParcelableStatus;
@ParcelablePlease
@JsonObject
public class StatusObjectExtras implements ActionExtras, Parcelable {
public class StatusObjectActionExtras implements ActionExtras, Parcelable {
@JsonField(name = "status")
public ParcelableStatus status;
ParcelableStatus status;
public ParcelableStatus getStatus() {
return status;
}
public void setStatus(final ParcelableStatus status) {
this.status = status;
}
@Override
public int describeContents() {
@ -47,18 +55,18 @@ public class StatusObjectExtras implements ActionExtras, Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
StatusObjectExtrasParcelablePlease.writeToParcel(this, dest, flags);
StatusObjectActionExtrasParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<StatusObjectExtras> CREATOR = new Creator<StatusObjectExtras>() {
public StatusObjectExtras createFromParcel(Parcel source) {
StatusObjectExtras target = new StatusObjectExtras();
StatusObjectExtrasParcelablePlease.readFromParcel(target, source);
public static final Creator<StatusObjectActionExtras> CREATOR = new Creator<StatusObjectActionExtras>() {
public StatusObjectActionExtras createFromParcel(Parcel source) {
StatusObjectActionExtras target = new StatusObjectActionExtras();
StatusObjectActionExtrasParcelablePlease.readFromParcel(target, source);
return target;
}
public StatusObjectExtras[] newArray(int size) {
return new StatusObjectExtras[size];
public StatusObjectActionExtras[] newArray(int size) {
return new StatusObjectActionExtras[size];
}
};
}

View File

@ -25,10 +25,10 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.model.ParcelableMessage.MessageType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
@ -42,14 +42,14 @@ public abstract class MessageExtras implements Parcelable {
if (json == null) return null;
switch (messageType) {
case MessageType.STICKER:
return JsonSerializer.parse(json, StickerExtras.class);
return LoganSquare.parse(json, StickerExtras.class);
case MessageType.JOIN_CONVERSATION:
case MessageType.PARTICIPANTS_LEAVE:
case MessageType.PARTICIPANTS_JOIN:
return JsonSerializer.parse(json, UserArrayExtras.class);
return LoganSquare.parse(json, UserArrayExtras.class);
case MessageType.CONVERSATION_NAME_UPDATE:
case MessageType.CONVERSATION_AVATAR_UPDATE:
return JsonSerializer.parse(json, ConversationInfoUpdatedExtras.class);
return LoganSquare.parse(json, ConversationInfoUpdatedExtras.class);
}
return null;
}

View File

@ -25,8 +25,9 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
@ -39,9 +40,9 @@ public abstract class ConversationExtras implements Parcelable {
if (json == null) return null;
switch (extrasType) {
case ExtrasType.TWITTER_OFFICIAL: {
return JsonSerializer.parse(json, TwitterOfficialConversationExtras.class);
return LoganSquare.parse(json, TwitterOfficialConversationExtras.class);
}
}
return JsonSerializer.parse(json, DefaultConversationExtras.class);
return LoganSquare.parse(json, DefaultConversationExtras.class);
}
}

View File

@ -0,0 +1,5 @@
/**
* Remember not to use {@link org.mariotaku.twidere.util.JsonSerializer} because it's single threaded
* Such operation may lead to dead lock
*/
package org.mariotaku.twidere.model;

View File

@ -26,13 +26,13 @@ import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.util.Arrays;
@ -102,17 +102,17 @@ public class TabArguments implements TwidereConstants {
case CustomTabType.NOTIFICATIONS_TIMELINE:
case CustomTabType.DIRECT_MESSAGES:
case CustomTabType.TRENDS_SUGGESTIONS: {
return JsonSerializer.parse(json, TabArguments.class);
return LoganSquare.parse(json, TabArguments.class);
}
case CustomTabType.USER_TIMELINE:
case CustomTabType.FAVORITES: {
return JsonSerializer.parse(json, UserArguments.class);
return LoganSquare.parse(json, UserArguments.class);
}
case CustomTabType.LIST_TIMELINE: {
return JsonSerializer.parse(json, UserListArguments.class);
return LoganSquare.parse(json, UserListArguments.class);
}
case CustomTabType.SEARCH_STATUSES: {
return JsonSerializer.parse(json, TextQueryArguments.class);
return LoganSquare.parse(json, TextQueryArguments.class);
}
}
return null;

View File

@ -27,10 +27,10 @@ import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
@ -49,13 +49,13 @@ public abstract class TabExtras implements Parcelable {
if (json == null) return null;
switch (type) {
case CustomTabType.NOTIFICATIONS_TIMELINE: {
return JsonSerializer.parse(json, InteractionsTabExtras.class);
return LoganSquare.parse(json, InteractionsTabExtras.class);
}
case CustomTabType.HOME_TIMELINE: {
return JsonSerializer.parse(json, HomeTabExtras.class);
return LoganSquare.parse(json, HomeTabExtras.class);
}
case CustomTabType.TRENDS_SUGGESTIONS: {
return JsonSerializer.parse(json, TrendsTabExtras.class);
return LoganSquare.parse(json, TrendsTabExtras.class);
}
}
return null;

View File

@ -25,10 +25,11 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.message.conversation.ConversationExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Messages;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@ -47,6 +48,6 @@ public class ConversationExtrasConverter implements CursorFieldConverter<Convers
@Override
public void writeField(ContentValues values, ConversationExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return;
values.put(columnName, JsonSerializer.serialize(object));
values.put(columnName, LoganSquare.serialize(object));
}
}

View File

@ -23,16 +23,19 @@ package org.mariotaku.twidere.model.util;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Draft;
import org.mariotaku.twidere.model.draft.ActionExtras;
import org.mariotaku.twidere.model.draft.QuoteStatusActionExtras;
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras;
import org.mariotaku.twidere.model.draft.StatusObjectExtras;
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras;
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@ -40,35 +43,46 @@ import java.lang.reflect.ParameterizedType;
/**
* Created by mariotaku on 16/2/20.
*/
public class DraftExtrasConverter implements CursorFieldConverter<ActionExtras> {
public class DraftExtrasFieldConverter implements CursorFieldConverter<ActionExtras> {
@Override
public ActionExtras parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException {
final String actionType = cursor.getString(cursor.getColumnIndex(Drafts.ACTION_TYPE));
final String json = cursor.getString(columnIndex);
if (TextUtils.isEmpty(actionType) || TextUtils.isEmpty(json)) return null;
switch (actionType) {
case Draft.Action.UPDATE_STATUS_COMPAT_1:
case Draft.Action.UPDATE_STATUS_COMPAT_2:
case Draft.Action.UPDATE_STATUS:
case Draft.Action.REPLY:
case Draft.Action.QUOTE: {
return JsonSerializer.parse(json, UpdateStatusActionExtras.class);
}
case Draft.Action.SEND_DIRECT_MESSAGE_COMPAT:
case Draft.Action.SEND_DIRECT_MESSAGE: {
return JsonSerializer.parse(json, SendDirectMessageActionExtras.class);
}
case Draft.Action.FAVORITE:
case Draft.Action.RETWEET: {
return JsonSerializer.parse(json, StatusObjectExtras.class);
}
}
return null;
return parseExtras(actionType, json);
}
@Override
public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return;
values.put(columnName, JsonSerializer.serialize(object));
values.put(columnName, serializeExtras(object));
}
@Nullable
public static ActionExtras parseExtras(final String actionType, final String json) throws IOException {
if (TextUtils.isEmpty(actionType) || TextUtils.isEmpty(json)) return null;
switch (actionType) {
case Draft.Action.UPDATE_STATUS_COMPAT_1:
case Draft.Action.UPDATE_STATUS_COMPAT_2:
case Draft.Action.UPDATE_STATUS:
case Draft.Action.REPLY: {
return LoganSquare.parse(json, UpdateStatusActionExtras.class);
}
case Draft.Action.SEND_DIRECT_MESSAGE_COMPAT:
case Draft.Action.SEND_DIRECT_MESSAGE: {
return LoganSquare.parse(json, SendDirectMessageActionExtras.class);
}
case Draft.Action.FAVORITE:
case Draft.Action.RETWEET: {
return LoganSquare.parse(json, StatusObjectActionExtras.class);
}
case Draft.Action.QUOTE: {
return LoganSquare.parse(json, QuoteStatusActionExtras.class);
}
}
return null;
}
public static String serializeExtras(final ActionExtras object) throws IOException {
return LoganSquare.serialize(object);
}
}

View File

@ -25,10 +25,11 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.message.MessageExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Messages;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@ -47,6 +48,6 @@ public class MessageExtrasConverter implements CursorFieldConverter<MessageExtra
@Override
public void writeField(ContentValues values, MessageExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return;
values.put(columnName, JsonSerializer.serialize(object));
values.put(columnName, LoganSquare.serialize(object));
}
}

View File

@ -25,11 +25,12 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.argument.TabArguments;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@ -50,7 +51,7 @@ public class TabArgumentsFieldConverter implements CursorFieldConverter<TabArgum
public void writeField(ContentValues values, TabArguments object, String columnName, ParameterizedType fieldType) {
if (object == null) return;
try {
values.put(columnName, JsonSerializer.serialize(object));
values.put(columnName, LoganSquare.serialize(object));
} catch (IOException e) {
// Ignore
}

View File

@ -25,11 +25,12 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.extra.TabExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.util.JsonSerializer;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@ -48,6 +49,6 @@ public class TabExtrasFieldConverter implements CursorFieldConverter<TabExtras>
@Override
public void writeField(ContentValues values, TabExtras object, String columnName, ParameterizedType fieldType) throws IOException {
if (object == null) return;
values.put(columnName, JsonSerializer.serialize(object));
values.put(columnName, LoganSquare.serialize(object));
}
}

View File

@ -554,6 +554,9 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.HomeActivity"/>
</activity>
<activity
android:name=".activity.content.RetweetQuoteDialogActivity"
android:theme="@style/Theme.Twidere.NoDisplay"/>
<service
android:name=".service.LegacyTaskService"

View File

@ -43,7 +43,10 @@ import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.argument.TabArguments;
import org.mariotaku.twidere.model.tab.extra.HomeTabExtras;
import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras;
import org.mariotaku.twidere.model.tab.extra.TabExtras;
import org.mariotaku.twidere.model.tab.extra.TrendsTabExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import java.io.IOException;
@ -116,13 +119,26 @@ public class CustomTabUtils implements Constants {
}
}
/**
* Remember to make this method correspond to {@link TabExtras#parse(String, String)}
*
* @see TabExtras#parse(String, String)
*/
@Nullable
public static TabExtras newTabExtras(@NonNull @CustomTabType String type) {
try {
return TabExtras.parse(type, "{}");
} catch (IOException e) {
throw new RuntimeException(e);
switch (type) {
case CustomTabType.NOTIFICATIONS_TIMELINE: {
return new InteractionsTabExtras();
}
case CustomTabType.HOME_TIMELINE: {
return new HomeTabExtras();
}
case CustomTabType.TRENDS_SUGGESTIONS: {
return new TrendsTabExtras();
}
}
return null;
}
@Nullable

View File

@ -0,0 +1,45 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.ktextension
import android.content.ContentValues
inline fun ContentValues(action: ContentValues.() -> Unit): ContentValues {
val bundle = ContentValues()
action(bundle)
return bundle
}
operator fun ContentValues.set(key: String, value: Boolean) {
return put(key, value)
}
operator fun ContentValues.set(key: String, value: Int) {
return put(key, value)
}
operator fun ContentValues.set(key: String, value: Long) {
return put(key, value)
}
operator fun ContentValues.set(key: String, value: String?) {
return put(key, value)
}

View File

@ -34,6 +34,10 @@ fun Menu.setGroupAvailability(groupId: Int, available: Boolean) {
setGroupVisible(groupId, available)
}
fun Menu.isItemChecked(id: Int): Boolean {
return findItem(id)?.isChecked ?: false
}
fun Menu.setItemChecked(id: Int, checked: Boolean) {
findItem(id)?.isChecked = checked
}

View File

@ -81,6 +81,7 @@ import org.mariotaku.twidere.extension.model.textLimit
import org.mariotaku.twidere.extension.model.unique_id_non_null
import org.mariotaku.twidere.extension.text.twitter.extractReplyTextAndMentions
import org.mariotaku.twidere.extension.text.twitter.getTweetLength
import org.mariotaku.twidere.extension.withAppendedPath
import org.mariotaku.twidere.fragment.*
import org.mariotaku.twidere.fragment.PermissionRequestDialog.PermissionRequestCancelCallback
import org.mariotaku.twidere.model.*
@ -873,9 +874,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
}
private fun displayNewDraftNotification(draftUri: Uri) {
val values = ContentValues()
values.put(BaseColumns._ID, draftUri.lastPathSegment)
contentResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, values)
val notificationUri = Drafts.CONTENT_URI_NOTIFICATIONS.withAppendedPath(draftUri.lastPathSegment)
contentResolver.insert(notificationUri, null)
}
private val media: Array<ParcelableMediaUpdate>
@ -1476,7 +1476,13 @@ 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? UpdateStatusActionExtras)?.attachmentUrl
update.draft_extras = UpdateStatusActionExtras().also {
it.inReplyToStatus = inReplyToStatus
it.isPossiblySensitive = isPossiblySensitive
it.displayCoordinates = attachPreciseLocation
}
LengthyOperationsService.updateStatusesAsync(this, action, statuses = update,
scheduleInfo = scheduleInfo)
if (preferences[noCloseAfterTweetSentKey] && inReplyToStatus == null) {

View File

@ -0,0 +1,50 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.activity.content
import android.os.Bundle
import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.fragment.RetweetQuoteDialogFragment
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
/**
* Created by mariotaku on 2017/4/8.
*/
class RetweetQuoteDialogActivity : BaseActivity() {
private val status: ParcelableStatus
get() = intent.getParcelableExtra(EXTRA_STATUS)
private val accountKey: UserKey?
get() = intent.getParcelableExtra(EXTRA_ACCOUNT_KEY)
private val text: String?
get() = intent.getStringExtra(EXTRA_TEXT)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
RetweetQuoteDialogFragment.show(supportFragmentManager, status, accountKey, text)
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension
import android.net.Uri
fun Uri.withAppendedPath(path: String) = Uri.withAppendedPath(this, path)

View File

@ -21,8 +21,6 @@ package org.mariotaku.twidere.fragment
import android.app.Activity
import android.app.Dialog
import android.app.NotificationManager
import android.content.Context
import android.content.DialogInterface
import android.content.DialogInterface.OnClickListener
import android.content.Intent
@ -48,8 +46,10 @@ import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.setItemAvailability
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.OrderBy
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.content.RetweetQuoteDialogActivity
import org.mariotaku.twidere.activity.iface.IBaseActivity
import org.mariotaku.twidere.adapter.DraftsAdapter
import org.mariotaku.twidere.constant.IntentConstants
@ -57,6 +57,7 @@ import org.mariotaku.twidere.constant.textSizeKey
import org.mariotaku.twidere.extension.*
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.analyzer.PurchaseFinished
import org.mariotaku.twidere.model.draft.QuoteStatusActionExtras
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.Analyzer
@ -197,18 +198,28 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
}
override fun onItemClick(view: AdapterView<*>, child: View, position: Int, id: Long) {
val item = adapter.getDraft(position)
if (TextUtils.isEmpty(item.action_type)) {
editDraft(item)
return
}
when (item.action_type) {
"0", "1", Draft.Action.UPDATE_STATUS, Draft.Action.REPLY, Draft.Action.QUOTE -> {
editDraft(item)
val draft = adapter.getDraft(position)
var deleteDraft = false
if (TextUtils.isEmpty(draft.action_type)) {
deleteDraft = editUpdateStatusDraft(draft)
} else when (draft.action_type) {
"0", "1", Draft.Action.UPDATE_STATUS, Draft.Action.REPLY -> {
deleteDraft = editUpdateStatusDraft(draft)
}
Draft.Action.QUOTE -> {
deleteDraft = editQuoteStatusDraft(draft)
}
}
}
if (deleteDraft) {
val draftIdString = draft._id.toString()
val where = Expression.equalsArgs(Drafts._ID).sql
val whereArgs = arrayOf(draftIdString)
val cr = context.contentResolver
cr.delete(Drafts.CONTENT_URI, where, whereArgs)
cr.delete(Drafts.CONTENT_URI_NOTIFICATIONS.withAppendedPath(draftIdString), null, null)
}
}
fun setListShown(listShown: Boolean) {
listContainer.visibility = if (listShown) View.VISIBLE else View.GONE
@ -216,11 +227,25 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
emptyView.visibility = if (listShown && adapter.isEmpty) View.VISIBLE else View.GONE
}
private fun editDraft(draft: Draft) {
val intent = Intent(INTENT_ACTION_EDIT_DRAFT)
intent.putExtra(EXTRA_DRAFT, draft)
context.contentResolver.delete(Drafts.CONTENT_URI, Expression.equals(Drafts._ID, draft._id).sql, null)
private fun editUpdateStatusDraft(draft: Draft): Boolean {
val intent = Intent(INTENT_ACTION_EDIT_DRAFT).apply {
`package` = BuildConfig.APPLICATION_ID
putExtra(EXTRA_DRAFT, draft)
}
startActivityForResult(intent, REQUEST_COMPOSE)
return true
}
private fun editQuoteStatusDraft(draft: Draft): Boolean {
val extras = draft.action_extras as? QuoteStatusActionExtras ?: return false
val status = extras.status ?: return false
val intent = Intent(context, RetweetQuoteDialogActivity::class.java).apply {
putExtra(EXTRA_STATUS, status)
putExtra(EXTRA_ACCOUNT_KEY, draft.account_keys?.singleOrNull())
putExtra(EXTRA_TEXT, draft.text)
}
startActivityForResult(intent, REQUEST_COMPOSE)
return true
}
private fun sendDrafts(list: LongArray): Boolean {
@ -247,7 +272,7 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val args = arguments ?: return
AsyncTaskUtils.executeTask(DeleteDraftsTask(activity, args.getLongArray(IntentConstants.EXTRA_IDS)))
AsyncTaskUtils.executeTask(DeleteDraftsTask(activity, args.getLongArray(EXTRA_IDS)))
}
}
}
@ -278,6 +303,10 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
override fun doInBackground(vararg params: Any) {
val activity = activityRef.get() ?: return
deleteDrafts(activity, ids)
ids.forEach { id ->
val uri = Drafts.CONTENT_URI_NOTIFICATIONS.withAppendedPath(id.toString())
activity.contentResolver.delete(uri, null, null)
}
}
override fun onPreExecute() {
@ -297,11 +326,6 @@ class DraftsFragment : BaseFragment(), LoaderCallbacks<Cursor?>, OnItemClickList
f.dismiss()
}
}
val notificationManager = activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
ids.forEach { id ->
val tag = Uri.withAppendedPath(Drafts.CONTENT_URI, id.toString()).toString()
notificationManager.cancel(tag, NOTIFICATION_ID_DRAFTS)
}
}
companion object {

View File

@ -25,6 +25,7 @@ import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.BaseColumns
import android.support.annotation.CheckResult
import android.support.v4.app.FragmentManager
import android.support.v7.app.AlertDialog
@ -33,16 +34,13 @@ import android.text.Editable
import android.text.TextWatcher
import android.view.Gravity
import android.view.View
import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.*
import com.bumptech.glide.Glide
import com.twitter.Validator
import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.set
import org.mariotaku.ktextension.setItemAvailability
import org.mariotaku.ktextension.*
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.content.RetweetQuoteDialogActivity
import org.mariotaku.twidere.adapter.DummyItemAdapter
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.IntentConstants.*
@ -50,7 +48,9 @@ import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_QUICK_SEND
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.extension.model.textLimit
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.draft.QuoteStatusActionExtras
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.Analyzer
import org.mariotaku.twidere.util.EditTextEnterHandler
@ -60,10 +60,30 @@ import org.mariotaku.twidere.view.ColorLabelRelativeLayout
import org.mariotaku.twidere.view.ComposeEditText
import org.mariotaku.twidere.view.StatusTextCountView
import org.mariotaku.twidere.view.holder.StatusViewHolder
import java.util.*
class RetweetQuoteDialogFragment : BaseDialogFragment() {
private lateinit var popupMenu: PopupMenu
private val PopupMenu.quoteOriginalStatus get() = menu.isItemChecked(R.id.quote_original_status)
private val Dialog.itemContent get() = findViewById(R.id.itemContent) as ColorLabelRelativeLayout
private val Dialog.textCountView get() = findViewById(R.id.commentTextCount) as StatusTextCountView
private val Dialog.itemMenu get() = findViewById(R.id.itemMenu) as ImageButton
private val Dialog.actionButtons get() = findViewById(R.id.actionButtons) as LinearLayout
private val Dialog.commentContainer get() = findViewById(R.id.commentContainer) as RelativeLayout
private val Dialog.editComment get() = findViewById(R.id.editComment) as ComposeEditText
private val Dialog.commentMenu get() = findViewById(R.id.commentMenu) as ImageButton
private val status: ParcelableStatus
get() = arguments.getParcelable<ParcelableStatus>(EXTRA_STATUS)
private val accountKey: UserKey
get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY) ?: status.account_key
private val text: String?
get() = arguments.getString(EXTRA_TEXT)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
val accountKey = this.accountKey
@ -87,34 +107,27 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
val dialog = builder.create()
dialog.setOnShowListener {
it as AlertDialog
it.applyTheme()
val itemContent = it.findViewById(R.id.itemContent) as ColorLabelRelativeLayout
val textCountView = it.findViewById(R.id.commentTextCount) as StatusTextCountView
val itemMenu = it.findViewById(R.id.itemMenu) as ImageButton
val actionButtons = it.findViewById(R.id.actionButtons) as LinearLayout
val commentContainer = it.findViewById(R.id.commentContainer) as RelativeLayout
val editComment = it.findViewById(R.id.editComment) as ComposeEditText
val commentMenu = it.findViewById(R.id.commentMenu) as ImageButton
dialog.setOnShowListener { dialog ->
dialog as AlertDialog
dialog.applyTheme()
val adapter = DummyItemAdapter(context, requestManager = Glide.with(this))
adapter.setShouldShowAccountsColor(true)
val holder = StatusViewHolder(adapter, itemContent)
val holder = StatusViewHolder(adapter, dialog.itemContent)
holder.displayStatus(status = status, displayInReplyTo = false)
textCountView.maxLength = details.textLimit
dialog.textCountView.maxLength = details.textLimit
itemMenu.visibility = View.GONE
actionButtons.visibility = View.GONE
itemContent.isFocusable = false
dialog.itemMenu.visibility = View.GONE
dialog.actionButtons.visibility = View.GONE
dialog.itemContent.isFocusable = false
val useQuote = useQuote(!status.user_is_protected, details)
commentContainer.visibility = if (useQuote) View.VISIBLE else View.GONE
editComment.accountKey = details.key
dialog.commentContainer.visibility = if (useQuote) View.VISIBLE else View.GONE
dialog.editComment.accountKey = details.key
val sendByEnter = preferences.getBoolean(KEY_QUICK_SEND)
val enterHandler = EditTextEnterHandler.attach(editComment, object : EditTextEnterHandler.EnterListener {
val enterHandler = EditTextEnterHandler.attach(dialog.editComment, object : EditTextEnterHandler.EnterListener {
override fun shouldCallListener(): Boolean {
return true
}
@ -141,7 +154,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
})
popupMenu = PopupMenu(context, commentMenu, Gravity.NO_GRAVITY,
popupMenu = PopupMenu(context, dialog.commentMenu, Gravity.NO_GRAVITY,
R.attr.actionOverflowMenuStyle, 0).apply {
inflate(R.menu.menu_dialog_comment)
menu.setItemAvailability(R.id.quote_original_status, status.retweet_id != null || status.quoted_id != null)
@ -153,13 +166,13 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
false
})
}
commentMenu.setOnClickListener { popupMenu.show() }
commentMenu.setOnTouchListener(popupMenu.dragToOpenListener)
commentMenu.visibility = if (popupMenu.menu.hasVisibleItems()) View.VISIBLE else View.GONE
dialog.commentMenu.setOnClickListener { popupMenu.show() }
dialog.commentMenu.setOnTouchListener(popupMenu.dragToOpenListener)
dialog.commentMenu.visibility = if (popupMenu.menu.hasVisibleItems()) View.VISIBLE else View.GONE
it.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
var dismissDialog = false
if (editComment.length() > 0) {
if (dialog.editComment.length() > 0) {
dismissDialog = retweetOrQuote(details, status, SHOW_PROTECTED_CONFIRM)
} else if (isMyRetweet(status)) {
twitterWrapper.cancelRetweetAsync(details.key, status.id, status.my_retweet_id)
@ -174,11 +187,36 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
}
updateTextCount(it, editComment.text, status, details)
if (savedInstanceState == null) {
dialog.editComment.setText(text)
}
dialog.editComment.setSelection(dialog.editComment.length())
updateTextCount(dialog, dialog.editComment.text, status, details)
}
return dialog
}
override fun onCancel(dialog: DialogInterface) {
if (dialog !is Dialog) return
if (dialog.editComment.empty) return
dialog.saveToDrafts()
Toast.makeText(context, R.string.message_toast_status_saved_to_draft, Toast.LENGTH_SHORT).show()
finishRetweetQuoteActivity()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
finishRetweetQuoteActivity()
}
private fun finishRetweetQuoteActivity() {
val activity = this.activity
if (activity is RetweetQuoteDialogActivity && !activity.isFinishing) {
activity.finish()
}
}
private fun updateTextCount(dialog: DialogInterface, s: CharSequence, status: ParcelableStatus,
credentials: AccountDetails) {
if (dialog !is AlertDialog) return
@ -197,16 +235,9 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
positiveButton.isEnabled = !status.user_is_protected
}
val textCountView = dialog.findViewById(R.id.commentTextCount) as StatusTextCountView
val am = AccountManager.get(context)
textCountView.textCount = validator.getTweetLength(s.toString())
}
private val status: ParcelableStatus
get() = arguments.getParcelable<ParcelableStatus>(EXTRA_STATUS)
private val accountKey: UserKey
get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY) ?: status.account_key
@CheckResult
private fun retweetOrQuote(account: AccountDetails, status: ParcelableStatus,
showProtectedConfirmation: Boolean): Boolean {
@ -214,10 +245,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
val dialog = dialog ?: return false
val editComment = dialog.findViewById(R.id.editComment) as EditText
if (useQuote(editComment.length() > 0, account)) {
val menu = popupMenu.menu
val itemQuoteOriginalStatus = menu.findItem(R.id.quote_original_status)
val statusLink: Uri
val quoteOriginalStatus = itemQuoteOriginalStatus.isChecked
val quoteOriginalStatus = popupMenu.quoteOriginalStatus
var commentText: String
val update = ParcelableStatusUpdate()
@ -247,10 +275,10 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
}
else -> {
if (!status.is_quote || !quoteOriginalStatus) {
statusLink = LinkCreator.getStatusWebLink(status)
val statusLink = if (!status.is_quote || !quoteOriginalStatus) {
LinkCreator.getStatusWebLink(status)
} else {
statusLink = LinkCreator.getQuotedStatusWebLink(status)
LinkCreator.getQuotedStatusWebLink(status)
}
update.attachment_url = statusLink.toString()
commentText = editingComment
@ -258,6 +286,11 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
update.text = commentText
update.is_possibly_sensitive = status.is_possibly_sensitive
update.draft_action = Draft.Action.QUOTE
update.draft_extras = QuoteStatusActionExtras().apply {
this.status = status
this.isQuoteOriginalStatus = quoteOriginalStatus
}
LengthyOperationsService.updateStatusesAsync(context, Draft.Action.QUOTE, update)
} else {
twitter.retweetStatusAsync(account.key, status)
@ -270,6 +303,33 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
}
private fun Dialog.saveToDrafts() {
val text = dialog.editComment.text.toString()
val draft = Draft()
draft.unique_id = UUID.randomUUID().toString()
draft.action_type = Draft.Action.QUOTE
draft.account_keys = arrayOf(accountKey)
draft.text = text
draft.timestamp = System.currentTimeMillis()
draft.action_extras = QuoteStatusActionExtras().apply {
this.status = this@RetweetQuoteDialogFragment.status
this.isQuoteOriginalStatus = popupMenu.quoteOriginalStatus
}
val values = ObjectCursor.valuesCreatorFrom(Draft::class.java).create(draft)
val contentResolver = context.contentResolver
val draftUri = contentResolver.insert(Drafts.CONTENT_URI, values)
displayNewDraftNotification(draftUri)
}
private fun displayNewDraftNotification(draftUri: Uri) {
val contentResolver = context.contentResolver
val values = ContentValues {
this[BaseColumns._ID] = draftUri.lastPathSegment
}
contentResolver.insert(Drafts.CONTENT_URI_NOTIFICATIONS, values)
}
class QuoteProtectedStatusWarnFragment : BaseDialogFragment(), DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface, which: Int) {
@ -322,11 +382,13 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
val FRAGMENT_TAG = "retweet_quote"
private val SHOW_PROTECTED_CONFIRM = java.lang.Boolean.parseBoolean("false")
fun show(fm: FragmentManager, status: ParcelableStatus, accountKey: UserKey? = null): RetweetQuoteDialogFragment {
fun show(fm: FragmentManager, status: ParcelableStatus, accountKey: UserKey? = null,
text: String? = null): RetweetQuoteDialogFragment {
val f = RetweetQuoteDialogFragment()
f.arguments = Bundle {
this[EXTRA_STATUS] = status
this[EXTRA_ACCOUNT_KEY] = accountKey
this[EXTRA_TEXT] = text
}
f.show(fm, FRAGMENT_TAG)
return f

View File

@ -2,10 +2,15 @@ package org.mariotaku.twidere.model.util
import android.accounts.AccountManager
import android.content.Context
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.unique_id_non_null
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableStatusUpdate
import org.mariotaku.twidere.model.draft.QuoteStatusActionExtras
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
import org.mariotaku.twidere.util.LinkCreator
/**
* Created by mariotaku on 16/2/12.
@ -20,12 +25,52 @@ object ParcelableStatusUpdateUtils {
statusUpdate.text = draft.text
statusUpdate.location = draft.location
statusUpdate.media = draft.media
if (draft.action_extras is UpdateStatusActionExtras) {
val extra = draft.action_extras as UpdateStatusActionExtras?
statusUpdate.in_reply_to_status = extra!!.inReplyToStatus
statusUpdate.is_possibly_sensitive = extra.isPossiblySensitive
statusUpdate.display_coordinates = extra.displayCoordinates
statusUpdate.attachment_url = extra.attachmentUrl
val actionExtras = draft.action_extras
when (actionExtras) {
is UpdateStatusActionExtras -> {
statusUpdate.in_reply_to_status = actionExtras.inReplyToStatus
statusUpdate.is_possibly_sensitive = actionExtras.isPossiblySensitive
statusUpdate.display_coordinates = actionExtras.displayCoordinates
statusUpdate.attachment_url = actionExtras.attachmentUrl
}
is QuoteStatusActionExtras -> {
val onlyAccount = statusUpdate.accounts.singleOrNull()
val status = actionExtras.status
val quoteOriginalStatus = actionExtras.isQuoteOriginalStatus
if (onlyAccount != null) {
when (onlyAccount.type) {
AccountType.FANFOU -> {
if (!status.is_quote || !quoteOriginalStatus) {
statusUpdate.repost_status_id = status.id
statusUpdate.text = context.getString(R.string.fanfou_repost_format,
draft.text, status.user_screen_name, status.text_plain)
} else {
statusUpdate.text = context.getString(R.string.fanfou_repost_format,
draft.text, status.quoted_user_screen_name,
status.quoted_text_plain)
statusUpdate.repost_status_id = status.quoted_id
}
}
else -> {
val statusLink = if (!status.is_quote || !quoteOriginalStatus) {
LinkCreator.getStatusWebLink(status)
} else {
LinkCreator.getQuotedStatusWebLink(status)
}
statusUpdate.attachment_url = statusLink.toString()
statusUpdate.text = draft.text
}
}
}
}
is StatusObjectActionExtras -> {
when (draft.action_type) {
Draft.Action.QUOTE -> {
val link = LinkCreator.getStatusWebLink(actionExtras.status)
statusUpdate.attachment_url = link.toString()
}
}
}
}
statusUpdate.draft_action = draft.action_type
statusUpdate.draft_unique_id = draft.unique_id_non_null

View File

@ -19,10 +19,8 @@
package org.mariotaku.twidere.provider
import android.app.PendingIntent
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Intent
import android.database.Cursor
import android.database.MatrixCursor
import android.database.SQLException
@ -33,35 +31,28 @@ import android.os.Binder
import android.os.Handler
import android.os.Looper
import android.os.Process
import android.provider.BaseColumns
import android.support.v4.app.NotificationCompat
import android.support.v4.text.BidiFormatter
import com.squareup.otto.Bus
import okhttp3.Dns
import org.mariotaku.ktextension.isNullOrEmpty
import org.mariotaku.ktextension.toNulls
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.sqliteqb.library.Columns.Column
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.RawItemArray
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.extension.withAppendedPath
import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.UnreadCountUpdatedEvent
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.SQLiteDatabaseWrapper.LazyLoadCallback
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.database.CachedUsersQueryBuilder
import org.mariotaku.twidere.util.database.SuggestionsCursorCreator
import java.io.IOException
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import javax.inject.Inject
@ -345,62 +336,21 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
private fun deleteInternal(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
val tableId = DataStoreUtils.getTableId(uri)
val table = DataStoreUtils.getTableNameById(tableId) ?: return 0
val result = databaseWrapper.delete(table, selection, selectionArgs)
if (result > 0) {
onDatabaseUpdated(tableId, uri)
when (tableId) {
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS -> {
notificationManager.cancel(uri.toString(), NOTIFICATION_ID_DRAFTS)
return 1
}
else -> {
val table = DataStoreUtils.getTableNameById(tableId) ?: return 0
val result = databaseWrapper.delete(table, selection, selectionArgs)
if (result > 0) {
onDatabaseUpdated(tableId, uri)
}
onItemDeleted(uri, tableId)
return result
}
}
return result
}
private fun showDraftNotification(values: ContentValues?): Long {
val context = context
if (values == null || context == null) return -1
val draftId = values.getAsLong(BaseColumns._ID) ?: return -1
val where = Expression.equals(Drafts._ID, draftId)
val c = context.contentResolver.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.sql, null, null) ?: return -1
val i = ObjectCursor.indicesFrom(c, Draft::class.java)
val item: Draft
try {
if (!c.moveToFirst()) return -1
item = i.newObject(c)
} catch (e: IOException) {
return -1
} finally {
c.close()
}
val title = context.getString(R.string.status_not_updated)
val message = context.getString(R.string.status_not_updated_summary)
val intent = Intent()
intent.`package` = BuildConfig.APPLICATION_ID
val uriBuilder = Uri.Builder()
uriBuilder.scheme(SCHEME_TWIDERE)
uriBuilder.authority(AUTHORITY_DRAFTS)
intent.data = uriBuilder.build()
val nb = NotificationCompat.Builder(context)
nb.setTicker(message)
nb.setContentTitle(title)
nb.setContentText(item.text)
nb.setAutoCancel(true)
nb.setWhen(System.currentTimeMillis())
nb.setSmallIcon(R.drawable.ic_stat_draft)
val discardIntent = Intent(context, LengthyOperationsService::class.java)
discardIntent.action = INTENT_ACTION_DISCARD_DRAFT
val draftUri = Uri.withAppendedPath(Drafts.CONTENT_URI, draftId.toString())
discardIntent.data = draftUri
nb.addAction(R.drawable.ic_action_delete, context.getString(R.string.discard), PendingIntent.getService(context, 0,
discardIntent, PendingIntent.FLAG_ONE_SHOT))
val sendIntent = Intent(context, LengthyOperationsService::class.java)
sendIntent.action = INTENT_ACTION_SEND_DRAFT
sendIntent.data = draftUri
nb.addAction(R.drawable.ic_action_send, context.getString(R.string.action_send),
PendingIntent.getService(context, 0, sendIntent, PendingIntent.FLAG_ONE_SHOT))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
nb.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT))
notificationManager.notify(draftUri.toString(), NOTIFICATION_ID_DRAFTS,
nb.build())
return draftId
}
@ -452,7 +402,7 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
}
}
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS -> {
rowId = showDraftNotification(values)
rowId = contentNotificationManager.showDraft(uri)
}
else -> {
val conflictAlgorithm = getConflictAlgorithm(tableId)
@ -468,7 +418,7 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
}
onDatabaseUpdated(tableId, uri)
onNewItemsInserted(uri, tableId, arrayOf(values))
return Uri.withAppendedPath(uri, rowId.toString())
return uri.withAppendedPath(rowId.toString())
}
private fun updateInternal(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
@ -500,6 +450,9 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
notifyContentObserver(uri)
}
private fun onItemDeleted(uri: Uri, tableId: Int) {
}
private fun onNewItemsInserted(uri: Uri, tableId: Int, valuesArray: Array<ContentValues?>?) {
val context = context ?: return
if (valuesArray.isNullOrEmpty()) return

View File

@ -60,7 +60,7 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
import org.mariotaku.twidere.model.draft.StatusObjectExtras
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
import org.mariotaku.twidere.model.schedule.ScheduleInfo
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
@ -390,7 +390,7 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
private fun <T> performStatusAction(draft: Draft, action: (accountKey: UserKey, status: ParcelableStatus) -> AbstractTask<*, T, *>): Boolean {
val accountKey = draft.account_keys?.firstOrNull() ?: return false
val status = (draft.action_extras as? StatusObjectExtras)?.status ?: return false
val status = (draft.action_extras as? StatusObjectActionExtras)?.status ?: return false
val task = action(accountKey, status)
invokeBeforeExecute(task)
val result = ManualTaskStarter.invokeExecute(task)

View File

@ -18,7 +18,7 @@ import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.draft.StatusObjectExtras
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
import org.mariotaku.twidere.model.event.FavoriteTaskEvent
import org.mariotaku.twidere.model.event.StatusListChangedEvent
import org.mariotaku.twidere.model.util.AccountUtils
@ -45,7 +45,7 @@ class CreateFavoriteTask(
override fun doLongOperation(params: Any?): SingleResponse<ParcelableStatus> {
val draftId = UpdateStatusTask.saveDraft(context, Draft.Action.FAVORITE) {
this@saveDraft.account_keys = arrayOf(accountKey)
this@saveDraft.action_extras = StatusObjectExtras().apply {
this@saveDraft.action_extras = StatusObjectActionExtras().apply {
this@apply.status = this@CreateFavoriteTask.status
}
}

View File

@ -17,7 +17,7 @@ import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.draft.StatusObjectExtras
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
import org.mariotaku.twidere.model.event.StatusListChangedEvent
import org.mariotaku.twidere.model.event.StatusRetweetedEvent
import org.mariotaku.twidere.model.util.AccountUtils
@ -40,7 +40,7 @@ class RetweetStatusTask(
override fun doLongOperation(params: Any?): SingleResponse<ParcelableStatus> {
val draftId = UpdateStatusTask.saveDraft(context, Draft.Action.RETWEET) {
this@saveDraft.account_keys = arrayOf(accountKey)
this@saveDraft.action_extras = StatusObjectExtras().apply {
this@saveDraft.action_extras = StatusObjectActionExtras().apply {
this@apply.status = this@RetweetStatusTask.status
}
}

View File

@ -43,7 +43,6 @@ import org.mariotaku.twidere.extension.text.twitter.getTweetLength
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.account.AccountExtras
import org.mariotaku.twidere.model.analyzer.UpdateStatus
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
import org.mariotaku.twidere.model.schedule.ScheduleInfo
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
@ -528,13 +527,7 @@ class UpdateStatusTask(
this.location = statusUpdate.location
this.media = statusUpdate.media
this.timestamp = System.currentTimeMillis()
this.action_extras = UpdateStatusActionExtras().apply {
inReplyToStatus = statusUpdate.in_reply_to_status
isPossiblySensitive = statusUpdate.is_possibly_sensitive
isRepostStatusId = statusUpdate.repost_status_id
displayCoordinates = statusUpdate.display_coordinates
attachmentUrl = statusUpdate.attachment_url
}
this.action_extras = statusUpdate.draft_extras
}
}

View File

@ -35,6 +35,7 @@ import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.sqliteqb.library.*
import org.mariotaku.sqliteqb.library.Columns.Column
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.HomeActivity
@ -50,10 +51,10 @@ import org.mariotaku.twidere.extension.model.notificationDisabled
import org.mariotaku.twidere.extension.rawQuery
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.receiver.NotificationReceiver
import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.database.FilterQueryBuilder
import org.oshkimaadziig.george.androidutils.SpanFormatter
import java.io.IOException
@ -355,6 +356,53 @@ class ContentNotificationManager(
notificationManager.notify(tag, NOTIFICATION_ID_USER_NOTIFICATION, builder.build())
}
fun showDraft(draftUri: Uri): Long {
val draftId = draftUri.lastPathSegment.toLongOrNull() ?: return -1
val where = Expression.equals(Drafts._ID, draftId)
val c = context.contentResolver.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.sql,
null, null) ?: return -1
val i = ObjectCursor.indicesFrom(c, Draft::class.java)
val item: Draft
try {
if (!c.moveToFirst()) return -1
item = i.newObject(c)
} catch (e: IOException) {
return -1
} finally {
c.close()
}
val title = context.getString(R.string.status_not_updated)
val message = context.getString(R.string.status_not_updated_summary)
val intent = Intent()
intent.`package` = BuildConfig.APPLICATION_ID
val uriBuilder = Uri.Builder()
uriBuilder.scheme(SCHEME_TWIDERE)
uriBuilder.authority(AUTHORITY_DRAFTS)
intent.data = uriBuilder.build()
val nb = NotificationCompat.Builder(context)
nb.setTicker(message)
nb.setContentTitle(title)
nb.setContentText(item.text)
nb.setAutoCancel(true)
nb.setWhen(System.currentTimeMillis())
nb.setSmallIcon(R.drawable.ic_stat_draft)
val discardIntent = Intent(context, LengthyOperationsService::class.java)
discardIntent.action = INTENT_ACTION_DISCARD_DRAFT
discardIntent.data = draftUri
nb.addAction(R.drawable.ic_action_delete, context.getString(R.string.discard),
PendingIntent.getService(context, 0, discardIntent, PendingIntent.FLAG_ONE_SHOT))
val sendIntent = Intent(context, LengthyOperationsService::class.java)
sendIntent.action = INTENT_ACTION_SEND_DRAFT
sendIntent.data = draftUri
nb.addAction(R.drawable.ic_action_send, context.getString(R.string.action_send),
PendingIntent.getService(context, 0, sendIntent, PendingIntent.FLAG_ONE_SHOT))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
nb.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT))
notificationManager.notify(draftUri.toString(), NOTIFICATION_ID_DRAFTS, nb.build())
return draftId
}
fun updatePreferences() {
nameFirst = preferences[nameFirstKey]
useStarForLikes = preferences[iWantMyStarsBackKey]

View File

@ -116,7 +116,7 @@ object DataStoreUtils {
VIRTUAL_TABLE_ID_DRAFTS_UNSENT)
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_NOTIFICATIONS,
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS)
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_NOTIFICATIONS,
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Drafts.CONTENT_PATH_NOTIFICATIONS + "/#",
VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS)
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Suggestions.AutoComplete.CONTENT_PATH,
VIRTUAL_TABLE_ID_SUGGESTIONS_AUTO_COMPLETE)

View File

@ -28,7 +28,7 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.extension.model.getActionName
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.draft.StatusObjectExtras
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.Utils
@ -54,7 +54,7 @@ class DraftViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
media = media)
}
Draft.Action.FAVORITE, Draft.Action.RETWEET -> {
val extras = draft.action_extras as? StatusObjectExtras
val extras = draft.action_extras as? StatusObjectActionExtras
if (extras != null) {
summaryText = extras.status.text_unescaped
}