code cleanup

This commit is contained in:
Mariotaku Lee 2016-12-14 16:02:50 +08:00
parent ea39c48d55
commit b842520d53
34 changed files with 901 additions and 1021 deletions

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_debug">true</bool>
</resources>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="min_database_item_limit">10</integer>
<bool name="is_debug">true</bool>
</resources>

View File

@ -461,7 +461,14 @@
<service
android:name=".service.RefreshService"
android:enabled="@bool/use_legacy_refresh_service"
android:label="@string/label_refresh_service"/>
<service
android:name=".service.JobRefreshService"
android:enabled="@bool/use_job_refresh_service"
android:exported="true"
android:label="@string/label_refresh_service"
android:permission="android.permission.BIND_JOB_SERVICE"/>
<service
android:name=".service.StreamingService"
android:label="@string/label_streaming_service"/>

View File

@ -1,43 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.service;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Build;
/**
* Created by mariotaku on 14/12/12.
*/
@SuppressLint("Registered")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class BackgroundJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}

View File

@ -1,411 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.service;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.apache.commons.lang3.math.NumberUtils;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.model.SimpleRefreshTaskParam;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.receiver.PowerStateReceiver;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import java.util.Arrays;
import javax.inject.Inject;
import edu.tsinghua.hotmobi.model.BatteryRecord;
import edu.tsinghua.hotmobi.model.ScreenEvent;
public class RefreshService extends Service implements Constants {
@Inject
SharedPreferencesWrapper mPreferences;
private AlarmManager mAlarmManager;
@Inject
AsyncTwitterWrapper mTwitterWrapper;
private PendingIntent mPendingRefreshHomeTimelineIntent, mPendingRefreshMentionsIntent,
mPendingRefreshDirectMessagesIntent, mPendingRefreshTrendsIntent;
private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, String.format("Refresh service received action %s", action));
}
switch (action) {
case BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING: {
rescheduleHomeTimelineRefreshing();
break;
}
case BROADCAST_RESCHEDULE_MENTIONS_REFRESHING: {
rescheduleMentionsRefreshing();
break;
}
case BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING: {
rescheduleDirectMessagesRefreshing();
break;
}
case BROADCAST_RESCHEDULE_TRENDS_REFRESHING: {
rescheduleTrendsRefreshing();
break;
}
case BROADCAST_REFRESH_HOME_TIMELINE: {
if (isAutoRefreshAllowed()) {
mTwitterWrapper.getHomeTimelineAsync(new SimpleRefreshTaskParam() {
private UserKey[] accountIds;
@NonNull
@Override
public UserKey[] getAccountKeysWorker() {
if (accountIds != null) return accountIds;
final AccountPreferences[] prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context));
return accountIds = getRefreshableIds(prefs, HomeRefreshableFilter.INSTANCE);
}
@Nullable
@Override
public String[] getSinceIds() {
return DataStoreUtils.getNewestStatusIds(context,
Statuses.CONTENT_URI, getAccountKeys());
}
});
}
break;
}
case BROADCAST_REFRESH_NOTIFICATIONS: {
if (isAutoRefreshAllowed()) {
mTwitterWrapper.getActivitiesAboutMeAsync(new SimpleRefreshTaskParam() {
private UserKey[] accountIds;
@NonNull
@Override
public UserKey[] getAccountKeysWorker() {
if (accountIds != null) return accountIds;
final AccountPreferences[] prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context));
return accountIds = getRefreshableIds(prefs, MentionsRefreshableFilter.INSTANCE);
}
@Nullable
@Override
public String[] getSinceIds() {
return DataStoreUtils.getNewestActivityMaxPositions(context,
Activities.AboutMe.CONTENT_URI, getAccountKeys());
}
});
}
break;
}
case BROADCAST_REFRESH_DIRECT_MESSAGES: {
if (isAutoRefreshAllowed()) {
mTwitterWrapper.getReceivedDirectMessagesAsync(new SimpleRefreshTaskParam() {
private UserKey[] accountIds;
@NonNull
@Override
public UserKey[] getAccountKeysWorker() {
if (accountIds != null) return accountIds;
final AccountPreferences[] prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context));
return accountIds = getRefreshableIds(prefs, MessagesRefreshableFilter.INSTANCE);
}
@Nullable
@Override
public String[] getSinceIds() {
return DataStoreUtils.getNewestMessageIds(context,
DirectMessages.Inbox.CONTENT_URI, getAccountKeys());
}
});
}
break;
}
case BROADCAST_REFRESH_TRENDS: {
if (isAutoRefreshAllowed()) {
final AccountPreferences[] prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context));
final UserKey[] refreshIds = getRefreshableIds(prefs, TrendsRefreshableFilter.INSTANCE);
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, String.format("Auto refreshing trends for %s", Arrays.toString(refreshIds)));
}
getLocalTrends(refreshIds);
break;
}
}
}
}
};
private final BroadcastReceiver mPowerStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Intent.ACTION_BATTERY_CHANGED: {
BatteryRecord.log(context, intent);
break;
}
default: {
BatteryRecord.log(context);
break;
}
}
}
};
private final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
public long mPresentTime = -1;
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Intent.ACTION_SCREEN_ON: {
ScreenEvent.log(context, ScreenEvent.Action.ON, getPresentDuration());
break;
}
case Intent.ACTION_SCREEN_OFF: {
ScreenEvent.log(context, ScreenEvent.Action.OFF, getPresentDuration());
mPresentTime = -1;
break;
}
case Intent.ACTION_USER_PRESENT: {
mPresentTime = SystemClock.elapsedRealtime();
ScreenEvent.log(context, ScreenEvent.Action.PRESENT, -1);
break;
}
}
}
private long getPresentDuration() {
if (mPresentTime < 0) return -1;
return SystemClock.elapsedRealtime() - mPresentTime;
}
};
@Override
public IBinder onBind(final Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
GeneralComponentHelper.build(this).inject(this);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mPendingRefreshHomeTimelineIntent = PendingIntent.getBroadcast(this, 0, new Intent(
BROADCAST_REFRESH_HOME_TIMELINE), 0);
mPendingRefreshMentionsIntent = PendingIntent.getBroadcast(this, 0, new Intent(BROADCAST_REFRESH_NOTIFICATIONS), 0);
mPendingRefreshDirectMessagesIntent = PendingIntent.getBroadcast(this, 0, new Intent(
BROADCAST_REFRESH_DIRECT_MESSAGES), 0);
mPendingRefreshTrendsIntent = PendingIntent.getBroadcast(this, 0, new Intent(BROADCAST_REFRESH_TRENDS), 0);
final IntentFilter refreshFilter = new IntentFilter(BROADCAST_NOTIFICATION_DELETED);
refreshFilter.addAction(BROADCAST_REFRESH_HOME_TIMELINE);
refreshFilter.addAction(BROADCAST_REFRESH_NOTIFICATIONS);
refreshFilter.addAction(BROADCAST_REFRESH_DIRECT_MESSAGES);
refreshFilter.addAction(BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING);
refreshFilter.addAction(BROADCAST_RESCHEDULE_MENTIONS_REFRESHING);
refreshFilter.addAction(BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING);
registerReceiver(mStateReceiver, refreshFilter);
final IntentFilter batteryFilter = new IntentFilter();
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
batteryFilter.addAction(Intent.ACTION_BATTERY_OKAY);
batteryFilter.addAction(Intent.ACTION_BATTERY_LOW);
batteryFilter.addAction(Intent.ACTION_POWER_CONNECTED);
batteryFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
final IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
screenFilter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(mPowerStateReceiver, batteryFilter);
registerReceiver(mScreenStateReceiver, screenFilter);
PowerStateReceiver.Companion.setServiceReceiverStarted(true);
if (Utils.hasAutoRefreshAccounts(this)) {
startAutoRefresh();
} else {
stopSelf();
}
}
@Override
public void onDestroy() {
PowerStateReceiver.Companion.setServiceReceiverStarted(false);
unregisterReceiver(mScreenStateReceiver);
unregisterReceiver(mPowerStateReceiver);
unregisterReceiver(mStateReceiver);
if (Utils.hasAutoRefreshAccounts(this)) {
// Auto refresh enabled, so I will try to start service after it was
// stopped.
startService(new Intent(this, getClass()));
}
super.onDestroy();
}
protected boolean isAutoRefreshAllowed() {
return Utils.isNetworkAvailable(this) && (Utils.isBatteryOkay(this) || !Utils.shouldStopAutoRefreshOnBatteryLow(this));
}
private void getLocalTrends(final UserKey[] accountIds) {
final UserKey account_id = Utils.getDefaultAccountKey(this);
final int woeid = mPreferences.getInt(KEY_LOCAL_TRENDS_WOEID, 1);
mTwitterWrapper.getLocalTrendsAsync(account_id, woeid);
}
private UserKey[] getRefreshableIds(final AccountPreferences[] prefs, final RefreshableAccountFilter filter) {
if (prefs == null) return null;
final UserKey[] temp = new UserKey[prefs.length];
int i = 0;
for (final AccountPreferences pref : prefs) {
if (pref.isAutoRefreshEnabled() && filter.isRefreshable(pref)) {
temp[i++] = pref.getAccountKey();
}
}
final UserKey[] result = new UserKey[i];
System.arraycopy(temp, 0, result, 0, i);
return result;
}
private long getRefreshInterval() {
if (mPreferences == null) return 0;
final int prefValue = NumberUtils.toInt(mPreferences.getString(KEY_REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL), -1);
return Math.max(prefValue, 3) * 60 * 1000;
}
private void rescheduleDirectMessagesRefreshing() {
mAlarmManager.cancel(mPendingRefreshDirectMessagesIntent);
final long refreshInterval = getRefreshInterval();
if (refreshInterval > 0) {
mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, mPendingRefreshDirectMessagesIntent);
}
}
private void rescheduleHomeTimelineRefreshing() {
mAlarmManager.cancel(mPendingRefreshHomeTimelineIntent);
final long refreshInterval = getRefreshInterval();
if (refreshInterval > 0) {
mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, mPendingRefreshHomeTimelineIntent);
}
}
private void rescheduleMentionsRefreshing() {
mAlarmManager.cancel(mPendingRefreshMentionsIntent);
final long refreshInterval = getRefreshInterval();
if (refreshInterval > 0) {
mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, mPendingRefreshMentionsIntent);
}
}
private void rescheduleTrendsRefreshing() {
mAlarmManager.cancel(mPendingRefreshTrendsIntent);
final long refreshInterval = getRefreshInterval();
if (refreshInterval > 0) {
mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, mPendingRefreshTrendsIntent);
}
}
private boolean startAutoRefresh() {
stopAutoRefresh();
final long refreshInterval = getRefreshInterval();
if (refreshInterval <= 0) return false;
rescheduleHomeTimelineRefreshing();
rescheduleMentionsRefreshing();
rescheduleDirectMessagesRefreshing();
rescheduleTrendsRefreshing();
return true;
}
private void stopAutoRefresh() {
mAlarmManager.cancel(mPendingRefreshHomeTimelineIntent);
mAlarmManager.cancel(mPendingRefreshMentionsIntent);
mAlarmManager.cancel(mPendingRefreshDirectMessagesIntent);
mAlarmManager.cancel(mPendingRefreshTrendsIntent);
}
private interface RefreshableAccountFilter {
boolean isRefreshable(AccountPreferences pref);
}
private static class HomeRefreshableFilter implements RefreshableAccountFilter {
public static final RefreshableAccountFilter INSTANCE = new HomeRefreshableFilter();
@Override
public boolean isRefreshable(final AccountPreferences pref) {
return pref.isAutoRefreshHomeTimelineEnabled();
}
}
private static class MentionsRefreshableFilter implements RefreshableAccountFilter {
static final RefreshableAccountFilter INSTANCE = new MentionsRefreshableFilter();
@Override
public boolean isRefreshable(final AccountPreferences pref) {
return pref.isAutoRefreshMentionsEnabled();
}
}
private static class MessagesRefreshableFilter implements RefreshableAccountFilter {
public static final RefreshableAccountFilter INSTANCE = new MentionsRefreshableFilter();
@Override
public boolean isRefreshable(final AccountPreferences pref) {
return pref.isAutoRefreshDirectMessagesEnabled();
}
}
private static class TrendsRefreshableFilter implements RefreshableAccountFilter {
public static final RefreshableAccountFilter INSTANCE = new TrendsRefreshableFilter();
@Override
public boolean isRefreshable(final AccountPreferences pref) {
return pref.isAutoRefreshTrendsEnabled();
}
}
}

