implementing quick search bar

improved link text color
This commit is contained in:
Mariotaku Lee 2015-01-08 17:13:20 +08:00
parent deae5ed743
commit 281eaf6910
32 changed files with 531 additions and 290 deletions

View File

@ -93,7 +93,7 @@ dependencies {
compile 'com.android.support:palette-v7:21.0.3'
compile 'com.sothree.slidinguppanel:library:2.0.4'
compile 'it.sephiroth.android.library.imagezoom:imagezoom:2.1.1'
compile 'com.twitter:twitter-text:1.10.2'
compile 'com.twitter:twitter-text:1.9.9'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
compile 'org.apache.httpcomponents:httpmime:4.3.5'

View File

@ -170,7 +170,7 @@
android:value=".activity.support.HomeActivity"/>
</activity>
<activity
android:name=".activity.support.GlobalSearchBoxActivity"
android:name=".activity.support.QuickSearchBoxActivity"
android:launchMode="singleTop"
android:theme="@style/Theme.Blank.Dialog"
android:windowSoftInputMode="adjustResize"

View File

@ -28,7 +28,7 @@ package org.mariotaku.twidere;
public interface Constants extends TwidereConstants {
public static final String DATABASES_NAME = "twidere.sqlite";
public static final int DATABASES_VERSION = 76;
public static final int DATABASES_VERSION = 78;
public static final int MENU_GROUP_STATUS_EXTENSION = 10;
public static final int MENU_GROUP_COMPOSE_EXTENSION = 11;

View File

@ -159,6 +159,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
public static final int TABLE_ID_FILTERED_SOURCES = 33;
public static final int TABLE_ID_FILTERED_LINKS = 34;
public static final int TABLE_ID_TRENDS_LOCAL = 41;
public static final int TABLE_ID_SAVED_SEARCHES = 42;
public static final int TABLE_ID_DRAFTS = 51;
public static final int TABLE_ID_TABS = 52;
public static final int TABLE_ID_CACHED_USERS = 61;

View File

@ -209,7 +209,7 @@ public class FiltersActivity extends BaseSupportActivity implements TabListener,
if (resultCode != RESULT_OK || !(filter instanceof FilteredUsersFragment) || !data.hasExtra(EXTRA_USER))
return;
final ParcelableUser user = data.getParcelableExtra(EXTRA_USER);
final ContentValues values = ContentValuesCreator.makeFilteredUserContentValues(user);
final ContentValues values = ContentValuesCreator.createFilteredUser(user);
final ContentResolver resolver = getContentResolver();
resolver.delete(Filters.Users.CONTENT_URI, Expression.equals(Filters.Users.USER_ID, user.id).getSQL(), null);
resolver.insert(Filters.Users.CONTENT_URI, values);

View File

@ -543,7 +543,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
if (hasMedia()) {
builder.media(getMedia());
}
final ContentValues values = ContentValuesCreator.makeStatusDraftContentValues(builder.build());
final ContentValues values = ContentValuesCreator.createStatusDraft(builder.build());
mResolver.insert(Drafts.CONTENT_URI, values);
}

View File

@ -838,7 +838,7 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
final IHomeActionButton hab = (IHomeActionButton) mActionsButton;
hab.setIcon(icon);
hab.setTitle(title);
hab.setShowProgress(hasActivatedTask);
// hab.setShowProgress(hasActivatedTask);
}
if (mSmartBarProgress != null) {
mSmartBarProgress.setVisibility(hasActivatedTask ? View.VISIBLE : View.INVISIBLE);

View File

@ -21,34 +21,39 @@ package org.mariotaku.twidere.activity.support;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import java.util.List;
/**
* Created by mariotaku on 15/1/6.
*/
public class GlobalSearchBoxActivity extends BaseSupportActivity {
public class QuickSearchBoxActivity extends BaseSupportActivity implements OnClickListener, OnEditorActionListener {
private Spinner mAccountSpinner;
private EditText mSearchQuery;
private View mSearchSubmit;
@Override
public int getThemeResourceId() {
return ThemeUtils.getGlobalSearchThemeResource(this);
}
@Override
public void onContentChanged() {
super.onContentChanged();
mAccountSpinner = (Spinner) findViewById(R.id.account_spinner);
return ThemeUtils.getQuickSearchBoxThemeResource(this);
}
@Override
@ -67,6 +72,8 @@ public class GlobalSearchBoxActivity extends BaseSupportActivity {
mAccountSpinner.setSelection(index);
}
}
mSearchSubmit.setOnClickListener(this);
mSearchQuery.setOnEditorActionListener(this);
}
@Override
@ -75,6 +82,44 @@ public class GlobalSearchBoxActivity extends BaseSupportActivity {
updateWindowAttributes();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.search_submit: {
doSearch();
break;
}
}
}
@Override
public void onContentChanged() {
super.onContentChanged();
mAccountSpinner = (Spinner) findViewById(R.id.account_spinner);
mSearchQuery = (EditText) findViewById(R.id.search_query);
mSearchSubmit = findViewById(R.id.search_submit);
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (event == null) return false;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER: {
doSearch();
return true;
}
}
return false;
}
private void doSearch() {
final String query = ParseUtils.parseString(mSearchQuery.getText());
if (TextUtils.isEmpty(query)) return;
final long accountId = mAccountSpinner.getSelectedItemId();
Utils.openSearch(this, accountId, query);
finish();
}
private void updateWindowAttributes() {
final Window window = getWindow();
final WindowManager.LayoutParams attributes = window.getAttributes();
@ -82,4 +127,8 @@ public class GlobalSearchBoxActivity extends BaseSupportActivity {
window.setAttributes(attributes);
}
public static class SuggestionItem {
}
}

View File

