2014-07-03 07:48:39 +02:00
|
|
|
/*
|
|
|
|
* Twidere - Twitter client for Android
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.mariotaku.twidere.provider;
|
|
|
|
|
2015-11-08 08:12:35 +01:00
|
|
|
import android.annotation.SuppressLint;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.ContentProvider;
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.content.ContentValues;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
2016-02-20 17:44:35 +01:00
|
|
|
import android.content.pm.PackageManager;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.database.MatrixCursor;
|
2015-11-08 08:12:35 +01:00
|
|
|
import android.database.MergeCursor;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.database.SQLException;
|
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
2015-10-06 04:50:44 +02:00
|
|
|
import android.database.sqlite.SQLiteFullException;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.database.sqlite.SQLiteOpenHelper;
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.graphics.BitmapFactory;
|
2015-03-24 16:40:47 +01:00
|
|
|
import android.graphics.Typeface;
|
2015-04-01 12:28:26 +02:00
|
|
|
import android.media.AudioManager;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Binder;
|
2014-12-07 16:13:07 +01:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Looper;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.os.ParcelFileDescriptor;
|
2016-02-20 17:44:35 +01:00
|
|
|
import android.os.Process;
|
2015-07-03 16:45:29 +02:00
|
|
|
import android.provider.BaseColumns;
|
2014-12-07 16:13:07 +01:00
|
|
|
import android.support.annotation.NonNull;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.support.v4.app.NotificationCompat;
|
2015-03-24 16:40:47 +01:00
|
|
|
import android.support.v4.app.NotificationCompat.InboxStyle;
|
2016-02-03 20:27:43 +01:00
|
|
|
import android.support.v4.text.BidiFormatter;
|
2015-04-04 16:31:37 +02:00
|
|
|
import android.support.v4.util.LongSparseArray;
|
2015-03-24 16:40:47 +01:00
|
|
|
import android.text.Spannable;
|
|
|
|
import android.text.SpannableStringBuilder;
|
2015-11-08 08:12:35 +01:00
|
|
|
import android.text.TextUtils;
|
2015-03-24 16:40:47 +01:00
|
|
|
import android.text.style.StyleSpan;
|
2014-07-03 07:48:39 +02:00
|
|
|
import android.util.Log;
|
|
|
|
|
2015-10-05 15:36:31 +02:00
|
|
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
2014-12-07 16:13:07 +01:00
|
|
|
import com.squareup.otto.Bus;
|
|
|
|
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.apache.commons.lang3.ArrayUtils;
|
2015-11-25 03:30:37 +01:00
|
|
|
import org.apache.commons.lang3.math.NumberUtils;
|
2015-07-06 11:36:18 +02:00
|
|
|
import org.mariotaku.sqliteqb.library.Columns.Column;
|
|
|
|
import org.mariotaku.sqliteqb.library.Expression;
|
2015-11-08 08:12:35 +01:00
|
|
|
import org.mariotaku.sqliteqb.library.OrderBy;
|
2015-07-06 11:36:18 +02:00
|
|
|
import org.mariotaku.sqliteqb.library.RawItemArray;
|
2015-11-08 08:12:35 +01:00
|
|
|
import org.mariotaku.sqliteqb.library.SQLConstants;
|
|
|
|
import org.mariotaku.sqliteqb.library.SQLFunctions;
|
2015-07-06 11:36:18 +02:00
|
|
|
import org.mariotaku.sqliteqb.library.query.SQLSelectQuery;
|
2015-05-13 12:54:13 +02:00
|
|
|
import org.mariotaku.twidere.BuildConfig;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.Constants;
|
|
|
|
import org.mariotaku.twidere.R;
|
|
|
|
import org.mariotaku.twidere.activity.support.HomeActivity;
|
2016-01-28 11:50:39 +01:00
|
|
|
import org.mariotaku.twidere.annotation.CustomTabType;
|
|
|
|
import org.mariotaku.twidere.annotation.NotificationType;
|
|
|
|
import org.mariotaku.twidere.annotation.ReadPositionTag;
|
2016-01-31 14:34:49 +01:00
|
|
|
import org.mariotaku.twidere.api.twitter.model.Activity;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.app.TwidereApplication;
|
|
|
|
import org.mariotaku.twidere.model.AccountPreferences;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.model.ActivityTitleSummaryMessage;
|
2016-02-21 05:47:21 +01:00
|
|
|
import org.mariotaku.twidere.model.Draft;
|
|
|
|
import org.mariotaku.twidere.model.DraftCursorIndices;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.model.ParcelableActivity;
|
|
|
|
import org.mariotaku.twidere.model.ParcelableActivityCursorIndices;
|
2016-01-31 14:34:49 +01:00
|
|
|
import org.mariotaku.twidere.model.ParcelableUser;
|
2015-03-25 14:20:51 +01:00
|
|
|
import org.mariotaku.twidere.model.StringLongPair;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.model.UnreadItem;
|
2016-02-17 12:23:47 +01:00
|
|
|
import org.mariotaku.twidere.model.message.UnreadCountUpdatedEvent;
|
2016-01-31 14:34:49 +01:00
|
|
|
import org.mariotaku.twidere.model.util.ParcelableActivityUtils;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
|
2015-10-06 04:50:44 +02:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedHashtags;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedImages;
|
2015-01-14 09:47:51 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
|
2015-10-06 04:50:44 +02:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedStatuses;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.DNS;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
|
2016-01-27 16:08:00 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Notifications;
|
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Permissions;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Preferences;
|
2015-11-08 08:12:35 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory;
|
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
2015-11-08 08:12:35 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
|
2015-04-04 16:31:37 +02:00
|
|
|
import org.mariotaku.twidere.receiver.NotificationReceiver;
|
2015-07-03 16:45:29 +02:00
|
|
|
import org.mariotaku.twidere.service.BackgroundOperationService;
|
2016-02-14 06:53:10 +01:00
|
|
|
import org.mariotaku.twidere.util.ActivityTracker;
|
2015-01-14 17:35:37 +01:00
|
|
|
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
2015-12-17 15:20:26 +01:00
|
|
|
import org.mariotaku.twidere.util.DataStoreUtils;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.util.ImagePreloader;
|
2015-11-14 13:36:56 +01:00
|
|
|
import org.mariotaku.twidere.util.NotificationManagerWrapper;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.util.ParseUtils;
|
|
|
|
import org.mariotaku.twidere.util.PermissionsManager;
|
2015-03-25 14:20:51 +01:00
|
|
|
import org.mariotaku.twidere.util.ReadStateManager;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.util.SQLiteDatabaseWrapper;
|
|
|
|
import org.mariotaku.twidere.util.SQLiteDatabaseWrapper.LazyLoadCallback;
|
|
|
|
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
2015-01-11 10:28:45 +01:00
|
|
|
import org.mariotaku.twidere.util.TwidereArrayUtils;
|
2016-02-20 17:44:35 +01:00
|
|
|
import org.mariotaku.twidere.util.TwidereListUtils;
|
2015-01-14 09:47:51 +01:00
|
|
|
import org.mariotaku.twidere.util.TwidereQueryBuilder.CachedUsersQueryBuilder;
|
|
|
|
import org.mariotaku.twidere.util.TwidereQueryBuilder.ConversationQueryBuilder;
|
2015-10-21 15:53:46 +02:00
|
|
|
import org.mariotaku.twidere.util.UriExtraUtils;
|
2015-04-24 08:14:33 +02:00
|
|
|
import org.mariotaku.twidere.util.UserColorNameManager;
|
2014-07-03 07:48:39 +02:00
|
|
|
import org.mariotaku.twidere.util.Utils;
|
2015-03-25 14:20:51 +01:00
|
|
|
import org.mariotaku.twidere.util.collection.CompactHashSet;
|
2015-12-31 09:32:27 +01:00
|
|
|
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
2016-02-21 17:13:24 +01:00
|
|
|
import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
|
2016-02-12 08:19:38 +01:00
|
|
|
import org.mariotaku.twidere.util.net.TwidereDns;
|
2016-01-28 05:10:30 +01:00
|
|
|
import org.oshkimaadziig.george.androidutils.SpanFormatter;
|
2014-07-03 07:48:39 +02:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
2015-03-14 16:11:20 +01:00
|
|
|
import java.net.InetAddress;
|
2015-03-25 14:20:51 +01:00
|
|
|
import java.util.ArrayList;
|
2016-02-20 17:44:35 +01:00
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collections;
|
2014-07-03 07:48:39 +02:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2016-01-29 18:57:15 +01:00
|
|
|
import java.util.concurrent.Executor;
|
|
|
|
import java.util.concurrent.Executors;
|
2015-11-08 08:12:35 +01:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2014-07-03 07:48:39 +02:00
|
|
|
|
2015-10-05 12:00:07 +02:00
|
|
|
import javax.inject.Inject;
|
|
|
|
|
2014-07-03 07:48:39 +02:00
|
|
|
public final class TwidereDataProvider extends ContentProvider implements Constants, OnSharedPreferenceChangeListener,
|
2014-11-26 08:30:05 +01:00
|
|
|
LazyLoadCallback {
|
|
|
|
|
2015-03-25 16:04:25 +01:00
|
|
|
public static final String TAG_OLDEST_MESSAGES = "oldest_messages";
|
2016-01-02 10:48:24 +01:00
|
|
|
private static final Pattern PATTERN_SCREEN_NAME = Pattern.compile("(?i)[@\uFF20]?([a-z0-9_]{1,20})");
|
2015-10-05 12:00:07 +02:00
|
|
|
@Inject
|
|
|
|
ReadStateManager mReadStateManager;
|
|
|
|
@Inject
|
|
|
|
AsyncTwitterWrapper mTwitterWrapper;
|
2015-10-05 15:36:31 +02:00
|
|
|
@Inject
|
|
|
|
ImageLoader mMediaLoader;
|
2015-11-14 13:36:56 +01:00
|
|
|
@Inject
|
|
|
|
NotificationManagerWrapper mNotificationManager;
|
2015-10-09 09:58:36 +02:00
|
|
|
@Inject
|
|
|
|
SharedPreferencesWrapper mPreferences;
|
2015-10-05 16:19:47 +02:00
|
|
|
@Inject
|
2016-02-12 08:19:38 +01:00
|
|
|
TwidereDns mDns;
|
2015-10-06 18:45:39 +02:00
|
|
|
@Inject
|
|
|
|
Bus mBus;
|
2015-10-09 09:58:36 +02:00
|
|
|
@Inject
|
|
|
|
UserColorNameManager mUserColorNameManager;
|
2016-02-03 20:27:43 +01:00
|
|
|
@Inject
|
|
|
|
BidiFormatter mBidiFormatter;
|
2016-02-14 06:53:10 +01:00
|
|
|
@Inject
|
|
|
|
ActivityTracker mActivityTracker;
|
|
|
|
@Inject
|
|
|
|
PermissionsManager mPermissionsManager;
|
2016-01-29 18:57:15 +01:00
|
|
|
|
2014-12-07 16:13:07 +01:00
|
|
|
private Handler mHandler;
|
2015-11-14 13:36:56 +01:00
|
|
|
private ContentResolver mContentResolver;
|
|
|
|
private SQLiteDatabaseWrapper mDatabaseWrapper;
|
|
|
|
private ImagePreloader mImagePreloader;
|
2016-01-29 18:57:15 +01:00
|
|
|
private Executor mBackgroundExecutor;
|
2016-01-27 16:08:00 +01:00
|
|
|
private boolean mNameFirst;
|
|
|
|
private boolean mUseStarForLikes;
|
|
|
|
|
2016-01-28 11:50:39 +01:00
|
|
|
private static PendingIntent getMarkReadDeleteIntent(Context context, @NotificationType String type,
|
|
|
|
long accountId, long position, boolean extraUserFollowing) {
|
2015-11-30 06:32:05 +01:00
|
|
|
return getMarkReadDeleteIntent(context, type, accountId, position, -1, -1, extraUserFollowing);
|
2015-10-10 15:12:03 +02:00
|
|
|
}
|
|
|
|
|
2016-01-28 11:50:39 +01:00
|
|
|
private static PendingIntent getMarkReadDeleteIntent(Context context, @NotificationType String type,
|
|
|
|
long accountId, long position, long extraId,
|
|
|
|
long extraUserId, boolean extraUserFollowing) {
|
2015-10-06 04:50:44 +02:00
|
|
|
// Setup delete intent
|
2015-10-12 08:59:17 +02:00
|
|
|
final Intent intent = new Intent(context, NotificationReceiver.class);
|
|
|
|
intent.setAction(BROADCAST_NOTIFICATION_DELETED);
|
|
|
|
final Uri.Builder linkBuilder = new Uri.Builder();
|
|
|
|
linkBuilder.scheme(SCHEME_TWIDERE);
|
2016-02-23 07:34:39 +01:00
|
|
|
linkBuilder.authority(AUTHORITY_INTERACTIONS);
|
2015-10-12 08:59:17 +02:00
|
|
|
linkBuilder.appendPath(type);
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_READ_POSITION, String.valueOf(position));
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE, type);
|
2015-10-21 15:53:46 +02:00
|
|
|
|
|
|
|
UriExtraUtils.addExtra(linkBuilder, "item_id", extraId);
|
|
|
|
UriExtraUtils.addExtra(linkBuilder, "item_user_id", extraUserId);
|
2015-11-30 06:32:05 +01:00
|
|
|
UriExtraUtils.addExtra(linkBuilder, "item_user_following", extraUserFollowing);
|
2015-10-12 08:59:17 +02:00
|
|
|
intent.setData(linkBuilder.build());
|
|
|
|
return PendingIntent.getBroadcast(context, 0, intent, 0);
|
2015-10-06 04:50:44 +02:00
|
|
|
}
|
|
|
|
|
2016-01-28 11:50:39 +01:00
|
|
|
private static PendingIntent getMarkReadDeleteIntent(Context context, @NotificationType String notificationType,
|
|
|
|
long accountId, StringLongPair[] positions) {
|
2015-10-06 04:50:44 +02:00
|
|
|
// Setup delete intent
|
2015-10-12 08:59:17 +02:00
|
|
|
final Intent intent = new Intent(context, NotificationReceiver.class);
|
|
|
|
final Uri.Builder linkBuilder = new Uri.Builder();
|
|
|
|
linkBuilder.scheme(SCHEME_TWIDERE);
|
2016-02-23 07:34:39 +01:00
|
|
|
linkBuilder.authority(AUTHORITY_INTERACTIONS);
|
2016-01-28 11:50:39 +01:00
|
|
|
linkBuilder.appendPath(notificationType);
|
2015-10-12 08:59:17 +02:00
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_READ_POSITIONS, StringLongPair.toString(positions));
|
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
2016-01-28 11:50:39 +01:00
|
|
|
linkBuilder.appendQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE, notificationType);
|
2015-10-12 08:59:17 +02:00
|
|
|
intent.setData(linkBuilder.build());
|
|
|
|
return PendingIntent.getBroadcast(context, 0, intent, 0);
|
2015-10-06 04:50:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static Cursor getPreferencesCursor(final SharedPreferencesWrapper preferences, final String key) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(Preferences.MATRIX_COLUMNS);
|
2015-10-06 04:50:44 +02:00
|
|
|
final Map<String, Object> map = new HashMap<>();
|
|
|
|
final Map<String, ?> all = preferences.getAll();
|
|
|
|
if (key == null) {
|
|
|
|
map.putAll(all);
|
|
|
|
} else {
|
|
|
|
map.put(key, all.get(key));
|
|
|
|
}
|
|
|
|
for (final Map.Entry<String, ?> item : map.entrySet()) {
|
|
|
|
final Object value = item.getValue();
|
|
|
|
final int type = getPreferenceType(value);
|
|
|
|
c.addRow(new Object[]{item.getKey(), ParseUtils.parseString(value), type});
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int getPreferenceType(final Object object) {
|
|
|
|
if (object == null)
|
|
|
|
return Preferences.TYPE_NULL;
|
|
|
|
else if (object instanceof Boolean)
|
|
|
|
return Preferences.TYPE_BOOLEAN;
|
|
|
|
else if (object instanceof Integer)
|
|
|
|
return Preferences.TYPE_INTEGER;
|
|
|
|
else if (object instanceof Long)
|
|
|
|
return Preferences.TYPE_LONG;
|
|
|
|
else if (object instanceof Float)
|
|
|
|
return Preferences.TYPE_FLOAT;
|
|
|
|
else if (object instanceof String) return Preferences.TYPE_STRING;
|
|
|
|
return Preferences.TYPE_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int getUnreadCount(final List<UnreadItem> set, final long... accountIds) {
|
|
|
|
if (set == null || set.isEmpty()) return 0;
|
|
|
|
int count = 0;
|
|
|
|
for (final UnreadItem item : set.toArray(new UnreadItem[set.size()])) {
|
|
|
|
if (item != null && ArrayUtils.contains(accountIds, item.account_id)) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean shouldReplaceOnConflict(final int table_id) {
|
|
|
|
switch (table_id) {
|
|
|
|
case TABLE_ID_CACHED_HASHTAGS:
|
|
|
|
case TABLE_ID_CACHED_STATUSES:
|
|
|
|
case TABLE_ID_CACHED_USERS:
|
|
|
|
case TABLE_ID_CACHED_RELATIONSHIPS:
|
|
|
|
case TABLE_ID_SEARCH_HISTORY:
|
|
|
|
case TABLE_ID_FILTERED_USERS:
|
|
|
|
case TABLE_ID_FILTERED_KEYWORDS:
|
|
|
|
case TABLE_ID_FILTERED_SOURCES:
|
|
|
|
case TABLE_ID_FILTERED_LINKS:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
|
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public int bulkInsert(@NonNull final Uri uri, @NonNull final ContentValues[] valuesArray) {
|
2014-11-26 08:30:05 +01:00
|
|
|
try {
|
2015-10-06 04:50:44 +02:00
|
|
|
return bulkInsertInternal(uri, valuesArray);
|
|
|
|
} catch (final SQLException e) {
|
|
|
|
if (handleSQLException(e)) {
|
|
|
|
try {
|
|
|
|
return bulkInsertInternal(uri, valuesArray);
|
|
|
|
} catch (SQLException e1) {
|
|
|
|
throw new IllegalStateException(e1);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-06 04:50:44 +02:00
|
|
|
private boolean handleSQLException(SQLException e) {
|
|
|
|
try {
|
|
|
|
if (e instanceof SQLiteFullException) {
|
|
|
|
// Drop cached databases
|
|
|
|
mDatabaseWrapper.delete(CachedUsers.TABLE_NAME, null, null);
|
|
|
|
mDatabaseWrapper.delete(CachedStatuses.TABLE_NAME, null, null);
|
|
|
|
mDatabaseWrapper.delete(CachedHashtags.TABLE_NAME, null, null);
|
|
|
|
mDatabaseWrapper.execSQL("VACUUM");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (SQLException ee) {
|
|
|
|
throw new IllegalStateException(ee);
|
|
|
|
}
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
private int bulkInsertInternal(@NonNull Uri uri, @NonNull ContentValues[] valuesArray) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int tableId = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(tableId);
|
2015-10-06 04:50:44 +02:00
|
|
|
checkWritePermission(tableId, table);
|
|
|
|
switch (tableId) {
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
final long[] newIds = new long[valuesArray.length];
|
2015-10-19 11:28:16 +02:00
|
|
|
if (table != null && valuesArray.length > 0) {
|
2015-10-06 04:50:44 +02:00
|
|
|
mDatabaseWrapper.beginTransaction();
|
|
|
|
if (tableId == TABLE_ID_CACHED_USERS) {
|
|
|
|
for (final ContentValues values : valuesArray) {
|
|
|
|
final Expression where = Expression.equals(CachedUsers.USER_ID,
|
|
|
|
values.getAsLong(CachedUsers.USER_ID));
|
|
|
|
mDatabaseWrapper.update(table, values, where.getSQL(), null);
|
|
|
|
newIds[result++] = mDatabaseWrapper.insertWithOnConflict(table, null,
|
|
|
|
values, SQLiteDatabase.CONFLICT_REPLACE);
|
|
|
|
}
|
|
|
|
} else if (tableId == TABLE_ID_SEARCH_HISTORY) {
|
|
|
|
for (final ContentValues values : valuesArray) {
|
|
|
|
values.put(SearchHistory.RECENT_QUERY, System.currentTimeMillis());
|
|
|
|
final Expression where = Expression.equalsArgs(SearchHistory.QUERY);
|
|
|
|
final String[] args = {values.getAsString(SearchHistory.QUERY)};
|
|
|
|
mDatabaseWrapper.update(table, values, where.getSQL(), args);
|
|
|
|
newIds[result++] = mDatabaseWrapper.insertWithOnConflict(table, null,
|
|
|
|
values, SQLiteDatabase.CONFLICT_IGNORE);
|
|
|
|
}
|
|
|
|
} else if (shouldReplaceOnConflict(tableId)) {
|
|
|
|
for (final ContentValues values : valuesArray) {
|
|
|
|
newIds[result++] = mDatabaseWrapper.insertWithOnConflict(table, null,
|
|
|
|
values, SQLiteDatabase.CONFLICT_REPLACE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (final ContentValues values : valuesArray) {
|
|
|
|
newIds[result++] = mDatabaseWrapper.insert(table, null, values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mDatabaseWrapper.setTransactionSuccessful();
|
|
|
|
mDatabaseWrapper.endTransaction();
|
|
|
|
}
|
|
|
|
if (result > 0) {
|
|
|
|
onDatabaseUpdated(tableId, uri);
|
|
|
|
}
|
2016-01-30 18:14:40 +01:00
|
|
|
onNewItemsInserted(uri, tableId, valuesArray);
|
2015-10-06 04:50:44 +02:00
|
|
|
return result;
|
|
|
|
}
|
2015-03-25 16:04:25 +01:00
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public int delete(@NonNull final Uri uri, final String selection, final String[] selectionArgs) {
|
2014-11-26 08:30:05 +01:00
|
|
|
try {
|
2015-10-06 04:50:44 +02:00
|
|
|
return deleteInternal(uri, selection, selectionArgs);
|
|
|
|
} catch (final SQLException e) {
|
|
|
|
if (handleSQLException(e)) {
|
|
|
|
try {
|
|
|
|
return deleteInternal(uri, selection, selectionArgs);
|
|
|
|
} catch (SQLException e1) {
|
|
|
|
throw new IllegalStateException(e1);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
}
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int deleteInternal(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int tableId = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(tableId);
|
2015-10-06 04:50:44 +02:00
|
|
|
checkWritePermission(tableId, table);
|
|
|
|
switch (tableId) {
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES:
|
|
|
|
return 0;
|
|
|
|
case VIRTUAL_TABLE_ID_NOTIFICATIONS: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
|
|
|
if (segments.size() == 1) {
|
|
|
|
clearNotification();
|
|
|
|
} else if (segments.size() == 2) {
|
2015-11-25 03:30:37 +01:00
|
|
|
final int notificationType = NumberUtils.toInt(segments.get(1), -1);
|
2015-10-06 04:50:44 +02:00
|
|
|
clearNotification(notificationType, 0);
|
|
|
|
} else if (segments.size() == 3) {
|
2015-11-25 03:30:37 +01:00
|
|
|
final int notificationType = NumberUtils.toInt(segments.get(1), -1);
|
|
|
|
final long accountId = NumberUtils.toLong(segments.get(2), -1);
|
2015-10-06 04:50:44 +02:00
|
|
|
clearNotification(notificationType, accountId);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
return 1;
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
case VIRTUAL_TABLE_ID_UNREAD_COUNTS: {
|
|
|
|
return 0;
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
if (table == null) return 0;
|
|
|
|
final int result = mDatabaseWrapper.delete(table, selection, selectionArgs);
|
|
|
|
if (result > 0) {
|
|
|
|
onDatabaseUpdated(tableId, uri);
|
|
|
|
}
|
|
|
|
return result;
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public String getType(@NonNull final Uri uri) {
|
2014-11-26 08:30:05 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public Uri insert(@NonNull final Uri uri, final ContentValues values) {
|
2014-11-26 08:30:05 +01:00
|
|
|
try {
|
2015-10-06 04:50:44 +02:00
|
|
|
return insertInternal(uri, values);
|
|
|
|
} catch (final SQLException e) {
|
|
|
|
if (handleSQLException(e)) {
|
|
|
|
try {
|
|
|
|
return insertInternal(uri, values);
|
|
|
|
} catch (SQLException e1) {
|
|
|
|
throw new IllegalStateException(e1);
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Uri insertInternal(@NonNull Uri uri, ContentValues values) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int tableId = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(tableId);
|
2015-10-06 04:50:44 +02:00
|
|
|
checkWritePermission(tableId, table);
|
|
|
|
switch (tableId) {
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
final long rowId;
|
|
|
|
if (tableId == TABLE_ID_CACHED_USERS) {
|
2016-03-04 17:09:02 +01:00
|
|
|
final Expression where = Expression.and(Expression.equalsArgs(CachedUsers.USER_ID),
|
2016-03-04 18:15:38 +01:00
|
|
|
Expression.equalsArgs(CachedUsers.USER_HOST));
|
2016-03-04 17:09:02 +01:00
|
|
|
final String[] whereArgs = {values.getAsString(CachedUsers.USER_ID),
|
2016-03-04 18:15:38 +01:00
|
|
|
values.getAsString(CachedUsers.USER_HOST)};
|
2016-03-04 17:09:02 +01:00
|
|
|
mDatabaseWrapper.update(table, values, where.getSQL(), whereArgs);
|
2015-10-06 04:50:44 +02:00
|
|
|
rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values,
|
|
|
|
SQLiteDatabase.CONFLICT_IGNORE);
|
|
|
|
} else if (tableId == TABLE_ID_SEARCH_HISTORY) {
|
|
|
|
values.put(SearchHistory.RECENT_QUERY, System.currentTimeMillis());
|
|
|
|
final Expression where = Expression.equalsArgs(SearchHistory.QUERY);
|
|
|
|
final String[] args = {values.getAsString(SearchHistory.QUERY)};
|
|
|
|
mDatabaseWrapper.update(table, values, where.getSQL(), args);
|
|
|
|
rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values,
|
|
|
|
SQLiteDatabase.CONFLICT_IGNORE);
|
|
|
|
} else if (tableId == TABLE_ID_CACHED_RELATIONSHIPS) {
|
|
|
|
final long accountId = values.getAsLong(CachedRelationships.ACCOUNT_ID);
|
|
|
|
final long userId = values.getAsLong(CachedRelationships.USER_ID);
|
|
|
|
final Expression where = Expression.and(
|
|
|
|
Expression.equals(CachedRelationships.ACCOUNT_ID, accountId),
|
|
|
|
Expression.equals(CachedRelationships.USER_ID, userId)
|
|
|
|
);
|
|
|
|
if (mDatabaseWrapper.update(table, values, where.getSQL(), null) > 0) {
|
|
|
|
final String[] projection = {CachedRelationships._ID};
|
|
|
|
final Cursor c = mDatabaseWrapper.query(table, projection, where.getSQL(), null,
|
|
|
|
null, null, null);
|
|
|
|
if (c.moveToFirst()) {
|
|
|
|
rowId = c.getLong(0);
|
2015-01-14 09:47:51 +01:00
|
|
|
} else {
|
2015-10-06 04:50:44 +02:00
|
|
|
rowId = 0;
|
2015-01-14 09:47:51 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
c.close();
|
2015-07-03 16:45:29 +02:00
|
|
|
} else {
|
2015-10-06 04:50:44 +02:00
|
|
|
rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values,
|
|
|
|
SQLiteDatabase.CONFLICT_IGNORE);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
} else if (tableId == VIRTUAL_TABLE_ID_DRAFTS_NOTIFICATIONS) {
|
2016-01-02 10:48:24 +01:00
|
|
|
rowId = showDraftNotification(values);
|
2015-10-06 04:50:44 +02:00
|
|
|
} else if (shouldReplaceOnConflict(tableId)) {
|
|
|
|
rowId = mDatabaseWrapper.insertWithOnConflict(table, null, values,
|
|
|
|
SQLiteDatabase.CONFLICT_REPLACE);
|
|
|
|
} else if (table != null) {
|
|
|
|
rowId = mDatabaseWrapper.insert(table, null, values);
|
|
|
|
} else {
|
|
|
|
return null;
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
2015-10-06 04:50:44 +02:00
|
|
|
onDatabaseUpdated(tableId, uri);
|
2016-01-30 18:14:40 +01:00
|
|
|
onNewItemsInserted(uri, tableId, values);
|
2015-10-06 04:50:44 +02:00
|
|
|
return Uri.withAppendedPath(uri, String.valueOf(rowId));
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
2016-01-02 10:48:24 +01:00
|
|
|
private long showDraftNotification(ContentValues values) {
|
2015-10-06 04:50:44 +02:00
|
|
|
final Context context = getContext();
|
|
|
|
if (values == null || context == null) return -1;
|
2015-07-03 16:45:29 +02:00
|
|
|
final Long draftId = values.getAsLong(BaseColumns._ID);
|
|
|
|
if (draftId == null) return -1;
|
|
|
|
final Expression where = Expression.equals(Drafts._ID, draftId);
|
|
|
|
final Cursor c = getContentResolver().query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.getSQL(), null, null);
|
2015-10-06 04:50:44 +02:00
|
|
|
if (c == null) return -1;
|
2016-02-21 05:47:21 +01:00
|
|
|
final DraftCursorIndices i = new DraftCursorIndices(c);
|
|
|
|
final Draft item;
|
2015-07-03 16:45:29 +02:00
|
|
|
try {
|
|
|
|
if (!c.moveToFirst()) return -1;
|
2015-11-30 06:32:05 +01:00
|
|
|
item = i.newObject(c);
|
2015-07-03 16:45:29 +02:00
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
final String title = context.getString(R.string.status_not_updated);
|
|
|
|
final String message = context.getString(R.string.status_not_updated_summary);
|
|
|
|
final Intent intent = new Intent();
|
|
|
|
intent.setPackage(BuildConfig.APPLICATION_ID);
|
|
|
|
final Uri.Builder uriBuilder = new Uri.Builder();
|
|
|
|
uriBuilder.scheme(SCHEME_TWIDERE);
|
|
|
|
uriBuilder.authority(AUTHORITY_DRAFTS);
|
|
|
|
intent.setData(uriBuilder.build());
|
2016-02-21 17:13:24 +01:00
|
|
|
final NotificationCompat.Builder nb = new 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);
|
2015-07-03 16:45:29 +02:00
|
|
|
final Intent discardIntent = new Intent(context, BackgroundOperationService.class);
|
|
|
|
discardIntent.setAction(INTENT_ACTION_DISCARD_DRAFT);
|
2016-02-21 17:13:24 +01:00
|
|
|
final Uri draftUri = Uri.withAppendedPath(Drafts.CONTENT_URI, String.valueOf(draftId));
|
|
|
|
discardIntent.setData(draftUri);
|
|
|
|
nb.addAction(R.drawable.ic_action_delete, context.getString(R.string.discard), PendingIntent.getService(context, 0,
|
|
|
|
discardIntent, PendingIntent.FLAG_ONE_SHOT));
|
2015-07-03 16:45:29 +02:00
|
|
|
|
|
|
|
final Intent sendIntent = new Intent(context, BackgroundOperationService.class);
|
|
|
|
sendIntent.setAction(INTENT_ACTION_SEND_DRAFT);
|
2016-02-21 17:13:24 +01:00
|
|
|
sendIntent.setData(draftUri);
|
|
|
|
nb.addAction(R.drawable.ic_action_send, context.getString(R.string.send),
|
2015-07-03 16:45:29 +02:00
|
|
|
PendingIntent.getService(context, 0, sendIntent, PendingIntent.FLAG_ONE_SHOT));
|
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
2016-02-21 17:13:24 +01:00
|
|
|
nb.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT));
|
|
|
|
mNotificationManager.notify(draftUri.toString(), NOTIFICATION_ID_DRAFTS,
|
|
|
|
nb.build());
|
2015-07-03 16:45:29 +02:00
|
|
|
return draftId;
|
|
|
|
}
|
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
@Override
|
|
|
|
public boolean onCreate() {
|
|
|
|
final Context context = getContext();
|
2015-10-09 09:58:36 +02:00
|
|
|
assert context != null;
|
2015-12-31 09:32:27 +01:00
|
|
|
GeneralComponentHelper.build(context).inject(this);
|
2014-12-07 16:13:07 +01:00
|
|
|
mHandler = new Handler(Looper.getMainLooper());
|
2014-11-26 08:30:05 +01:00
|
|
|
mDatabaseWrapper = new SQLiteDatabaseWrapper(this);
|
|
|
|
mPreferences.registerOnSharedPreferenceChangeListener(this);
|
2016-01-29 18:57:15 +01:00
|
|
|
mBackgroundExecutor = Executors.newSingleThreadExecutor();
|
2014-11-26 08:30:05 +01:00
|
|
|
updatePreferences();
|
2015-10-05 15:36:31 +02:00
|
|
|
mImagePreloader = new ImagePreloader(context, mMediaLoader);
|
2014-11-26 08:30:05 +01:00
|
|
|
// final GetWritableDatabaseTask task = new
|
|
|
|
// GetWritableDatabaseTask(context, helper, mDatabaseWrapper);
|
2014-12-05 14:25:27 +01:00
|
|
|
// task.executeTask();
|
2014-11-26 08:30:05 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public SQLiteDatabase onCreateSQLiteDatabase() {
|
|
|
|
final TwidereApplication app = TwidereApplication.getInstance(getContext());
|
|
|
|
final SQLiteOpenHelper helper = app.getSQLiteOpenHelper();
|
|
|
|
return helper.getWritableDatabase();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
|
|
|
|
updatePreferences();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public ParcelFileDescriptor openFile(@NonNull final Uri uri, @NonNull final String mode) throws FileNotFoundException {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int table_id = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(table_id);
|
2016-01-28 11:50:39 +01:00
|
|
|
final int modeCode;
|
|
|
|
switch (mode) {
|
|
|
|
case "r":
|
|
|
|
modeCode = ParcelFileDescriptor.MODE_READ_ONLY;
|
|
|
|
break;
|
|
|
|
case "rw":
|
|
|
|
modeCode = ParcelFileDescriptor.MODE_READ_WRITE;
|
|
|
|
break;
|
|
|
|
case "rwt":
|
|
|
|
modeCode = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_TRUNCATE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
|
|
|
if (modeCode == ParcelFileDescriptor.MODE_READ_ONLY) {
|
2014-11-26 08:30:05 +01:00
|
|
|
checkReadPermission(table_id, table, null);
|
2016-01-28 11:50:39 +01:00
|
|
|
} else if ((modeCode & ParcelFileDescriptor.MODE_READ_WRITE) != 0) {
|
2014-11-26 08:30:05 +01:00
|
|
|
checkReadPermission(table_id, table, null);
|
|
|
|
checkWritePermission(table_id, table);
|
|
|
|
}
|
|
|
|
switch (table_id) {
|
|
|
|
case VIRTUAL_TABLE_ID_CACHED_IMAGES: {
|
|
|
|
return getCachedImageFd(uri.getQueryParameter(QUERY_PARAM_URL));
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_CACHE_FILES: {
|
|
|
|
return getCacheFileFd(uri.getLastPathSegment());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public Cursor query(@NonNull final Uri uri, final String[] projection, final String selection, final String[] selectionArgs,
|
2014-11-26 08:30:05 +01:00
|
|
|
final String sortOrder) {
|
|
|
|
try {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int tableId = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(tableId);
|
2014-11-26 08:30:05 +01:00
|
|
|
checkReadPermission(tableId, table, projection);
|
|
|
|
switch (tableId) {
|
|
|
|
case VIRTUAL_TABLE_ID_DATABASE_READY: {
|
|
|
|
if (mDatabaseWrapper.isReady())
|
|
|
|
return new MatrixCursor(projection != null ? projection : new String[0]);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_PERMISSIONS: {
|
2016-02-20 17:44:35 +01:00
|
|
|
final Context context = getContext();
|
|
|
|
if (context == null) return null;
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(Permissions.MATRIX_COLUMNS);
|
2016-02-20 17:44:35 +01:00
|
|
|
final PackageManager pm = context.getPackageManager();
|
|
|
|
if (Binder.getCallingUid() == Process.myUid()) {
|
|
|
|
final Map<String, String> map = mPermissionsManager.getAll();
|
|
|
|
for (final Map.Entry<String, String> item : map.entrySet()) {
|
|
|
|
c.addRow(new Object[]{item.getKey(), item.getValue()});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
final Map<String, String> map = mPermissionsManager.getAll();
|
|
|
|
final String[] callingPackages = pm.getPackagesForUid(Binder.getCallingUid());
|
|
|
|
for (final Map.Entry<String, String> item : map.entrySet()) {
|
|
|
|
final String key = item.getKey();
|
|
|
|
if (ArrayUtils.contains(callingPackages, key)) {
|
|
|
|
c.addRow(new Object[]{key, item.getValue()});
|
|
|
|
}
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_ALL_PREFERENCES: {
|
|
|
|
return getPreferencesCursor(mPreferences, null);
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_PREFERENCES: {
|
|
|
|
return getPreferencesCursor(mPreferences, uri.getLastPathSegment());
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_DNS: {
|
|
|
|
return getDNSCursor(uri.getLastPathSegment());
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_CACHED_IMAGES: {
|
|
|
|
return getCachedImageCursor(uri.getQueryParameter(QUERY_PARAM_URL));
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_NOTIFICATIONS: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
2015-11-25 03:30:37 +01:00
|
|
|
if (segments.size() == 2) {
|
|
|
|
final int def = -1;
|
|
|
|
return getNotificationsCursor(NumberUtils.toInt(segments.get(1), def));
|
2015-11-30 06:32:05 +01:00
|
|
|
} else
|
2014-11-26 08:30:05 +01:00
|
|
|
return getNotificationsCursor();
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_UNREAD_COUNTS: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
2015-11-25 03:30:37 +01:00
|
|
|
if (segments.size() == 2) {
|
|
|
|
final int def = -1;
|
|
|
|
return getUnreadCountsCursor(NumberUtils.toInt(segments.get(1), def));
|
2015-11-30 06:32:05 +01:00
|
|
|
} else
|
2014-11-26 08:30:05 +01:00
|
|
|
return getUnreadCountsCursor();
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_UNREAD_COUNTS_BY_TYPE: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
|
|
|
if (segments.size() != 3) return null;
|
|
|
|
return getUnreadCountsCursorByType(segments.get(2));
|
|
|
|
}
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
|
|
|
if (segments.size() != 4) return null;
|
2015-11-25 03:30:37 +01:00
|
|
|
final long accountId = NumberUtils.toLong(segments.get(2), -1);
|
|
|
|
final long conversationId = NumberUtils.toLong(segments.get(3), -1);
|
2015-01-14 09:47:51 +01:00
|
|
|
final SQLSelectQuery query = ConversationQueryBuilder.buildByConversationId(projection,
|
2014-11-26 08:30:05 +01:00
|
|
|
accountId, conversationId, selection, sortOrder);
|
2015-01-14 09:47:51 +01:00
|
|
|
final Cursor c = mDatabaseWrapper.rawQuery(query.getSQL(), selectionArgs);
|
2014-11-26 08:30:05 +01:00
|
|
|
setNotificationUri(c, DirectMessages.CONTENT_URI);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION_SCREEN_NAME: {
|
|
|
|
final List<String> segments = uri.getPathSegments();
|
|
|
|
if (segments.size() != 4) return null;
|
2015-11-25 03:30:37 +01:00
|
|
|
final long accountId = NumberUtils.toLong(segments.get(2), -1);
|
2014-11-26 08:30:05 +01:00
|
|
|
final String screenName = segments.get(3);
|
2015-01-14 09:47:51 +01:00
|
|
|
final SQLSelectQuery query = ConversationQueryBuilder.buildByScreenName(projection,
|
2014-11-26 08:30:05 +01:00
|
|
|
accountId, screenName, selection, sortOrder);
|
2015-01-14 09:47:51 +01:00
|
|
|
final Cursor c = mDatabaseWrapper.rawQuery(query.getSQL(), selectionArgs);
|
2014-11-26 08:30:05 +01:00
|
|
|
setNotificationUri(c, DirectMessages.CONTENT_URI);
|
|
|
|
return c;
|
|
|
|
}
|
2015-01-14 09:47:51 +01:00
|
|
|
case VIRTUAL_TABLE_ID_CACHED_USERS_WITH_RELATIONSHIP: {
|
2015-11-25 03:30:37 +01:00
|
|
|
final long def = -1;
|
|
|
|
final long accountId = NumberUtils.toLong(uri.getLastPathSegment(), def);
|
2015-04-03 23:12:33 +02:00
|
|
|
final SQLSelectQuery query = CachedUsersQueryBuilder.withRelationship(projection,
|
2015-01-14 09:47:51 +01:00
|
|
|
selection, sortOrder, accountId);
|
|
|
|
final Cursor c = mDatabaseWrapper.rawQuery(query.getSQL(), selectionArgs);
|
|
|
|
setNotificationUri(c, CachedUsers.CONTENT_URI);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_CACHED_USERS_WITH_SCORE: {
|
2015-11-25 03:30:37 +01:00
|
|
|
final long def = -1;
|
|
|
|
final long accountId = NumberUtils.toLong(uri.getLastPathSegment(), def);
|
2015-04-03 23:12:33 +02:00
|
|
|
final SQLSelectQuery query = CachedUsersQueryBuilder.withScore(projection,
|
2015-11-08 08:12:35 +01:00
|
|
|
selection, sortOrder, accountId, 0);
|
2015-01-14 09:47:51 +01:00
|
|
|
final Cursor c = mDatabaseWrapper.rawQuery(query.getSQL(), selectionArgs);
|
|
|
|
setNotificationUri(c, CachedUsers.CONTENT_URI);
|
|
|
|
return c;
|
|
|
|
}
|
2015-01-14 17:35:37 +01:00
|
|
|
case VIRTUAL_TABLE_ID_DRAFTS_UNSENT: {
|
2015-10-05 12:00:07 +02:00
|
|
|
final AsyncTwitterWrapper twitter = mTwitterWrapper;
|
2015-01-14 17:35:37 +01:00
|
|
|
final RawItemArray sendingIds = new RawItemArray(twitter.getSendingDraftIds());
|
|
|
|
final Expression where;
|
|
|
|
if (selection != null) {
|
|
|
|
where = Expression.and(new Expression(selection),
|
|
|
|
Expression.notIn(new Column(Drafts._ID), sendingIds));
|
|
|
|
} else {
|
|
|
|
where = Expression.and(Expression.notIn(new Column(Drafts._ID), sendingIds));
|
|
|
|
}
|
|
|
|
final Cursor c = mDatabaseWrapper.query(Drafts.TABLE_NAME, projection,
|
|
|
|
where.getSQL(), selectionArgs, null, null, sortOrder);
|
2016-01-27 16:08:00 +01:00
|
|
|
setNotificationUri(c, Utils.getNotificationUri(tableId, uri));
|
2015-01-14 17:35:37 +01:00
|
|
|
return c;
|
|
|
|
}
|
2015-11-08 08:12:35 +01:00
|
|
|
case VIRTUAL_TABLE_ID_SUGGESTIONS_AUTO_COMPLETE: {
|
|
|
|
return getAutoCompleteSuggestionsCursor(uri);
|
|
|
|
}
|
|
|
|
case VIRTUAL_TABLE_ID_SUGGESTIONS_SEARCH: {
|
|
|
|
return getSearchSuggestionCursor(uri);
|
|
|
|
}
|
2015-11-30 06:32:05 +01:00
|
|
|
case VIRTUAL_TABLE_ID_EMPTY: {
|
|
|
|
return new MatrixCursor(projection);
|
|
|
|
}
|
2016-02-03 20:27:43 +01:00
|
|
|
case VIRTUAL_TABLE_ID_RAW_QUERY: {
|
|
|
|
if (projection != null || selection != null || sortOrder != null) {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
|
|
|
return mDatabaseWrapper.rawQuery(uri.getLastPathSegment(), selectionArgs);
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
if (table == null) return null;
|
|
|
|
final Cursor c = mDatabaseWrapper.query(table, projection, selection, selectionArgs, null, null, sortOrder);
|
2016-01-27 16:08:00 +01:00
|
|
|
setNotificationUri(c, Utils.getNotificationUri(tableId, uri));
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
} catch (final SQLException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-08 08:12:35 +01:00
|
|
|
private Cursor getSearchSuggestionCursor(Uri uri) {
|
|
|
|
final String query = uri.getQueryParameter(QUERY_PARAM_QUERY);
|
2015-11-25 03:30:37 +01:00
|
|
|
final long def = -1;
|
|
|
|
final long accountId = NumberUtils.toLong(uri.getQueryParameter(QUERY_PARAM_ACCOUNT_ID), def);
|
2015-11-08 08:12:35 +01:00
|
|
|
if (query == null || accountId <= 0) return null;
|
|
|
|
final boolean emptyQuery = TextUtils.isEmpty(query);
|
|
|
|
final String queryEscaped = query.replace("_", "^_");
|
|
|
|
final Cursor[] cursors;
|
|
|
|
final String[] historyProjection = {
|
|
|
|
new Column(SearchHistory._ID, Suggestions.Search._ID).getSQL(),
|
|
|
|
new Column("'" + Suggestions.Search.TYPE_SEARCH_HISTORY + "'", Suggestions.Search.TYPE).getSQL(),
|
|
|
|
new Column(SearchHistory.QUERY, Suggestions.Search.TITLE).getSQL(),
|
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(),
|
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(),
|
|
|
|
new Column("0", Suggestions.Search.EXTRA_ID).getSQL(),
|
2015-11-21 06:45:55 +01:00
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
|
|
|
|
new Column(SearchHistory.QUERY, Suggestions.Search.VALUE).getSQL(),
|
2015-11-08 08:12:35 +01:00
|
|
|
};
|
|
|
|
final Expression historySelection = Expression.likeRaw(new Column(SearchHistory.QUERY), "?||'%'", "^");
|
|
|
|
@SuppressLint("Recycle") final Cursor historyCursor = mDatabaseWrapper.query(true,
|
|
|
|
SearchHistory.TABLE_NAME, historyProjection, historySelection.getSQL(),
|
|
|
|
new String[]{queryEscaped}, null, null, SearchHistory.DEFAULT_SORT_ORDER,
|
|
|
|
TextUtils.isEmpty(query) ? "3" : "2");
|
|
|
|
if (emptyQuery) {
|
|
|
|
final String[] savedSearchesProjection = {
|
|
|
|
new Column(SavedSearches._ID, Suggestions.Search._ID).getSQL(),
|
|
|
|
new Column("'" + Suggestions.Search.TYPE_SAVED_SEARCH + "'", Suggestions.Search.TYPE).getSQL(),
|
|
|
|
new Column(SavedSearches.QUERY, Suggestions.Search.TITLE).getSQL(),
|
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(),
|
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(),
|
|
|
|
new Column("0", Suggestions.Search.EXTRA_ID).getSQL(),
|
2015-11-21 06:45:55 +01:00
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
|
|
|
|
new Column(SavedSearches.QUERY, Suggestions.Search.VALUE).getSQL()
|
2015-11-08 08:12:35 +01:00
|
|
|
};
|
|
|
|
final Expression savedSearchesWhere = Expression.equals(SavedSearches.ACCOUNT_ID, accountId);
|
|
|
|
@SuppressLint("Recycle") final Cursor savedSearchesCursor = mDatabaseWrapper.query(true,
|
|
|
|
SavedSearches.TABLE_NAME, savedSearchesProjection, savedSearchesWhere.getSQL(),
|
|
|
|
null, null, null, SavedSearches.DEFAULT_SORT_ORDER, null);
|
|
|
|
cursors = new Cursor[2];
|
|
|
|
cursors[1] = savedSearchesCursor;
|
|
|
|
} else {
|
|
|
|
final String[] usersProjection = {
|
|
|
|
new Column(CachedUsers._ID, Suggestions.Search._ID).getSQL(),
|
|
|
|
new Column("'" + Suggestions.Search.TYPE_USER + "'", Suggestions.Search.TYPE).getSQL(),
|
|
|
|
new Column(CachedUsers.NAME, Suggestions.Search.TITLE).getSQL(),
|
|
|
|
new Column(CachedUsers.SCREEN_NAME, Suggestions.Search.SUMMARY).getSQL(),
|
|
|
|
new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.Search.ICON).getSQL(),
|
|
|
|
new Column(CachedUsers.USER_ID, Suggestions.Search.EXTRA_ID).getSQL(),
|
2015-11-21 06:45:55 +01:00
|
|
|
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
|
|
|
|
new Column(CachedUsers.SCREEN_NAME, Suggestions.Search.VALUE).getSQL(),
|
2015-11-08 08:12:35 +01:00
|
|
|
};
|
2015-11-10 09:03:03 +01:00
|
|
|
String queryTrimmed = queryEscaped.startsWith("@") ? queryEscaped.substring(1) : queryEscaped;
|
2015-11-08 08:12:35 +01:00
|
|
|
final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager);
|
|
|
|
final Expression usersSelection = Expression.or(
|
|
|
|
Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
|
|
|
|
Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"),
|
|
|
|
Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
|
2015-11-10 09:03:03 +01:00
|
|
|
final String[] selectionArgs = new String[]{queryTrimmed, queryTrimmed};
|
2015-11-21 06:45:55 +01:00
|
|
|
final String[] order = {CachedUsers.LAST_SEEN, CachedUsers.SCORE, CachedUsers.SCREEN_NAME,
|
|
|
|
CachedUsers.NAME};
|
2015-11-08 08:12:35 +01:00
|
|
|
final boolean[] ascending = {false, false, true, true};
|
|
|
|
final OrderBy orderBy = new OrderBy(order, ascending);
|
|
|
|
|
|
|
|
final SQLSelectQuery usersQuery = CachedUsersQueryBuilder.withScore(usersProjection,
|
|
|
|
usersSelection.getSQL(), orderBy.getSQL(), accountId, 0);
|
|
|
|
@SuppressLint("Recycle") final Cursor usersCursor = mDatabaseWrapper.rawQuery(usersQuery.getSQL(), selectionArgs);
|
|
|
|
final Expression exactUserSelection = Expression.or(Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?", "^"));
|
|
|
|
final Cursor exactUserCursor = mDatabaseWrapper.query(CachedUsers.TABLE_NAME,
|
|
|
|
new String[]{SQLFunctions.COUNT()}, exactUserSelection.getSQL(),
|
2015-11-10 09:03:03 +01:00
|
|
|
new String[]{queryTrimmed}, null, null, null, "1");
|
2015-11-08 08:12:35 +01:00
|
|
|
final boolean hasName = exactUserCursor.moveToPosition(0) && exactUserCursor.getInt(0) > 0;
|
|
|
|
exactUserCursor.close();
|
|
|
|
final MatrixCursor screenNameCursor = new MatrixCursor(Suggestions.Search.COLUMNS);
|
|
|
|
if (!hasName) {
|
|
|
|
final Matcher m = PATTERN_SCREEN_NAME.matcher(query);
|
|
|
|
if (m.matches()) {
|
2016-01-04 04:32:48 +01:00
|
|
|
final String screenName = m.group(1);
|
2015-11-08 08:12:35 +01:00
|
|
|
screenNameCursor.addRow(new Object[]{0, Suggestions.Search.TYPE_SCREEN_NAME,
|
2016-01-04 04:32:48 +01:00
|
|
|
screenName, null, null, 0, null, screenName});
|
2015-11-08 08:12:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cursors = new Cursor[3];
|
|
|
|
cursors[1] = screenNameCursor;
|
|
|
|
cursors[2] = usersCursor;
|
|
|
|
}
|
|
|
|
cursors[0] = historyCursor;
|
|
|
|
return new MergeCursor(cursors);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getAutoCompleteSuggestionsCursor(@NonNull Uri uri) {
|
|
|
|
final String query = uri.getQueryParameter(QUERY_PARAM_QUERY);
|
|
|
|
final String type = uri.getQueryParameter(QUERY_PARAM_TYPE);
|
|
|
|
final String accountId = uri.getQueryParameter(QUERY_PARAM_ACCOUNT_ID);
|
|
|
|
if (query == null || type == null) return null;
|
|
|
|
final String queryEscaped = query.replace("_", "^_");
|
|
|
|
if (Suggestions.AutoComplete.TYPE_USERS.equals(type)) {
|
|
|
|
final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager);
|
|
|
|
final Expression where = Expression.or(Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
|
|
|
|
Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"),
|
|
|
|
Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
|
|
|
|
final String[] whereArgs = {queryEscaped, queryEscaped};
|
|
|
|
final String[] mappedProjection = {
|
|
|
|
new Column(CachedUsers._ID, Suggestions._ID).getSQL(),
|
|
|
|
new Column("'" + Suggestions.AutoComplete.TYPE_USERS + "'", Suggestions.TYPE).getSQL(),
|
|
|
|
new Column(CachedUsers.NAME, Suggestions.TITLE).getSQL(),
|
|
|
|
new Column(CachedUsers.SCREEN_NAME, Suggestions.SUMMARY).getSQL(),
|
|
|
|
new Column(CachedUsers.USER_ID, Suggestions.EXTRA_ID).getSQL(),
|
|
|
|
new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.ICON).getSQL(),
|
2015-11-21 06:45:55 +01:00
|
|
|
new Column(CachedUsers.SCREEN_NAME, Suggestions.VALUE).getSQL(),
|
2015-11-08 08:12:35 +01:00
|
|
|
};
|
2015-11-21 06:45:55 +01:00
|
|
|
final String[] orderBy = {CachedUsers.SCORE, CachedUsers.LAST_SEEN, CachedUsers.SCREEN_NAME,
|
2015-11-10 09:03:03 +01:00
|
|
|
CachedUsers.NAME};
|
|
|
|
final boolean[] ascending = {false, false, true, true};
|
2015-11-08 08:12:35 +01:00
|
|
|
return query(Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountId),
|
2015-11-10 09:03:03 +01:00
|
|
|
mappedProjection, where.getSQL(), whereArgs, new OrderBy(orderBy, ascending).getSQL());
|
2015-11-08 08:12:35 +01:00
|
|
|
} else if (Suggestions.AutoComplete.TYPE_HASHTAGS.equals(type)) {
|
|
|
|
final Expression where = Expression.likeRaw(new Column(CachedHashtags.NAME), "?||'%'", "^");
|
|
|
|
final String[] whereArgs = new String[]{queryEscaped};
|
|
|
|
final String[] mappedProjection = {
|
|
|
|
new Column(CachedHashtags._ID, Suggestions._ID).getSQL(),
|
|
|
|
new Column("'" + Suggestions.AutoComplete.TYPE_HASHTAGS + "'", Suggestions.TYPE).getSQL(),
|
|
|
|
new Column(CachedHashtags.NAME, Suggestions.TITLE).getSQL(),
|
|
|
|
new Column("NULL", Suggestions.SUMMARY).getSQL(),
|
|
|
|
new Column("0", Suggestions.EXTRA_ID).getSQL(),
|
|
|
|
new Column("NULL", Suggestions.ICON).getSQL(),
|
2015-11-21 06:45:55 +01:00
|
|
|
new Column(CachedHashtags.NAME, Suggestions.VALUE).getSQL(),
|
2015-11-08 08:12:35 +01:00
|
|
|
};
|
|
|
|
return query(CachedHashtags.CONTENT_URI, mappedProjection, where.getSQL(),
|
|
|
|
whereArgs, null);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
@Override
|
2015-10-05 12:00:07 +02:00
|
|
|
public int update(@NonNull final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) {
|
2014-11-26 08:30:05 +01:00
|
|
|
try {
|
2015-10-06 04:50:44 +02:00
|
|
|
return updateInternal(uri, values, selection, selectionArgs);
|
|
|
|
} catch (final SQLException e) {
|
|
|
|
if (handleSQLException(e)) {
|
|
|
|
try {
|
|
|
|
return updateInternal(uri, values, selection, selectionArgs);
|
|
|
|
} catch (SQLException e1) {
|
|
|
|
throw new IllegalStateException(e1);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-06 04:50:44 +02:00
|
|
|
private int updateInternal(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final int tableId = DataStoreUtils.getTableId(uri);
|
|
|
|
final String table = DataStoreUtils.getTableNameById(tableId);
|
2015-10-06 04:50:44 +02:00
|
|
|
checkWritePermission(tableId, table);
|
|
|
|
int result = 0;
|
|
|
|
if (table != null) {
|
|
|
|
switch (tableId) {
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
result = mDatabaseWrapper.update(table, values, selection, selectionArgs);
|
|
|
|
}
|
|
|
|
if (result > 0) {
|
|
|
|
onDatabaseUpdated(tableId, uri);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
private boolean checkPermission(final String... permissions) {
|
|
|
|
return mPermissionsManager.checkCallingPermission(permissions);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkReadPermission(final int id, final String table, final String[] projection) {
|
2016-02-20 17:44:35 +01:00
|
|
|
if (Binder.getCallingPid() == Process.myPid()) return;
|
2014-11-26 08:30:05 +01:00
|
|
|
switch (id) {
|
2016-02-20 17:44:35 +01:00
|
|
|
case VIRTUAL_TABLE_ID_PERMISSIONS: {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
case VIRTUAL_TABLE_ID_PREFERENCES:
|
|
|
|
case VIRTUAL_TABLE_ID_DNS: {
|
|
|
|
if (!checkPermission(PERMISSION_PREFERENCES))
|
|
|
|
throw new SecurityException("Access preferences requires level PERMISSION_LEVEL_PREFERENCES");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TABLE_ID_ACCOUNTS: {
|
2016-02-20 17:44:35 +01:00
|
|
|
// Reading some information like user_id, screen_name etc is
|
2014-11-26 08:30:05 +01:00
|
|
|
// okay, but reading columns like password requires higher
|
|
|
|
// permission level.
|
2016-02-20 17:44:35 +01:00
|
|
|
if (checkPermission(PERMISSION_ACCOUNTS)) {
|
|
|
|
break;
|
2015-04-13 18:34:10 +02:00
|
|
|
}
|
2016-02-20 17:44:35 +01:00
|
|
|
// Only querying basic information
|
|
|
|
if (TwidereArrayUtils.contains(Accounts.COLUMNS_NO_CREDENTIALS, projection) && !checkPermission(PERMISSION_READ)) {
|
2015-04-13 18:34:10 +02:00
|
|
|
final String pkgName = mPermissionsManager.getPackageNameByUid(Binder.getCallingUid());
|
|
|
|
throw new SecurityException("Access database " + table + " requires level PERMISSION_LEVEL_READ, package: " + pkgName);
|
|
|
|
}
|
2016-02-20 17:44:35 +01:00
|
|
|
final String pkgName = mPermissionsManager.getPackageNameByUid(Binder.getCallingUid());
|
|
|
|
final List<String> callingSensitiveCols = new ArrayList<>();
|
|
|
|
if (projection != null) {
|
|
|
|
Collections.addAll(callingSensitiveCols, projection);
|
|
|
|
callingSensitiveCols.removeAll(Arrays.asList(Accounts.COLUMNS_NO_CREDENTIALS));
|
|
|
|
} else {
|
|
|
|
callingSensitiveCols.add("*");
|
|
|
|
}
|
|
|
|
throw new SecurityException("Access column " + TwidereListUtils.toString(callingSensitiveCols, ',', true)
|
|
|
|
+ " in database accounts requires level PERMISSION_LEVEL_ACCOUNTS, package: " + pkgName);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_INBOX:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_OUTBOX:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION_SCREEN_NAME:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES: {
|
|
|
|
if (!checkPermission(PERMISSION_DIRECT_MESSAGES))
|
|
|
|
throw new SecurityException("Access database " + table
|
|
|
|
+ " requires level PERMISSION_LEVEL_DIRECT_MESSAGES");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TABLE_ID_STATUSES:
|
|
|
|
case TABLE_ID_MENTIONS:
|
|
|
|
case TABLE_ID_TABS:
|
|
|
|
case TABLE_ID_DRAFTS:
|
|
|
|
case TABLE_ID_CACHED_USERS:
|
|
|
|
case TABLE_ID_FILTERED_USERS:
|
|
|
|
case TABLE_ID_FILTERED_KEYWORDS:
|
|
|
|
case TABLE_ID_FILTERED_SOURCES:
|
|
|
|
case TABLE_ID_FILTERED_LINKS:
|
|
|
|
case TABLE_ID_TRENDS_LOCAL:
|
|
|
|
case TABLE_ID_CACHED_STATUSES:
|
|
|
|
case TABLE_ID_CACHED_HASHTAGS: {
|
|
|
|
if (!checkPermission(PERMISSION_READ))
|
|
|
|
throw new SecurityException("Access database " + table + " requires level PERMISSION_LEVEL_READ");
|
|
|
|
break;
|
|
|
|
}
|
2015-06-24 17:13:03 +02:00
|
|
|
default: {
|
|
|
|
if (!mPermissionsManager.checkSignature(Binder.getCallingUid())) {
|
2016-02-20 17:44:35 +01:00
|
|
|
throw new SecurityException("Internal database " + id + " is not allowed for third-party applications");
|
2015-06-24 17:13:03 +02:00
|
|
|
}
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkWritePermission(final int id, final String table) {
|
2016-02-20 17:44:35 +01:00
|
|
|
if (Binder.getCallingPid() == Process.myPid()) return;
|
2014-11-26 08:30:05 +01:00
|
|
|
switch (id) {
|
|
|
|
case TABLE_ID_ACCOUNTS: {
|
|
|
|
// Writing to accounts database is not allowed for third-party
|
|
|
|
// applications.
|
|
|
|
if (!mPermissionsManager.checkSignature(Binder.getCallingUid()))
|
|
|
|
throw new SecurityException(
|
|
|
|
"Writing to accounts database is not allowed for third-party applications");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_INBOX:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_OUTBOX:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATION_SCREEN_NAME:
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_CONVERSATIONS_ENTRIES: {
|
|
|
|
if (!checkPermission(PERMISSION_DIRECT_MESSAGES))
|
|
|
|
throw new SecurityException("Access database " + table
|
|
|
|
+ " requires level PERMISSION_LEVEL_DIRECT_MESSAGES");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TABLE_ID_STATUSES:
|
|
|
|
case TABLE_ID_MENTIONS:
|
|
|
|
case TABLE_ID_TABS:
|
|
|
|
case TABLE_ID_DRAFTS:
|
|
|
|
case TABLE_ID_CACHED_USERS:
|
|
|
|
case TABLE_ID_FILTERED_USERS:
|
|
|
|
case TABLE_ID_FILTERED_KEYWORDS:
|
|
|
|
case TABLE_ID_FILTERED_SOURCES:
|
|
|
|
case TABLE_ID_FILTERED_LINKS:
|
|
|
|
case TABLE_ID_TRENDS_LOCAL:
|
|
|
|
case TABLE_ID_CACHED_STATUSES:
|
|
|
|
case TABLE_ID_CACHED_HASHTAGS: {
|
|
|
|
if (!checkPermission(PERMISSION_WRITE))
|
|
|
|
throw new SecurityException("Access database " + table + " requires level PERMISSION_LEVEL_WRITE");
|
|
|
|
break;
|
|
|
|
}
|
2015-06-24 17:13:03 +02:00
|
|
|
default: {
|
|
|
|
if (!mPermissionsManager.checkSignature(Binder.getCallingUid())) {
|
|
|
|
throw new SecurityException("Internal database is not allowed for third-party applications");
|
|
|
|
}
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void clearNotification() {
|
2015-11-14 13:36:56 +01:00
|
|
|
mNotificationManager.cancelAll();
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void clearNotification(final int notificationType, final long accountId) {
|
2015-11-14 13:36:56 +01:00
|
|
|
mNotificationManager.cancelById(Utils.getNotificationId(notificationType, accountId));
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getCachedImageCursor(final String url) {
|
2015-05-13 12:54:13 +02:00
|
|
|
if (BuildConfig.DEBUG) {
|
2014-11-26 08:30:05 +01:00
|
|
|
Log.d(LOGTAG, String.format("getCachedImageCursor(%s)", url));
|
|
|
|
}
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(CachedImages.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
final File file = mImagePreloader.getCachedImageFile(url);
|
|
|
|
if (url != null && file != null) {
|
|
|
|
c.addRow(new String[]{url, file.getPath()});
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private ParcelFileDescriptor getCachedImageFd(final String url) throws FileNotFoundException {
|
2015-05-13 12:54:13 +02:00
|
|
|
if (BuildConfig.DEBUG) {
|
2014-11-26 08:30:05 +01:00
|
|
|
Log.d(LOGTAG, String.format("getCachedImageFd(%s)", url));
|
|
|
|
}
|
|
|
|
final File file = mImagePreloader.getCachedImageFile(url);
|
|
|
|
if (file == null) return null;
|
|
|
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
|
|
|
}
|
|
|
|
|
|
|
|
private ParcelFileDescriptor getCacheFileFd(final String name) throws FileNotFoundException {
|
|
|
|
if (name == null) return null;
|
2015-10-06 04:50:44 +02:00
|
|
|
final Context context = getContext();
|
2015-11-08 08:12:35 +01:00
|
|
|
assert context != null;
|
2015-10-06 04:50:44 +02:00
|
|
|
final File cacheDir = context.getCacheDir();
|
2014-11-26 08:30:05 +01:00
|
|
|
final File file = new File(cacheDir, name);
|
|
|
|
if (!file.exists()) return null;
|
|
|
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
|
|
|
}
|
|
|
|
|
|
|
|
private ContentResolver getContentResolver() {
|
|
|
|
if (mContentResolver != null) return mContentResolver;
|
|
|
|
final Context context = getContext();
|
2015-11-08 08:12:35 +01:00
|
|
|
assert context != null;
|
2014-11-26 08:30:05 +01:00
|
|
|
return mContentResolver = context.getContentResolver();
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getDNSCursor(final String host) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(DNS.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
try {
|
2016-02-04 19:25:28 +01:00
|
|
|
final List<InetAddress> addresses = mDns.lookup(host);
|
2015-03-14 16:11:20 +01:00
|
|
|
for (InetAddress address : addresses) {
|
|
|
|
c.addRow(new String[]{host, address.getHostAddress()});
|
|
|
|
}
|
|
|
|
} catch (final IOException ignore) {
|
2015-05-13 12:54:13 +02:00
|
|
|
if (BuildConfig.DEBUG) {
|
2015-03-14 16:11:20 +01:00
|
|
|
Log.w(LOGTAG, ignore);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getNotificationsCursor() {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(Notifications.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getNotificationsCursor(final int id) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(Notifications.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2016-01-30 18:14:40 +01:00
|
|
|
private Bitmap getProfileImageForNotification(final String profileImageUrl) {
|
2014-11-26 08:30:05 +01:00
|
|
|
final Context context = getContext();
|
2015-11-08 08:12:35 +01:00
|
|
|
assert context != null;
|
2014-11-26 08:30:05 +01:00
|
|
|
final Resources res = context.getResources();
|
|
|
|
final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
|
|
|
|
final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
|
2016-01-30 18:14:40 +01:00
|
|
|
final File profile_image_file = mImagePreloader.getCachedImageFile(profileImageUrl);
|
2014-11-26 08:30:05 +01:00
|
|
|
final Bitmap profile_image = profile_image_file != null && profile_image_file.isFile() ? BitmapFactory
|
|
|
|
.decodeFile(profile_image_file.getPath()) : null;
|
|
|
|
if (profile_image != null) return Bitmap.createScaledBitmap(profile_image, w, h, true);
|
2015-03-23 04:57:55 +01:00
|
|
|
return Bitmap.createScaledBitmap(BitmapFactory.decodeResource(res, R.mipmap.ic_launcher), w, h, true);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getUnreadCountsCursor() {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(UnreadCounts.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getUnreadCountsCursor(final int position) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(UnreadCounts.MATRIX_COLUMNS);
|
2015-03-24 16:40:47 +01:00
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getUnreadCountsCursorByType(final String type) {
|
2016-01-27 16:08:00 +01:00
|
|
|
final MatrixCursor c = new MatrixCursor(UnreadCounts.MATRIX_COLUMNS);
|
2014-11-26 08:30:05 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isNotificationAudible() {
|
2016-02-17 12:23:47 +01:00
|
|
|
return !mActivityTracker.isHomeActivityStarted();
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void notifyContentObserver(final Uri uri) {
|
2015-04-13 18:34:10 +02:00
|
|
|
mHandler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
final ContentResolver cr = getContentResolver();
|
|
|
|
if (uri == null || cr == null) return;
|
|
|
|
cr.notifyChange(uri, null);
|
|
|
|
}
|
|
|
|
});
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void notifyUnreadCountChanged(final int position) {
|
2015-10-06 18:45:39 +02:00
|
|
|
mHandler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
mBus.post(new UnreadCountUpdatedEvent(position));
|
|
|
|
}
|
|
|
|
});
|
2014-11-26 08:30:05 +01:00
|
|
|
notifyContentObserver(UnreadCounts.CONTENT_URI);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onDatabaseUpdated(final int tableId, final Uri uri) {
|
|
|
|
if (uri == null) return;
|
|
|
|
switch (tableId) {
|
|
|
|
case TABLE_ID_ACCOUNTS: {
|
2016-02-03 20:27:43 +01:00
|
|
|
DataStoreUtils.clearAccountColor();
|
|
|
|
DataStoreUtils.clearAccountName();
|
2014-11-26 08:30:05 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-01-27 16:08:00 +01:00
|
|
|
notifyContentObserver(Utils.getNotificationUri(tableId, uri));
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
2016-01-30 18:14:40 +01:00
|
|
|
private void onNewItemsInserted(final Uri uri, final int tableId, final ContentValues values) {
|
|
|
|
onNewItemsInserted(uri, tableId, new ContentValues[]{values});
|
2015-01-14 17:35:37 +01:00
|
|
|
}
|
|
|
|
|
2016-01-30 18:14:40 +01:00
|
|
|
private void onNewItemsInserted(final Uri uri, final int tableId, final ContentValues[] valuesArray) {
|
2015-05-13 09:05:06 +02:00
|
|
|
final Context context = getContext();
|
|
|
|
if (uri == null || valuesArray == null || valuesArray.length == 0 || context == null)
|
|
|
|
return;
|
2015-01-01 11:38:34 +01:00
|
|
|
preloadImages(valuesArray);
|
2014-11-26 08:30:05 +01:00
|
|
|
if (!uri.getBooleanQueryParameter(QUERY_PARAM_NOTIFY, true)) return;
|
2015-01-01 11:38:34 +01:00
|
|
|
switch (tableId) {
|
2014-11-26 08:30:05 +01:00
|
|
|
case TABLE_ID_STATUSES: {
|
2016-01-29 18:57:15 +01:00
|
|
|
mBackgroundExecutor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
2016-03-06 13:59:24 +01:00
|
|
|
DataStoreUtils.getAccountKeys(context));
|
2016-01-29 18:57:15 +01:00
|
|
|
for (final AccountPreferences pref : prefs) {
|
|
|
|
if (!pref.isHomeTimelineNotificationEnabled()) continue;
|
2016-03-06 13:59:24 +01:00
|
|
|
final long positionTag = getPositionTag(CustomTabType.HOME_TIMELINE, pref.getAccountKey());
|
2016-01-29 18:57:15 +01:00
|
|
|
showTimelineNotification(pref, positionTag);
|
|
|
|
}
|
|
|
|
notifyUnreadCountChanged(NOTIFICATION_ID_HOME_TIMELINE);
|
|
|
|
}
|
|
|
|
});
|
2014-11-26 08:30:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-01-02 10:48:24 +01:00
|
|
|
case TABLE_ID_ACTIVITIES_ABOUT_ME: {
|
2016-01-29 18:57:15 +01:00
|
|
|
mBackgroundExecutor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
2016-03-06 13:59:24 +01:00
|
|
|
DataStoreUtils.getAccountKeys(context));
|
2016-01-29 18:57:15 +01:00
|
|
|
final boolean combined = mPreferences.getBoolean(KEY_COMBINED_NOTIFICATIONS);
|
|
|
|
for (final AccountPreferences pref : prefs) {
|
|
|
|
if (!pref.isInteractionsNotificationEnabled()) continue;
|
|
|
|
showInteractionsNotification(pref, getPositionTag(ReadPositionTag.ACTIVITIES_ABOUT_ME,
|
2016-03-06 13:59:24 +01:00
|
|
|
pref.getAccountKey()), combined);
|
2016-01-29 18:57:15 +01:00
|
|
|
}
|
|
|
|
notifyUnreadCountChanged(NOTIFICATION_ID_INTERACTIONS_TIMELINE);
|
|
|
|
}
|
|
|
|
});
|
2014-11-26 08:30:05 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TABLE_ID_DIRECT_MESSAGES_INBOX: {
|
2015-05-13 09:05:06 +02:00
|
|
|
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(context,
|
2016-03-06 13:59:24 +01:00
|
|
|
DataStoreUtils.getAccountKeys(context));
|
2014-11-26 08:30:05 +01:00
|
|
|
for (final AccountPreferences pref : prefs) {
|
2015-03-25 14:20:51 +01:00
|
|
|
if (!pref.isDirectMessagesNotificationEnabled()) continue;
|
2016-01-28 11:50:39 +01:00
|
|
|
final StringLongPair[] pairs = mReadStateManager.getPositionPairs(CustomTabType.DIRECT_MESSAGES);
|
2015-03-25 16:04:25 +01:00
|
|
|
showMessagesNotification(pref, pairs, valuesArray);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
notifyUnreadCountChanged(NOTIFICATION_ID_DIRECT_MESSAGES);
|
|
|
|
break;
|
|
|
|
}
|
2015-01-01 11:38:34 +01:00
|
|
|
case TABLE_ID_DRAFTS: {
|
|
|
|
break;
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-30 17:05:41 +02:00
|
|
|
private long getPositionTag(String tag, long accountId) {
|
|
|
|
final long position = mReadStateManager.getPosition(Utils.getReadPositionTagWithAccounts(tag,
|
|
|
|
accountId));
|
|
|
|
if (position != -1) return position;
|
|
|
|
return mReadStateManager.getPosition(tag);
|
|
|
|
}
|
|
|
|
|
2015-03-24 16:40:47 +01:00
|
|
|
private void showTimelineNotification(AccountPreferences pref, long position) {
|
2016-03-06 13:59:24 +01:00
|
|
|
final long accountId = pref.getAccountKey();
|
2015-03-24 16:40:47 +01:00
|
|
|
final Context context = getContext();
|
2016-01-29 18:57:15 +01:00
|
|
|
if (context == null) return;
|
2015-03-24 16:40:47 +01:00
|
|
|
final Resources resources = context.getResources();
|
2015-11-14 13:36:56 +01:00
|
|
|
final NotificationManagerWrapper nm = mNotificationManager;
|
2015-03-24 16:40:47 +01:00
|
|
|
final Expression selection = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, accountId),
|
|
|
|
Expression.greaterThan(Statuses.STATUS_ID, position));
|
2015-12-17 15:20:26 +01:00
|
|
|
final String filteredSelection = DataStoreUtils.buildStatusFilterWhereClause(Statuses.TABLE_NAME,
|
2015-04-09 13:50:32 +02:00
|
|
|
selection).getSQL();
|
2015-03-24 16:40:47 +01:00
|
|
|
final String[] userProjection = {Statuses.USER_ID, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME};
|
2015-04-04 16:31:37 +02:00
|
|
|
final String[] statusProjection = {Statuses.STATUS_ID};
|
2015-03-24 16:40:47 +01:00
|
|
|
final Cursor statusCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, statusProjection,
|
2016-02-13 07:39:25 +01:00
|
|
|
filteredSelection, null, null, null, Statuses.DEFAULT_SORT_ORDER);
|
2015-03-24 16:40:47 +01:00
|
|
|
final Cursor userCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, userProjection,
|
2016-02-13 07:39:25 +01:00
|
|
|
filteredSelection, null, Statuses.USER_ID, null, Statuses.DEFAULT_SORT_ORDER);
|
2015-04-21 05:29:05 +02:00
|
|
|
//noinspection TryFinallyCanBeTryWithResources
|
2015-03-24 16:40:47 +01:00
|
|
|
try {
|
|
|
|
final int usersCount = userCursor.getCount();
|
|
|
|
final int statusesCount = statusCursor.getCount();
|
|
|
|
if (statusesCount == 0 || usersCount == 0) return;
|
2015-04-04 16:31:37 +02:00
|
|
|
final int idxStatusId = statusCursor.getColumnIndex(Statuses.STATUS_ID),
|
|
|
|
idxUserName = userCursor.getColumnIndex(Statuses.USER_NAME),
|
2015-03-24 16:40:47 +01:00
|
|
|
idxUserScreenName = userCursor.getColumnIndex(Statuses.USER_NAME),
|
|
|
|
idxUserId = userCursor.getColumnIndex(Statuses.USER_NAME);
|
2015-04-04 16:31:37 +02:00
|
|
|
final long statusId = statusCursor.moveToFirst() ? statusCursor.getLong(idxStatusId) : -1;
|
2015-03-24 16:40:47 +01:00
|
|
|
final String notificationTitle = resources.getQuantityString(R.plurals.N_new_statuses,
|
|
|
|
statusesCount, statusesCount);
|
|
|
|
final String notificationContent;
|
|
|
|
userCursor.moveToFirst();
|
2015-10-09 09:58:36 +02:00
|
|
|
final String displayName = mUserColorNameManager.getUserNickname(userCursor.getLong(idxUserId),
|
2015-03-24 16:40:47 +01:00
|
|
|
mNameFirst ? userCursor.getString(idxUserName) : userCursor.getString(idxUserScreenName));
|
|
|
|
if (usersCount == 1) {
|
|
|
|
notificationContent = context.getString(R.string.from_name, displayName);
|
|
|
|
} else if (usersCount == 2) {
|
|
|
|
userCursor.moveToPosition(1);
|
2015-10-09 09:58:36 +02:00
|
|
|
final String othersName = mUserColorNameManager.getUserNickname(userCursor.getLong(idxUserId),
|
2015-03-24 16:40:47 +01:00
|
|
|
mNameFirst ? userCursor.getString(idxUserName) : userCursor.getString(idxUserScreenName));
|
2016-02-05 16:14:19 +01:00
|
|
|
notificationContent = resources.getString(R.string.from_name_and_name, displayName, othersName);
|
2015-03-24 16:40:47 +01:00
|
|
|
} else {
|
2016-02-05 16:14:19 +01:00
|
|
|
notificationContent = resources.getString(R.string.from_name_and_N_others, displayName, usersCount - 1);
|
2015-03-24 16:40:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup notification
|
|
|
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
2015-03-28 19:30:51 +01:00
|
|
|
builder.setAutoCancel(true);
|
2015-03-24 16:40:47 +01:00
|
|
|
builder.setSmallIcon(R.drawable.ic_stat_twitter);
|
|
|
|
builder.setTicker(notificationTitle);
|
|
|
|
builder.setContentTitle(notificationTitle);
|
|
|
|
builder.setContentText(notificationContent);
|
|
|
|
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setContentIntent(getContentIntent(context, CustomTabType.HOME_TIMELINE,
|
2016-02-29 02:33:39 +01:00
|
|
|
NotificationType.HOME_TIMELINE, accountId, statusId));
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.HOME_TIMELINE,
|
|
|
|
accountId, statusId, false));
|
2015-03-24 16:40:47 +01:00
|
|
|
builder.setNumber(statusesCount);
|
2016-01-28 06:51:51 +01:00
|
|
|
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
|
2016-01-02 10:48:24 +01:00
|
|
|
applyNotificationPreferences(builder, pref, pref.getHomeTimelineNotificationType());
|
2015-06-30 01:59:22 +02:00
|
|
|
try {
|
2015-11-14 13:36:56 +01:00
|
|
|
nm.notify("home_" + accountId, Utils.getNotificationId(NOTIFICATION_ID_HOME_TIMELINE, accountId), builder.build());
|
2015-06-30 01:59:22 +02:00
|
|
|
Utils.sendPebbleNotification(context, notificationContent);
|
|
|
|
} catch (SecurityException e) {
|
|
|
|
// Silently ignore
|
|
|
|
}
|
2015-03-24 16:40:47 +01:00
|
|
|
} finally {
|
|
|
|
statusCursor.close();
|
|
|
|
userCursor.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-02 10:48:24 +01:00
|
|
|
private void showInteractionsNotification(AccountPreferences pref, long position, boolean combined) {
|
2015-03-24 16:40:47 +01:00
|
|
|
final Context context = getContext();
|
2016-01-27 16:08:00 +01:00
|
|
|
if (context == null) return;
|
2016-03-06 13:59:24 +01:00
|
|
|
final long accountId = pref.getAccountKey();
|
2016-01-27 16:08:00 +01:00
|
|
|
final String where = Expression.and(
|
2016-03-06 13:59:24 +01:00
|
|
|
Expression.equals(Activities.ACCOUNT_ID, pref.getAccountKey()),
|
2016-02-03 20:42:59 +01:00
|
|
|
Expression.greaterThan(Activities.TIMESTAMP, position)
|
2016-01-27 16:08:00 +01:00
|
|
|
).getSQL();
|
|
|
|
Cursor c = query(Activities.AboutMe.CONTENT_URI, Activities.COLUMNS, where, null,
|
|
|
|
new OrderBy(Activities.TIMESTAMP, false).getSQL());
|
|
|
|
if (c == null) return;
|
|
|
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
|
|
|
try {
|
|
|
|
final int count = c.getCount();
|
|
|
|
if (count == 0) return;
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setSmallIcon(R.drawable.ic_stat_notification);
|
2016-01-28 05:10:30 +01:00
|
|
|
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
|
2016-02-19 04:50:17 +01:00
|
|
|
applyNotificationPreferences(builder, pref, pref.getMentionsNotificationType());
|
2016-01-27 16:08:00 +01:00
|
|
|
|
|
|
|
final Resources resources = context.getResources();
|
|
|
|
final String accountName = DataStoreUtils.getAccountDisplayName(context, accountId, mNameFirst);
|
|
|
|
builder.setContentText(accountName);
|
2016-01-28 06:51:51 +01:00
|
|
|
final InboxStyle style = new InboxStyle();
|
2016-01-27 16:08:00 +01:00
|
|
|
builder.setStyle(style);
|
2016-01-28 06:51:51 +01:00
|
|
|
builder.setAutoCancel(true);
|
2016-01-27 16:08:00 +01:00
|
|
|
style.setSummaryText(accountName);
|
|
|
|
final ParcelableActivityCursorIndices ci = new ParcelableActivityCursorIndices(c);
|
|
|
|
int messageLines = 0;
|
|
|
|
|
2016-01-28 06:51:51 +01:00
|
|
|
long timestamp = -1;
|
2016-02-02 08:10:44 +01:00
|
|
|
c.moveToPosition(-1);
|
2016-01-31 14:34:49 +01:00
|
|
|
while (c.moveToNext()) {
|
2016-01-27 16:08:00 +01:00
|
|
|
if (messageLines == 5) {
|
2016-01-28 05:10:30 +01:00
|
|
|
style.addLine(resources.getString(R.string.and_N_more, count - c.getPosition()));
|
2016-01-27 16:08:00 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
final ParcelableActivity activity = ci.newObject(c);
|
2016-02-23 08:06:07 +01:00
|
|
|
if (pref.isNotificationMentionsOnly() && !ArrayUtils.contains(Activity.Action.MENTION_ACTIONS,
|
2016-01-31 14:34:49 +01:00
|
|
|
activity.action)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
final long[] filteredUserIds = DataStoreUtils.getFilteredUserIds(context);
|
2016-01-28 06:51:51 +01:00
|
|
|
if (timestamp == -1) {
|
|
|
|
timestamp = activity.timestamp;
|
|
|
|
}
|
2016-01-31 14:34:49 +01:00
|
|
|
ParcelableActivityUtils.initAfterFilteredSourceIds(activity, filteredUserIds,
|
|
|
|
pref.isNotificationFollowingOnly());
|
|
|
|
final ParcelableUser[] sources = ParcelableActivityUtils.getAfterFilteredSources(activity);
|
|
|
|
if (ArrayUtils.isEmpty(sources)) continue;
|
2016-01-27 16:08:00 +01:00
|
|
|
final ActivityTitleSummaryMessage message = ActivityTitleSummaryMessage.get(context,
|
2016-01-31 14:34:49 +01:00
|
|
|
mUserColorNameManager, activity, sources,
|
|
|
|
0, false, mUseStarForLikes, mNameFirst);
|
2016-01-27 16:08:00 +01:00
|
|
|
if (message != null) {
|
2016-01-28 05:10:30 +01:00
|
|
|
final CharSequence summary = message.getSummary();
|
|
|
|
if (TextUtils.isEmpty(summary)) {
|
|
|
|
style.addLine(message.getTitle());
|
|
|
|
} else {
|
|
|
|
style.addLine(SpanFormatter.format(resources.getString(R.string.title_summary_line_format),
|
|
|
|
message.getTitle(), summary));
|
|
|
|
}
|
2016-01-27 16:08:00 +01:00
|
|
|
messageLines++;
|
|
|
|
}
|
|
|
|
}
|
2016-02-01 15:31:11 +01:00
|
|
|
if (messageLines == 0) return;
|
|
|
|
final int displayCount = messageLines + count - c.getPosition();
|
|
|
|
final String title = resources.getQuantityString(R.plurals.N_new_interactions,
|
|
|
|
displayCount, displayCount);
|
|
|
|
builder.setContentTitle(title);
|
|
|
|
style.setBigContentTitle(title);
|
2016-02-02 08:10:44 +01:00
|
|
|
builder.setNumber(displayCount);
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setContentIntent(getContentIntent(context, CustomTabType.NOTIFICATIONS_TIMELINE,
|
2016-02-29 02:33:39 +01:00
|
|
|
NotificationType.INTERACTIONS, accountId, timestamp));
|
2016-01-28 06:51:51 +01:00
|
|
|
if (timestamp != -1) {
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setDeleteIntent(getMarkReadDeleteIntent(context,
|
|
|
|
NotificationType.INTERACTIONS, accountId, timestamp, false));
|
2016-01-28 06:51:51 +01:00
|
|
|
}
|
2016-01-27 16:08:00 +01:00
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
final int notificationId = Utils.getNotificationId(NOTIFICATION_ID_INTERACTIONS_TIMELINE, accountId);
|
|
|
|
mNotificationManager.notify("interactions", notificationId, builder.build());
|
2015-03-24 16:40:47 +01:00
|
|
|
}
|
|
|
|
|
2016-01-28 11:50:39 +01:00
|
|
|
private PendingIntent getContentIntent(final Context context, @CustomTabType final String type,
|
|
|
|
@NotificationType final String notificationType,
|
2016-02-29 02:33:39 +01:00
|
|
|
final long accountId, final long readPosition) {
|
2015-04-04 16:31:37 +02:00
|
|
|
// Setup click intent
|
|
|
|
final Intent homeIntent = new Intent(context, HomeActivity.class);
|
|
|
|
final Uri.Builder homeLinkBuilder = new Uri.Builder();
|
|
|
|
homeLinkBuilder.scheme(SCHEME_TWIDERE);
|
|
|
|
homeLinkBuilder.authority(type);
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
|
2015-10-10 15:12:03 +02:00
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_FROM_NOTIFICATION, String.valueOf(true));
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
2016-02-28 06:49:27 +01:00
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE, notificationType);
|
2016-02-29 02:33:39 +01:00
|
|
|
if (readPosition > 0) {
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_READ_POSITION, String.valueOf(readPosition));
|
|
|
|
}
|
2015-10-10 15:12:03 +02:00
|
|
|
homeIntent.setData(homeLinkBuilder.build());
|
2016-02-28 06:49:27 +01:00
|
|
|
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
2015-10-10 15:12:03 +02:00
|
|
|
return PendingIntent.getActivity(context, 0, homeIntent, 0);
|
|
|
|
}
|
|
|
|
|
2016-01-28 11:50:39 +01:00
|
|
|
private PendingIntent getStatusContentIntent(final Context context, @CustomTabType final String type,
|
|
|
|
@NotificationType final String notificationType,
|
|
|
|
long accountId, long statusId,
|
|
|
|
long userId, boolean userFollowing) {
|
2015-10-10 15:12:03 +02:00
|
|
|
// Setup click intent
|
|
|
|
final Intent homeIntent = new Intent(Intent.ACTION_VIEW);
|
|
|
|
homeIntent.setPackage(BuildConfig.APPLICATION_ID);
|
|
|
|
final Uri.Builder homeLinkBuilder = new Uri.Builder();
|
|
|
|
homeLinkBuilder.scheme(SCHEME_TWIDERE);
|
|
|
|
homeLinkBuilder.authority(AUTHORITY_STATUS);
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_STATUS_ID, String.valueOf(statusId));
|
2015-10-26 09:10:37 +01:00
|
|
|
UriExtraUtils.addExtra(homeLinkBuilder, "item_id", statusId);
|
|
|
|
UriExtraUtils.addExtra(homeLinkBuilder, "item_user_id", userId);
|
2015-11-30 06:32:05 +01:00
|
|
|
UriExtraUtils.addExtra(homeLinkBuilder, "item_user_following", userFollowing);
|
2015-10-10 15:12:03 +02:00
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_FROM_NOTIFICATION, String.valueOf(true));
|
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
|
2016-02-28 06:49:27 +01:00
|
|
|
homeLinkBuilder.appendQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE, notificationType);
|
2015-04-04 16:31:37 +02:00
|
|
|
homeIntent.setData(homeLinkBuilder.build());
|
|
|
|
return PendingIntent.getActivity(context, 0, homeIntent, 0);
|
|
|
|
}
|
|
|
|
|
2016-01-02 10:48:24 +01:00
|
|
|
private void applyNotificationPreferences(NotificationCompat.Builder builder, AccountPreferences pref, int defaultFlags) {
|
2015-04-01 12:28:26 +02:00
|
|
|
int notificationDefaults = 0;
|
|
|
|
if (AccountPreferences.isNotificationHasLight(defaultFlags)) {
|
|
|
|
notificationDefaults |= NotificationCompat.DEFAULT_LIGHTS;
|
|
|
|
}
|
2015-04-08 12:39:25 +02:00
|
|
|
if (isNotificationAudible()) {
|
|
|
|
if (AccountPreferences.isNotificationHasVibration(defaultFlags)) {
|
|
|
|
notificationDefaults |= NotificationCompat.DEFAULT_VIBRATE;
|
|
|
|
} else {
|
|
|
|
notificationDefaults &= ~NotificationCompat.DEFAULT_VIBRATE;
|
|
|
|
}
|
|
|
|
if (AccountPreferences.isNotificationHasRingtone(defaultFlags)) {
|
|
|
|
builder.setSound(pref.getNotificationRingtone(), AudioManager.STREAM_NOTIFICATION);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
notificationDefaults &= ~(NotificationCompat.DEFAULT_VIBRATE | NotificationCompat.DEFAULT_SOUND);
|
2015-04-01 12:28:26 +02:00
|
|
|
}
|
2015-10-10 15:12:03 +02:00
|
|
|
builder.setColor(pref.getNotificationLightColor());
|
2015-04-01 12:28:26 +02:00
|
|
|
builder.setDefaults(notificationDefaults);
|
|
|
|
}
|
|
|
|
|
2015-03-25 16:04:25 +01:00
|
|
|
private void showMessagesNotification(AccountPreferences pref, StringLongPair[] pairs, ContentValues[] valuesArray) {
|
2015-11-08 08:12:35 +01:00
|
|
|
final Context context = getContext();
|
|
|
|
assert context != null;
|
2016-03-06 13:59:24 +01:00
|
|
|
final long accountId = pref.getAccountKey();
|
2015-03-25 16:04:25 +01:00
|
|
|
final long prevOldestId = mReadStateManager.getPosition(TAG_OLDEST_MESSAGES, String.valueOf(accountId));
|
|
|
|
long oldestId = -1;
|
|
|
|
for (final ContentValues contentValues : valuesArray) {
|
|
|
|
final long messageId = contentValues.getAsLong(DirectMessages.MESSAGE_ID);
|
|
|
|
oldestId = oldestId < 0 ? messageId : Math.min(oldestId, messageId);
|
|
|
|
if (messageId <= prevOldestId) return;
|
|
|
|
}
|
|
|
|
mReadStateManager.setPosition(TAG_OLDEST_MESSAGES, String.valueOf(accountId), oldestId, false);
|
2015-03-25 14:20:51 +01:00
|
|
|
final Resources resources = context.getResources();
|
2015-11-14 13:36:56 +01:00
|
|
|
final NotificationManagerWrapper nm = mNotificationManager;
|
2015-03-25 14:20:51 +01:00
|
|
|
final ArrayList<Expression> orExpressions = new ArrayList<>();
|
|
|
|
final String prefix = accountId + "-";
|
|
|
|
final int prefixLength = prefix.length();
|
|
|
|
final Set<Long> senderIds = new CompactHashSet<>();
|
|
|
|
for (StringLongPair pair : pairs) {
|
|
|
|
final String key = pair.getKey();
|
|
|
|
if (key.startsWith(prefix)) {
|
|
|
|
final long senderId = Long.parseLong(key.substring(prefixLength));
|
|
|
|
senderIds.add(senderId);
|
|
|
|
final Expression expression = Expression.and(
|
|
|
|
Expression.equals(DirectMessages.SENDER_ID, senderId),
|
|
|
|
Expression.greaterThan(DirectMessages.MESSAGE_ID, pair.getValue())
|
|
|
|
);
|
|
|
|
orExpressions.add(expression);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
orExpressions.add(Expression.notIn(new Column(DirectMessages.SENDER_ID), new RawItemArray(senderIds.toArray())));
|
2015-03-25 16:04:25 +01:00
|
|
|
final Expression selection = Expression.and(
|
|
|
|
Expression.equals(DirectMessages.ACCOUNT_ID, accountId),
|
|
|
|
Expression.greaterThan(DirectMessages.MESSAGE_ID, prevOldestId),
|
|
|
|
Expression.or(orExpressions.toArray(new Expression[orExpressions.size()]))
|
|
|
|
);
|
2015-03-25 14:20:51 +01:00
|
|
|
final String filteredSelection = selection.getSQL();
|
|
|
|
final String[] userProjection = {DirectMessages.SENDER_ID, DirectMessages.SENDER_NAME,
|
|
|
|
DirectMessages.SENDER_SCREEN_NAME};
|
2015-04-04 16:31:37 +02:00
|
|
|
final String[] messageProjection = {DirectMessages.MESSAGE_ID, DirectMessages.SENDER_ID,
|
|
|
|
DirectMessages.SENDER_NAME, DirectMessages.SENDER_SCREEN_NAME, DirectMessages.TEXT_UNESCAPED,
|
2015-03-25 14:20:51 +01:00
|
|
|
DirectMessages.MESSAGE_TIMESTAMP};
|
|
|
|
final Cursor messageCursor = mDatabaseWrapper.query(DirectMessages.Inbox.TABLE_NAME, messageProjection,
|
|
|
|
filteredSelection, null, null, null, DirectMessages.DEFAULT_SORT_ORDER);
|
|
|
|
final Cursor userCursor = mDatabaseWrapper.query(DirectMessages.Inbox.TABLE_NAME, userProjection,
|
|
|
|
filteredSelection, null, DirectMessages.SENDER_ID, null, DirectMessages.DEFAULT_SORT_ORDER);
|
2015-04-21 05:29:05 +02:00
|
|
|
//noinspection TryFinallyCanBeTryWithResources
|
2015-03-25 14:20:51 +01:00
|
|
|
try {
|
|
|
|
final int usersCount = userCursor.getCount();
|
|
|
|
final int messagesCount = messageCursor.getCount();
|
|
|
|
if (messagesCount == 0 || usersCount == 0) return;
|
2016-01-03 05:43:08 +01:00
|
|
|
final String accountName = DataStoreUtils.getAccountName(context, accountId);
|
2015-12-17 15:20:26 +01:00
|
|
|
final String accountScreenName = DataStoreUtils.getAccountScreenName(context, accountId);
|
2015-03-25 14:20:51 +01:00
|
|
|
final int idxMessageText = messageCursor.getColumnIndex(DirectMessages.TEXT_UNESCAPED),
|
|
|
|
idxMessageTimestamp = messageCursor.getColumnIndex(DirectMessages.MESSAGE_TIMESTAMP),
|
2015-04-04 16:31:37 +02:00
|
|
|
idxMessageId = messageCursor.getColumnIndex(DirectMessages.MESSAGE_ID),
|
|
|
|
idxMessageUserId = messageCursor.getColumnIndex(DirectMessages.SENDER_ID),
|
2015-03-25 14:20:51 +01:00
|
|
|
idxMessageUserName = messageCursor.getColumnIndex(DirectMessages.SENDER_NAME),
|
|
|
|
idxMessageUserScreenName = messageCursor.getColumnIndex(DirectMessages.SENDER_SCREEN_NAME),
|
|
|
|
idxUserName = userCursor.getColumnIndex(DirectMessages.SENDER_NAME),
|
|
|
|
idxUserScreenName = userCursor.getColumnIndex(DirectMessages.SENDER_NAME),
|
|
|
|
idxUserId = userCursor.getColumnIndex(DirectMessages.SENDER_NAME);
|
|
|
|
|
|
|
|
final CharSequence notificationTitle = resources.getQuantityString(R.plurals.N_new_messages,
|
|
|
|
messagesCount, messagesCount);
|
|
|
|
final String notificationContent;
|
|
|
|
userCursor.moveToFirst();
|
2015-12-31 09:32:27 +01:00
|
|
|
final String displayName = mUserColorNameManager.getUserNickname(userCursor.getLong(idxUserId),
|
2015-03-25 14:20:51 +01:00
|
|
|
mNameFirst ? userCursor.getString(idxUserName) : userCursor.getString(idxUserScreenName));
|
|
|
|
if (usersCount == 1) {
|
|
|
|
if (messagesCount == 1) {
|
|
|
|
notificationContent = context.getString(R.string.notification_direct_message, displayName);
|
|
|
|
} else {
|
|
|
|
notificationContent = context.getString(R.string.notification_direct_message_multiple_messages,
|
|
|
|
displayName, messagesCount);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
notificationContent = context.getString(R.string.notification_direct_message_multiple_users,
|
|
|
|
displayName, usersCount - 1, messagesCount);
|
|
|
|
}
|
|
|
|
|
2015-04-04 16:31:37 +02:00
|
|
|
final LongSparseArray<Long> idsMap = new LongSparseArray<>();
|
2015-03-25 14:20:51 +01:00
|
|
|
// Add rich notification and get latest tweet timestamp
|
|
|
|
long when = -1;
|
|
|
|
final InboxStyle style = new InboxStyle();
|
2015-04-04 16:31:37 +02:00
|
|
|
for (int i = 0; messageCursor.moveToPosition(i) && i < messagesCount; i++) {
|
2015-03-25 14:20:51 +01:00
|
|
|
if (when < 0) {
|
|
|
|
when = messageCursor.getLong(idxMessageTimestamp);
|
|
|
|
}
|
2015-04-04 16:31:37 +02:00
|
|
|
if (i < 5) {
|
|
|
|
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
2015-12-31 09:32:27 +01:00
|
|
|
sb.append(mUserColorNameManager.getUserNickname(messageCursor.getLong(idxUserId),
|
2015-04-04 16:31:37 +02:00
|
|
|
mNameFirst ? messageCursor.getString(idxMessageUserName) : messageCursor.getString(idxMessageUserScreenName)));
|
|
|
|
sb.setSpan(new StyleSpan(Typeface.BOLD), 0, sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
sb.append(' ');
|
|
|
|
sb.append(messageCursor.getString(idxMessageText));
|
|
|
|
style.addLine(sb);
|
|
|
|
}
|
|
|
|
final long userId = messageCursor.getLong(idxMessageUserId);
|
|
|
|
final long messageId = messageCursor.getLong(idxMessageId);
|
|
|
|
idsMap.put(userId, Math.max(idsMap.get(userId, -1L), messageId));
|
2015-03-25 14:20:51 +01:00
|
|
|
}
|
|
|
|
if (mNameFirst) {
|
|
|
|
style.setSummaryText(accountName);
|
|
|
|
} else {
|
|
|
|
style.setSummaryText("@" + accountScreenName);
|
|
|
|
}
|
2015-04-04 16:31:37 +02:00
|
|
|
final StringLongPair[] positions = new StringLongPair[idsMap.size()];
|
|
|
|
for (int i = 0, j = idsMap.size(); i < j; i++) {
|
|
|
|
positions[i] = new StringLongPair(String.valueOf(idsMap.keyAt(i)), idsMap.valueAt(i));
|
|
|
|
}
|
2015-03-25 14:20:51 +01:00
|
|
|
|
|
|
|
// Setup notification
|
|
|
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
2015-03-28 19:30:51 +01:00
|
|
|
builder.setAutoCancel(true);
|
2015-05-04 17:53:52 +02:00
|
|
|
builder.setSmallIcon(R.drawable.ic_stat_message);
|
2015-03-25 14:20:51 +01:00
|
|
|
builder.setTicker(notificationTitle);
|
|
|
|
builder.setContentTitle(notificationTitle);
|
|
|
|
builder.setContentText(notificationContent);
|
2016-01-28 06:51:51 +01:00
|
|
|
builder.setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setContentIntent(getContentIntent(context, CustomTabType.DIRECT_MESSAGES,
|
2016-02-29 02:33:39 +01:00
|
|
|
NotificationType.DIRECT_MESSAGES, accountId, -1));
|
2016-01-28 11:50:39 +01:00
|
|
|
builder.setDeleteIntent(getMarkReadDeleteIntent(context,
|
|
|
|
NotificationType.DIRECT_MESSAGES, accountId, positions));
|
2015-03-25 14:20:51 +01:00
|
|
|
builder.setNumber(messagesCount);
|
|
|
|
builder.setWhen(when);
|
|
|
|
builder.setStyle(style);
|
|
|
|
builder.setColor(pref.getNotificationLightColor());
|
2016-01-02 10:48:24 +01:00
|
|
|
applyNotificationPreferences(builder, pref, pref.getDirectMessagesNotificationType());
|
2015-06-30 01:59:22 +02:00
|
|
|
try {
|
|
|
|
nm.notify("messages_" + accountId, NOTIFICATION_ID_DIRECT_MESSAGES, builder.build());
|
|
|
|
Utils.sendPebbleNotification(context, notificationContent);
|
|
|
|
} catch (SecurityException e) {
|
|
|
|
// Silently ignore
|
|
|
|
}
|
2015-03-25 14:20:51 +01:00
|
|
|
} finally {
|
|
|
|
messageCursor.close();
|
|
|
|
userCursor.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-26 08:30:05 +01:00
|
|
|
private void preloadImages(final ContentValues... values) {
|
|
|
|
if (values == null) return;
|
|
|
|
for (final ContentValues v : values) {
|
|
|
|
if (mPreferences.getBoolean(KEY_PRELOAD_PROFILE_IMAGES, false)) {
|
|
|
|
mImagePreloader.preloadImage(v.getAsString(Statuses.USER_PROFILE_IMAGE_URL));
|
|
|
|
mImagePreloader.preloadImage(v.getAsString(DirectMessages.SENDER_PROFILE_IMAGE_URL));
|
|
|
|
mImagePreloader.preloadImage(v.getAsString(DirectMessages.RECIPIENT_PROFILE_IMAGE_URL));
|
|
|
|
}
|
|
|
|
if (mPreferences.getBoolean(KEY_PRELOAD_PREVIEW_IMAGES, false)) {
|
|
|
|
final String textHtml = v.getAsString(Statuses.TEXT_HTML);
|
2016-02-21 17:13:24 +01:00
|
|
|
for (final String link : PreviewMediaExtractor.getSupportedLinksInStatus(textHtml)) {
|
|
|
|
mImagePreloader.preloadImage(link);
|
|
|
|
}
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setNotificationUri(final Cursor c, final Uri uri) {
|
|
|
|
final ContentResolver cr = getContentResolver();
|
|
|
|
if (cr == null || c == null || uri == null) return;
|
|
|
|
c.setNotificationUri(cr, uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updatePreferences() {
|
2016-01-27 16:08:00 +01:00
|
|
|
mNameFirst = mPreferences.getBoolean(KEY_NAME_FIRST);
|
|
|
|
mUseStarForLikes = mPreferences.getBoolean(KEY_I_WANT_MY_STARS_BACK);
|
2014-11-26 08:30:05 +01:00
|
|
|
}
|
|
|
|
|
2014-07-03 07:48:39 +02:00
|
|
|
}
|