View File

@ -1,156 +0,0 @@
package org.mariotaku.twidere.task;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.squareup.otto.Bus;
import org.apache.commons.lang3.math.NumberUtils;
import org.mariotaku.abstask.library.AbstractTask;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.ErrorInfo;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.microblog.library.twitter.model.ResponseList;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.model.RefreshTaskParam;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.message.GetMessagesTaskEvent;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.ErrorInfoStore;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.TwitterWrapper;
import org.mariotaku.twidere.util.UriUtils;
import org.mariotaku.twidere.util.content.ContentResolverUtils;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
/**
* Created by mariotaku on 16/2/14.
*/
public abstract class GetDirectMessagesTask extends AbstractTask<RefreshTaskParam,
List<TwitterWrapper.MessageListResponse>, Object> implements Constants {
protected final Context context;
@Inject
protected ErrorInfoStore errorInfoStore;
@Inject
protected SharedPreferencesWrapper preferences;
@Inject
protected Bus bus;
public GetDirectMessagesTask(Context context) {
this.context = context;
GeneralComponentHelper.build(context).inject(this);
}
public abstract ResponseList<DirectMessage> getDirectMessages(MicroBlog twitter, Paging paging)
throws MicroBlogException;
protected abstract Uri getDatabaseUri();
protected abstract boolean isOutgoing();
@Override
public List<TwitterWrapper.MessageListResponse> doLongOperation(final RefreshTaskParam param) {
final UserKey[] accountKeys = param.getAccountKeys();
final String[] sinceIds = param.getSinceIds(), maxIds = param.getMaxIds();
final List<TwitterWrapper.MessageListResponse> result = new ArrayList<>();
int idx = 0;
final int loadItemLimit = preferences.getInt(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT);
for (final UserKey accountKey : accountKeys) {
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(context, accountKey);
if (twitter == null) continue;
try {
final Paging paging = new Paging();
paging.setCount(loadItemLimit);
String maxId = null, sinceId = null;
if (maxIds != null && maxIds[idx] != null) {
maxId = maxIds[idx];
paging.setMaxId(maxId);
}
if (sinceIds != null && sinceIds[idx] != null) {
sinceId = sinceIds[idx];
long sinceIdLong = NumberUtils.toLong(sinceId, -1);
//TODO handle non-twitter case
if (sinceIdLong != -1) {
paging.sinceId(String.valueOf(sinceIdLong - 1));
} else {
paging.sinceId(sinceId);
}
if (maxIds == null || sinceIds[idx] == null) {
paging.setLatestResults(true);
}
}
final List<DirectMessage> messages = getDirectMessages(twitter, paging);
result.add(new TwitterWrapper.MessageListResponse(accountKey, maxId, sinceId, messages));
storeMessages(accountKey, messages, isOutgoing(), true);
errorInfoStore.remove(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey);
} catch (final MicroBlogException e) {
if (e.getErrorCode() == ErrorInfo.NO_DIRECT_MESSAGE_PERMISSION) {
errorInfoStore.put(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey,
ErrorInfoStore.CODE_NO_DM_PERMISSION);
} else if (e.isCausedByNetworkIssue()) {
errorInfoStore.put(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey,
ErrorInfoStore.CODE_NETWORK_ERROR);
}
if (BuildConfig.DEBUG) {
Log.w(TwidereConstants.LOGTAG, e);
}
result.add(new TwitterWrapper.MessageListResponse(accountKey, e));
}
idx++;
}
return result;
}
private boolean storeMessages(UserKey accountKey, List<DirectMessage> messages, boolean isOutgoing, boolean notify) {
if (messages == null) return true;
final Uri uri = getDatabaseUri();
final ContentValues[] valuesArray = new ContentValues[messages.size()];
for (int i = 0, j = messages.size(); i < j; i++) {
final DirectMessage message = messages.get(i);
try {
valuesArray[i] = ContentValuesCreator.createDirectMessage(message, accountKey, isOutgoing);
} catch (IOException e) {
return false;
}
}
// Delete all rows conflicting before new data inserted.
// final Expression deleteWhere = Expression.and(Expression.equals(DirectMessages.ACCOUNT_ID, accountKey),
// Expression.in(new Column(DirectMessages.MESSAGE_ID), new RawItemArray(messageIds)));
// final Uri deleteUri = UriUtils.appendQueryParameters(uri, QUERY_PARAM_NOTIFY, false);
// mResolver.delete(deleteUri, deleteWhere.getSQL(), null);
// Insert previously fetched items.
final Uri insertUri = UriUtils.appendQueryParameters(uri, TwidereConstants.QUERY_PARAM_NOTIFY, notify);
ContentResolverUtils.bulkInsert(context.getContentResolver(), insertUri, valuesArray);
return false;
}
public void beforeExecute(RefreshTaskParam params) {
bus.post(new GetMessagesTaskEvent(getDatabaseUri(), true, null));
}
@Override
protected void afterExecute(Object handler, List<TwitterWrapper.MessageListResponse> result) {
bus.post(new GetMessagesTaskEvent(getDatabaseUri(), false, AsyncTwitterWrapper.getException(result)));
}
}