@ -51,6 +51,7 @@ import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment;
import org.mariotaku.twidere.provider.TweetStore.Accounts;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator;
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator.AuthenticationException;
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator.AuthenticityTokenException;
@ -76,9 +77,7 @@ import twitter4j.http.HttpClientWrapper;
import twitter4j.http.HttpResponse;
import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeAccountContentValuesBasic;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeAccountContentValuesOAuth;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeAccountContentValuesTWIP;
import static org.mariotaku.twidere.util.ContentValuesCreator.createAccount;
import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds;
import static org.mariotaku.twidere.util.Utils.getNonEmptyString;
import static org.mariotaku.twidere.util.Utils.isUserLoggedIn;
@ -435,19 +434,19 @@ public class SignInActivity extends BaseSupportActivity implements TwitterConsta
final ContentValues values;
switch (result.auth_type) {
case Accounts.AUTH_TYPE_BASIC: {
values = makeAccountContentValuesBasic(result.conf, result.basic_username,
values = createAccount(result.conf, result.basic_username,
result.basic_password, result.user, result.color,
result.api_url_format, result.no_version_suffix);
break;
}
case Accounts.AUTH_TYPE_TWIP_O_MODE: {
values = makeAccountContentValuesTWIP(result.conf, result.user, result.color,
values = ContentValuesCreator.createAccount(result.conf, result.user, result.color,
result.api_url_format, result.no_version_suffix);
break;
}
case Accounts.AUTH_TYPE_OAUTH:
case Accounts.AUTH_TYPE_XAUTH: {
values = makeAccountContentValuesOAuth(result.conf, result.access_token,
values = ContentValuesCreator.createAccount(result.conf, result.access_token,
result.user, result.auth_type, result.color, result.api_url_format,
result.same_oauth_signing_url, result.no_version_suffix);
break;

View File

@ -65,7 +65,7 @@ import org.mariotaku.twidere.activity.iface.IThemedActivity;
import org.mariotaku.twidere.activity.support.AccountsManagerActivity;
import org.mariotaku.twidere.activity.support.ComposeActivity;
import org.mariotaku.twidere.activity.support.DraftsActivity;
import org.mariotaku.twidere.activity.support.GlobalSearchBoxActivity;
import org.mariotaku.twidere.activity.support.QuickSearchBoxActivity;
import org.mariotaku.twidere.activity.support.HomeActivity;
import org.mariotaku.twidere.activity.support.UserProfileEditorActivity;
import org.mariotaku.twidere.adapter.ArrayAdapter;
@ -194,7 +194,7 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
// } else {
// getActivity().onSearchRequested();
// }
final Intent intent = new Intent(getActivity(), GlobalSearchBoxActivity.class);
final Intent intent = new Intent(getActivity(), QuickSearchBoxActivity.class);
intent.putExtra(EXTRA_ACCOUNT_ID, account.account_id);
startActivity(intent);
closeAccountsDrawer();

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.fragment.support;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeFilteredUserContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.createFilteredUser;
import static org.mariotaku.twidere.util.UserColorNameUtils.getDisplayName;
import static org.mariotaku.twidere.util.content.ContentResolverUtils.bulkDelete;
import static org.mariotaku.twidere.util.content.ContentResolverUtils.bulkInsert;
@ -73,11 +73,11 @@ public class AddStatusFilterDialogFragment extends BaseSupportDialogFragment imp
if (value instanceof ParcelableUserMention) {
final ParcelableUserMention mention = (ParcelableUserMention) value;
user_ids.add(mention.id);
user_values.add(makeFilteredUserContentValues(mention));
user_values.add(createFilteredUser(mention));
} else if (value instanceof ParcelableStatus) {
final ParcelableStatus status = (ParcelableStatus) value;
user_ids.add(status.user_id);
user_values.add(ContentValuesCreator.makeFilteredUserContentValues(status));
user_values.add(ContentValuesCreator.createFilteredUser(status));
} else if (info.type == FilterItemInfo.FILTER_TYPE_KEYWORD) {
if (value != null) {
final String keyword = ParseUtils.parseString(value);

View File

@ -19,15 +19,11 @@
package org.mariotaku.twidere.fragment.support;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.message.TaskStateChangedEvent;
@ -57,6 +53,9 @@ public class HomeTimelineFragment extends CursorStatusesFragment {
public int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds) {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null) return -1;
if (maxIds == null) {
return twitter.refreshAll(accountIds);
}
return twitter.getHomeTimelineAsync(accountIds, maxIds, sinceIds);
}

View File

@ -84,6 +84,7 @@ public class SearchFragment extends BaseSupportFragment implements RefreshScroll
mViewPager.setAdapter(mAdapter);
mViewPager.setOffscreenPageLimit(2);
mPagerIndicator.setViewPager(mViewPager);
mPagerIndicator.setTabDisplayOption(TabPagerIndicator.LABEL);
if (activity instanceof IThemedActivity) {
mPagerIndicator.setStripColor(((IThemedActivity) activity).getCurrentThemeColor());
} else {

View File

@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
@ -58,6 +59,7 @@ import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TextView;
import org.apache.http.protocol.HTTP;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
@ -97,6 +99,8 @@ import org.mariotaku.twidere.view.holder.GapViewHolder;
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@ -340,7 +344,7 @@ public class StatusFragment extends BaseSupportFragment
if (status == null) return;
final Bundle args = new Bundle();
args.putLong(EXTRA_ACCOUNT_ID, status.account_id);
args.putLong(EXTRA_STATUS_ID, status.id);
args.putLong(EXTRA_STATUS_ID, status.retweet_id > 0 ? status.retweet_id : status.id);
args.putString(EXTRA_SCREEN_NAME, status.user_screen_name);
if (mRepliesLoaderInitialized) {
getLoaderManager().restartLoader(LOADER_ID_STATUS_REPLIES, args, mRepliesLoaderCallback);
@ -863,6 +867,13 @@ public class StatusFragment extends BaseSupportFragment
activityOption);
break;
}
case R.id.retweets_container: {
final ParcelableStatus status = adapter.getStatus(getPosition());
final Fragment fragment = adapter.getFragment();
final FragmentActivity activity = fragment.getActivity();
Utils.openStatusRetweeters(activity, status.account_id, status.id);
break;
}
}
}
@ -947,7 +958,20 @@ public class StatusFragment extends BaseSupportFragment
if (ParcelableCredentials.isOfficialCredentials(activity, account)) {
StatusTranslateDialogFragment.show(fragment.getFragmentManager(), status);
} else {
final Resources resources = fragment.getResources();
final Locale locale = resources.getConfiguration().locale;
try {
final String template = "http://translate.google.com/#%s|%s|%s";
final String sourceLang = "auto";
final String targetLang = URLEncoder.encode(locale.getLanguage(), HTTP.UTF_8);
final String text = URLEncoder.encode(status.text_unescaped, HTTP.UTF_8);
final Uri uri = Uri.parse(String.format(Locale.ROOT, template, sourceLang, targetLang, text));
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
fragment.startActivity(intent);
} catch (UnsupportedEncodingException ignore) {
}
}
break;
}
@ -1079,6 +1103,8 @@ public class StatusFragment extends BaseSupportFragment
mediaPreviewLoad.setOnClickListener(this);
profileContainer.setOnClickListener(this);
retweetsContainer.setOnClickListener(this);
final float defaultTextSize = adapter.getTextSize();
nameView.setTextSize(defaultTextSize * 1.25f);
textView.setTextSize(defaultTextSize * 1.25f);

View File

@ -794,7 +794,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
cr.delete(Filters.Users.CONTENT_URI, where.getSQL(), null);
showInfoMessage(getActivity(), R.string.message_user_unmuted, false);
} else {
cr.insert(Filters.Users.CONTENT_URI, ContentValuesCreator.makeFilteredUserContentValues(user));
cr.insert(Filters.Users.CONTENT_URI, ContentValuesCreator.createFilteredUser(user));
showInfoMessage(getActivity(), R.string.message_user_muted, false);
}
break;

View File

@ -38,7 +38,7 @@ import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeCachedUserContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.createCachedUser;
import static org.mariotaku.twidere.util.Utils.getTwitterInstance;
import static org.mariotaku.twidere.util.Utils.isMyAccount;
@ -92,7 +92,7 @@ public final class ParcelableUserLoader extends AsyncTaskLoader<SingleResponse<P
}
try {
final User user = TwitterWrapper.tryShowUser(twitter, mUserId, mScreenName);
final ContentValues cachedUserValues = makeCachedUserContentValues(user);
final ContentValues cachedUserValues = createCachedUser(user);
final long userId = user.getId();
final String cachedUserWhere = Expression.equals(CachedUsers.USER_ID, userId).getSQL();
resolver.delete(CachedUsers.CONTENT_URI, cachedUserWhere, null);

View File

@ -425,6 +425,26 @@ public interface TweetStore {
public static final String[] COLUMNS = new String[]{_ID, HOST, ADDRESS};
}
public static interface SavedSearches extends BaseColumns {
public static final String TABLE_NAME = "saved_searches";
public static final String CONTENT_PATH = TABLE_NAME;
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
public static final String ACCOUNT_ID = "account_id";
public static final String SEARCH_ID = "search_id";
public static final String QUERY = "query";
public static final String NAME = "name";
public static final String CREATED_AT = "created_at";
public static final String[] COLUMNS = new String[]{_ID, ACCOUNT_ID, SEARCH_ID, CREATED_AT,
QUERY, NAME};
public static final String[] TYPES = new String[]{TYPE_PRIMARY_KEY, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_TEXT, TYPE_TEXT};
}
public static interface Drafts extends BaseColumns {
public static final int ACTION_UPDATE_STATUS = 1;

View File

@ -88,8 +88,7 @@ import twitter4j.TwitterException;
import twitter4j.UserMentionEntity;
import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeDirectMessageContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeDirectMessageDraftContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.createMessageDraft;
import static org.mariotaku.twidere.util.Utils.getImagePathFromUri;
import static org.mariotaku.twidere.util.Utils.getImageUploadStatus;
import static org.mariotaku.twidere.util.Utils.getTwitterInstance;
@ -241,14 +240,14 @@ public class BackgroundOperationService extends IntentService implements Constan
final SingleResponse<ParcelableDirectMessage> result = sendDirectMessage(builder, accountId, recipientId, text,
imageUri);
if (result.getData() != null && result.getData().id > 0) {
final ContentValues values = makeDirectMessageContentValues(result.getData());
final ContentValues values = ContentValuesCreator.createDirectMessage(result.getData());
final String delete_where = DirectMessages.ACCOUNT_ID + " = " + accountId + " AND "
+ DirectMessages.MESSAGE_ID + " = " + result.getData().id;
mResolver.delete(DirectMessages.Outbox.CONTENT_URI, delete_where, null);
mResolver.insert(DirectMessages.Outbox.CONTENT_URI, values);
showOkMessage(R.string.direct_message_sent, false);
} else {
final ContentValues values = makeDirectMessageDraftContentValues(accountId, recipientId, text, imageUri);
final ContentValues values = createMessageDraft(accountId, recipientId, text, imageUri);
mResolver.insert(Drafts.CONTENT_URI, values);
showErrorMessage(R.string.action_sending_direct_message, result.getException(), true);
}
@ -323,7 +322,7 @@ public class BackgroundOperationService extends IntentService implements Constan
}
private void saveDrafts(final ParcelableStatusUpdate status, final List<Long> account_ids) {
final ContentValues values = ContentValuesCreator.makeStatusDraftContentValues(status,
final ContentValues values = ContentValuesCreator.createStatusDraft(status,
ArrayUtils.fromList(account_ids));
mResolver.insert(Drafts.CONTENT_URI, values);
final String title = getString(R.string.status_not_updated);

View File

@ -19,8 +19,8 @@
package org.mariotaku.twidere.task;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeCachedUserContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeStatusContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.createCachedUser;
import static org.mariotaku.twidere.util.ContentValuesCreator.createStatus;
import static org.mariotaku.twidere.util.content.ContentResolverUtils.bulkDelete;
import static org.mariotaku.twidere.util.content.ContentResolverUtils.bulkInsert;
@ -76,7 +76,7 @@ public class CacheUsersStatusesTask extends TwidereAsyncTask<Void, Void, Void> i
continue;
}
status_ids.add(status.getId());
cached_statuses_values.add(makeStatusContentValues(status, values.account_id));
cached_statuses_values.add(createStatus(status, values.account_id));
hashtags.addAll(extractor.extractHashtags(status.getText()));
final User user = status.getUser();
if (user != null && user.getId() > 0) {
@ -103,7 +103,7 @@ public class CacheUsersStatusesTask extends TwidereAsyncTask<Void, Void, Void> i
for (final User user : users) {
userIds.add(user.getId());
cachedUsersValues.add(makeCachedUserContentValues(user));
cachedUsersValues.add(createCachedUser(user));
}
bulkDelete(resolver, CachedUsers.CONTENT_URI, CachedUsers.USER_ID, userIds, null, false);
bulkInsert(resolver, CachedUsers.CONTENT_URI, cachedUsersValues);

View File

@ -25,6 +25,7 @@ import android.text.style.URLSpan;
import android.view.View;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener;
public class TwidereURLSpan extends URLSpan implements Constants {
@ -65,7 +66,7 @@ public class TwidereURLSpan extends URLSpan implements Constants {
ds.setUnderlineText(true);
}
if ((highlightStyle & VALUE_LINK_HIGHLIGHT_OPTION_CODE_HIGHLIGHT) != 0) {
ds.setColor(ds.linkColor);
ds.setColor(ThemeUtils.getOptimalLinkColor(ds.linkColor, ds.getColor()));
}
}
}

View File

@ -54,12 +54,14 @@ import org.mariotaku.twidere.provider.TweetStore.CachedTrends;
import org.mariotaku.twidere.provider.TweetStore.CachedUsers;
import org.mariotaku.twidere.provider.TweetStore.DirectMessages;
import org.mariotaku.twidere.provider.TweetStore.Mentions;
import org.mariotaku.twidere.provider.TweetStore.SavedSearches;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.service.BackgroundOperationService;
import org.mariotaku.twidere.task.CacheUsersStatusesTask;
import org.mariotaku.twidere.task.ManagedAsyncTask;
import org.mariotaku.twidere.task.TwidereAsyncTask;
import org.mariotaku.twidere.util.collection.LongSparseMap;
import org.mariotaku.twidere.util.content.ContentResolverUtils;
import org.mariotaku.twidere.util.message.FavoriteCreatedEvent;
import org.mariotaku.twidere.util.message.FavoriteDestroyedEvent;
import org.mariotaku.twidere.util.message.FriendshipUpdatedEvent;
@ -88,9 +90,9 @@ import twitter4j.UserList;
import twitter4j.http.HttpResponseCode;
import static org.mariotaku.twidere.provider.TweetStore.STATUSES_URIS;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeDirectMessageContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeStatusContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.makeTrendsContentValues;
import static org.mariotaku.twidere.util.ContentValuesCreator.createDirectMessage;
import static org.mariotaku.twidere.util.ContentValuesCreator.createStatus;
import static org.mariotaku.twidere.util.ContentValuesCreator.createTrends;
import static org.mariotaku.twidere.util.Utils.appendQueryParameters;
import static org.mariotaku.twidere.util.Utils.getActivatedAccountIds;
import static org.mariotaku.twidere.util.Utils.getDefaultAccountId;
@ -142,6 +144,14 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
return mAsyncTaskManager.add(task, true);
}
public Context getContext() {
return mContext;
}
public AsyncTaskManager getTaskManager() {
return mAsyncTaskManager;
}
public boolean isCreatingFavorite(final long accountId, final long statusId) {
return mCreatingFavoriteIds.has(accountId, statusId);
}
@ -388,6 +398,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
final int woeId = mPreferences.getInt(KEY_LOCAL_TRENDS_WOEID, 1);
getLocalTrendsAsync(accountId, woeId);
}
getSavedSearchesAsync(accountIds);
final long[] statusSinceIds = getNewestStatusIdsFromDatabase(mContext, Statuses.CONTENT_URI, accountIds);
return getHomeTimelineAsync(accountIds, null, statusSinceIds);
}
@ -2182,7 +2193,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
for (int i = 0, j = messages.size(); i < j; i++) {
final DirectMessage message = messages.get(i);
messageIds[i] = message.getId();
values_array[i] = makeDirectMessageContentValues(message, accountId, isOutgoing());
values_array[i] = createDirectMessage(message, accountId, isOutgoing());
}
// Delete all rows conflicting before new data inserted.
@ -2248,6 +2259,43 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
public int getSavedSearchesAsync(long[] accountIds) {
final GetSavedSearchesTask task = new GetSavedSearchesTask(this);
final Long[] ids = new Long[accountIds.length];
for (int i = 0, j = accountIds.length; i < j; i++) {
ids[i] = accountIds[i];
}
return mAsyncTaskManager.add(task, true, ids);
}
static class GetSavedSearchesTask extends ManagedAsyncTask<Long, Void, SingleResponse<Void>> {
private final Context mContext;
GetSavedSearchesTask(AsyncTwitterWrapper twitter) {
super(twitter.getContext(), twitter.getTaskManager());
this.mContext = twitter.getContext();
}
@Override
protected SingleResponse<Void> doInBackground(Long... params) {
final ContentResolver cr = mContext.getContentResolver();
for (long accountId : params) {
final Twitter twitter = Utils.getTwitterInstance(mContext, accountId, true);
try {
final ResponseList<SavedSearch> searches = twitter.getSavedSearches();
final ContentValues[] values = ContentValuesCreator.createSavedSearches(searches, accountId);
final Expression where = Expression.equals(SavedSearches.ACCOUNT_ID, accountId);
cr.delete(SavedSearches.CONTENT_URI, where.getSQL(), null);
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values);
} catch (TwitterException e) {
e.printStackTrace();
}
}
return SingleResponse.getInstance();
}
}
class StoreReceivedDirectMessagesTask extends StoreDirectMessagesTask {
public StoreReceivedDirectMessagesTask(final List<MessageListResponse> result, final boolean notify) {
@ -2303,7 +2351,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
final long[] statusIds = new long[statuses.size()];
for (int i = 0, j = statuses.size(); i < j; i++) {
final twitter4j.Status status = statuses.get(i);
values[i] = makeStatusContentValues(status, accountId);
values[i] = createStatus(status, accountId);
statusIds[i] = status.getId();
}
// Delete all rows conflicting before new data inserted.
@ -2366,7 +2414,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
final ArrayList<String> hashtags = new ArrayList<>();
final ArrayList<ContentValues> hashtagValues = new ArrayList<>();
if (messages != null && messages.size() > 0) {
final ContentValues[] valuesArray = makeTrendsContentValues(messages);
final ContentValues[] valuesArray = createTrends(messages);
for (final ContentValues values : valuesArray) {
final String hashtag = values.getAsString(CachedTrends.NAME).replaceFirst("#", "");
if (hashtags.contains(hashtag)) {

View File

@ -0,0 +1,45 @@
/*
* 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.util;
import android.graphics.Color;
/**
* Created by mariotaku on 15/1/8.
*/
public class ColorUtils {
public static void colorToYIQ(int color, int[] yiq) {
final int r = Color.red(color), g = Color.green(color), b = Color.blue(color);
yiq[0] = (r * 299 + g * 587 + b * 114) / 1000;
yiq[1] = (r * 596 - g * 275 - b * 321) / 1000;
yiq[2] = (r * 212 - g * 523 + b * 311) / 1000;
}
public static int YIQToColor(int alpha, int[] yiq) {
final int r = MathUtils.clamp((yiq[0] * 1000 + yiq[1] * 956 + yiq[2] * 620) / 1000, 0, 255);
final int g = MathUtils.clamp((yiq[0] * 1000 - yiq[1] * 272 - yiq[2] * 647) / 1000, 0, 255);
final int b = MathUtils.clamp((yiq[0] * 1000 - yiq[1] * 1108 + yiq[2] * 1705) / 1000, 0, 255);
return Color.argb(alpha, r, g, b);
}
}

View File

@ -40,6 +40,7 @@ import org.mariotaku.twidere.provider.TweetStore.CachedUsers;
import org.mariotaku.twidere.provider.TweetStore.DirectMessages;
import org.mariotaku.twidere.provider.TweetStore.Drafts;
import org.mariotaku.twidere.provider.TweetStore.Filters;
import org.mariotaku.twidere.provider.TweetStore.SavedSearches;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import java.util.ArrayList;
@ -47,6 +48,7 @@ import java.util.List;
import twitter4j.DirectMessage;
import twitter4j.GeoLocation;
import twitter4j.SavedSearch;
import twitter4j.Status;
import twitter4j.Trend;
import twitter4j.Trends;
@ -59,9 +61,10 @@ import static org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText;
public final class ContentValuesCreator implements TwidereConstants {
public static ContentValues makeAccountContentValuesBasic(final Configuration conf, final String basicUsername,
final String basicPassword, final User user, final int color, final String apiUrlFormat,
final boolean noVersionSuffix) {
public static ContentValues createAccount(final Configuration conf, final String basicUsername,
final String basicPassword, final User user,
final int color, final String apiUrlFormat,
final boolean noVersionSuffix) {
if (user == null || user.getId() <= 0) return null;
final ContentValues values = new ContentValues();
if (basicUsername == null || basicPassword == null) return null;
@ -80,9 +83,10 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeAccountContentValuesOAuth(final Configuration conf, final AccessToken accessToken,
final User user, final int authType, final int color, final String apiUrlFormat,
final boolean sameOAuthSigningUrl, final boolean noVersionSuffix) {
public static ContentValues createAccount(final Configuration conf, final AccessToken accessToken,
final User user, final int authType, final int color,
final String apiUrlFormat, final boolean sameOAuthSigningUrl,
final boolean noVersionSuffix) {
if (user == null || user.getId() <= 0 || accessToken == null || user.getId() != accessToken.getUserId())
return null;
final ContentValues values = new ContentValues();
@ -104,8 +108,8 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeAccountContentValuesTWIP(final Configuration conf, final User user,
final int color, final String apiUrlFormat, final boolean noVersionSuffix) {
public static ContentValues createAccount(final Configuration conf, final User user, final int color,
final String apiUrlFormat, final boolean noVersionSuffix) {
if (user == null || user.getId() <= 0) return null;
final ContentValues values = new ContentValues();
values.put(Accounts.AUTH_TYPE, Accounts.AUTH_TYPE_TWIP_O_MODE);
@ -121,7 +125,7 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeCachedUserContentValues(final User user) {
public static ContentValues createCachedUser(final User user) {
if (user == null || user.getId() <= 0) return null;
final String profile_image_url = ParseUtils.parseString(user.getProfileImageUrlHttps());
final String url = ParseUtils.parseString(user.getURL());
@ -155,15 +159,15 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeDirectMessageContentValues(final DirectMessage message, final long account_id,
final boolean is_outgoing) {
if (message == null || message.getId() <= 0) return null;
public static ContentValues createDirectMessage(final DirectMessage message, final long accountId,
final boolean isOutgoing) {
if (message == null) return null;
final ContentValues values = new ContentValues();
final User sender = message.getSender(), recipient = message.getRecipient();
if (sender == null || recipient == null) return null;
final String sender_profile_image_url = ParseUtils.parseString(sender.getProfileImageUrlHttps());
final String recipient_profile_image_url = ParseUtils.parseString(recipient.getProfileImageUrlHttps());
values.put(DirectMessages.ACCOUNT_ID, account_id);
values.put(DirectMessages.ACCOUNT_ID, accountId);
values.put(DirectMessages.MESSAGE_ID, message.getId());
values.put(DirectMessages.MESSAGE_TIMESTAMP, message.getCreatedAt().getTime());
values.put(DirectMessages.SENDER_ID, sender.getId());
@ -172,7 +176,7 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(DirectMessages.TEXT_HTML, text_html);
values.put(DirectMessages.TEXT_PLAIN, message.getText());
values.put(DirectMessages.TEXT_UNESCAPED, toPlainText(text_html));
values.put(DirectMessages.IS_OUTGOING, is_outgoing);
values.put(DirectMessages.IS_OUTGOING, isOutgoing);
values.put(DirectMessages.SENDER_NAME, sender.getName());
values.put(DirectMessages.SENDER_SCREEN_NAME, sender.getScreenName());
values.put(DirectMessages.RECIPIENT_NAME, recipient.getName());
@ -187,8 +191,8 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeDirectMessageContentValues(final ParcelableDirectMessage message) {
if (message == null || message.id <= 0) return null;
public static ContentValues createDirectMessage(final ParcelableDirectMessage message) {
if (message == null) return null;
final ContentValues values = new ContentValues();
values.put(DirectMessages.ACCOUNT_ID, message.account_id);
values.put(DirectMessages.MESSAGE_ID, message.id);
@ -211,8 +215,8 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeDirectMessageDraftContentValues(final long accountId, final long recipientId,
final String text, final String imageUri) {
public static ContentValues createMessageDraft(final long accountId, final long recipientId,
final String text, final String imageUri) {
final ContentValues values = new ContentValues();
values.put(Drafts.ACTION_TYPE, Drafts.ACTION_SEND_DIRECT_MESSAGE);
values.put(Drafts.TEXT, text);
@ -232,7 +236,7 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeFilteredUserContentValues(final ParcelableStatus status) {
public static ContentValues createFilteredUser(final ParcelableStatus status) {
if (status == null) return null;
final ContentValues values = new ContentValues();
values.put(Filters.Users.USER_ID, status.user_id);
@ -241,7 +245,7 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeFilteredUserContentValues(final ParcelableUser user) {
public static ContentValues createFilteredUser(final ParcelableUser user) {
if (user == null) return null;
final ContentValues values = new ContentValues();
values.put(Filters.Users.USER_ID, user.id);
@ -250,7 +254,7 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeFilteredUserContentValues(final ParcelableUserMention user) {
public static ContentValues createFilteredUser(final ParcelableUserMention user) {
if (user == null) return null;
final ContentValues values = new ContentValues();
values.put(Filters.Users.USER_ID, user.id);
@ -259,7 +263,7 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeStatusContentValues(final Status orig, final long accountId) {
public static ContentValues createStatus(final Status orig, final long accountId) {
if (orig == null || orig.getId() <= 0) return null;
final ContentValues values = new ContentValues();
values.put(Statuses.ACCOUNT_ID, accountId);
@ -329,12 +333,12 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues makeStatusDraftContentValues(final ParcelableStatusUpdate status) {
return makeStatusDraftContentValues(status, ParcelableAccount.getAccountIds(status.accounts));
public static ContentValues createStatusDraft(final ParcelableStatusUpdate status) {
return createStatusDraft(status, ParcelableAccount.getAccountIds(status.accounts));
}
public static ContentValues makeStatusDraftContentValues(final ParcelableStatusUpdate status,
final long[] accountIds) {
public static ContentValues createStatusDraft(final ParcelableStatusUpdate status,
final long[] accountIds) {
final ContentValues values = new ContentValues();
values.put(Drafts.ACTION_TYPE, Drafts.ACTION_UPDATE_STATUS);
values.put(Drafts.TEXT, status.text);
@ -349,13 +353,30 @@ public final class ContentValuesCreator implements TwidereConstants {
return values;
}
public static ContentValues[] makeTrendsContentValues(final List<Trends> trendsList) {
public static ContentValues createSavedSearch(final SavedSearch savedSearch, final long accountId) {
final ContentValues values = new ContentValues();
values.put(SavedSearches.ACCOUNT_ID, accountId);
values.put(SavedSearches.SEARCH_ID, savedSearch.getId());
values.put(SavedSearches.CREATED_AT, savedSearch.getCreatedAt().getTime());
values.put(SavedSearches.NAME, savedSearch.getName());
values.put(SavedSearches.QUERY, savedSearch.getQuery());
return values;
}
public static ContentValues[] createSavedSearches(final List<SavedSearch> savedSearches, long accountId) {
final ContentValues[] resultValuesArray = new ContentValues[savedSearches.size()];
for (int i = 0, j = savedSearches.size(); i < j; i++) {
resultValuesArray[i] = createSavedSearch(savedSearches.get(i), accountId);
}
return resultValuesArray;
}
public static ContentValues[] createTrends(final List<Trends> trendsList) {
if (trendsList == null) return new ContentValues[0];
final List<ContentValues> resultList = new ArrayList<>();
for (final Trends trends : trendsList) {
if (trends == null) {
continue;
}
final long timestamp = trends.getTrendAt().getTime();
for (final Trend trend : trends.getTrends()) {
final ContentValues values = new ContentValues();

View File

@ -141,11 +141,11 @@ public class MultiSelectEventHandler implements Constants, ActionMode.Callback,
if (object instanceof ParcelableStatus) {
final ParcelableStatus status = (ParcelableStatus) object;
userIds.add(status.user_id);
valuesList.add(ContentValuesCreator.makeFilteredUserContentValues(status));
valuesList.add(ContentValuesCreator.createFilteredUser(status));
} else if (object instanceof ParcelableUser) {
final ParcelableUser user = (ParcelableUser) object;
userIds.add(user.id);
valuesList.add(ContentValuesCreator.makeFilteredUserContentValues(user));
valuesList.add(ContentValuesCreator.createFilteredUser(user));
} else {
continue;
}

View File

@ -35,7 +35,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
@ -215,13 +214,27 @@ public class ThemeUtils implements Constants {
return view;
}
public static int getGlobalSearchThemeResource(final Context context) {
public static int getOptimalLinkColor(int linkColor, int color) {
final int[] yiq = new int[3];
ColorUtils.colorToYIQ(color, yiq);
final int y = yiq[0];
ColorUtils.colorToYIQ(linkColor, yiq);
if (y < 32 && yiq[0] < 192) {
return linkColor;
} else if (y > 192 && yiq[0] > 32) {
return linkColor;
}
yiq[0] = yiq[0] + (y - yiq[0]) / 2;
return ColorUtils.YIQToColor(Color.alpha(linkColor), yiq);
}
public static int getQuickSearchBoxThemeResource(final Context context) {
return getGlobalSearchThemeResource(getThemeNameOption(context));
}
public static int getGlobalSearchThemeResource(final String name) {
if (VALUE_THEME_NAME_DARK.equals(name)) return R.style.Theme_Twidere_Dark_GlobalSearch;
return R.style.Theme_Twidere_Light_GlobalSearch;
if (VALUE_THEME_NAME_DARK.equals(name)) return R.style.Theme_Twidere_Dark_QuickSearchBox;
return R.style.Theme_Twidere_Light_QuickSearchBox;
}
private static void applyColorTintForView(View view, int tintColor) {
@ -244,9 +257,6 @@ public class ThemeUtils implements Constants {
final ColorStateList tintList = ColorStateList.valueOf(tintColor);
final CompoundButton compoundButton = (CompoundButton) view;
ViewAccessor.setButtonTintList(compoundButton, tintList);
} else if (view instanceof TextView) {
final TextView textView = (TextView) view;
textView.setLinkTextColor(tintColor);
}
}

View File

@ -181,6 +181,7 @@ import org.mariotaku.twidere.provider.TweetStore.Mentions;
import org.mariotaku.twidere.provider.TweetStore.Notifications;
import org.mariotaku.twidere.provider.TweetStore.Permissions;
import org.mariotaku.twidere.provider.TweetStore.Preferences;
import org.mariotaku.twidere.provider.TweetStore.SavedSearches;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.provider.TweetStore.Tabs;
import org.mariotaku.twidere.provider.TweetStore.UnreadCounts;
@ -305,6 +306,8 @@ public final class Utils implements Constants, TwitterConstants {
CONTENT_PROVIDER_URI_MATCHER.addURI(TweetStore.AUTHORITY, DNS.CONTENT_PATH + "/*", VIRTUAL_TABLE_ID_DNS);
CONTENT_PROVIDER_URI_MATCHER.addURI(TweetStore.AUTHORITY, CachedImages.CONTENT_PATH,
VIRTUAL_TABLE_ID_CACHED_IMAGES);
CONTENT_PROVIDER_URI_MATCHER.addURI(TweetStore.AUTHORITY, SavedSearches.CONTENT_PATH,
TABLE_ID_SAVED_SEARCHES);
CONTENT_PROVIDER_URI_MATCHER.addURI(TweetStore.AUTHORITY, CacheFiles.CONTENT_PATH + "/*",
VIRTUAL_TABLE_ID_CACHE_FILES);
CONTENT_PROVIDER_URI_MATCHER.addURI(TweetStore.AUTHORITY, Preferences.CONTENT_PATH,
@ -1136,7 +1139,7 @@ public final class Utils implements Constants, TwitterConstants {
final ContentResolver resolver = context.getContentResolver();
resolver.delete(CachedStatuses.CONTENT_URI, where, null);
resolver.insert(CachedStatuses.CONTENT_URI,
ContentValuesCreator.makeStatusContentValues(status, accountId));
ContentValuesCreator.createStatus(status, accountId));
return new ParcelableStatus(status, accountId, false);
}
@ -2245,6 +2248,8 @@ public final class Utils implements Constants, TwitterConstants {
return CachedUsers.TABLE_NAME;
case TABLE_ID_CACHED_HASHTAGS:
return CachedHashtags.TABLE_NAME;
case TABLE_ID_SAVED_SEARCHES:
return SavedSearches.TABLE_NAME;
default:
return null;
}
@ -3046,15 +3051,15 @@ public final class Utils implements Constants, TwitterConstants {
activity.startActivity(intent);
}
public static void openStatusRetweeters(final Activity activity, final long accountId, final long statusId) {
if (activity == null) return;
public static void openStatusRetweeters(final Context context, final long accountId, final long statusId) {
if (context == null) return;
final Uri.Builder builder = new Uri.Builder();
builder.scheme(SCHEME_TWIDERE);
builder.authority(AUTHORITY_STATUS_RETWEETERS);
builder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
builder.appendQueryParameter(QUERY_PARAM_STATUS_ID, String.valueOf(statusId));
final Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
activity.startActivity(intent);
context.startActivity(intent);
}
public static void openTweetSearch(final Context context, final long accountId, final String query) {
@ -3945,7 +3950,7 @@ public final class Utils implements Constants, TwitterConstants {
*/
public static int getContrastYIQ(int color, int threshold, int colorDark, int colorLight) {
final int r = Color.red(color), g = Color.green(color), b = Color.blue(color);
int yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
final int yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return (yiq >= threshold) ? colorDark : colorLight;
}

View File

@ -39,6 +39,7 @@ import org.mariotaku.twidere.provider.TweetStore.DirectMessages;
import org.mariotaku.twidere.provider.TweetStore.Drafts;
import org.mariotaku.twidere.provider.TweetStore.Filters;
import org.mariotaku.twidere.provider.TweetStore.Mentions;
import org.mariotaku.twidere.provider.TweetStore.SavedSearches;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.provider.TweetStore.Tabs;
import org.mariotaku.twidere.util.TwidereQueryBuilder.ConversationsEntryQueryBuilder;
@ -79,6 +80,7 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C
db.execSQL(createTable(CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS, CachedTrends.Local.TYPES,
true));
db.execSQL(createTable(Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, true));
db.execSQL(createTable(SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true));
db.execSQL(createDirectMessagesView().getSQL());
db.execSQL(createDirectMessageConversationEntriesView().getSQL());
db.setTransactionSuccessful();
@ -135,21 +137,26 @@ public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements C
safeUpgrade(db, Mentions.TABLE_NAME, Mentions.COLUMNS, Mentions.TYPES, true, null);
safeUpgrade(db, Drafts.TABLE_NAME, Drafts.COLUMNS, Drafts.TYPES, false, draftsAlias);
safeUpgrade(db, CachedUsers.TABLE_NAME, CachedUsers.COLUMNS, CachedUsers.TYPES, true, null);
safeUpgrade(db, CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES, false, null);
safeUpgrade(db, CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES, false, null);
safeUpgrade(db, Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES, oldVersion < 49, null);
safeUpgrade(db, Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES, oldVersion < 49,
filtersAlias);
safeUpgrade(db, Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES, oldVersion < 49,
filtersAlias);
safeUpgrade(db, Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES, oldVersion < 49,
filtersAlias);
safeUpgrade(db, DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS, DirectMessages.Inbox.TYPES,
true, null);
safeUpgrade(db, DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS, DirectMessages.Outbox.TYPES,
true, null);
safeUpgrade(db, CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS, CachedTrends.Local.TYPES, true, null);
safeUpgrade(db, CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES,
false, null);
safeUpgrade(db, CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES,
false, null);
safeUpgrade(db, Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES,
oldVersion < 49, null);
safeUpgrade(db, Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES,
oldVersion < 49, filtersAlias);
safeUpgrade(db, Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES,
oldVersion < 49, filtersAlias);
safeUpgrade(db, Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES,
oldVersion < 49, filtersAlias);
safeUpgrade(db, DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS,
DirectMessages.Inbox.TYPES, true, null);
safeUpgrade(db, DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS,
DirectMessages.Outbox.TYPES, true, null);
safeUpgrade(db, CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS,
CachedTrends.Local.TYPES, true, null);
safeUpgrade(db, Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, false, null);
safeUpgrade(db, SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true, null);
db.beginTransaction();
db.execSQL(createDirectMessagesView().getSQL());
db.execSQL(createDirectMessageConversationEntriesView().getSQL());

View File

@ -19,8 +19,6 @@
package org.mariotaku.twidere.util.net;
import static android.text.TextUtils.isEmpty;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
@ -33,184 +31,181 @@ import org.xbill.DNS.AAAARecord;
import org.xbill.DNS.ARecord;
import org.xbill.DNS.CNAMERecord;
import org.xbill.DNS.DClass;
import org.xbill.DNS.Message;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Name;
import org.xbill.DNS.Record;
import org.xbill.DNS.Resolver;
import org.xbill.DNS.Section;
import org.xbill.DNS.SimpleResolver;
import org.xbill.DNS.Type;
import twitter4j.http.HostAddressResolver;
import java.io.IOException;
import java.net.InetAddress;
import java.util.LinkedHashMap;
import twitter4j.http.HostAddressResolver;
import static android.text.TextUtils.isEmpty;
public class TwidereHostAddressResolver implements Constants, HostAddressResolver {
private static final String RESOLVER_LOGTAG = "TwidereHostAddressResolver";
private static final String RESOLVER_LOGTAG = "TwidereHostAddressResolver";
private static final String DEFAULT_DNS_SERVER_ADDRESS = "8.8.8.8";
private static final String DEFAULT_DNS_SERVER_ADDRESS = "8.8.8.8";
private final SharedPreferences mHostMapping, mPreferences;
private final HostsFileParser mSystemHosts = new HostsFileParser();
private final HostCache mHostCache = new HostCache(512);
private final boolean mLocalMappingOnly;
private final String mDnsAddress;
private final SharedPreferences mHostMapping, mPreferences;
private final HostsFileParser mSystemHosts = new HostsFileParser();
private final HostCache mHostCache = new HostCache(512);
private final boolean mLocalMappingOnly;
private final String mDnsAddress;
private Resolver mDns;
private Resolver mDns;
public TwidereHostAddressResolver(final Context context) {
this(context, false);
}
public TwidereHostAddressResolver(final Context context) {
this(context, false);
}
public TwidereHostAddressResolver(final Context context, final boolean local_only) {
mHostMapping = context.getSharedPreferences(HOST_MAPPING_PREFERENCES_NAME, Context.MODE_PRIVATE);
mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final String address = mPreferences.getString(KEY_DNS_SERVER, DEFAULT_DNS_SERVER_ADDRESS);
mDnsAddress = isValidIpAddress(address) ? address : DEFAULT_DNS_SERVER_ADDRESS;
mLocalMappingOnly = local_only;
}
public TwidereHostAddressResolver(final Context context, final boolean localOnly) {
mHostMapping = context.getSharedPreferences(HOST_MAPPING_PREFERENCES_NAME, Context.MODE_PRIVATE);
mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final String address = mPreferences.getString(KEY_DNS_SERVER, DEFAULT_DNS_SERVER_ADDRESS);
mDnsAddress = isValidIpAddress(address) ? address : DEFAULT_DNS_SERVER_ADDRESS;
mLocalMappingOnly = localOnly;
}
public synchronized void removeCachedHost(final String host) {
mHostCache.remove(host);
}
public synchronized void removeCachedHost(final String host) {
mHostCache.remove(host);
}
@Override
public String resolve(final String host) throws IOException {
if (host == null) return null;
if (isValidIpAddress(host)) return null;
// First, I'll try to load address cached.
if (mHostCache.containsKey(host)) {
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got cached address " + mHostCache.get(host) + " for host " + host);
}
return mHostCache.get(host);
}
// Then I'll try to load from custom host mapping.
// Stupid way to find top domain, but really fast.
if (mHostMapping.contains(host)) {
final String mappedAddr = mHostMapping.getString(host, null);
mHostCache.put(host, mappedAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + mappedAddr + " for host " + host);
}
return mappedAddr;
}
mSystemHosts.reloadIfNeeded();
if (mSystemHosts.contains(host)) {
final String hostAddr = mSystemHosts.getAddress(host);
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + hostAddr + " for host " + host);
}
return hostAddr;
}
final String customMappedHost = findHost(host);
if (customMappedHost != null) {
mHostCache.put(host, customMappedHost);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host);
}
return customMappedHost;
}
initDns();
// Use TCP DNS Query if enabled.
if (mDns != null && mPreferences.getBoolean(KEY_TCP_DNS_QUERY, false)) {
final Name name = new Name(host);
final Record query = Record.newRecord(name, Type.A, DClass.IN);
if (query == null) return host;
final Message response;
try {
response = mDns.send(Message.newQuery(query));
} catch (final IOException e) {
return host;
}
if (response == null) return host;
final Record[] records = response.getSectionArray(Section.ANSWER);
if (records == null || records.length < 1) throw new IOException("Could not find " + host);
String hostAddr = null;
// Test each IP address resolved.
for (final Record record : records) {
if (record instanceof ARecord) {
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
if (ipv4Addr.isReachable(300)) {
hostAddr = ipv4Addr.getHostAddress();
}
} else if (record instanceof AAAARecord) {
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
if (ipv6Addr.isReachable(300)) {
hostAddr = ipv6Addr.getHostAddress();
}
}
if (hostAddr != null) {
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Resolved address " + hostAddr + " for host " + host);
}
return hostAddr;
}
}
// No address is reachable, but I believe the IP is correct.
final Record record = records[0];
if (record instanceof ARecord) {
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
hostAddr = ipv4Addr.getHostAddress();
} else if (record instanceof AAAARecord) {
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
hostAddr = ipv6Addr.getHostAddress();
} else if (record instanceof CNAMERecord) return resolve(((CNAMERecord) record).getTarget().toString());
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Resolved address " + hostAddr + " for host " + host);
}
return hostAddr;
}
if (Utils.isDebugBuild()) {
Log.w(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host");
}
return host;
}
@Override
public String resolve(final String host) throws IOException {
if (host == null) return null;
if (isValidIpAddress(host)) return null;
// First, I'll try to load address cached.
if (mHostCache.containsKey(host)) {
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got cached address " + mHostCache.get(host) + " for host " + host);
}
return mHostCache.get(host);
}
// Then I'll try to load from custom host mapping.
// Stupid way to find top domain, but really fast.
if (mHostMapping.contains(host)) {
final String mappedAddr = mHostMapping.getString(host, null);
mHostCache.put(host, mappedAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + mappedAddr + " for host " + host);
}
return mappedAddr;
}
mSystemHosts.reloadIfNeeded();
if (mSystemHosts.contains(host)) {
final String hostAddr = mSystemHosts.getAddress(host);
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + hostAddr + " for host " + host);
}
return hostAddr;
}
final String customMappedHost = findHost(host);
if (customMappedHost != null) {
mHostCache.put(host, customMappedHost);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host);
}
return customMappedHost;
}
initDns();
// Use TCP DNS Query if enabled.
if (mDns != null && mPreferences.getBoolean(KEY_TCP_DNS_QUERY, false)) {
final Lookup lookup = new Lookup(new Name(host), Type.A, DClass.IN);
final Record[] records;
lookup.setResolver(mDns);
lookup.run();
final int result = lookup.getResult();
if (result != Lookup.SUCCESSFUL) {
throw new IOException("Could not find " + host);
}
records = lookup.getAnswers();
String hostAddr = null;
// Test each IP address resolved.
for (final Record record : records) {
if (record instanceof ARecord) {
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
if (ipv4Addr.isReachable(300)) {
hostAddr = ipv4Addr.getHostAddress();
}
} else if (record instanceof AAAARecord) {
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
if (ipv6Addr.isReachable(300)) {
hostAddr = ipv6Addr.getHostAddress();
}
}
if (hostAddr != null) {
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Resolved address " + hostAddr + " for host " + host);
}
return hostAddr;
}
}
// No address is reachable, but I believe the IP is correct.
final Record record = records[0];
if (record instanceof ARecord) {
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
hostAddr = ipv4Addr.getHostAddress();
} else if (record instanceof AAAARecord) {
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
hostAddr = ipv6Addr.getHostAddress();
} else if (record instanceof CNAMERecord)
return resolve(((CNAMERecord) record).getTarget().toString());
mHostCache.put(host, hostAddr);
if (Utils.isDebugBuild()) {
Log.d(RESOLVER_LOGTAG, "Resolved address " + hostAddr + " for host " + host);
}
return hostAddr;
}
if (Utils.isDebugBuild()) {
Log.w(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host");
}
return host;
}
private String findHost(final String host) {
for (final String rule : mHostMapping.getAll().keySet()) {
if (hostMatches(host, rule)) return mHostMapping.getString(rule, null);
}
return null;
}
private String findHost(final String host) {
for (final String rule : mHostMapping.getAll().keySet()) {
if (hostMatches(host, rule)) return mHostMapping.getString(rule, null);
}
return null;
}
private void initDns() throws IOException {
if (mDns != null) return;
mDns = mLocalMappingOnly ? null : new SimpleResolver(mDnsAddress);
if (mDns != null) {
mDns.setTCP(true);
}
}
private void initDns() throws IOException {
if (mDns != null) return;
mDns = new SimpleResolver(mDnsAddress);
mDns.setTCP(true);
}
private static boolean hostMatches(final String host, final String rule) {
if (rule == null || host == null) return false;
if (rule.startsWith(".")) return host.toLowerCase().endsWith(rule.toLowerCase());
return host.equalsIgnoreCase(rule);
}
private static boolean hostMatches(final String host, final String rule) {
if (rule == null || host == null) return false;
if (rule.startsWith(".")) return host.toLowerCase().endsWith(rule.toLowerCase());
return host.equalsIgnoreCase(rule);
}
private static boolean isValidIpAddress(final String address) {
if (isEmpty(address)) return false;
return InetAddressUtilsHC4.isIPv4Address(address) || InetAddressUtilsHC4.isIPv6Address(address);
}
private static boolean isValidIpAddress(final String address) {
if (isEmpty(address)) return false;
return InetAddressUtilsHC4.isIPv4Address(address) || InetAddressUtilsHC4.isIPv6Address(address);
}
private static class HostCache extends LinkedHashMap<String, String> {
private static class HostCache extends LinkedHashMap<String, String> {
private static final long serialVersionUID = -9216545511009449147L;
private static final long serialVersionUID = -9216545511009449147L;
HostCache(final int initialCapacity) {
super(initialCapacity);
}
HostCache(final int initialCapacity) {
super(initialCapacity);
}
@Override
public String put(final String key, final String value) {
if (value == null) return value;
return super.put(key, value);
}
}
@Override
public String put(final String key, final String value) {
if (value == null) return null;
return super.put(key, value);
}
}
}

View File

@ -44,6 +44,6 @@ public class ThemedTextView extends TextView implements IThemedView {
@Override
public void setThemeTintColor(ColorStateList color) {
setLinkTextColor(color);
setLinkTextColor(ThemeUtils.getOptimalLinkColor(color.getDefaultColor(), getCurrentTextColor()));
}
}

View File

@ -23,12 +23,12 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_normal">
android:layout_height="match_parent"
android:padding="@dimen/element_spacing_normal">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="@dimen/element_size_normal"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
@ -38,30 +38,47 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
android:layout_height="wrap_content"
android:orientation="vertical">
<Spinner
android:id="@+id/account_spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
tools:listitem="@layout/spinner_item_account_icon"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/element_size_normal"
android:orientation="horizontal">
<EditText
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="@string/search_hint"/>
<Spinner
android:id="@+id/account_spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
tools:listitem="@layout/spinner_item_account_icon"/>
<org.mariotaku.twidere.view.ActionIconButton
android:layout_width="@dimen/element_size_normal"
android:layout_height="match_parent"
android:background="?android:actionBarItemBackground"
android:layout_weight="0"
android:src="@drawable/ic_action_search"/>
<EditText
android:id="@+id/search_query"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="@string/search_hint"
android:imeActionLabel="@android:string/search_go"
android:imeOptions="actionSearch"
android:inputType="text"
android:singleLine="true"/>
<org.mariotaku.twidere.view.ActionIconButton
android:id="@+id/search_submit"
android:layout_width="@dimen/element_size_normal"
android:layout_height="match_parent"
android:layout_weight="0"
android:background="?android:actionBarItemBackground"
android:src="@drawable/ic_action_search"/>
</LinearLayout>
<ListView
android:id="@+id/suggestions_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -83,8 +83,6 @@
android:layout_height="@dimen/icon_size_profile_type"
android:layout_alignBottom="@id/profile_image"
android:layout_alignRight="@id/profile_image"
android:layout_marginBottom="-8dp"
android:layout_marginRight="-8dp"
android:scaleType="fitCenter"/>
<LinearLayout

View File

@ -178,14 +178,14 @@
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
<style name="Theme.Twidere.Light.GlobalSearch" parent="Theme.Twidere.Light.Dialog">
<style name="Theme.Twidere.Light.QuickSearchBox" parent="Theme.Twidere.Light.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">100%</item>
</style>
<style name="Theme.Twidere.Dark.GlobalSearch" parent="Theme.Twidere.Dark.Dialog">
<style name="Theme.Twidere.Dark.QuickSearchBox" parent="Theme.Twidere.Dark.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>