View File

@ -1,37 +0,0 @@
package org.mariotaku.twidere.task;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.Trends;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedTrends;
import java.util.List;
/**
* Created by mariotaku on 16/2/24.
*/
public class GetLocalTrendsTask extends GetTrendsTask {
private final int woeid;
public GetLocalTrendsTask(final Context context, final UserKey accountKey, final int woeid) {
super(context, accountKey);
this.woeid = woeid;
}
@Override
public List<Trends> getTrends(@NonNull final MicroBlog twitter) throws MicroBlogException {
return twitter.getLocationTrends(woeid);
}
@Override
protected Uri getContentUri() {
return CachedTrends.Local.CONTENT_URI;
}
}

View File

@ -1,57 +0,0 @@
package org.mariotaku.twidere.task;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.util.Log;
import org.mariotaku.abstask.library.AbstractTask;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.ResponseList;
import org.mariotaku.microblog.library.twitter.model.SavedSearch;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.content.ContentResolverUtils;
import static org.mariotaku.twidere.TwidereConstants.LOGTAG;
/**
* Created by mariotaku on 16/2/13.
*/
public class GetSavedSearchesTask extends AbstractTask<UserKey[], SingleResponse<Object>, Object> {
private final Context mContext;
public GetSavedSearchesTask(Context context) {
this.mContext = context;
}
@Override
public SingleResponse<Object> doLongOperation(UserKey[] params) {
final ContentResolver cr = mContext.getContentResolver();
for (UserKey accountKey : params) {
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(mContext, accountKey);
if (twitter == null) continue;
try {
final ResponseList<SavedSearch> searches = twitter.getSavedSearches();
final ContentValues[] values = ContentValuesCreator.createSavedSearches(searches,
accountKey);
final Expression where = Expression.equalsArgs(SavedSearches.ACCOUNT_KEY);
final String[] whereArgs = {accountKey.toString()};
cr.delete(SavedSearches.CONTENT_URI, where.getSQL(), whereArgs);
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values);
} catch (MicroBlogException e) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e);
}
}
}
return SingleResponse.Companion.getInstance();
}
}

View File

@ -1,89 +0,0 @@
package org.mariotaku.twidere.task;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import com.squareup.otto.Bus;
import org.mariotaku.abstask.library.AbstractTask;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.Trends;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.message.TrendsRefreshedEvent;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.content.ContentResolverUtils;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
/**
* Created by mariotaku on 16/2/24.
*/
public abstract class GetTrendsTask extends AbstractTask<Object, Object, Object> {
private final Context mContext;
private final UserKey mAccountId;
@Inject
protected Bus mBus;
public GetTrendsTask(Context context, final UserKey accountKey) {
GeneralComponentHelper.build(context).inject(this);
this.mContext = context;
this.mAccountId = accountKey;
}
public abstract List<Trends> getTrends(@NonNull MicroBlog twitter) throws MicroBlogException;
@Override
public Object doLongOperation(final Object param) {
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(mContext, mAccountId);
if (twitter == null) return null;
try {
final List<Trends> trends = getTrends(twitter);
storeTrends(mContext.getContentResolver(), getContentUri(), trends);
return null;
} catch (final MicroBlogException e) {
return null;
}
}
@Override
protected void afterExecute(Object handler, Object result) {
mBus.post(new TrendsRefreshedEvent());
}
protected abstract Uri getContentUri();
private static void storeTrends(ContentResolver cr, Uri uri, List<Trends> trendsList) {
final ArrayList<String> hashtags = new ArrayList<>();
final ArrayList<ContentValues> hashtagValues = new ArrayList<>();
if (trendsList != null && trendsList.size() > 0) {
final ContentValues[] valuesArray = ContentValuesCreator.createTrends(trendsList);
for (final ContentValues values : valuesArray) {
final String hashtag = values.getAsString(TwidereDataStore.CachedTrends.NAME).replaceFirst("#", "");
if (hashtags.contains(hashtag)) {
continue;
}
hashtags.add(hashtag);
final ContentValues hashtagValue = new ContentValues();
hashtagValue.put(TwidereDataStore.CachedHashtags.NAME, hashtag);
hashtagValues.add(hashtagValue);
}
cr.delete(uri, null, null);
ContentResolverUtils.bulkInsert(cr, uri, valuesArray);
ContentResolverUtils.bulkDelete(cr, TwidereDataStore.CachedHashtags.CONTENT_URI, TwidereDataStore.CachedHashtags.NAME, hashtags, null);
ContentResolverUtils.bulkInsert(cr, TwidereDataStore.CachedHashtags.CONTENT_URI,
hashtagValues.toArray(new ContentValues[hashtagValues.size()]));
}
}
}

View File

@ -1,82 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.task;
import android.app.Activity;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.Toast;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.provider.CacheProvider;
import java.io.File;
/**
* Created by mariotaku on 15/12/28.
*/
public class SaveMediaToGalleryTask extends ProgressSaveFileTask {
public SaveMediaToGalleryTask(@NonNull Activity activity, @NonNull Uri source, @NonNull File destination, String type) {
super(activity, source, destination, new CacheProvider.CacheFileTypeCallback(activity, type));
}
public static SaveFileTask create(final Activity activity, final Uri source,
@NonNull @CacheProvider.Type final String type) {
final File pubDir;
switch (type) {
case CacheProvider.Type.VIDEO: {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
break;
}
case CacheProvider.Type.IMAGE: {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
break;
}
default: {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
break;
}
}
final File saveDir = new File(pubDir, "Twidere");
return new SaveMediaToGalleryTask(activity, source, saveDir, type);
}
@Override
protected void onFileSaved(@NonNull File savedFile, @Nullable String mimeType) {
final Context context = getContext();
if (context == null) return;
MediaScannerConnection.scanFile(context, new String[]{savedFile.getPath()},
new String[]{mimeType}, null);
Toast.makeText(context, R.string.saved_to_gallery, Toast.LENGTH_SHORT).show();
}
@Override
protected void onFileSaveFailed() {
final Context context = getContext();
if (context == null) return;
Toast.makeText(context, R.string.error_occurred, Toast.LENGTH_SHORT).show();
}
}

View File

@ -47,9 +47,7 @@ import org.mariotaku.microblog.library.twitter.http.HttpResponseCode;
import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.ErrorInfo;
import org.mariotaku.microblog.library.twitter.model.FriendshipUpdate;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.microblog.library.twitter.model.Relationship;
import org.mariotaku.microblog.library.twitter.model.ResponseList;
import org.mariotaku.microblog.library.twitter.model.SavedSearch;
import org.mariotaku.microblog.library.twitter.model.User;
import org.mariotaku.microblog.library.twitter.model.UserList;
@ -106,10 +104,11 @@ import org.mariotaku.twidere.task.DestroyStatusTask;
import org.mariotaku.twidere.task.DestroyUserBlockTask;
import org.mariotaku.twidere.task.DestroyUserMuteTask;
import org.mariotaku.twidere.task.GetActivitiesAboutMeTask;
import org.mariotaku.twidere.task.GetDirectMessagesTask;
import org.mariotaku.twidere.task.GetHomeTimelineTask;
import org.mariotaku.twidere.task.GetLocalTrendsTask;
import org.mariotaku.twidere.task.GetReceivedDirectMessagesTask;
import org.mariotaku.twidere.task.GetSavedSearchesTask;
import org.mariotaku.twidere.task.GetSentDirectMessagesTask;
import org.mariotaku.twidere.task.ManagedAsyncTask;
import org.mariotaku.twidere.task.ReportSpamAndBlockTask;
import org.mariotaku.twidere.task.twitter.GetActivitiesTask;
@ -1325,61 +1324,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
static class GetReceivedDirectMessagesTask extends GetDirectMessagesTask {
public GetReceivedDirectMessagesTask(Context context) {
super(context);
}
@Override
public ResponseList<DirectMessage> getDirectMessages(final MicroBlog twitter, final Paging paging)
throws MicroBlogException {
return twitter.getDirectMessages(paging);
}
@Override
protected Uri getDatabaseUri() {
return Inbox.CONTENT_URI;
}
@Override
protected boolean isOutgoing() {
return false;
}
@Override
public void beforeExecute(RefreshTaskParam params) {
final Intent intent = new Intent(BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING);
context.sendBroadcast(intent);
super.beforeExecute(params);
}
}
static class GetSentDirectMessagesTask extends GetDirectMessagesTask {
public GetSentDirectMessagesTask(Context context) {
super(context);
}
@Override
public ResponseList<DirectMessage> getDirectMessages(final MicroBlog twitter, final Paging paging)
throws MicroBlogException {
return twitter.getSentDirectMessages(paging);
}
@Override
protected boolean isOutgoing() {
return true;
}
@Override
protected Uri getDatabaseUri() {
return Outbox.CONTENT_URI;
}
}
public SharedPreferencesWrapper getPreferences() {
return preferences;
}

View File

@ -1187,6 +1187,7 @@ public final class Utils implements Constants {
public static void startRefreshServiceIfNeeded(@NonNull final Context context) {
final Context appContext = context.getApplicationContext();
if (appContext == null) return;
if (!appContext.getResources().getBoolean(R.bool.use_legacy_refresh_service)) return;
final Intent refreshServiceIntent = new Intent(appContext, RefreshService.class);
AsyncTask.execute(new Runnable() {
@Override

View File

@ -396,15 +396,11 @@ class MediaViewerActivity : BaseActivity(), IExtendedActivity, ATEToolbarCustomi
val viewPager = findViewPager()
val adapter = viewPager.adapter
val f = adapter.instantiateItem(viewPager, saveToStoragePosition) as? CacheDownloadMediaViewerFragment ?: return
val result = f.downloadResult ?: return
val cacheUri = result.cacheUri
val hasMedia = cacheUri != null
if (!hasMedia) return
val task: SaveFileTask
when (f) {
is ImagePageFragment -> task = SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.IMAGE)
is VideoPageFragment -> task = SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.VIDEO)
is GifPageFragment -> task = SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.IMAGE)
val cacheUri = f.downloadResult?.cacheUri ?: return
val task: SaveFileTask = when (f) {
is ImagePageFragment -> SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.IMAGE)
is VideoPageFragment -> SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.VIDEO)
is GifPageFragment -> SaveMediaToGalleryTask.create(this, cacheUri, CacheProvider.Type.IMAGE)
else -> throw UnsupportedOperationException()
}
AsyncTaskUtils.executeTask(task)

View File

@ -27,7 +27,6 @@ import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.graphics.Color
import android.os.AsyncTask
import android.os.Handler
import android.support.design.widget.FloatingActionButton
import android.support.multidex.MultiDex
import android.support.v4.content.ContextCompat
@ -46,6 +45,8 @@ import nl.komponents.kovenant.android.stopKovenant
import nl.komponents.kovenant.task
import org.apache.commons.lang3.ArrayUtils
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.kpreferences.get
import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.configure
import org.mariotaku.mediaviewer.library.MediaDownloader
import org.mariotaku.restfu.http.RestHttpClient
@ -56,6 +57,8 @@ import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.AssistLauncherActivity
import org.mariotaku.twidere.activity.MainActivity
import org.mariotaku.twidere.activity.MainHondaJOJOActivity
import org.mariotaku.twidere.constant.apiLastChangeKey
import org.mariotaku.twidere.constant.bugReportsKey
import org.mariotaku.twidere.constant.defaultFeatureLastUpdated
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.service.RefreshService
@ -89,25 +92,14 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
@Inject
lateinit internal var kPreferences: KPreferences
var handler: Handler? = null
private set
private var profileImageViewViewProcessor: ProfileImageViewViewProcessor? = null
private var fontFamilyTagProcessor: FontFamilyTagProcessor? = null
private lateinit var profileImageViewViewProcessor: ProfileImageViewViewProcessor
private lateinit var fontFamilyTagProcessor: FontFamilyTagProcessor
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
fun initKeyboardShortcuts() {
val preferences = sharedPreferences
if (!preferences.getBoolean(KEY_KEYBOARD_SHORTCUT_INITIALIZED, false)) {
// getApplicationModule().getKeyboardShortcutsHandler().reset();
preferences.edit().putBoolean(KEY_KEYBOARD_SHORTCUT_INITIALIZED, true).apply()
}
}
val sqLiteDatabase: SQLiteDatabase by lazy {
StrictModeUtils.checkDiskIO()
@ -131,12 +123,14 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
initializeAsyncTask()
initDebugMode()
initBugReport()
handler = Handler()
updateEasterEggIcon()
migrateUsageStatisticsPreferences()
if (resources.getBoolean(R.bool.use_job_refresh_service)) {
} else {
Utils.startRefreshServiceIfNeeded(this)
}
GeneralComponentHelper.build(this).inject(this)
@ -225,9 +219,9 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
ATE.registerViewProcessor(ImageView::class.java, ImageViewViewProcessor())
ATE.registerViewProcessor(MaterialEditText::class.java, MaterialEditTextViewProcessor())
ATE.registerViewProcessor(ProgressWheel::class.java, ProgressWheelViewProcessor())
ATE.registerViewProcessor(ProfileImageView::class.java, profileImageViewViewProcessor!!)
ATE.registerViewProcessor(ProfileImageView::class.java, profileImageViewViewProcessor)
ATE.registerTagProcessor(OptimalLinkColorTagProcessor.TAG, OptimalLinkColorTagProcessor())
ATE.registerTagProcessor(FontFamilyTagProcessor.TAG, fontFamilyTagProcessor!!)
ATE.registerTagProcessor(FontFamilyTagProcessor.TAG, fontFamilyTagProcessor)
ATE.registerTagProcessor(IconActionButtonTagProcessor.PREFIX_COLOR,
IconActionButtonTagProcessor(IconActionButtonTagProcessor.PREFIX_COLOR))
ATE.registerTagProcessor(IconActionButtonTagProcessor.PREFIX_COLOR_ACTIVATED,
@ -256,8 +250,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
}
private fun initBugReport() {
val preferences = sharedPreferences
if (!preferences.getBoolean(KEY_BUG_REPORTS, BuildConfig.DEBUG)) return
if (!sharedPreferences[bugReportsKey]) return
BugReporter.setImplementation(TwidereBugReporter())
BugReporter.init(this)
}
@ -305,9 +298,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
}
KEY_CONSUMER_KEY, KEY_CONSUMER_SECRET, KEY_API_URL_FORMAT, KEY_CREDENTIALS_TYPE,
KEY_SAME_OAUTH_SIGNING_URL, KEY_THUMBOR_ENABLED, KEY_THUMBOR_ADDRESS, KEY_THUMBOR_SECURITY_KEY -> {
val editor = preferences.edit()
editor.putLong(KEY_API_LAST_CHANGE, System.currentTimeMillis())
editor.apply()
preferences[apiLastChangeKey] = System.currentTimeMillis()
}
KEY_EMOJI_SUPPORT -> {
externalThemeManager.reloadEmojiPreferences()
@ -321,11 +312,11 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
}
KEY_PROFILE_IMAGE_STYLE -> {
Config.markChanged(this, VALUE_THEME_NAME_LIGHT, VALUE_THEME_NAME_DARK)
profileImageViewViewProcessor!!.setStyle(Utils.getProfileImageStyle(preferences.getString(key, null)))
profileImageViewViewProcessor.setStyle(Utils.getProfileImageStyle(preferences.getString(key, null)))
}
KEY_THEME_FONT_FAMILY -> {
Config.markChanged(this, VALUE_THEME_NAME_LIGHT, VALUE_THEME_NAME_DARK)
fontFamilyTagProcessor!!.setFontFamily(ThemeUtils.getThemeFontFamily(preferences))
fontFamilyTagProcessor.setFontFamily(ThemeUtils.getThemeFontFamily(preferences))
}
KEY_THEME_COLOR -> {
val themeColor = preferences.getInt(key, ContextCompat.getColor(this,

View File

@ -4,10 +4,9 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import org.mariotaku.kpreferences.*
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.KEY_NO_CLOSE_AFTER_TWEET_SENT
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_ATTACH_PRECISE_LOCATION
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_SETTINGS_WIZARD_COMPLETED
import org.mariotaku.twidere.extension.getNonEmptyString
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials
@ -37,6 +36,9 @@ val noCloseAfterTweetSentKey = KBooleanKey(KEY_NO_CLOSE_AFTER_TWEET_SENT, false)
val loadItemLimitKey = KIntKey(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT)
val defaultFeatureLastUpdated = KLongKey("default_feature_last_updated", -1)
val drawerTutorialCompleted = KBooleanKey(KEY_SETTINGS_WIZARD_COMPLETED, false)
val stopAutoRefreshWhenBatteryLowKey = KBooleanKey(KEY_STOP_AUTO_REFRESH_WHEN_BATTERY_LOW, true)
val apiLastChangeKey = KLongKey(KEY_API_LAST_CHANGE, -1)
val bugReportsKey = KBooleanKey(KEY_BUG_REPORTS, BuildConfig.DEBUG)
object defaultAPIConfigKey : KPreferenceKey<CustomAPIConfig> {
override fun contains(preferences: SharedPreferences): Boolean {

View File

@ -41,9 +41,7 @@ import android.os.Looper
import android.support.design.widget.NavigationView
import android.support.v4.app.LoaderManager.LoaderCallbacks
import android.support.v4.content.AsyncTaskLoader
import android.support.v4.content.ContextCompat
import android.support.v4.content.Loader
import android.support.v4.graphics.drawable.DrawableCompat
import android.support.v4.view.MenuItemCompat
import android.support.v4.view.ViewPager
import android.support.v7.view.SupportMenuInflater
@ -55,7 +53,10 @@ import android.view.View.OnClickListener
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import kotlinx.android.synthetic.main.header_drawer_account_selector.view.*
import org.mariotaku.ktextension.*
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.convert
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.setItemAvailability
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.*
@ -79,9 +80,9 @@ import java.util.*
class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<AccountsInfo>, OnSharedPreferenceChangeListener, OnClickListener, KeyboardShortcutCallback, NavigationView.OnNavigationItemSelectedListener {
private val mSystemWindowsInsets = Rect()
private val systemWindowsInsets = Rect()
private var accountsAdapter: AccountSelectorAdapter? = null
private lateinit var accountsAdapter: AccountSelectorAdapter
private val hasNextAccountIndicator by lazy { accountsHeader.hasNextAccountIndicator }
private val hasPrevAccountIndicator by lazy { accountsHeader.hasPrevAccountIndicator }
@ -113,7 +114,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
accountsSelector.adapter = accountsAdapter
accountsSelector.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
val adapter = accountsAdapter ?: return
val adapter = accountsAdapter
val pagePosition = position + positionOffset
val pageCount = adapter.count
val visiblePages = 1 / adapter.getPageWidth(position)
@ -145,7 +146,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
} else {
when (item.itemId) {
R.id.compose -> {
val account = accountsAdapter!!.selectedAccount ?: return@OnMenuItemClickListener true
val account = accountsAdapter.selectedAccount ?: return@OnMenuItemClickListener true
val composeIntent = Intent(INTENT_ACTION_COMPOSE)
composeIntent.setClass(activity, ComposeActivity::class.java)
composeIntent.putExtra(EXTRA_ACCOUNT_KEY, account.key)
@ -225,7 +226,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
override fun onClick(v: View) {
when (v.id) {
R.id.profileContainer -> {
val account = accountsAdapter!!.selectedAccount ?: return
val account = accountsAdapter.selectedAccount ?: return
val activity = activity
if (account.user != null) {
IntentUtils.openUserProfile(activity, account.user!!, null,
@ -262,18 +263,18 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
profileContainer.visibility = View.INVISIBLE
}
useStarsForLikes = preferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)
accountsAdapter!!.accounts = accounts
accountsAdapter.accounts = accounts
val defaultKey = preferences.getString(KEY_DEFAULT_ACCOUNT_KEY, null)?.convert(UserKey::valueOf)
?: accounts.firstOrNull { it.activated }?.key
val defaultAccount = accounts.firstOrNull { it.key.maybeEquals(defaultKey) }
accountsAdapter!!.selectedAccount = defaultAccount
accountsAdapter.selectedAccount = defaultAccount
if (accountActionProvider != null) {
accountActionProvider!!.isExclusive = false
accountActionProvider!!.accounts = accounts
}
updateAccountActions()
val currentAccount = accountsAdapter!!.selectedAccount
val currentAccount = accountsAdapter.selectedAccount
if (currentAccount != null) {
displayAccountBanner(currentAccount)
displayCurrentAccount(null)
@ -297,7 +298,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
}
override fun fitSystemWindows(insets: Rect) {
mSystemWindowsInsets.set(insets)
systemWindowsInsets.set(insets)
updateSystemWindowsInsets()
}
@ -316,11 +317,10 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
internal fun updateAccountActions() {
val activity = activity as HomeActivity
val tabs = activity.tabs
val account = accountsAdapter!!.selectedAccount ?: return
val account = accountsAdapter.selectedAccount ?: return
var hasDmTab = false
var hasInteractionsTab = false
for (tab in tabs) {
if (tab.type == null) continue
when (tab.type) {
CustomTabType.DIRECT_MESSAGES -> {
if (!hasDmTab) {
@ -338,17 +338,8 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
menu.setItemAvailability(R.id.interactions, !hasInteractionsTab)
menu.setItemAvailability(R.id.messages, !hasDmTab)
if (useStarsForLikes) {
menu.setMenuItemTitle(R.id.favorites, R.string.favorites)
val icon = ContextCompat.getDrawable(context, R.drawable.ic_action_star)
DrawableCompat.setTintList(icon, navigationView.itemIconTintList)
menu.setMenuItemIcon(R.id.favorites, icon)
} else {
menu.setMenuItemTitle(R.id.favorites, R.string.likes)
val icon = ContextCompat.getDrawable(context, R.drawable.ic_action_heart)
DrawableCompat.setTintList(icon, navigationView.itemIconTintList)
menu.setMenuItemIcon(R.id.favorites, icon)
}
menu.setItemAvailability(R.id.favorites, useStarsForLikes)
menu.setItemAvailability(R.id.likes, !useStarsForLikes)
var hasLists = false
var hasGroups = false
var hasPublicTimeline = false
@ -434,7 +425,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
val profileDrawable = profileImageView.drawable
clickedDrawable = clickedImageView.drawable
clickedColors = clickedImageView.borderColors
val oldSelectedAccount = accountsAdapter!!.selectedAccount ?: return
val oldSelectedAccount = accountsAdapter.selectedAccount ?: return
mediaLoader.displayDashboardProfileImage(clickedImageView,
oldSelectedAccount, profileDrawable)
clickedImageView.setBorderColors(*profileImageView.borderColors)
@ -460,7 +451,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
preferences.edit()
.putString(KEY_DEFAULT_ACCOUNT_KEY, account.key.toString())
.apply()
accountsAdapter!!.selectedAccount = account
accountsAdapter.selectedAccount = account
updateAccountActions()
displayCurrentAccount(clickedDrawable)
snapshotView.visibility = View.INVISIBLE
@ -493,7 +484,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
}
private fun displayCurrentAccount(profileImageSnapshot: Drawable?) {
val account = accountsAdapter!!.selectedAccount ?: return
val account = accountsAdapter.selectedAccount ?: return
accountProfileNameView.text = account.user.name
accountProfileScreenNameView.text = String.format("@%s", account.user.screen_name)
mediaLoader.displayDashboardProfileImage(accountProfileImageView, account,
@ -506,7 +497,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val account = accountsAdapter!!.selectedAccount ?: return false
val account = accountsAdapter.selectedAccount ?: return false
when (item.itemId) {
R.id.search -> {
val intent = Intent(activity, QuickSearchBarActivity::class.java)
@ -520,9 +511,9 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
composeIntent.putExtra(EXTRA_ACCOUNT_KEY, account.key)
startActivity(composeIntent)
}
R.id.favorites -> {
IntentUtils.openUserFavorites(activity, account.key,
account.key, account.user.screen_name)
R.id.likes, R.id.favorites -> {
IntentUtils.openUserFavorites(activity, account.key, account.key,
account.user.screen_name)
}
R.id.lists -> {
IntentUtils.openUserLists(activity, account.key,

View File

@ -0,0 +1,101 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.service
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.job.JobParameters
import android.app.job.JobService
import android.os.Build
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.stopAutoRefreshWhenBatteryLowKey
import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.task.GetActivitiesAboutMeTask
import org.mariotaku.twidere.task.GetHomeTimelineTask
import org.mariotaku.twidere.task.GetReceivedDirectMessagesTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
/**
* Created by mariotaku on 14/12/12.
*/
@SuppressLint("Registered")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class JobRefreshService : JobService() {
@Inject
internal lateinit var preferences: KPreferences
override fun onCreate() {
super.onCreate()
GeneralComponentHelper.build(this).inject(this)
}
override fun onStartJob(params: JobParameters): Boolean {
if (!Utils.isBatteryOkay(this) && preferences[stopAutoRefreshWhenBatteryLowKey]) {
// Low battery, don't refresh
return false
}
val task = createJobTask(params) ?: return false
task.callback = {
this.jobFinished(params, true)
}
TaskStarter.execute(task)
return true
}
override fun onStopJob(params: JobParameters): Boolean {
return false
}
internal fun createJobTask(params: JobParameters): AbstractTask<*, *, () -> Unit>? {
when (params.extras?.getString(EXTRA_ACTION)) {
BROADCAST_REFRESH_HOME_TIMELINE -> {
val task = GetHomeTimelineTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(this, Statuses.CONTENT_URI, accountKeys)
}
return task
}
BROADCAST_REFRESH_NOTIFICATIONS -> {
val task = GetActivitiesAboutMeTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(this, Activities.AboutMe.CONTENT_URI, accountKeys)
}
return task
}
BROADCAST_REFRESH_DIRECT_MESSAGES -> {
val task = GetReceivedDirectMessagesTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(this, DirectMessages.Inbox.CONTENT_URI, accountKeys)
}
return task
}
}
return null
}
}

View File

@ -0,0 +1,304 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.service
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.os.SystemClock
import android.util.Log
import edu.tsinghua.hotmobi.model.BatteryRecord
import edu.tsinghua.hotmobi.model.ScreenEvent
import org.apache.commons.lang3.math.NumberUtils
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.model.SimpleRefreshTaskParam
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.receiver.PowerStateReceiver
import org.mariotaku.twidere.util.AsyncTwitterWrapper
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
class RefreshService : Service(), Constants {
@Inject
internal lateinit var preferences: SharedPreferencesWrapper
@Inject
internal lateinit var twitterWrapper: AsyncTwitterWrapper
private lateinit var alarmManager: AlarmManager
private var pendingRefreshHomeTimelineIntent: PendingIntent? = null
private var pendingRefreshMentionsIntent: PendingIntent? = null
private var pendingRefreshDirectMessagesIntent: PendingIntent? = null
private var pendingRefreshTrendsIntent: PendingIntent? = null
private val mStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, String.format("Refresh service received action %s", action))
}
when (action) {
BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING -> {
rescheduleHomeTimelineRefreshing()
}
BROADCAST_RESCHEDULE_MENTIONS_REFRESHING -> {
rescheduleMentionsRefreshing()
}
BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING -> {
rescheduleDirectMessagesRefreshing()
}
BROADCAST_RESCHEDULE_TRENDS_REFRESHING -> {
rescheduleTrendsRefreshing()
}
BROADCAST_REFRESH_HOME_TIMELINE -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getHomeTimelineAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_NOTIFICATIONS -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getActivitiesAboutMeAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(context,
Activities.AboutMe.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_DIRECT_MESSAGES -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getReceivedDirectMessagesAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(context,
DirectMessages.Inbox.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_TRENDS -> {
if (isAutoRefreshAllowed) {
val prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context)).filter(AccountPreferences::isAutoRefreshEnabled)
getLocalTrends(prefs.filter(AccountPreferences::isAutoRefreshTrendsEnabled)
.map(AccountPreferences::getAccountKey).toTypedArray())
}
}
}
}
}
private val mPowerStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_BATTERY_CHANGED -> {
BatteryRecord.log(context, intent)
}
else -> {
BatteryRecord.log(context)
}
}
}
}
private val mScreenStateReceiver = object : BroadcastReceiver() {
var mPresentTime: Long = -1
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_SCREEN_ON -> {
ScreenEvent.log(context, ScreenEvent.Action.ON, presentDuration)
}
Intent.ACTION_SCREEN_OFF -> {
ScreenEvent.log(context, ScreenEvent.Action.OFF, presentDuration)
mPresentTime = -1
}
Intent.ACTION_USER_PRESENT -> {
mPresentTime = SystemClock.elapsedRealtime()
ScreenEvent.log(context, ScreenEvent.Action.PRESENT, -1)
}
}
}
private val presentDuration: Long
get() {
if (mPresentTime < 0) return -1
return SystemClock.elapsedRealtime() - mPresentTime
}
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
GeneralComponentHelper.build(this).inject(this)
alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
pendingRefreshHomeTimelineIntent = PendingIntent.getBroadcast(this, 0, Intent(
BROADCAST_REFRESH_HOME_TIMELINE), 0)
pendingRefreshMentionsIntent = PendingIntent.getBroadcast(this, 0, Intent(BROADCAST_REFRESH_NOTIFICATIONS), 0)
pendingRefreshDirectMessagesIntent = PendingIntent.getBroadcast(this, 0, Intent(
BROADCAST_REFRESH_DIRECT_MESSAGES), 0)
pendingRefreshTrendsIntent = PendingIntent.getBroadcast(this, 0, Intent(BROADCAST_REFRESH_TRENDS), 0)
val refreshFilter = IntentFilter(BROADCAST_NOTIFICATION_DELETED)
refreshFilter.addAction(BROADCAST_REFRESH_HOME_TIMELINE)
refreshFilter.addAction(BROADCAST_REFRESH_NOTIFICATIONS)
refreshFilter.addAction(BROADCAST_REFRESH_DIRECT_MESSAGES)
refreshFilter.addAction(BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING)
refreshFilter.addAction(BROADCAST_RESCHEDULE_MENTIONS_REFRESHING)
refreshFilter.addAction(BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING)
registerReceiver(mStateReceiver, refreshFilter)
val batteryFilter = IntentFilter()
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
batteryFilter.addAction(Intent.ACTION_BATTERY_OKAY)
batteryFilter.addAction(Intent.ACTION_BATTERY_LOW)
batteryFilter.addAction(Intent.ACTION_POWER_CONNECTED)
batteryFilter.addAction(Intent.ACTION_POWER_DISCONNECTED)
val screenFilter = IntentFilter()
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
screenFilter.addAction(Intent.ACTION_USER_PRESENT)
registerReceiver(mPowerStateReceiver, batteryFilter)
registerReceiver(mScreenStateReceiver, screenFilter)
PowerStateReceiver.serviceReceiverStarted = true
if (Utils.hasAutoRefreshAccounts(this)) {
startAutoRefresh()
} else {
stopSelf()
}
}
override fun onDestroy() {
PowerStateReceiver.serviceReceiverStarted = false
unregisterReceiver(mScreenStateReceiver)
unregisterReceiver(mPowerStateReceiver)
unregisterReceiver(mStateReceiver)
if (Utils.hasAutoRefreshAccounts(this)) {
// Auto refresh enabled, so I will try to start service after it was
// stopped.
startService(Intent(this, javaClass))
}
super.onDestroy()
}
protected val isAutoRefreshAllowed: Boolean
get() = Utils.isNetworkAvailable(this) && (Utils.isBatteryOkay(this) || !Utils.shouldStopAutoRefreshOnBatteryLow(this))
private fun getLocalTrends(accountIds: Array<UserKey>) {
val account_id = Utils.getDefaultAccountKey(this)
val woeid = preferences.getInt(KEY_LOCAL_TRENDS_WOEID, 1)
twitterWrapper.getLocalTrendsAsync(account_id, woeid)
}
private val refreshInterval: Long
get() {
val prefValue = NumberUtils.toInt(preferences.getString(KEY_REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL), -1)
return (Math.max(prefValue, 3) * 60 * 1000).toLong()
}
private fun rescheduleDirectMessagesRefreshing() {
alarmManager.cancel(pendingRefreshDirectMessagesIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshDirectMessagesIntent)
}
}
private fun rescheduleHomeTimelineRefreshing() {
alarmManager.cancel(pendingRefreshHomeTimelineIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshHomeTimelineIntent)
}
}
private fun rescheduleMentionsRefreshing() {
alarmManager.cancel(pendingRefreshMentionsIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshMentionsIntent)
}
}
private fun rescheduleTrendsRefreshing() {
alarmManager.cancel(pendingRefreshTrendsIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshTrendsIntent)
}
}
private fun startAutoRefresh(): Boolean {
stopAutoRefresh()
val refreshInterval = refreshInterval
if (refreshInterval <= 0) return false
rescheduleHomeTimelineRefreshing()
rescheduleMentionsRefreshing()
rescheduleDirectMessagesRefreshing()
rescheduleTrendsRefreshing()
return true
}
private fun stopAutoRefresh() {
alarmManager.cancel(pendingRefreshHomeTimelineIntent)
alarmManager.cancel(pendingRefreshMentionsIntent)
alarmManager.cancel(pendingRefreshDirectMessagesIntent)
alarmManager.cancel(pendingRefreshTrendsIntent)
}
class AutoRefreshTaskParam(
val context: Context,
val refreshable: (AccountPreferences) -> Boolean,
val getSinceIds: (Array<UserKey>) -> Array<String?>?
) : SimpleRefreshTaskParam() {
override fun getAccountKeysWorker(): Array<UserKey> {
val prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context)).filter(AccountPreferences::isAutoRefreshEnabled)
return prefs.filter(refreshable)
.map(AccountPreferences::getAccountKey).toTypedArray()
}
override val sinceIds: Array<String?>?
get() = getSinceIds(accountKeys)
}
}

View File

@ -40,9 +40,7 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
}
@Throws(MicroBlogException::class)
override fun getActivities(twitter: MicroBlog,
details: AccountDetails,
paging: Paging): ResponseList<Activity> {
override fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity> {
if (details.isOfficial(context)) {
return twitter.getActivitiesAboutMe(paging)
}

View File

@ -0,0 +1,132 @@
package org.mariotaku.twidere.task
import android.content.Context
import android.net.Uri
import android.util.Log
import com.squareup.otto.Bus
import org.apache.commons.lang3.math.NumberUtils
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.kpreferences.get
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.ErrorInfo
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.constant.loadItemLimitKey
import org.mariotaku.twidere.model.RefreshTaskParam
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.message.GetMessagesTaskEvent
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.content.ContentResolverUtils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import java.util.*
import javax.inject.Inject
/**
* Created by mariotaku on 16/2/14.
*/
abstract class GetDirectMessagesTask(protected val context: Context) : AbstractTask<RefreshTaskParam,
List<TwitterWrapper.MessageListResponse>, () -> Unit>(), Constants {
@Inject
protected lateinit var errorInfoStore: ErrorInfoStore
@Inject
protected lateinit var preferences: SharedPreferencesWrapper
@Inject
protected lateinit var bus: Bus
init {
GeneralComponentHelper.build(context).inject(this)
}
@Throws(MicroBlogException::class)
abstract fun getDirectMessages(twitter: MicroBlog, paging: Paging): ResponseList<DirectMessage>
protected abstract val databaseUri: Uri
protected abstract val isOutgoing: Boolean
public override fun doLongOperation(param: RefreshTaskParam): List<TwitterWrapper.MessageListResponse> {
val accountKeys = param.accountKeys
val sinceIds = param.sinceIds
val maxIds = param.maxIds
val result = ArrayList<TwitterWrapper.MessageListResponse>()
var idx = 0
val loadItemLimit = preferences[loadItemLimitKey]
for (accountKey in accountKeys) {
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: continue
try {
val paging = Paging()
paging.setCount(loadItemLimit)
var maxId: String? = null
var sinceId: String? = null
if (maxIds != null && maxIds[idx] != null) {
maxId = maxIds[idx]
paging.setMaxId(maxId)
}
if (sinceIds != null && sinceIds[idx] != null) {
sinceId = sinceIds[idx]
val sinceIdLong = NumberUtils.toLong(sinceId, -1)
//TODO handle non-twitter case
if (sinceIdLong != -1L) {
paging.sinceId((sinceIdLong - 1).toString())
} else {
paging.sinceId(sinceId)
}
if (maxIds == null || sinceIds[idx] == null) {
paging.setLatestResults(true)
}
}
val messages = getDirectMessages(twitter, paging)
result.add(TwitterWrapper.MessageListResponse(accountKey, maxId, sinceId, messages))
storeMessages(accountKey, messages, isOutgoing, true)
errorInfoStore.remove(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey)
} catch (e: MicroBlogException) {
if (e.errorCode == ErrorInfo.NO_DIRECT_MESSAGE_PERMISSION) {
errorInfoStore.put(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey,
ErrorInfoStore.CODE_NO_DM_PERMISSION)
} else if (e.isCausedByNetworkIssue) {
errorInfoStore.put(ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey,
ErrorInfoStore.CODE_NETWORK_ERROR)
}
if (BuildConfig.DEBUG) {
Log.w(TwidereConstants.LOGTAG, e)
}
result.add(TwitterWrapper.MessageListResponse(accountKey, e))
}
idx++
}
return result
}
private fun storeMessages(accountKey: UserKey, messages: List<DirectMessage>?, isOutgoing: Boolean, notify: Boolean): Boolean {
if (messages == null) return true
val uri = databaseUri
val valuesArray = messages.map { ContentValuesCreator.createDirectMessage(it, accountKey, isOutgoing) }
// Delete all rows conflicting before new data inserted.
// final Expression deleteWhere = Expression.and(Expression.equals(DirectMessages.ACCOUNT_ID, accountKey),
// Expression.in(new Column(DirectMessages.MESSAGE_ID), new RawItemArray(messageIds)));
// final Uri deleteUri = UriUtils.appendQueryParameters(uri, QUERY_PARAM_NOTIFY, false);
// mResolver.delete(deleteUri, deleteWhere.getSQL(), null);
// Insert previously fetched items.
val insertUri = UriUtils.appendQueryParameters(uri, TwidereConstants.QUERY_PARAM_NOTIFY, notify)
ContentResolverUtils.bulkInsert(context.contentResolver, insertUri, valuesArray)
return false
}
override fun beforeExecute() {
bus.post(GetMessagesTaskEvent(databaseUri, true, null))
}
override fun afterExecute(handler: (() -> Unit)?, result: List<TwitterWrapper.MessageListResponse>?) {
bus.post(GetMessagesTaskEvent(databaseUri, false, AsyncTwitterWrapper.getException(result)))
}
}

View File

@ -0,0 +1,24 @@
package org.mariotaku.twidere.task
import android.content.Context
import android.net.Uri
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Trends
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.CachedTrends
/**
* Created by mariotaku on 16/2/24.
*/
class GetLocalTrendsTask(context: Context, accountKey: UserKey, private val woeid: Int) : GetTrendsTask(context, accountKey) {
@Throws(MicroBlogException::class)
override fun getTrends(twitter: MicroBlog): List<Trends> {
return twitter.getLocationTrends(woeid)
}
override val contentUri: Uri = CachedTrends.Local.CONTENT_URI
}

View File

@ -0,0 +1,27 @@
package org.mariotaku.twidere.task
import android.content.Context
import android.net.Uri
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages
/**
* Created by mariotaku on 2016/12/14.
*/
class GetReceivedDirectMessagesTask(context: Context) : GetDirectMessagesTask(context) {
@Throws(MicroBlogException::class)
override fun getDirectMessages(twitter: MicroBlog, paging: Paging): ResponseList<DirectMessage> {
return twitter.getDirectMessages(paging)
}
override val isOutgoing: Boolean = false
override val databaseUri: Uri = DirectMessages.Inbox.CONTENT_URI
}

View File

@ -0,0 +1,43 @@
package org.mariotaku.twidere.task
import android.content.Context
import android.util.Log
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches
import org.mariotaku.twidere.util.ContentValuesCreator
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.content.ContentResolverUtils
/**
* Created by mariotaku on 16/2/13.
*/
class GetSavedSearchesTask(private val context: Context) : AbstractTask<Array<UserKey>, SingleResponse<Any>, Any>() {
public override fun doLongOperation(params: Array<UserKey>): SingleResponse<Any> {
val cr = context.contentResolver
for (accountKey in params) {
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: continue
try {
val searches = twitter.savedSearches
val values = ContentValuesCreator.createSavedSearches(searches,
accountKey)
val where = Expression.equalsArgs(SavedSearches.ACCOUNT_KEY)
val whereArgs = arrayOf(accountKey.toString())
cr.delete(SavedSearches.CONTENT_URI, where.sql, whereArgs)
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values)
} catch (e: MicroBlogException) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e)
}
}
}
return SingleResponse.getInstance()
}
}

View File

@ -0,0 +1,26 @@
package org.mariotaku.twidere.task
import android.content.Context
import android.net.Uri
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages
/**
* Created by mariotaku on 2016/12/14.
*/
class GetSentDirectMessagesTask(context: Context) : GetDirectMessagesTask(context) {
@Throws(MicroBlogException::class)
override fun getDirectMessages(twitter: MicroBlog, paging: Paging): ResponseList<DirectMessage> {
return twitter.getSentDirectMessages(paging)
}
override val isOutgoing: Boolean = true
override val databaseUri: Uri = DirectMessages.Outbox.CONTENT_URI
}

View File

@ -0,0 +1,77 @@
package org.mariotaku.twidere.task
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import com.squareup.otto.Bus
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Trends
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.message.TrendsRefreshedEvent
import org.mariotaku.twidere.provider.TwidereDataStore
import org.mariotaku.twidere.util.ContentValuesCreator
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.content.ContentResolverUtils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import java.util.*
import javax.inject.Inject
/**
* Created by mariotaku on 16/2/24.
*/
abstract class GetTrendsTask(private val context: Context, private val accountId: UserKey) : AbstractTask<Any, Any, Any>() {
@Inject
lateinit var bus: Bus
init {
GeneralComponentHelper.build(context).inject(this)
}
@Throws(MicroBlogException::class)
abstract fun getTrends(twitter: MicroBlog): List<Trends>
public override fun doLongOperation(param: Any): Any? {
val twitter = MicroBlogAPIFactory.getInstance(context, accountId) ?: return null
try {
val trends = getTrends(twitter)
storeTrends(context.contentResolver, contentUri, trends)
return null
} catch (e: MicroBlogException) {
return null
}
}
override fun afterExecute(handler: Any?, result: Any?) {
bus.post(TrendsRefreshedEvent())
}
protected abstract val contentUri: Uri
private fun storeTrends(cr: ContentResolver, uri: Uri, trendsList: List<Trends>) {
val hashtags = ArrayList<String>()
val hashtagValues = ArrayList<ContentValues>()
if (trendsList.isNotEmpty()) {
val valuesArray = ContentValuesCreator.createTrends(trendsList)
for (values in valuesArray) {
val hashtag = values.getAsString(TwidereDataStore.CachedTrends.NAME).replaceFirst("#".toRegex(), "")
if (hashtags.contains(hashtag)) {
continue
}
hashtags.add(hashtag)
val hashtagValue = ContentValues()
hashtagValue.put(TwidereDataStore.CachedHashtags.NAME, hashtag)
hashtagValues.add(hashtagValue)
}
cr.delete(uri, null, null)
ContentResolverUtils.bulkInsert(cr, uri, valuesArray)
ContentResolverUtils.bulkDelete(cr, TwidereDataStore.CachedHashtags.CONTENT_URI, TwidereDataStore.CachedHashtags.NAME, hashtags, null)
ContentResolverUtils.bulkInsert(cr, TwidereDataStore.CachedHashtags.CONTENT_URI,
hashtagValues.toTypedArray())
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.task
import android.app.Activity
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Environment
import android.widget.Toast
import org.mariotaku.twidere.R
import org.mariotaku.twidere.provider.CacheProvider
import java.io.File
/**
* Created by mariotaku on 15/12/28.
*/
class SaveMediaToGalleryTask(activity: Activity, source: Uri, destination: File, type: String) : ProgressSaveFileTask(activity, source, destination, CacheProvider.CacheFileTypeCallback(activity, type)) {
override fun onFileSaved(savedFile: File, mimeType: String?) {
val context = context ?: return
MediaScannerConnection.scanFile(context, arrayOf(savedFile.path),
arrayOf(mimeType), null)
Toast.makeText(context, R.string.saved_to_gallery, Toast.LENGTH_SHORT).show()
}
override fun onFileSaveFailed() {
val context = context ?: return
Toast.makeText(context, R.string.error_occurred, Toast.LENGTH_SHORT).show()
}
companion object {
fun create(activity: Activity, source: Uri,
@CacheProvider.Type type: String): SaveFileTask {
val pubDir: File
when (type) {
CacheProvider.Type.VIDEO -> {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
}
CacheProvider.Type.IMAGE -> {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
}
else -> {
pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
}
}
val saveDir = File(pubDir, "Twidere")
return SaveMediaToGalleryTask(activity, source, saveDir, type)
}
}
}

View File

@ -9,6 +9,7 @@ import android.support.annotation.UiThread
import android.util.Log
import com.squareup.otto.Bus
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Activity
@ -19,7 +20,7 @@ import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_LOAD_ITEM_LIMIT
import org.mariotaku.twidere.constant.loadItemLimitKey
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.RefreshTaskParam
@ -37,9 +38,9 @@ import javax.inject.Inject
/**
* Created by mariotaku on 16/1/4.
*/
abstract class GetActivitiesTask(protected val context: Context) : AbstractTask<RefreshTaskParam, Any, Any>(), Constants {
abstract class GetActivitiesTask(protected val context: Context) : AbstractTask<RefreshTaskParam, Any, () -> Unit>(), Constants {
@Inject
lateinit var preferences: SharedPreferencesWrapper
lateinit var preferences: KPreferences
@Inject
lateinit var bus: Bus
@Inject
@ -60,12 +61,11 @@ abstract class GetActivitiesTask(protected val context: Context) : AbstractTask<
val maxSortIds = param.maxSortIds
val sinceIds = param.sinceIds
val cr = context.contentResolver
val loadItemLimit = preferences.getInt(KEY_LOAD_ITEM_LIMIT)
val loadItemLimit = preferences[loadItemLimitKey]
var saveReadPosition = false
for (i in accountIds.indices) {
val accountKey = accountIds[i]
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri,
accountKey) <= 0
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0
val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey) ?: continue
val microBlog = credentials.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
val paging = Paging()
@ -123,7 +123,7 @@ abstract class GetActivitiesTask(protected val context: Context) : AbstractTask<
private fun storeActivities(cr: ContentResolver, loadItemLimit: Int, details: AccountDetails,
noItemsBefore: Boolean, activities: ResponseList<Activity>,
sinceId: String?, maxId: String?, notify: Boolean) {
val deleteBound = LongArray(2, { return@LongArray -1 })
val deleteBound = LongArray(2) { -1 }
val valuesList = ArrayList<ContentValues>()
var minIdx = -1
var minPositionKey: Long = -1
@ -190,15 +190,12 @@ abstract class GetActivitiesTask(protected val context: Context) : AbstractTask<
}
}
protected abstract fun saveReadPosition(accountKey: UserKey,
details: AccountDetails, twitter: MicroBlog)
protected abstract fun saveReadPosition(accountKey: UserKey, details: AccountDetails, twitter: MicroBlog)
@Throws(MicroBlogException::class)
protected abstract fun getActivities(twitter: MicroBlog,
details: AccountDetails,
paging: Paging): ResponseList<Activity>
protected abstract fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity>
public override fun afterExecute(handler: Any?, result: Any?) {
public override fun afterExecute(handler: (() -> Unit)?, result: Any?) {
context.contentResolver.notifyChange(contentUri, null)
bus.post(GetActivitiesTaskEvent(contentUri, false, null))
}

View File

@ -45,7 +45,8 @@ import javax.inject.Inject
/**
* Created by mariotaku on 16/1/2.
*/
abstract class GetStatusesTask(protected val context: Context) : AbstractTask<RefreshTaskParam, List<TwitterWrapper.StatusListResponse>, Any>(), Constants {
abstract class GetStatusesTask(protected val context: Context) :
AbstractTask<RefreshTaskParam, List<TwitterWrapper.StatusListResponse>, () -> Unit>(), Constants {
@Inject
lateinit var preferences: KPreferences
@Inject
@ -68,9 +69,10 @@ abstract class GetStatusesTask(protected val context: Context) : AbstractTask<Re
protected abstract val timelineType: String
public override fun afterExecute(handler: Any?, result: List<TwitterWrapper.StatusListResponse>?) {
public override fun afterExecute(handler: (() -> Unit)?, result: List<TwitterWrapper.StatusListResponse>?) {
context.contentResolver.notifyChange(contentUri, null)
bus.post(GetStatusesTaskEvent(contentUri, false, AsyncTwitterWrapper.getException(result)))
handler?.invoke()
}
override fun beforeExecute() {

View File

@ -36,6 +36,7 @@ import org.mariotaku.twidere.preference.KeyboardShortcutPreference
import org.mariotaku.twidere.provider.CacheProvider
import org.mariotaku.twidere.provider.TwidereDataProvider
import org.mariotaku.twidere.service.BackgroundOperationService
import org.mariotaku.twidere.service.JobRefreshService
import org.mariotaku.twidere.service.RefreshService
import org.mariotaku.twidere.task.*
import org.mariotaku.twidere.task.twitter.GetActivitiesTask
@ -110,6 +111,8 @@ interface GeneralComponent {
fun inject(task: GetStatusesTask)
fun inject(service: JobRefreshService)
fun inject(task: GetActivitiesTask)
fun inject(task: GetDirectMessagesTask)

View File

@ -17,6 +17,12 @@
android:title="@string/direct_messages"/>
<item
android:id="@id/favorites"
android:enabled="false"
android:icon="@drawable/ic_action_star"
android:title="@string/favorites"
android:visible="false"/>
<item
android:id="@id/likes"
android:icon="@drawable/ic_action_heart"
android:title="@string/likes"/>
<item

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="use_job_refresh_service">true</bool>
<bool name="use_legacy_refresh_service">false</bool>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_debug">false</bool>
<bool name="use_job_refresh_service">false</bool>
<bool name="use_legacy_refresh_service">true</bool>
</resources>

View File

@ -6,6 +6,5 @@
<string name="default_profile_image_style">round</string>
<integer name="default_tab_columns">1</integer>
<integer name="min_database_item_limit">50</integer>
<bool name="is_debug">false</bool>
</resources>

View File

@ -57,6 +57,7 @@
<item name="delete_from_list" type="id"/>
<item name="statuses" type="id"/>
<item name="favorites" type="id"/>
<item name="likes" type="id"/>
<item name="lists" type="id"/>
<item name="groups" type="id"/>
<item name="public_timeline" type="id"/>