rewrite custom tab part

This commit is contained in:
Mariotaku Lee 2016-11-30 15:18:43 +08:00
parent ab67bd67fd
commit 5c0488d2cf
47 changed files with 1805 additions and 1374 deletions

View File

@ -39,8 +39,6 @@ public interface IntentConstants {
String INTENT_ACTION_PICK_DIRECTORY = INTENT_PACKAGE_PREFIX + "PICK_DIRECTORY";
String INTENT_ACTION_EXTENSIONS = INTENT_PACKAGE_PREFIX + "EXTENSIONS";
String INTENT_ACTION_CUSTOM_TABS = INTENT_PACKAGE_PREFIX + "CUSTOM_TABS";
String INTENT_ACTION_ADD_TAB = INTENT_PACKAGE_PREFIX + "ADD_TAB";
String INTENT_ACTION_EDIT_TAB = INTENT_PACKAGE_PREFIX + "EDIT_TAB";
String INTENT_ACTION_SERVICE_COMMAND = INTENT_PACKAGE_PREFIX + "SERVICE_COMMAND";
String INTENT_ACTION_REQUEST_PERMISSIONS = INTENT_PACKAGE_PREFIX + "REQUEST_PERMISSIONS";
String INTENT_ACTION_SELECT_USER_LIST = INTENT_PACKAGE_PREFIX + "SELECT_USER_LIST";
@ -220,4 +218,5 @@ public interface IntentConstants {
String EXTRA_SHOULD_INIT_LOADER = "should_init_loader";
String EXTRA_START_INTENT = "start_intent";
String EXTRA_SELECT_ONLY_ITEM = "select_only_item";
String EXTRA_OBJECT = "object";
}

View File

@ -341,19 +341,6 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".activity.CustomTabEditorActivity"
android:exported="false"
android:label="@string/add_tab"
android:theme="@style/Theme.Twidere.Dialog"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="org.mariotaku.twidere.ADD_TAB"/>
<action android:name="org.mariotaku.twidere.EDIT_TAB"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".activity.CreateComposeShortcutActivity"
android:label="@string/compose"

View File

@ -1,566 +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.activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.rengwuxian.materialedittext.MaterialEditText;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
import org.mariotaku.twidere.adapter.ArrayAdapter;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.fragment.BaseDialogFragment;
import org.mariotaku.twidere.model.CustomTabConfiguration;
import org.mariotaku.twidere.model.CustomTabConfiguration.ExtraConfiguration;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.tab.argument.TabArguments;
import org.mariotaku.twidere.model.tab.argument.TextQueryArguments;
import org.mariotaku.twidere.model.tab.argument.UserArguments;
import org.mariotaku.twidere.model.tab.argument.UserListArguments;
import org.mariotaku.twidere.model.tab.extra.TabExtras;
import org.mariotaku.twidere.util.CustomTabUtils;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.InternalParseUtils;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.ThemeUtils;
import java.text.Collator;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import static org.mariotaku.twidere.util.CustomTabUtils.findTabIconKey;
import static org.mariotaku.twidere.util.CustomTabUtils.getIconMap;
import static org.mariotaku.twidere.util.CustomTabUtils.getTabConfiguration;
import static org.mariotaku.twidere.util.CustomTabUtils.getTabTypeName;
public class CustomTabEditorActivity extends BaseActivity implements OnClickListener {
private SharedPreferences mPreferences;
private AccountsSpinnerAdapter mAccountsAdapter;
private CustomTabIconsAdapter mTabIconsAdapter;
private View mAccountContainer, mSecondaryFieldContainer, mExtraConfigurationsContainer;
private Spinner mTabIconSpinner, mAccountSpinner;
private MaterialEditText mEditTabName;
private TextView mSecondaryFieldLabel;
private LinearLayout mExtraConfigurationsContent;
private long mTabId;
private CustomTabConfiguration mTabConfiguration;
private Object mSecondaryFieldValue;
private final Bundle mExtrasBundle = new Bundle();
private final View.OnClickListener mOnExtraConfigurationClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof ExtraConfiguration) {
final ExtraConfiguration conf = (ExtraConfiguration) tag;
switch (conf.getType()) {
case BOOLEAN: {
final CheckBox checkBox = (CheckBox) v.findViewById(android.R.id.checkbox);
checkBox.toggle();
mExtrasBundle.putBoolean(conf.getKey(), checkBox.isChecked());
break;
}
default: {
break;
}
}
}
}
};
@Override
public void onClick(final View v) {
final CustomTabConfiguration conf = mTabConfiguration;
final Object value = mSecondaryFieldValue;
final UserKey accountKey = getAccountKey();
switch (v.getId()) {
case R.id.secondary_field: {
if (conf == null) return;
switch (conf.getSecondaryFieldType()) {
case CustomTabConfiguration.FIELD_TYPE_USER: {
final Intent intent = new Intent(this, UserListSelectorActivity.class);
intent.setAction(INTENT_ACTION_SELECT_USER);
intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey);
startActivityForResult(intent, REQUEST_SELECT_USER);
break;
}
case CustomTabConfiguration.FIELD_TYPE_USER_LIST: {
final Intent intent = new Intent(this, UserListSelectorActivity.class);
intent.setAction(INTENT_ACTION_SELECT_USER_LIST);
intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey);
startActivityForResult(intent, REQUEST_SELECT_USER_LIST);
break;
}
case CustomTabConfiguration.FIELD_TYPE_TEXT: {
final int title = conf.getSecondaryFieldTitle();
SecondaryFieldEditTextDialogFragment.show(this, ParseUtils.parseString(value),
getString(title > 0 ? title : R.string.content));
break;
}
}
break;
}
case R.id.save: {
if (isEditMode()) {
if (mTabId < 0) return;
final Intent data = new Intent();
data.putExtra(EXTRA_NAME, getTabName());
data.putExtra(EXTRA_ICON, getTabIconKey());
data.putExtra(EXTRA_ID, mTabId);
data.putExtra(EXTRA_EXTRAS, InternalParseUtils.bundleToJSON(mExtrasBundle));
setResult(RESULT_OK, data);
finish();
} else {
if (conf == null) return;
final boolean accountIdRequired = conf.getAccountRequirement() == CustomTabConfiguration.ACCOUNT_REQUIRED;
final boolean noAccountId = conf.getAccountRequirement() == CustomTabConfiguration.ACCOUNT_NONE;
final boolean secondaryFieldRequired = conf.getSecondaryFieldType() != CustomTabConfiguration.FIELD_TYPE_NONE;
final boolean accountIdInvalid = accountKey == null;
final boolean secondaryFieldInvalid = mSecondaryFieldValue == null;
if (accountIdRequired && accountIdInvalid) {
Toast.makeText(this, R.string.no_account_selected, Toast.LENGTH_SHORT).show();
return;
} else if (secondaryFieldRequired && secondaryFieldInvalid) {
Toast.makeText(this, getString(R.string.name_not_set, mSecondaryFieldLabel.getText()), Toast.LENGTH_SHORT).show();
return;
}
final Intent data = new Intent();
final TabArguments args = CustomTabUtils.newTabArguments(getTabType());
if (args != null) {
if (!noAccountId) {
if (accountKey == null) {
args.setAccountKeys(null);
} else {
args.setAccountKeys(new UserKey[]{accountKey});
}
}
if (secondaryFieldRequired) {
addSecondaryFieldValueToArguments(args);
}
}
data.putExtra(EXTRA_TYPE, getTabType());
data.putExtra(EXTRA_NAME, getTabName());
data.putExtra(EXTRA_ICON, getTabIconKey());
data.putExtra(EXTRA_ARGUMENTS, JsonSerializer.serialize(args));
data.putExtra(EXTRA_EXTRAS, InternalParseUtils.bundleToJSON(mExtrasBundle));
setResult(RESULT_OK, data);
finish();
}
break;
}
}
}
protected String getTabName() {
return ParseUtils.parseString(mEditTabName.getText());
}
@Override
public void onContentChanged() {
super.onContentChanged();
mAccountContainer = findViewById(R.id.account_container);
mSecondaryFieldContainer = findViewById(R.id.secondary_field_container);
mExtraConfigurationsContainer = findViewById(R.id.extra_configurations_container);
mEditTabName = (MaterialEditText) findViewById(R.id.tab_name);
mSecondaryFieldLabel = (TextView) findViewById(R.id.secondary_field_label);
mTabIconSpinner = (Spinner) findViewById(R.id.tab_icon_spinner);
mAccountSpinner = (Spinner) findViewById(R.id.account_spinner);
mExtraConfigurationsContent = (LinearLayout) findViewById(R.id.extra_configurations_content);
}
public void setExtraFieldSelectText(final View view, final int text) {
final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final TextView text2 = (TextView) view.findViewById(android.R.id.text2);
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
text1.setVisibility(View.VISIBLE);
text2.setVisibility(View.GONE);
icon.setVisibility(View.GONE);
text1.setText(text);
}
public void setExtraFieldView(final View view, final Object value) {
final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final TextView text2 = (TextView) view.findViewById(android.R.id.text2);
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
final boolean displayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true);
final boolean displayName = mPreferences.getBoolean(KEY_NAME_FIRST, true);
text1.setVisibility(View.VISIBLE);
text2.setVisibility(View.VISIBLE);
icon.setVisibility(displayProfileImage ? View.VISIBLE : View.GONE);
if (value instanceof ParcelableUser) {
final ParcelableUser user = (ParcelableUser) value;
text1.setText(userColorNameManager.getUserNickname(user.key, user.name));
text2.setText(String.format("@%s", user.screen_name));
if (displayProfileImage) {
mediaLoader.displayProfileImage(icon, user);
}
} else if (value instanceof ParcelableUserList) {
final ParcelableUserList userList = (ParcelableUserList) value;
final String createdBy = userColorNameManager.getDisplayName(userList, displayName);
text1.setText(userList.name);
text2.setText(getString(R.string.created_by, createdBy));
if (displayProfileImage) {
mediaLoader.displayProfileImage(icon, userList.user_profile_image_url);
}
} else if (value instanceof CharSequence) {
text2.setVisibility(View.GONE);
icon.setVisibility(View.GONE);
text1.setText((CharSequence) value);
}
}
public void setSecondaryFieldValue(final Object value) {
mSecondaryFieldValue = value;
setExtraFieldView(mSecondaryFieldContainer, value);
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (resultCode != RESULT_OK) return;
switch (requestCode) {
case REQUEST_SELECT_USER: {
setSecondaryFieldValue(data.getParcelableExtra(EXTRA_USER));
break;
}
case REQUEST_SELECT_USER_LIST: {
setSecondaryFieldValue(data.getParcelableExtra(EXTRA_USER_LIST));
break;
}
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
final Intent intent = getIntent();
final String type = getTabType();
final CustomTabConfiguration conf = getTabConfiguration(type);
if (type == null || conf == null) {
finish();
return;
}
mTabId = intent.getLongExtra(EXTRA_ID, -1);
setTitle(isEditMode() ? R.string.edit_tab : R.string.add_tab);
setContentView(R.layout.activity_custom_tab_editor);
mEditTabName.setFloatingLabelText(getTabTypeName(this, type));
mTabIconsAdapter = new CustomTabIconsAdapter(this);
mTabIconsAdapter.setData(getIconMap());
mAccountsAdapter = new AccountsSpinnerAdapter(this);
mAccountSpinner.setAdapter(mAccountsAdapter);
mTabIconSpinner.setAdapter(mTabIconsAdapter);
final String iconKey;
if (savedInstanceState != null) {
mExtrasBundle.putAll(savedInstanceState.getBundle(EXTRA_EXTRAS));
}
if (!isEditMode()) {
mTabConfiguration = conf;
final boolean hasSecondaryField = conf.getSecondaryFieldType() != CustomTabConfiguration.FIELD_TYPE_NONE;
final boolean accountIdNone = conf.getAccountRequirement() == CustomTabConfiguration.ACCOUNT_NONE;
mAccountContainer.setVisibility(accountIdNone ? View.GONE : View.VISIBLE);
mSecondaryFieldContainer.setVisibility(hasSecondaryField ? View.VISIBLE : View.GONE);
final boolean accountIdRequired = conf.getAccountRequirement() == CustomTabConfiguration.ACCOUNT_REQUIRED;
if (!accountIdRequired) {
mAccountsAdapter.add(ParcelableAccount.dummyCredentials());
}
final boolean officialKeyOnly = intent.getBooleanExtra(EXTRA_OFFICIAL_KEY_ONLY, false);
mAccountsAdapter.addAll(DataStoreUtils.getCredentialsList(this, false, officialKeyOnly));
mAccountsAdapter.setDummyItemText(R.string.activated_accounts);
switch (conf.getSecondaryFieldType()) {
case CustomTabConfiguration.FIELD_TYPE_USER: {
mSecondaryFieldLabel.setText(R.string.user);
setExtraFieldSelectText(mSecondaryFieldContainer, R.string.select_user);
break;
}
case CustomTabConfiguration.FIELD_TYPE_USER_LIST: {
mSecondaryFieldLabel.setText(R.string.user_list);
setExtraFieldSelectText(mSecondaryFieldContainer, R.string.select_user_list);
break;
}
case CustomTabConfiguration.FIELD_TYPE_TEXT: {
mSecondaryFieldLabel.setText(R.string.content);
setExtraFieldSelectText(mSecondaryFieldContainer, R.string.input_text);
break;
}
}
if (conf.getSecondaryFieldTitle() != 0) {
mSecondaryFieldLabel.setText(conf.getSecondaryFieldTitle());
}
iconKey = findTabIconKey(conf.getDefaultIcon());
mEditTabName.setText(mTabConfiguration.getDefaultTitle());
} else {
if (mTabId < 0) {
finish();
return;
}
mAccountContainer.setVisibility(View.GONE);
mSecondaryFieldContainer.setVisibility(View.GONE);
iconKey = intent.getStringExtra(EXTRA_ICON);
mEditTabName.setText(intent.getStringExtra(EXTRA_NAME));
if (savedInstanceState == null && intent.hasExtra(EXTRA_EXTRAS)) {
TabExtras extras = CustomTabUtils.parseTabExtras(type, intent.getStringExtra(EXTRA_EXTRAS));
if (extras != null) {
extras.copyToBundle(mExtrasBundle);
}
}
}
final int selection = mTabIconsAdapter.getIconPosition(iconKey);
mTabIconSpinner.setSelection(selection > 0 ? selection : 0);
final LayoutInflater inflater = getLayoutInflater();
final ExtraConfiguration[] extraConfigurations = conf.getExtraConfigurations();
if (extraConfigurations == null || extraConfigurations.length == 0) {
mExtraConfigurationsContainer.setVisibility(View.GONE);
} else {
mExtraConfigurationsContainer.setVisibility(View.VISIBLE);
for (final ExtraConfiguration config : extraConfigurations) {
final boolean hasCheckBox = config.getType() == ExtraConfiguration.Type.BOOLEAN;
final View view = inflater.inflate(R.layout.list_item_extra_config, mExtraConfigurationsContent, false);
final TextView title = (TextView) view.findViewById(android.R.id.title);
final CheckBox checkBox = (CheckBox) view.findViewById(android.R.id.checkbox);
title.setText(config.getTitleRes());
checkBox.setVisibility(hasCheckBox ? View.VISIBLE : View.GONE);
if (hasCheckBox) {
checkBox.setChecked(mExtrasBundle.getBoolean(config.getKey(), config.defaultBoolean()));
}
view.setTag(config);
view.setOnClickListener(mOnExtraConfigurationClickListener);
mExtraConfigurationsContent.addView(view);
}
}
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBundle(EXTRA_EXTRAS, mExtrasBundle);
}
@CustomTabType
public String getTabType() {
//noinspection ResourceType
return getIntent().getStringExtra(EXTRA_TYPE);
}
private void addFieldValueToArguments(final Object value, final TabArguments args) {
final CustomTabConfiguration conf = mTabConfiguration;
if (value == null || args == null || conf == null) return;
if (value instanceof ParcelableUser && args instanceof UserArguments) {
final ParcelableUser user = (ParcelableUser) value;
((UserArguments) args).setUserKey(user.key);
} else if (value instanceof ParcelableUserList && args instanceof UserListArguments) {
final ParcelableUserList userList = (ParcelableUserList) value;
((UserListArguments) args).setListId(userList.id);
} else if (value instanceof CharSequence) {
if (args instanceof TextQueryArguments) {
((TextQueryArguments) args).setQuery(value.toString());
}
}
}
private void addSecondaryFieldValueToArguments(final TabArguments args) {
final Object value = mSecondaryFieldValue;
addFieldValueToArguments(value, args);
}
private UserKey getAccountKey() {
final int pos = mAccountSpinner.getSelectedItemPosition();
if (mAccountSpinner.getCount() > pos && pos >= 0) {
ParcelableCredentials credentials = mAccountsAdapter.getItem(pos);
return credentials.account_key;
}
return null;
}
private String getTabIconKey() {
final int pos = mTabIconSpinner.getSelectedItemPosition();
if (mTabIconsAdapter.getCount() > pos && pos >= 0)
return mTabIconsAdapter.getItem(pos).getKey();
return null;
}
private boolean isEditMode() {
return INTENT_ACTION_EDIT_TAB.equals(getIntent().getAction());
}
public static class SecondaryFieldEditTextDialogFragment extends BaseDialogFragment implements
DialogInterface.OnClickListener {
private static final String FRAGMENT_TAG_EDIT_SECONDARY_FIELD = "edit_secondary_field";
private EditText mEditText;
@Override
public void onClick(final DialogInterface dialog, final int which) {
final FragmentActivity activity = getActivity();
if (activity instanceof CustomTabEditorActivity) {
((CustomTabEditorActivity) activity)
.setSecondaryFieldValue(ParseUtils.parseString(mEditText.getText()));
}
}
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = getArguments();
final Context context = getActivity();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(args.getString(EXTRA_TITLE));
builder.setPositiveButton(android.R.string.ok, this);
builder.setNegativeButton(android.R.string.cancel, null);
final FrameLayout view = new FrameLayout(getActivity());
mEditText = new EditText(getActivity());
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
lp.leftMargin = lp.topMargin = lp.bottomMargin = lp.rightMargin = getResources().getDimensionPixelSize(
R.dimen.element_spacing_normal);
view.addView(mEditText, lp);
builder.setView(view);
mEditText.setText(args.getString(EXTRA_TEXT));
return builder.create();
}
public static SecondaryFieldEditTextDialogFragment show(final FragmentActivity activity, final String text,
final String title) {
final SecondaryFieldEditTextDialogFragment f = new SecondaryFieldEditTextDialogFragment();
final Bundle args = new Bundle();
args.putString(EXTRA_TEXT, text);
args.putString(EXTRA_TITLE, title);
f.setArguments(args);
f.show(activity.getSupportFragmentManager(), FRAGMENT_TAG_EDIT_SECONDARY_FIELD);
return f;
}
}
static class CustomTabIconsAdapter extends ArrayAdapter<Entry<String, Integer>> {
private final Resources mResources;
private final int mIconColor;
public CustomTabIconsAdapter(final Context context) {
super(context, R.layout.spinner_item_custom_tab_icon);
setDropDownViewResource(R.layout.list_item_two_line_small);
mResources = context.getResources();
mIconColor = ThemeUtils.getThemeForegroundColor(context);
}
@Override
public View getDropDownView(final int position, final View convertView, final ViewGroup parent) {
final View view = super.getDropDownView(position, convertView, parent);
view.findViewById(android.R.id.text2).setVisibility(View.GONE);
final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final Entry<String, Integer> item = getItem(position);
final int value = item.getValue();
if (value > 0) {
final String key = item.getKey();
final String name = key.substring(0, 1).toUpperCase(Locale.US) + key.substring(1, key.length());
text1.setText(name);
} else {
text1.setText(R.string.customize);
}
bindIconView(position, item, view);
return view;
}
public int getIconPosition(final String key) {
if (key == null) return -1;
for (int i = 0, j = getCount(); i < j; i++) {
if (key.equals(getItem(i).getKey())) return i;
}
return -1;
}
@Override
public View getView(final int position, @Nullable final View convertView, final ViewGroup parent) {
final View view = super.getView(position, convertView, parent);
bindIconView(position, getItem(position), view);
return view;
}
public void setData(final Map<String, Integer> map) {
clear();
if (map == null) return;
addAll(map.entrySet());
sort(new LocationComparator(mResources));
}
private void bindIconView(final int position, final Entry<String, Integer> item, final View view) {
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
icon.setColorFilter(mIconColor, Mode.SRC_ATOP);
final int value = item.getValue();
if (value > 0) {
icon.setImageResource(item.getValue());
} else {
icon.setImageDrawable(null);
}
}
private static class LocationComparator implements Comparator<Entry<String, Integer>> {
private final Collator mCollator;
LocationComparator(final Resources res) {
mCollator = Collator.getInstance(res.getConfiguration().locale);
}
@Override
public int compare(final Entry<String, Integer> object1, final Entry<String, Integer> object2) {
if (object1.getValue() <= 0) return Integer.MAX_VALUE;
if (object2.getValue() <= 0) return Integer.MIN_VALUE;
return mCollator.compare(object1.getKey(), object2.getKey());
}
}
}
}

View File

@ -1,190 +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.model;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.TwidereConstants;
import java.util.Comparator;
import java.util.Map.Entry;
public final class CustomTabConfiguration {
public static final int FIELD_TYPE_NONE = 0;
public static final int FIELD_TYPE_USER = 1;
public static final int FIELD_TYPE_USER_LIST = 2;
public static final int FIELD_TYPE_TEXT = 3;
public static final int ACCOUNT_NONE = 0;
public static final int ACCOUNT_REQUIRED = 1;
public static final int ACCOUNT_OPTIONAL = 2;
@StringRes
private final int title;
@DrawableRes
private final int icon;
private final int secondaryFieldType;
private final int secondaryFieldTitle;
private final int sortPosition;
private final int accountRequirement;
private final Class<? extends Fragment> cls;
private final String secondaryFieldTextKey;
private final boolean singleTab;
private final ExtraConfiguration[] extraConfigurations;
public CustomTabConfiguration(final Class<? extends Fragment> cls, final int title, final int icon,
final int accountRequirement, final int secondaryFieldType, final int sortPosition,
final boolean singleTab, final ExtraConfiguration... extraConfigurations) {
this(cls, title, icon, accountRequirement, secondaryFieldType, 0, TwidereConstants.EXTRA_TEXT, sortPosition,
singleTab, extraConfigurations);
}
public CustomTabConfiguration(final Class<? extends Fragment> cls, final int title, final int icon,
final int accountRequirement, final int secondaryFieldType, final int sortPosition,
final ExtraConfiguration... extraConfigurations) {
this(cls, title, icon, accountRequirement, secondaryFieldType, 0, TwidereConstants.EXTRA_TEXT, sortPosition,
false, extraConfigurations);
}
public CustomTabConfiguration(final Class<? extends Fragment> cls, final int title, final int icon,
final int accountRequirement, final int secondaryFieldType, final int secondaryFieldTitle,
final String secondaryFieldTextKey, final int sortPosition, final boolean singleTab,
final ExtraConfiguration... extraConfigurations) {
this.cls = cls;
this.title = title;
this.icon = icon;
this.sortPosition = sortPosition;
this.accountRequirement = accountRequirement;
this.secondaryFieldType = secondaryFieldType;
this.secondaryFieldTitle = secondaryFieldTitle;
this.secondaryFieldTextKey = secondaryFieldTextKey;
this.singleTab = singleTab;
this.extraConfigurations = extraConfigurations;
}
public CustomTabConfiguration(final Class<? extends Fragment> cls, final int title, final int icon,
final int accountRequirement, final int secondaryFieldType, final int secondaryFieldTitle,
final String secondaryFieldTextKey, final int sortPosition, final ExtraConfiguration... extraConfigurations) {
this(cls, title, icon, accountRequirement, secondaryFieldType, 0, secondaryFieldTextKey, sortPosition, false,
extraConfigurations);
}
public int getAccountRequirement() {
return accountRequirement;
}
public int getDefaultIcon() {
return icon;
}
public int getDefaultTitle() {
return title;
}
public ExtraConfiguration[] getExtraConfigurations() {
return extraConfigurations;
}
public Class<? extends Fragment> getFragmentClass() {
return cls;
}
public String getSecondaryFieldTextKey() {
return secondaryFieldTextKey;
}
public int getSecondaryFieldTitle() {
return secondaryFieldTitle;
}
public int getSecondaryFieldType() {
return secondaryFieldType;
}
public int getSortPosition() {
return sortPosition;
}
public boolean isSingleTab() {
return singleTab;
}
@Override
public String toString() {
return "CustomTabConfiguration{title=" + title + ", icon=" + icon + ", secondaryFieldType="
+ secondaryFieldType + ", secondaryFieldTitle=" + secondaryFieldTitle + ", sortPosition="
+ sortPosition + ", accountRequirement=" + accountRequirement + ", cls=" + cls
+ ", secondaryFieldTextKey=" + secondaryFieldTextKey + ", singleTab=" + singleTab + "}";
}
public static class CustomTabConfigurationComparator implements Comparator<Entry<String, CustomTabConfiguration>> {
public static final CustomTabConfigurationComparator SINGLETON = new CustomTabConfigurationComparator();
@Override
public int compare(final Entry<String, CustomTabConfiguration> lhs,
final Entry<String, CustomTabConfiguration> rhs) {
return lhs.getValue().getSortPosition() - rhs.getValue().getSortPosition();
}
}
public static class ExtraConfiguration {
private final String key;
private final int titleRes;
private final Type type;
private final Object defaultValue;
public ExtraConfiguration(final String key, final int titleRes, final Type type, final Object defaultValue) {
this.key = key;
this.titleRes = titleRes;
this.type = type;
this.defaultValue = defaultValue;
}
public boolean defaultBoolean() {
return (Boolean) defaultValue;
}
public String getKey() {
return key;
}
public int getTitleRes() {
return titleRes;
}
public Type getType() {
return type;
}
public static ExtraConfiguration newBoolean(final String key, final int titleRes, final boolean def) {
return new ExtraConfiguration(key, titleRes, Type.BOOLEAN, def);
}
public enum Type {
BOOLEAN
}
}
}

View File

@ -1,11 +1,15 @@
package org.mariotaku.twidere.model;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
import com.bluelinelabs.logansquare.annotation.OnPreJsonSerialize;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.library.objectcursor.annotation.CursorObject;
@ -24,28 +28,34 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
/**
* Created by mariotaku on 16/3/6.
*/
@ParcelablePlease(allFields = false)
@CursorObject(valuesCreator = true, tableInfo = true)
@JsonObject
public class Tab {
public class Tab implements Parcelable {
@CursorField(value = Tabs._ID, excludeWrite = true)
@JsonField(name = "id")
@ParcelableThisPlease
long id;
@CursorField(Tabs.NAME)
@JsonField(name = "name")
@ParcelableThisPlease
String name;
@CursorField(Tabs.ICON)
@JsonField(name = "icon")
@ParcelableThisPlease
String icon;
@CursorField(Tabs.TYPE)
@JsonField(name = "type")
@CustomTabType
@ParcelableThisPlease
String type;
@CursorField(Tabs.POSITION)
@JsonField(name = "position")
@ParcelableThisPlease
int position;
@Nullable
@ -58,10 +68,12 @@ public class Tab {
@Nullable
@JsonField(name = "arguments")
@ParcelableThisPlease
InternalArguments internalArguments;
@Nullable
@JsonField(name = "extras")
@ParcelableThisPlease
InternalExtras internalExtras;
public long getId() {
@ -103,20 +115,28 @@ public class Tab {
@Nullable
public TabArguments getArguments() {
if (arguments == null && internalArguments != null) {
arguments = internalArguments.getArguments();
}
return arguments;
}
public void setArguments(@Nullable TabArguments arguments) {
this.arguments = arguments;
this.internalArguments = InternalArguments.from(arguments);
}
@Nullable
public TabExtras getExtras() {
if (extras == null && internalExtras != null) {
extras = internalExtras.getExtras();
}
return extras;
}
public void setExtras(@Nullable TabExtras extras) {
this.extras = extras;
this.internalExtras = InternalExtras.from(extras);
}
@ -149,15 +169,19 @@ public class Tab {
'}';
}
@ParcelablePlease(allFields = false)
@JsonObject
static class InternalArguments {
static class InternalArguments implements Parcelable {
@JsonField(name = "base")
TabArguments base;
@JsonField(name = "text_query")
@ParcelableThisPlease
TextQueryArguments textQuery;
@JsonField(name = "user")
@ParcelableThisPlease
UserArguments user;
@JsonField(name = "user_list")
@ParcelableThisPlease
UserListArguments userList;
public static InternalArguments from(TabArguments arguments) {
@ -186,16 +210,41 @@ public class Tab {
return base;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Tab$InternalArgumentsParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<InternalArguments> CREATOR = new Creator<InternalArguments>() {
public InternalArguments createFromParcel(Parcel source) {
InternalArguments target = new InternalArguments();
Tab$InternalArgumentsParcelablePlease.readFromParcel(target, source);
return target;
}
public InternalArguments[] newArray(int size) {
return new InternalArguments[size];
}
};
}
@ParcelablePlease(allFields = false)
@JsonObject
static class InternalExtras {
static class InternalExtras implements Parcelable {
@JsonField(name = "base")
TabExtras base;
@JsonField(name = "interactions")
@ParcelableThisPlease
InteractionsTabExtras interactions;
@JsonField(name = "home")
@ParcelableThisPlease
HomeTabExtras home;
public static InternalExtras from(TabExtras extras) {
@ -221,5 +270,49 @@ public class Tab {
return base;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Tab$InternalExtrasParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<InternalExtras> CREATOR = new Creator<InternalExtras>() {
public InternalExtras createFromParcel(Parcel source) {
InternalExtras target = new InternalExtras();
Tab$InternalExtrasParcelablePlease.readFromParcel(target, source);
return target;
}
public InternalExtras[] newArray(int size) {
return new InternalExtras[size];
}
};
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
TabParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<Tab> CREATOR = new Creator<Tab>() {
public Tab createFromParcel(Parcel source) {
Tab target = new Tab();
TabParcelablePlease.readFromParcel(target, source);
return target;
}
public Tab[] newArray(int size) {
return new Tab[size];
}
};
}

View File

@ -0,0 +1,206 @@
package org.mariotaku.twidere.model.tab;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import org.apache.commons.lang3.text.WordUtils;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.dagger.DependencyHolder;
import java.util.ArrayList;
import java.util.List;
import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_I_WANT_MY_STARS_BACK;
/**
* Created by mariotaku on 2016/11/27.
*/
public abstract class DrawableHolder {
private String name;
@Nullable
public static DrawableHolder builtin(@NonNull String key) {
switch (key) {
// Default built-in icons map, don't remove icons below
case "accounts":
return Builtin.ACCOUNTS;
case "hashtag":
return Builtin.HASHTAG;
case "heart":
return Builtin.HEART;
case "home":
return Builtin.HOME;
case "list":
return Builtin.LIST;
case "mention":
return Builtin.MENTION;
case "notifications":
return Builtin.NOTIFICATIONS;
case "gallery":
return Builtin.GALLERY;
case "message":
return Builtin.MESSAGE;
case "quote":
return Builtin.QUOTE;
case "search":
return Builtin.SEARCH;
case "staggered":
return Builtin.STAGGERED;
case "star":
return Builtin.STAR;
case "trends":
return Builtin.TRENDS;
case "twidere":
return Builtin.TWIDERE;
case "twitter":
return Builtin.TWITTER;
case "user":
return Builtin.USER;
// End of default built-in icons
case "favorite":
return Builtin.FAVORITE;
}
return null;
}
@NonNull
public static DrawableHolder resource(int resId) {
return new Resource(resId);
}
@NonNull
public abstract String getPersistentKey();
@NonNull
public abstract Drawable createDrawable(Context context);
@Nullable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static DrawableHolder parse(String str) {
DrawableHolder icon = builtin(str);
if (icon != null) {
return icon;
}
return null;
}
public static List<DrawableHolder> builtins() {
String[] keys = {
// Default built-in icons map, don't remove icons below
"accounts",
"hashtag",
"heart",
"home",
"list",
"mention",
"notifications",
"gallery",
"message",
"quote",
"search",
"staggered",
"star",
"trends",
"twidere",
"twitter",
"user",
// End of default built-in icons
"favorite",
};
List<DrawableHolder> list = new ArrayList<>();
for (String key : keys) {
list.add(builtin(key));
}
return list;
}
private static class Resource extends DrawableHolder {
private int resId;
public Resource(int resId) {
this.resId = resId;
}
@Override
@NonNull
public String getPersistentKey() {
return String.valueOf(resId);
}
@NonNull
@Override
public Drawable createDrawable(Context context) {
return ContextCompat.getDrawable(context, resId);
}
}
public static class Builtin extends DrawableHolder {
public static final Builtin HOME = new Builtin("home", R.drawable.ic_action_home);
public static final Builtin HEART = new Builtin("heart", R.drawable.ic_action_heart);
public static final Builtin HASHTAG = new Builtin("hashtag", R.drawable.ic_action_hashtag);
public static final Builtin ACCOUNTS = new Builtin("accounts", R.drawable.ic_action_accounts);
public static final Builtin LIST = new Builtin("list", R.drawable.ic_action_list);
public static final Builtin MENTION = new Builtin("mention", R.drawable.ic_action_at);
public static final Builtin NOTIFICATIONS = new Builtin("notifications", R.drawable.ic_action_notification);
public static final Builtin GALLERY = new Builtin("gallery", R.drawable.ic_action_gallery);
public static final Builtin MESSAGE = new Builtin("message", R.drawable.ic_action_message);
public static final Builtin QUOTE = new Builtin("quote", R.drawable.ic_action_quote);
public static final Builtin SEARCH = new Builtin("search", R.drawable.ic_action_search);
public static final Builtin STAGGERED = new Builtin("staggered", R.drawable.ic_action_view_quilt);
public static final Builtin STAR = new Builtin("star", R.drawable.ic_action_star);
public static final Builtin TRENDS = new Builtin("trends", R.drawable.ic_action_trends);
public static final Builtin TWIDERE = new Builtin("twidere", R.drawable.ic_action_twidere);
public static final Builtin TWITTER = new Builtin("twitter", R.drawable.ic_action_twitter);
public static final Builtin USER = new Builtin("user", R.drawable.ic_action_user);
public static final DrawableHolder FAVORITE = new DrawableHolder() {
@NonNull
@Override
public String getPersistentKey() {
return "favorite";
}
@NonNull
@Override
public Drawable createDrawable(Context context) {
if (DependencyHolder.Companion.get(context).preferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) {
return ContextCompat.getDrawable(context, R.drawable.ic_action_star);
}
return ContextCompat.getDrawable(context, R.drawable.ic_action_heart);
}
};
private final String key;
private final int resId;
public Builtin(String key, int resId) {
this.key = key;
this.resId = resId;
setName(WordUtils.capitalize(key));
}
@Override
@NonNull
public String getPersistentKey() {
return key;
}
@NonNull
@Override
public Drawable createDrawable(Context context) {
return ContextCompat.getDrawable(context, resId);
}
}
}

View File

@ -0,0 +1,53 @@
package org.mariotaku.twidere.model.tab;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.StringRes;
/**
* Created by mariotaku on 2016/11/28.
*/
public abstract class StringHolder implements Parcelable {
public abstract String createString(Context context);
public static StringHolder resource(@StringRes int resourceId) {
return new Resource(resourceId);
}
private static class Resource extends StringHolder implements Parcelable {
private final int resourceId;
Resource(int resourceId) {
this.resourceId = resourceId;
}
@Override
public String createString(Context context) {
return context.getString(resourceId);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(resourceId);
}
public static final Creator<Resource> CREATOR = new Creator<Resource>() {
public Resource createFromParcel(Parcel source) {
return new Resource(source.readInt());
}
public Resource[] newArray(int size) {
return new Resource[size];
}
};
}
}

View File

@ -0,0 +1,278 @@
package org.mariotaku.twidere.model.tab;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.impl.DMTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.FavoriteTimelineTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.HomeTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.InteractionsTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.MessagesTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.SearchTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.TrendsTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.UserListTimelineTabConfiguration;
import org.mariotaku.twidere.model.tab.impl.UserTimelineTabConfiguration;
import java.util.ArrayList;
import java.util.List;
import kotlin.Pair;
/**
* Created by mariotaku on 2016/11/27.
*/
public abstract class TabConfiguration {
public static final int FLAG_HAS_ACCOUNT = 0b001;
public static final int FLAG_ACCOUNT_REQUIRED = 0b010;
public static final int FLAG_ACCOUNT_MULTIPLE = 0b100;
@NonNull
public abstract StringHolder getName();
@NonNull
public abstract DrawableHolder getIcon();
@AccountRequirement
public abstract int getAccountRequirement();
public boolean isSingleTab() {
return false;
}
public int getSortPosition() {
return 0;
}
@Nullable
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return null;
}
@NonNull
public abstract Class<? extends Fragment> getFragmentClass();
public void applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
}
public void readExtraConfigurationFrom(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
}
@IntDef(value = {FLAG_HAS_ACCOUNT, FLAG_ACCOUNT_REQUIRED, FLAG_ACCOUNT_MULTIPLE}, flag = true)
protected @interface AccountRequirement {
}
@NonNull
public static List<Pair<String, TabConfiguration>> all() {
List<Pair<String, TabConfiguration>> all = new ArrayList<>();
for (String type : allTypes()) {
all.add(new Pair<>(type, ofType(type)));
}
return all;
}
@NonNull
public static String[] allTypes() {
return new String[]{
CustomTabType.HOME_TIMELINE,
CustomTabType.NOTIFICATIONS_TIMELINE,
CustomTabType.TRENDS_SUGGESTIONS,
CustomTabType.DIRECT_MESSAGES,
CustomTabType.FAVORITES,
CustomTabType.USER_TIMELINE,
CustomTabType.SEARCH_STATUSES,
CustomTabType.LIST_TIMELINE
};
}
@Nullable
public static TabConfiguration ofType(@CustomTabType String type) {
switch (type) {
case CustomTabType.HOME_TIMELINE:
return new HomeTabConfiguration();
case CustomTabType.NOTIFICATIONS_TIMELINE:
return new InteractionsTabConfiguration();
case CustomTabType.DIRECT_MESSAGES:
return new MessagesTabConfiguration();
case CustomTabType.DIRECT_MESSAGES_NEXT:
return new DMTabConfiguration();
case CustomTabType.LIST_TIMELINE:
return new UserListTimelineTabConfiguration();
case CustomTabType.FAVORITES:
return new FavoriteTimelineTabConfiguration();
case CustomTabType.USER_TIMELINE:
return new UserTimelineTabConfiguration();
case CustomTabType.TRENDS_SUGGESTIONS:
return new TrendsTabConfiguration();
case CustomTabType.SEARCH_STATUSES:
return new SearchTabConfiguration();
}
return null;
}
public static abstract class ExtraConfiguration {
private final String key;
private StringHolder title;
@Nullable
private StringHolder headerTitle;
protected ExtraConfiguration(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public StringHolder getTitle() {
return title;
}
public void setTitle(StringHolder title) {
this.title = title;
}
public ExtraConfiguration title(StringHolder title) {
setTitle(title);
return this;
}
public ExtraConfiguration title(@StringRes int titleRes) {
setTitle(StringHolder.resource(titleRes));
return this;
}
@Nullable
public StringHolder getHeaderTitle() {
return headerTitle;
}
public void setHeaderTitle(@Nullable StringHolder headerTitle) {
this.headerTitle = headerTitle;
}
public ExtraConfiguration headerTitle(@Nullable StringHolder title) {
setHeaderTitle(title);
return this;
}
public ExtraConfiguration headerTitle(@StringRes int titleRes) {
setHeaderTitle(StringHolder.resource(titleRes));
return this;
}
@NonNull
public abstract View onCreateView(Context context, ViewGroup parent);
public void onViewCreated(@NonNull Context context, @NonNull View view, @NonNull DialogFragment fragment) {
}
}
public static class BooleanExtraConfiguration extends ExtraConfiguration {
private final boolean def;
private CheckBox checkBox;
public BooleanExtraConfiguration(String key, boolean def) {
super(key);
this.def = def;
}
@NonNull
@Override
public View onCreateView(Context context, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_item_extra_config, parent, false);
}
@Override
public void onViewCreated(@NonNull Context context, @NonNull View view, @NonNull DialogFragment fragment) {
final TextView titleView = (TextView) view.findViewById(android.R.id.title);
titleView.setText(getTitle().createString(context));
checkBox = (CheckBox) view.findViewById(android.R.id.checkbox);
checkBox.setVisibility(View.VISIBLE);
checkBox.setChecked(def);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkBox.toggle();
}
});
}
public void setValue(boolean value) {
checkBox.setChecked(value);
}
public boolean getValue() {
return checkBox.isChecked();
}
}
public static class StringExtraConfiguration extends ExtraConfiguration {
private final String def;
private int maxLines;
private EditText editText;
public StringExtraConfiguration(String key, String def) {
super(key);
this.def = def;
}
@NonNull
@Override
public View onCreateView(Context context, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.layout_extra_config_text, parent, false);
}
@Override
public void onViewCreated(@NonNull Context context, @NonNull View view, @NonNull DialogFragment fragment) {
editText = (EditText) view.findViewById(R.id.editText);
editText.setHint(getTitle().createString(context));
editText.setText(def);
}
public StringExtraConfiguration maxLines(int maxLines) {
setMaxLines(maxLines);
return this;
}
public void setMaxLines(int maxLines) {
this.maxLines = maxLines;
}
public int getMaxLines() {
return maxLines;
}
public String getValue() {
return editText.getText().toString();
}
public void setValue(String value) {
editText.setText(value);
}
}
}

View File

@ -3,6 +3,7 @@ package org.mariotaku.twidere.model.tab.argument;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
@ -23,13 +24,15 @@ public class TabArguments implements TwidereConstants {
String accountId;
@JsonField(name = "account_keys")
@Nullable
UserKey[] accountKeys;
@Nullable
public UserKey[] getAccountKeys() {
return accountKeys;
}
public void setAccountKeys(UserKey[] accountKeys) {
public void setAccountKeys(@Nullable UserKey[] accountKeys) {
this.accountKeys = accountKeys;
}
@ -39,7 +42,9 @@ public class TabArguments implements TwidereConstants {
@CallSuper
public void copyToBundle(@NonNull Bundle bundle) {
final UserKey[] accountKeys = this.accountKeys;
if (!ArrayUtils.isEmpty(accountKeys)) {
assert accountKeys != null;
for (UserKey key : accountKeys) {
if (key == null) return;
}

View File

@ -1,16 +1,20 @@
package org.mariotaku.twidere.model.tab.argument;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
/**
* Created by mariotaku on 16/3/6.
*/
@ParcelablePlease
@JsonObject
public class TextQueryArguments extends TabArguments {
public class TextQueryArguments extends TabArguments implements Parcelable {
@JsonField(name = "query")
String query;
@ -34,4 +38,26 @@ public class TextQueryArguments extends TabArguments {
"query='" + query + '\'' +
"} " + super.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
TextQueryArgumentsParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<TextQueryArguments> CREATOR = new Creator<TextQueryArguments>() {
public TextQueryArguments createFromParcel(Parcel source) {
TextQueryArguments target = new TextQueryArguments();
TextQueryArgumentsParcelablePlease.readFromParcel(target, source);
return target;
}
public TextQueryArguments[] newArray(int size) {
return new TextQueryArguments[size];
}
};
}

View File

@ -1,18 +1,22 @@
package org.mariotaku.twidere.model.tab.argument;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import org.mariotaku.twidere.model.UserKey;
/**
* Created by mariotaku on 16/3/6.
*/
@ParcelablePlease
@JsonObject
public class UserArguments extends TabArguments {
public class UserArguments extends TabArguments implements Parcelable {
@JsonField(name = "user_id")
String userId;
@JsonField(name = "user_key")
@ -39,4 +43,26 @@ public class UserArguments extends TabArguments {
", userKey=" + userKey +
"} " + super.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
UserArgumentsParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<UserArguments> CREATOR = new Creator<UserArguments>() {
public UserArguments createFromParcel(Parcel source) {
UserArguments target = new UserArguments();
UserArgumentsParcelablePlease.readFromParcel(target, source);
return target;
}
public UserArguments[] newArray(int size) {
return new UserArguments[size];
}
};
}

View File

@ -1,16 +1,20 @@
package org.mariotaku.twidere.model.tab.argument;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
/**
* Created by mariotaku on 16/3/6.
*/
@ParcelablePlease
@JsonObject
public class UserListArguments extends TabArguments {
public class UserListArguments extends TabArguments implements Parcelable {
@JsonField(name = "list_id")
String listId;
@ -34,4 +38,26 @@ public class UserListArguments extends TabArguments {
"listId=" + listId +
"} " + super.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
UserListArgumentsParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<UserListArguments> CREATOR = new Creator<UserListArguments>() {
public UserListArguments createFromParcel(Parcel source) {
UserListArguments target = new UserListArguments();
UserListArgumentsParcelablePlease.readFromParcel(target, source);
return target;
}
public UserListArguments[] newArray(int size) {
return new UserListArguments[size];
}
};
}

View File

@ -0,0 +1,44 @@
package org.mariotaku.twidere.model.tab.conf;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.UserListSelectorActivity;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_SELECT_USER;
/**
* Created by mariotaku on 2016/11/28.
*/
public class UserExtraConfiguration extends TabConfiguration.ExtraConfiguration {
public UserExtraConfiguration(String key) {
super(key);
}
@NonNull
@Override
public View onCreateView(Context context, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_item_simple_user, parent, false);
}
@Override
public void onViewCreated(@NonNull final Context context, @NonNull View view, @NonNull final DialogFragment fragment) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final Intent intent = new Intent(INTENT_ACTION_SELECT_USER);
intent.setClass(context, UserListSelectorActivity.class);
fragment.startActivity(intent);
}
});
}
}

View File

@ -0,0 +1,45 @@
package org.mariotaku.twidere.model.tab.conf;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.UserListSelectorActivity;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_SELECT_USER;
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_SELECT_USER_LIST;
/**
* Created by mariotaku on 2016/11/28.
*/
public class UserListExtraConfiguration extends TabConfiguration.ExtraConfiguration {
public UserListExtraConfiguration(String key) {
super(key);
}
@NonNull
@Override
public View onCreateView(Context context, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_item_simple_user_list, parent, false);
}
@Override
public void onViewCreated(@NonNull final Context context, @NonNull final View view, @NonNull final DialogFragment fragment) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final Intent intent = new Intent(INTENT_ACTION_SELECT_USER_LIST);
intent.setClass(context, UserListSelectorActivity.class);
fragment.startActivity(intent);
}
});
}
}

View File

@ -0,0 +1,40 @@
package org.mariotaku.twidere.model.tab.impl;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.MessagesEntriesFragment;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
/**
* Created by mariotaku on 2016/11/27.
*/
public class DMTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.direct_messages_next);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.MESSAGE;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_MULTIPLE;
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return MessagesEntriesFragment.class;
}
}

View File

@ -0,0 +1,53 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.UserFavoritesFragment;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.conf.UserExtraConfiguration;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER;
/**
* Created by mariotaku on 2016/11/27.
*/
public class FavoriteTimelineTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.favorites);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.FAVORITE;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_REQUIRED;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new UserExtraConfiguration(EXTRA_USER).title(R.string.user).headerTitle(R.string.user)
};
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return UserFavoritesFragment.class;
}
}

View File

@ -0,0 +1,98 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.HomeTimelineFragment;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.extra.HomeTabExtras;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_HIDE_QUOTES;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_HIDE_REPLIES;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_HIDE_RETWEETS;
/**
* Created by mariotaku on 2016/11/27.
*/
public class HomeTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.home);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.HOME;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_MULTIPLE;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new BooleanExtraConfiguration(EXTRA_HIDE_RETWEETS, false).title(R.string.hide_retweets),
new BooleanExtraConfiguration(EXTRA_HIDE_QUOTES, false).title(R.string.hide_quotes),
new BooleanExtraConfiguration(EXTRA_HIDE_REPLIES, false).title(R.string.hide_replies),
};
}
@Override
public void applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final HomeTabExtras extras = (HomeTabExtras) tab.getExtras();
assert extras != null;
switch (extraConf.getKey()) {
case EXTRA_HIDE_RETWEETS: {
extras.setHideRetweets(((BooleanExtraConfiguration) extraConf).getValue());
break;
}
case EXTRA_HIDE_QUOTES: {
extras.setHideQuotes(((BooleanExtraConfiguration) extraConf).getValue());
break;
}
case EXTRA_HIDE_REPLIES: {
extras.setHideReplies(((BooleanExtraConfiguration) extraConf).getValue());
break;
}
}
}
@Override
public void readExtraConfigurationFrom(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final HomeTabExtras extras = (HomeTabExtras) tab.getExtras();
if (extras == null) return;
switch (extraConf.getKey()) {
case EXTRA_HIDE_RETWEETS: {
((BooleanExtraConfiguration) extraConf).setValue(extras.isHideRetweets());
break;
}
case EXTRA_HIDE_QUOTES: {
((BooleanExtraConfiguration) extraConf).setValue(extras.isHideQuotes());
break;
}
case EXTRA_HIDE_REPLIES: {
((BooleanExtraConfiguration) extraConf).setValue(extras.isHideReplies());
break;
}
}
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return HomeTimelineFragment.class;
}
}

View File

@ -0,0 +1,88 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_MENTIONS_ONLY;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_MY_FOLLOWING_ONLY;
/**
* Created by mariotaku on 2016/11/27.
*/
public class InteractionsTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.interactions);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.NOTIFICATIONS;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_MULTIPLE;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new BooleanExtraConfiguration(EXTRA_MY_FOLLOWING_ONLY, false).title(R.string.following_only),
new BooleanExtraConfiguration(EXTRA_MENTIONS_ONLY, false).title(R.string.mentions_only),
};
}
@Override
public void applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final InteractionsTabExtras extras = (InteractionsTabExtras) tab.getExtras();
assert extras != null;
switch (extraConf.getKey()) {
case EXTRA_MY_FOLLOWING_ONLY: {
extras.setMyFollowingOnly(((BooleanExtraConfiguration) extraConf).getValue());
break;
}
case EXTRA_MENTIONS_ONLY: {
extras.setMentionsOnly(((BooleanExtraConfiguration) extraConf).getValue());
break;
}
}
}
@Override
public void readExtraConfigurationFrom(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final InteractionsTabExtras extras = (InteractionsTabExtras) tab.getExtras();
if (extras == null) return;
switch (extraConf.getKey()) {
case EXTRA_MY_FOLLOWING_ONLY: {
((BooleanExtraConfiguration) extraConf).setValue(extras.isMyFollowingOnly());
break;
}
case EXTRA_MENTIONS_ONLY: {
((BooleanExtraConfiguration) extraConf).setValue(extras.isMentionsOnly());
break;
}
}
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return InteractionsTimelineFragment.class;
}
}

View File

@ -0,0 +1,40 @@
package org.mariotaku.twidere.model.tab.impl;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.DirectMessagesFragment;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
/**
* Created by mariotaku on 2016/11/27.
*/
public class MessagesTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.direct_messages);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.MESSAGE;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_MULTIPLE;
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return DirectMessagesFragment.class;
}
}

View File

@ -0,0 +1,78 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.TrendsSuggestionsFragment;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.argument.TextQueryArguments;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_QUERY;
/**
* Created by mariotaku on 2016/11/27.
*/
public class SearchTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.search);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.SEARCH;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_REQUIRED;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new StringExtraConfiguration(EXTRA_QUERY, null).maxLines(1).title(R.string.search_statuses).headerTitle(R.string.query)
};
}
@Override
public void applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final TextQueryArguments arguments = (TextQueryArguments) tab.getArguments();
assert arguments != null;
switch (extraConf.getKey()) {
case EXTRA_QUERY: {
arguments.setQuery(((StringExtraConfiguration) extraConf).getValue());
break;
}
}
}
@Override
public void readExtraConfigurationFrom(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
final TextQueryArguments arguments = (TextQueryArguments) tab.getArguments();
if (arguments == null) return;
switch (extraConf.getKey()) {
case EXTRA_QUERY: {
((StringExtraConfiguration) extraConf).setValue(arguments.getQuery());
break;
}
}
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return TrendsSuggestionsFragment.class;
}
}

View File

@ -0,0 +1,40 @@
package org.mariotaku.twidere.model.tab.impl;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.TrendsSuggestionsFragment;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
/**
* Created by mariotaku on 2016/11/27.
*/
public class TrendsTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.trends);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.TRENDS;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT;
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return TrendsSuggestionsFragment.class;
}
}

View File

@ -0,0 +1,53 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.UserListTimelineFragment;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.conf.UserListExtraConfiguration;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER_LIST;
/**
* Created by mariotaku on 2016/11/27.
*/
public class UserListTimelineTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.list_timeline);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.LIST;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_REQUIRED;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new UserListExtraConfiguration(EXTRA_USER_LIST).title(R.string.user_list).headerTitle(R.string.user_list)
};
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return UserListTimelineFragment.class;
}
}

View File

@ -0,0 +1,53 @@
package org.mariotaku.twidere.model.tab.impl;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.fragment.UserTimelineFragment;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.StringHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.conf.UserExtraConfiguration;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER;
/**
* Created by mariotaku on 2016/11/27.
*/
public class UserTimelineTabConfiguration extends TabConfiguration {
@NonNull
@Override
public StringHolder getName() {
return StringHolder.resource(R.string.users_statuses);
}
@NonNull
@Override
public DrawableHolder getIcon() {
return DrawableHolder.Builtin.USER;
}
@AccountRequirement
@Override
public int getAccountRequirement() {
return FLAG_HAS_ACCOUNT | FLAG_ACCOUNT_REQUIRED;
}
@Nullable
@Override
public ExtraConfiguration[] getExtraConfigurations(Context context) {
return new ExtraConfiguration[]{
new UserExtraConfiguration(EXTRA_USER).title(R.string.user).headerTitle(R.string.user)
};
}
@NonNull
@Override
public Class<? extends Fragment> getFragmentClass() {
return UserTimelineFragment.class;
}
}

View File

@ -21,40 +21,28 @@ package org.mariotaku.twidere.util;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.SimpleArrayMap;
import android.text.TextUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.annotation.ReadPositionTag;
import org.mariotaku.twidere.fragment.DirectMessagesFragment;
import org.mariotaku.twidere.fragment.HomeTimelineFragment;
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment;
import org.mariotaku.twidere.fragment.InvalidTabFragment;
import org.mariotaku.twidere.fragment.MessagesEntriesFragment;
import org.mariotaku.twidere.fragment.StatusesSearchFragment;
import org.mariotaku.twidere.fragment.TrendsSuggestionsFragment;
import org.mariotaku.twidere.fragment.UserFavoritesFragment;
import org.mariotaku.twidere.fragment.UserListTimelineFragment;
import org.mariotaku.twidere.fragment.UserTimelineFragment;
import org.mariotaku.twidere.model.CustomTabConfiguration;
import org.mariotaku.twidere.model.CustomTabConfiguration.ExtraConfiguration;
import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.model.Tab;
import org.mariotaku.twidere.model.TabCursorIndices;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.tab.DrawableHolder;
import org.mariotaku.twidere.model.tab.TabConfiguration;
import org.mariotaku.twidere.model.tab.argument.TabArguments;
import org.mariotaku.twidere.model.tab.argument.TextQueryArguments;
import org.mariotaku.twidere.model.tab.argument.UserArguments;
@ -64,139 +52,70 @@ import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras;
import org.mariotaku.twidere.model.tab.extra.TabExtras;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
public class CustomTabUtils implements Constants {
private static final HashMap<String, CustomTabConfiguration> CUSTOM_TABS_CONFIGURATION_MAP = new HashMap<>();
private static final HashMap<String, Integer> CUSTOM_TABS_ICON_NAME_MAP = new HashMap<>();
static {
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.HOME_TIMELINE, new CustomTabConfiguration(
HomeTimelineFragment.class, R.string.home, R.drawable.ic_action_home,
CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 0, false,
ExtraConfiguration.newBoolean(EXTRA_HIDE_RETWEETS, R.string.hide_retweets, false),
ExtraConfiguration.newBoolean(EXTRA_HIDE_QUOTES, R.string.hide_quotes, false),
ExtraConfiguration.newBoolean(EXTRA_HIDE_REPLIES, R.string.hide_replies, false)));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.NOTIFICATIONS_TIMELINE, new CustomTabConfiguration(
InteractionsTimelineFragment.class, R.string.interactions, R.drawable.ic_action_notification,
CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 1, false,
ExtraConfiguration.newBoolean(EXTRA_MY_FOLLOWING_ONLY, R.string.following_only, false),
ExtraConfiguration.newBoolean(EXTRA_MENTIONS_ONLY, R.string.mentions_only, false)));
if (BuildConfig.DEBUG) {
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.DIRECT_MESSAGES_NEXT, new CustomTabConfiguration(
MessagesEntriesFragment.class, R.string.direct_messages_next, R.drawable.ic_action_message,
CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 2, false));
}
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.DIRECT_MESSAGES, new CustomTabConfiguration(
DirectMessagesFragment.class, R.string.direct_messages, R.drawable.ic_action_message,
CustomTabConfiguration.ACCOUNT_OPTIONAL, CustomTabConfiguration.FIELD_TYPE_NONE, 2, false));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.TRENDS_SUGGESTIONS, new CustomTabConfiguration(
TrendsSuggestionsFragment.class, R.string.trends, R.drawable.ic_action_hashtag,
CustomTabConfiguration.ACCOUNT_NONE, CustomTabConfiguration.FIELD_TYPE_NONE, 3, true));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.FAVORITES, new CustomTabConfiguration(UserFavoritesFragment.class,
R.string.likes, R.drawable.ic_action_heart, CustomTabConfiguration.ACCOUNT_REQUIRED,
CustomTabConfiguration.FIELD_TYPE_USER, 4));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.USER_TIMELINE, new CustomTabConfiguration(
UserTimelineFragment.class, R.string.users_statuses, R.drawable.ic_action_quote,
CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER, 5));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.SEARCH_STATUSES, new CustomTabConfiguration(
StatusesSearchFragment.class, R.string.search_statuses, R.drawable.ic_action_search,
CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_TEXT, R.string.query,
EXTRA_QUERY, 6));
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.LIST_TIMELINE, new CustomTabConfiguration(
UserListTimelineFragment.class, R.string.list_timeline, R.drawable.ic_action_list,
CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER_LIST, 7));
CUSTOM_TABS_ICON_NAME_MAP.put("accounts", R.drawable.ic_action_accounts);
CUSTOM_TABS_ICON_NAME_MAP.put("hashtag", R.drawable.ic_action_hashtag);
CUSTOM_TABS_ICON_NAME_MAP.put("heart", R.drawable.ic_action_heart);
CUSTOM_TABS_ICON_NAME_MAP.put("home", R.drawable.ic_action_home);
CUSTOM_TABS_ICON_NAME_MAP.put("list", R.drawable.ic_action_list);
CUSTOM_TABS_ICON_NAME_MAP.put("mention", R.drawable.ic_action_at);
CUSTOM_TABS_ICON_NAME_MAP.put("notifications", R.drawable.ic_action_notification);
CUSTOM_TABS_ICON_NAME_MAP.put("gallery", R.drawable.ic_action_gallery);
CUSTOM_TABS_ICON_NAME_MAP.put("message", R.drawable.ic_action_message);
CUSTOM_TABS_ICON_NAME_MAP.put("quote", R.drawable.ic_action_quote);
CUSTOM_TABS_ICON_NAME_MAP.put("search", R.drawable.ic_action_search);
CUSTOM_TABS_ICON_NAME_MAP.put("staggered", R.drawable.ic_action_view_quilt);
CUSTOM_TABS_ICON_NAME_MAP.put("star", R.drawable.ic_action_star);
CUSTOM_TABS_ICON_NAME_MAP.put("trends", R.drawable.ic_action_trends);
CUSTOM_TABS_ICON_NAME_MAP.put("twidere", R.drawable.ic_action_twidere);
CUSTOM_TABS_ICON_NAME_MAP.put("twitter", R.drawable.ic_action_twitter);
CUSTOM_TABS_ICON_NAME_MAP.put("user", R.drawable.ic_action_user);
}
private static final SimpleArrayMap<String, Integer> CUSTOM_TABS_ICON_NAME_MAP = new SimpleArrayMap<>();
private CustomTabUtils() {
}
@Nullable
public static String findTabIconKey(final int iconRes) {
for (final Entry<String, Integer> entry : getIconMap().entrySet()) {
if (entry.getValue() == iconRes) return entry.getKey();
}
return null;
}
public static String findTabType(final Class<? extends Fragment> cls) {
for (final Entry<String, CustomTabConfiguration> entry : getConfigurationMap().entrySet()) {
if (cls == entry.getValue().getFragmentClass()) return entry.getKey();
for (int i = 0, j = CUSTOM_TABS_ICON_NAME_MAP.size(); i < j; i++) {
if (CUSTOM_TABS_ICON_NAME_MAP.valueAt(i) == iconRes) {
return CUSTOM_TABS_ICON_NAME_MAP.keyAt(i);
}
}
return null;
}
public static HashMap<String, CustomTabConfiguration> getConfigurationMap() {
return new HashMap<>(CUSTOM_TABS_CONFIGURATION_MAP);
}
public static List<SupportTabSpec> getHomeTabs(final Context context) {
if (context == null) return Collections.emptyList();
public static List<Tab> getTabs(@NonNull final Context context) {
final ContentResolver resolver = context.getContentResolver();
final Cursor cur = resolver.query(Tabs.CONTENT_URI, Tabs.COLUMNS, null, null, Tabs.DEFAULT_SORT_ORDER);
if (cur == null) return Collections.emptyList();
final ArrayList<SupportTabSpec> tabs = new ArrayList<>();
final ArrayList<Tab> tabs = new ArrayList<>();
cur.moveToFirst();
TabCursorIndices indices = new TabCursorIndices(cur);
final int idxArguments = cur.getColumnIndex(Tabs.ARGUMENTS);
final int idxExtras = cur.getColumnIndex(Tabs.EXTRAS);
while (!cur.isAfterLast()) {
tabs.add(indices.newObject(cur));
cur.moveToNext();
}
return tabs;
}
public static List<SupportTabSpec> getHomeTabs(@NonNull final Context context) {
List<SupportTabSpec> specs = new ArrayList<>();
for (Tab tab : getTabs(context)) {
@CustomTabType
final String type = getTabTypeAlias(cur.getString(indices.type));
final int position = cur.getInt(indices.position);
final String iconType = cur.getString(indices.icon);
final String name = cur.getString(indices.name);
final String type = tab.getType();
final int position = tab.getPosition();
final String iconType = tab.getIcon();
final String name = tab.getName();
final Bundle args = new Bundle();
final TabArguments tabArguments = parseTabArguments(type, cur.getString(idxArguments));
final TabArguments tabArguments = tab.getArguments();
if (tabArguments != null) {
tabArguments.copyToBundle(args);
}
@ReadPositionTag
final String tag = getTagByType(type);
args.putInt(EXTRA_TAB_POSITION, position);
args.putLong(EXTRA_TAB_ID, cur.getLong(indices.id));
final TabExtras tabExtras = parseTabExtras(type, cur.getString(idxExtras));
args.putLong(EXTRA_TAB_ID, tab.getId());
final TabExtras tabExtras = tab.getExtras();
if (tabExtras != null) {
args.putParcelable(EXTRA_EXTRAS, tabExtras);
}
final CustomTabConfiguration conf = getTabConfiguration(type);
final TabConfiguration conf = TabConfiguration.ofType(type);
final Class<? extends Fragment> cls = conf != null ? conf.getFragmentClass() : InvalidTabFragment.class;
final String tabTypeName = getTabTypeName(context, type);
final Object tabIconObject = getTabIconObject(iconType);
tabs.add(new SupportTabSpec(TextUtils.isEmpty(name) ? tabTypeName : name, tabIconObject,
final DrawableHolder icon = DrawableHolder.parse(iconType);
specs.add(new SupportTabSpec(TextUtils.isEmpty(name) ? tabTypeName : name, icon,
type, cls, args, position, tag));
cur.moveToNext();
}
cur.close();
Collections.sort(tabs);
return tabs;
return specs;
}
@Nullable
@ -204,6 +123,11 @@ public class CustomTabUtils implements Constants {
return parseTabArguments(type, "{}");
}
@Nullable
public static TabExtras newTabExtras(@NonNull @CustomTabType String type) {
return parseTabExtras(type, "{}");
}
@Nullable
public static TabArguments parseTabArguments(@NonNull @CustomTabType String type, String json) {
switch (type) {
@ -254,13 +178,9 @@ public class CustomTabUtils implements Constants {
return null;
}
public static HashMap<String, Integer> getIconMap() {
return new HashMap<>(CUSTOM_TABS_ICON_NAME_MAP);
}
public static CustomTabConfiguration getTabConfiguration(final String tabType) {
public static TabConfiguration getTabConfiguration(final String tabType) {
if (tabType == null) return null;
return CUSTOM_TABS_CONFIGURATION_MAP.get(getTabTypeAlias(tabType));
return TabConfiguration.ofType(getTabTypeAlias(tabType));
}
@CustomTabType
@ -274,68 +194,18 @@ public class CustomTabUtils implements Constants {
return key;
}
public static Drawable getTabIconDrawable(final Context context, final Object iconObj) {
return getTabIconDrawable(context.getResources(), iconObj);
}
public static Drawable getTabIconDrawable(final Resources res, final Object iconObj) {
if (res == null) return null;
if (iconObj instanceof Integer) {
try {
return ResourcesCompat.getDrawable(res, (Integer) iconObj, null);
} catch (final Resources.NotFoundException e) {
// Ignore.
}
} else if (iconObj instanceof Bitmap)
return new BitmapDrawable(res, (Bitmap) iconObj);
else if (iconObj instanceof Drawable)
return (Drawable) iconObj;
else if (iconObj instanceof File) {
final Bitmap b = getTabIconFromFile((File) iconObj, res);
if (b != null) return new BitmapDrawable(res, b);
public static Drawable getTabIconDrawable(final Context context, final DrawableHolder icon) {
if (icon == null) {
return ContextCompat.getDrawable(context, R.drawable.ic_action_list);
}
return ResourcesCompat.getDrawable(res, R.drawable.ic_action_list, null);
}
public static Bitmap getTabIconFromFile(final File file, final Resources res) {
if (file == null || !file.exists()) return null;
final String path = file.getPath();
final BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
if (o.outHeight <= 0 || o.outWidth <= 0) return null;
o.inSampleSize = (int) (Math.max(o.outWidth, o.outHeight) / (48 * res.getDisplayMetrics().density));
o.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, o);
}
public static Object getTabIconObject(final String type) {
if (type == null) return R.drawable.ic_action_list;
final Integer value = CUSTOM_TABS_ICON_NAME_MAP.get(type);
if (value != null)
return value;
else if (type.contains("/")) {
try {
final File file = new File(type);
if (file.exists()) return file;
} catch (final Exception e) {
return R.drawable.ic_action_list;
}
}
return R.drawable.ic_action_list;
return icon.createDrawable(context);
}
public static String getTabTypeName(final Context context, final String type) {
if (context == null) return null;
final CustomTabConfiguration conf = getTabConfiguration(type);
final TabConfiguration conf = TabConfiguration.ofType(type);
if (conf == null) return null;
return context.getString(conf.getDefaultTitle());
}
public static boolean isSingleTab(final String type) {
if (type == null) return false;
final CustomTabConfiguration conf = getTabConfiguration(type);
return conf != null && conf.isSingleTab();
return conf.getName().createString(context);
}
public static boolean isTabAdded(final Context context, final String type) {
@ -351,7 +221,7 @@ public class CustomTabUtils implements Constants {
}
public static boolean isTabTypeValid(final String tabType) {
return tabType != null && CUSTOM_TABS_CONFIGURATION_MAP.containsKey(getTabTypeAlias(tabType));
return tabType != null && TabConfiguration.ofType(getTabTypeAlias(tabType)) != null;
}
public static boolean hasAccountId(final Context context, @NonNull final Bundle args,

View File

@ -22,7 +22,6 @@ package org.mariotaku.twidere.activity
import android.Manifest
import android.app.Activity
import android.app.Dialog
import android.content.ContentValues
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
@ -45,15 +44,19 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.SupportTabsAdapter
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.constant.IntentConstants
import org.mariotaku.twidere.fragment.*
import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.BasePreferenceFragment
import org.mariotaku.twidere.fragment.BaseSupportFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.TabValuesCreator
import org.mariotaku.twidere.model.tab.TabConfiguration
import org.mariotaku.twidere.preference.WizardPageHeaderPreference
import org.mariotaku.twidere.preference.WizardPageNavPreference
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.content.ContentResolverUtils
import org.mariotaku.twidere.view.LinePageIndicator
import java.io.File
import java.util.*
class SettingsWizardActivity : BaseActivity() {
@ -151,13 +154,13 @@ class SettingsWizardActivity : BaseActivity() {
}
private fun initPages() {
adapter!!.addTab(WizardPageWelcomeFragment::class.java, null, getString(R.string.wizard_page_welcome_title), null, 0, null)
adapter!!.addTab(WizardPageThemeFragment::class.java, null, getString(R.string.theme), null, 0, null)
adapter!!.addTab(WizardPageTabsFragment::class.java, null, getString(R.string.tabs), null, 0, null)
adapter!!.addTab(WizardPageCardsFragment::class.java, null, getString(R.string.cards), null, 0, null)
adapter!!.addTab(WizardPageUsageStatisticsFragment::class.java, null, getString(R.string.usage_statistics), null, 0, null)
adapter!!.addTab(WizardPageHintsFragment::class.java, null, getString(R.string.hints), null, 0, null)
adapter!!.addTab(WizardPageFinishedFragment::class.java, null, getString(R.string.wizard_page_finished_title), null, 0, null)
adapter!!.addTab(cls = WizardPageWelcomeFragment::class.java, name = getString(R.string.wizard_page_welcome_title))
adapter!!.addTab(cls = WizardPageThemeFragment::class.java, name = getString(R.string.theme))
adapter!!.addTab(cls = WizardPageTabsFragment::class.java, name = getString(R.string.tabs))
adapter!!.addTab(cls = WizardPageCardsFragment::class.java, name = getString(R.string.cards))
adapter!!.addTab(cls = WizardPageUsageStatisticsFragment::class.java, name = getString(R.string.usage_statistics))
adapter!!.addTab(cls = WizardPageHintsFragment::class.java, name = getString(R.string.hints))
adapter!!.addTab(cls = WizardPageFinishedFragment::class.java, name = getString(R.string.wizard_page_finished_title))
}
private fun openImportSettingsDialog() {
@ -471,45 +474,30 @@ class SettingsWizardActivity : BaseActivity() {
internal abstract class AbsInitialSettingsTask(protected val activity: SettingsWizardActivity) : AsyncTask<Any, Any, Boolean>() {
override fun doInBackground(vararg params: Any): Boolean? {
override fun doInBackground(vararg params: Any): Boolean {
val resolver = activity.contentResolver
val tabs = CustomTabUtils.getHomeTabs(activity)
val tabs = CustomTabUtils.getTabs(activity)
if (wasConfigured(tabs)) return true
Collections.sort(tabs)
var i = 0
val values_list = ArrayList<ContentValues>()
for (type in DEFAULT_TAB_TYPES) {
val values = ContentValues()
val conf = CustomTabUtils.getTabConfiguration(type)
values.put(Tabs.TYPE, type)
values.put(Tabs.ICON, CustomTabUtils.findTabIconKey(conf.defaultIcon))
values.put(Tabs.POSITION, i++)
values_list.add(values)
val conf = TabConfiguration.ofType(type)!!
val tab = Tab()
tab.type = type
tab.icon = conf.icon.persistentKey
tab.position = i++
tabs.add(tab)
}
for (spec in tabs) {
val type = CustomTabUtils.findTabType(spec.cls)
if (type != null) {
val values = ContentValues()
values.put(Tabs.TYPE, type)
values.put(Tabs.ARGUMENTS, InternalParseUtils.bundleToJSON(spec.args))
values.put(Tabs.NAME, ParseUtils.parseString(spec.name))
val icon = spec.icon
if (icon is Int) {
values.put(Tabs.ICON, CustomTabUtils.findTabIconKey(icon))
} else if (icon is File) {
values.put(Tabs.ICON, icon.path)
}
values.put(Tabs.POSITION, i++)
}
for (tab in tabs) {
tab.position = i++
}
resolver.delete(Tabs.CONTENT_URI, null, null)
resolver.bulkInsert(Tabs.CONTENT_URI, values_list.toTypedArray())
ContentResolverUtils.bulkInsert(resolver, Tabs.CONTENT_URI, tabs.map(TabValuesCreator::create))
return true
}
protected abstract fun nextStep()
override fun onPostExecute(result: Boolean?) {
override fun onPostExecute(result: Boolean) {
activity.executeAfterFragmentResumed {
val activity = it as SettingsWizardActivity
val fm = activity.supportFragmentManager
@ -526,15 +514,15 @@ class SettingsWizardActivity : BaseActivity() {
}
}
private fun wasConfigured(tabs: List<SupportTabSpec>): Boolean {
for (spec in tabs) {
if (spec.cls == HomeTimelineFragment::class.java
|| spec.cls == InteractionsTimelineFragment::class.java
|| spec.cls == DirectMessagesFragment::class.java
|| spec.cls == MessagesEntriesFragment::class.java)
return true
}
return false
private fun wasConfigured(tabs: List<Tab>): Boolean {
return tabs.find({ tab ->
when (tab.type) {
CustomTabType.HOME_TIMELINE, CustomTabType.NOTIFICATIONS_TIMELINE,
CustomTabType.DIRECT_MESSAGES, CustomTabType.DIRECT_MESSAGES_NEXT ->
return@find true
}
return@find false
}) != null
}
companion object {

View File

@ -30,6 +30,7 @@ import android.view.ViewGroup
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.util.CustomTabUtils.getTabIconDrawable
import org.mariotaku.twidere.util.Utils.announceForAccessibilityCompat
import org.mariotaku.twidere.view.iface.PagerIndicator
@ -44,40 +45,35 @@ class SupportTabsAdapter @JvmOverloads constructor(
private val columns: Int = 1
) : SupportFixedFragmentStatePagerAdapter(fm), TabProvider, TabListener {
private val mTabs = ArrayList<SupportTabSpec>()
private val tab = ArrayList<SupportTabSpec>()
init {
clear()
}
fun addTab(cls: Class<out Fragment>, args: Bundle?, name: String,
icon: Int?, position: Int, tag: String?) {
fun addTab(cls: Class<out Fragment>, args: Bundle? = null, name: String,
icon: DrawableHolder? = null, type: String? = null, position: Int = 0, tag: String? = null) {
addTab(SupportTabSpec(name = name, icon = icon, cls = cls, args = args,
position = position, tag = tag))
}
fun addTab(cls: Class<out Fragment>, args: Bundle?, name: String,
icon: Int?, type: String, position: Int, tag: String?) {
addTab(SupportTabSpec(name, icon, type, cls, args, position, tag))
position = position, type = type, tag = tag))
}
fun addTab(spec: SupportTabSpec) {
mTabs.add(spec)
tab.add(spec)
notifyDataSetChanged()
}
fun addTabs(specs: Collection<SupportTabSpec>) {
mTabs.addAll(specs)
tab.addAll(specs)
notifyDataSetChanged()
}
fun clear() {
mTabs.clear()
tab.clear()
notifyDataSetChanged()
}
override fun getCount(): Int {
return mTabs.size
return tab.size
}
override fun getItemPosition(obj: Any?): Int {
@ -92,7 +88,7 @@ class SupportTabsAdapter @JvmOverloads constructor(
}
override fun getPageTitle(position: Int): CharSequence? {
return mTabs[position].name
return tab[position].name
}
override fun getPageWidth(position: Int): Float {
@ -100,8 +96,8 @@ class SupportTabsAdapter @JvmOverloads constructor(
}
override fun getItem(position: Int): Fragment {
val fragment = Fragment.instantiate(context, mTabs[position].cls.name)
fragment.arguments = getPageArguments(mTabs[position], position)
val fragment = Fragment.instantiate(context, tab[position].cls.name)
fragment.arguments = getPageArguments(tab[position], position)
return fragment
}
@ -110,15 +106,15 @@ class SupportTabsAdapter @JvmOverloads constructor(
}
override fun getPageIcon(position: Int): Drawable {
return getTabIconDrawable(context, mTabs[position].icon)
return getTabIconDrawable(context, tab[position].icon)
}
fun getTab(position: Int): SupportTabSpec {
return mTabs[position]
return tab[position]
}
val tabs: List<SupportTabSpec>
get() = mTabs
get() = tab
override fun onPageReselected(position: Int) {
if (context !is SupportFragmentCallback) return
@ -143,10 +139,7 @@ class SupportTabsAdapter @JvmOverloads constructor(
}
fun setTabLabel(position: Int, label: CharSequence) {
for (spec in mTabs) {
if (position == spec.position)
spec.name = label
}
tab.filter { position == it.position }.forEach { it.name = label }
notifyDataSetChanged()
}

View File

@ -250,7 +250,7 @@ class AccountsManagerFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>,
override fun onClick(dialog: DialogInterface, which: Int) {
val id = arguments.getLong(EXTRA_ID)
val resolver = contentResolver
val resolver = context.contentResolver
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val where = Expression.equalsArgs(Accounts._ID).sql

View File

@ -104,7 +104,7 @@ class AddStatusFilterDialogFragment : BaseDialogFragment() {
sourceValues.add(values)
}
}
val resolver = contentResolver
val resolver = context.contentResolver
ContentResolverUtils.bulkDelete(resolver, Filters.Users.CONTENT_URI, Filters.Users.USER_KEY, userKeys, null)
ContentResolverUtils.bulkDelete(resolver, Filters.Keywords.CONTENT_URI, Filters.Keywords.VALUE, keywords, null)
ContentResolverUtils.bulkDelete(resolver, Filters.Sources.CONTENT_URI, Filters.Sources.VALUE, sources, null)

View File

@ -19,11 +19,9 @@
package org.mariotaku.twidere.fragment
import android.content.ContentResolver
import android.content.Context
import android.support.v4.app.DialogFragment
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
@ -41,23 +39,6 @@ open class BaseDialogFragment : DialogFragment(), Constants {
@Inject
lateinit var keyboardShortcutsHandler: KeyboardShortcutsHandler
val application: TwidereApplication?
get() {
val activity = activity
if (activity != null) return activity.application as TwidereApplication
return null
}
val contentResolver: ContentResolver
get() = activity.contentResolver
fun getSystemService(name: String): Any? {
val activity = activity
if (activity != null) return activity.getSystemService(name)
return null
}
override fun onAttach(context: Context?) {
super.onAttach(context)
GeneralComponentHelper.build(context!!).inject(this)

View File

@ -230,9 +230,8 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
if (TextUtils.isEmpty(text)) return
val values = ContentValues()
values.put(Filters.VALUE, text)
val args = arguments
val uri = args.getParcelable<Uri>(EXTRA_URI)!!
contentResolver.insert(uri, values)
val uri: Uri = arguments.getParcelable(EXTRA_URI)
context.contentResolver.insert(uri, values)
}
}

View File

@ -77,11 +77,6 @@ open class BaseSupportFragment : Fragment(), IBaseFragment {
return activity!!.contentResolver!!
}
fun invalidateOptionsMenu() {
val activity = activity ?: return
activity.supportInvalidateOptionsMenu()
}
override val extraConfiguration: Bundle?
get() {
return null

View File

@ -20,8 +20,10 @@
package org.mariotaku.twidere.fragment
import android.app.Activity
import android.app.Dialog
import android.content.ContentValues
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.database.Cursor
import android.graphics.Paint
@ -30,35 +32,41 @@ import android.os.Bundle
import android.support.v4.app.LoaderManager.LoaderCallbacks
import android.support.v4.content.CursorLoader
import android.support.v4.content.Loader
import android.support.v4.content.res.ResourcesCompat
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.*
import android.widget.AbsListView
import android.widget.*
import android.widget.AbsListView.MultiChoiceModeListener
import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener
import android.widget.ListView
import com.afollestad.appthemeengine.ATEActivity
import com.afollestad.appthemeengine.Config
import com.mobeta.android.dslv.SimpleDragSortCursorAdapter
import kotlinx.android.synthetic.main.layout_draggable_list_with_empty_view.*
import kotlinx.android.synthetic.main.list_item_section_header.view.*
import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.set
import org.mariotaku.sqliteqb.library.Columns.Column
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.RawItemArray
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.CustomTabEditorActivity
import org.mariotaku.twidere.activity.SettingsActivity
import org.mariotaku.twidere.model.CustomTabConfiguration
import org.mariotaku.twidere.model.CustomTabConfiguration.CustomTabConfigurationComparator
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter
import org.mariotaku.twidere.adapter.ArrayAdapter
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.model.ParcelableAccount
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.TabCursorIndices
import org.mariotaku.twidere.model.TabValuesCreator
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.model.tab.TabConfiguration
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs
import org.mariotaku.twidere.util.CustomTabUtils
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.view.holder.TwoLineWithIconViewHolder
import java.util.*
class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, MultiChoiceModeListener, OnItemClickListener {
class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, MultiChoiceModeListener {
private var adapter: CustomTabsAdapter? = null
@ -81,7 +89,14 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
adapter = CustomTabsAdapter(context)
listView.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
listView.setMultiChoiceModeListener(this)
listView.onItemClickListener = this
listView.onItemClickListener = OnItemClickListener { parent, view, position, id ->
val tab = adapter!!.getTab(position)
val df = TabEditorDialogFragment()
df.arguments = Bundle {
this[EXTRA_OBJECT] = tab
}
df.show(fragmentManager, TabEditorDialogFragment.TAG_EDIT_TAB)
}
listView.adapter = adapter
listView.emptyView = emptyView
listView.setDropListener { from, to ->
@ -97,19 +112,6 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
setListShown(false)
}
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val c = adapter!!.cursor
c.moveToPosition(adapter!!.getCursorPosition(position))
val intent = Intent(INTENT_ACTION_EDIT_TAB)
intent.setClass(activity, CustomTabEditorActivity::class.java)
intent.putExtra(EXTRA_ID, c.getLong(c.getColumnIndex(Tabs._ID)))
intent.putExtra(EXTRA_TYPE, c.getString(c.getColumnIndex(Tabs.TYPE)))
intent.putExtra(EXTRA_NAME, c.getString(c.getColumnIndex(Tabs.NAME)))
intent.putExtra(EXTRA_ICON, c.getString(c.getColumnIndex(Tabs.ICON)))
intent.putExtra(EXTRA_EXTRAS, c.getString(c.getColumnIndex(Tabs.EXTRAS)))
startActivityForResult(intent, REQUEST_EDIT_TAB)
}
private fun setListShown(shown: Boolean) {
listContainer.visibility = if (shown) View.VISIBLE else View.GONE
progressContainer.visibility = if (shown) View.GONE else View.VISIBLE
@ -154,40 +156,40 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
return CursorLoader(activity, Tabs.CONTENT_URI, Tabs.COLUMNS, null, null, Tabs.DEFAULT_SORT_ORDER)
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater!!.inflate(R.menu.menu_custom_tabs, menu)
val res = resources
val activity = activity
val accountIds = DataStoreUtils.getAccountKeys(activity)
val itemAdd = menu!!.findItem(R.id.add_submenu)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_custom_tabs, menu)
val context = this.context
val accountIds = DataStoreUtils.getAccountKeys(context)
val itemAdd = menu.findItem(R.id.add_submenu)
if (itemAdd != null && itemAdd.hasSubMenu()) {
val subMenu = itemAdd.subMenu
subMenu.clear()
val map = CustomTabUtils.getConfigurationMap()
val tabs = ArrayList(
map.entries)
Collections.sort(tabs, CustomTabConfigurationComparator.SINGLETON)
for ((type, conf) in tabs) {
val accountIdRequired = conf.accountRequirement == CustomTabConfiguration.ACCOUNT_REQUIRED
val intent = Intent(INTENT_ACTION_ADD_TAB)
intent.setClass(activity, CustomTabEditorActivity::class.java)
intent.putExtra(EXTRA_TYPE, type)
val subItem = subMenu.add(conf.defaultTitle)
val disabledByNoAccount = accountIdRequired && accountIds.size == 0
val disabledByDuplicateTab = conf.isSingleTab && CustomTabUtils.isTabAdded(activity, type)
for ((type, conf) in TabConfiguration.all()) {
val accountIdRequired = (conf.accountRequirement and TabConfiguration.FLAG_ACCOUNT_REQUIRED) != 0
val subItem = subMenu.add(0, 0, conf.sortPosition, conf.name.createString(context))
val disabledByNoAccount = accountIdRequired && accountIds.isEmpty()
val disabledByDuplicateTab = conf.isSingleTab && CustomTabUtils.isTabAdded(context, type)
val shouldDisable = disabledByDuplicateTab || disabledByNoAccount
subItem.isVisible = !shouldDisable
subItem.isEnabled = !shouldDisable
val icon = ResourcesCompat.getDrawable(res, conf.defaultIcon, null)
if (icon != null && activity is ATEActivity) {
icon.mutate().setColorFilter(Config.textColorPrimary(activity,
activity.ateKey), Mode.SRC_ATOP)
val icon = conf.icon.createDrawable(context)
if (context is ATEActivity) {
icon.mutate().setColorFilter(Config.textColorPrimary(context, context.ateKey),
Mode.SRC_ATOP)
}
subItem.icon = icon
subItem.intent = intent
subItem.setOnMenuItemClickListener { item ->
val df = TabEditorDialogFragment()
df.arguments = Bundle {
this[EXTRA_TAB_TYPE] = type
val adapter = adapter!!
if (!adapter.isEmpty) {
this[EXTRA_TAB_POSITION] = adapter.getTab(adapter.count - 1).position + 1
}
}
df.show(fragmentManager, TabEditorDialogFragment.TAG_ADD_TAB)
return@setOnMenuItemClickListener true
}
}
}
}
@ -257,13 +259,180 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
mode.title = resources.getQuantityString(R.plurals.Nitems_selected, count, count)
}
class CustomTabsAdapter(context: Context) : SimpleDragSortCursorAdapter(context, R.layout.list_item_custom_tab, null, arrayOfNulls<String>(0), IntArray(0), 0) {
class TabEditorDialogFragment : BaseDialogFragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
}
private val mIconColor: Int
private var indices: CursorIndices? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setView(R.layout.dialog_custom_tab_editor)
builder.setPositiveButton(R.string.save, null)
builder.setNegativeButton(android.R.string.cancel, null)
@CustomTabType
val tabType: String
val tab: Tab
val conf: TabConfiguration
when (tag) {
TAG_ADD_TAB -> {
tabType = arguments.getString(EXTRA_TAB_TYPE)
tab = Tab()
conf = TabConfiguration.ofType(tabType)!!
tab.type = tabType
tab.icon = conf.icon.persistentKey
tab.position = arguments.getInt(EXTRA_TAB_POSITION)
}
TAG_EDIT_TAB -> {
tab = arguments.getParcelable(EXTRA_OBJECT)
tabType = tab.type
conf = TabConfiguration.ofType(tabType)!!
}
else -> {
throw AssertionError()
}
}
val dialog = builder.create()
dialog.setOnShowListener {
val tabName = dialog.findViewById(R.id.tabName) as EditText
val iconSpinner = dialog.findViewById(R.id.tab_icon_spinner) as Spinner
val accountSpinner = dialog.findViewById(R.id.account_spinner) as Spinner
val accountContainer = dialog.findViewById(R.id.account_container)!!
val accountSectionHeader = accountContainer.sectionHeader
val extraConfigContainer = dialog.findViewById(R.id.extra_config_container) as LinearLayout
val positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
val iconsAdapter = TabIconsAdapter(context)
val accountsAdapter = AccountsSpinnerAdapter(context)
iconSpinner.adapter = iconsAdapter
accountSpinner.adapter = accountsAdapter
iconsAdapter.setData(DrawableHolder.builtins())
tabName.setText(tab.name ?: conf.name.createString(context))
iconSpinner.setSelection(iconsAdapter.findPositionByKey(tab.icon))
accountSectionHeader.setText(R.string.account)
val hasAccount = conf.accountRequirement and TabConfiguration.FLAG_HAS_ACCOUNT != 0
if (hasAccount) {
accountContainer.visibility = View.VISIBLE
val accountIdRequired = conf.accountRequirement and TabConfiguration.FLAG_ACCOUNT_REQUIRED != 0
accountsAdapter.clear()
if (!accountIdRequired) {
accountsAdapter.add(ParcelableAccount.dummyCredentials())
}
val officialKeyOnly = arguments.getBoolean(EXTRA_OFFICIAL_KEY_ONLY, false)
accountsAdapter.addAll(DataStoreUtils.getCredentialsList(context, false, officialKeyOnly))
accountsAdapter.setDummyItemText(R.string.activated_accounts)
tab.arguments?.accountKeys?.firstOrNull()?.let { key ->
accountSpinner.setSelection(accountsAdapter.findPositionByKey(key))
}
} else {
accountContainer.visibility = View.GONE
}
val extraConfigurations = conf.getExtraConfigurations(context).orEmpty()
fun inflateHeader(title: String): View {
val headerView = LayoutInflater.from(context).inflate(R.layout.list_item_section_header,
extraConfigContainer, false)
headerView.sectionHeader.text = title
return headerView
}
extraConfigurations.forEachIndexed { idx, extraConf ->
extraConf.headerTitle?.let {
extraConfigContainer.addView(inflateHeader(it.createString(context)))
}
val view = extraConf.onCreateView(context, extraConfigContainer)
extraConf.onViewCreated(context, view, this)
conf.readExtraConfigurationFrom(tab, extraConf)
extraConfigContainer.addView(view)
}
positiveButton.setOnClickListener {
tab.name = tabName.text.toString()
tab.icon = (iconSpinner.selectedItem as DrawableHolder).persistentKey
tab.arguments = CustomTabUtils.newTabArguments(tabType)
if (hasAccount) {
val account = accountSpinner.selectedItem as ParcelableAccount
if (!account.is_dummy) {
tab.arguments?.accountKeys = arrayOf(account.account_key)
} else {
tab.arguments?.accountKeys = null
}
}
tab.extras = CustomTabUtils.newTabExtras(tabType)
extraConfigurations.forEach {
conf.applyExtraConfigurationTo(tab, it)
}
context.contentResolver.insert(Tabs.CONTENT_URI, TabValuesCreator.create(tab))
dismiss()
}
}
return dialog
}
companion object {
const val TAG_EDIT_TAB = "edit_tab"
const val TAG_ADD_TAB = "add_tab"
}
}
internal class TabIconsAdapter(context: Context) : ArrayAdapter<DrawableHolder>(context, R.layout.spinner_item_custom_tab_icon) {
private val iconColor: Int
init {
mIconColor = ThemeUtils.getThemeForegroundColor(context)
setDropDownViewResource(R.layout.list_item_two_line_small)
iconColor = ThemeUtils.getThemeForegroundColor(context)
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getDropDownView(position, convertView, parent)
view.findViewById(android.R.id.text2).visibility = View.GONE
val text1 = view.findViewById(android.R.id.text1) as TextView
val item = getItem(position)
text1.text = item.name
bindIconView(item, view)
return view
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent)
bindIconView(getItem(position), view)
return view
}
fun setData(list: List<DrawableHolder>?) {
clear()
if (list == null) return
addAll(list)
}
private fun bindIconView(item: DrawableHolder, view: View) {
val icon = view.findViewById(android.R.id.icon) as ImageView
icon.setColorFilter(iconColor, Mode.SRC_ATOP)
icon.setImageDrawable(item.createDrawable(icon.context))
}
fun findPositionByKey(key: String): Int {
return (0 until count).indexOfFirst { getItem(it).persistentKey == key }
}
}
class CustomTabsAdapter(context: Context) : SimpleDragSortCursorAdapter(context,
R.layout.list_item_custom_tab, null, emptyArray(), intArrayOf(), 0) {
private val iconColor: Int
private var indices: TabCursorIndices? = null
init {
iconColor = ThemeUtils.getThemeForegroundColor(context)
}
override fun bindView(view: View, context: Context?, cursor: Cursor) {
@ -284,19 +453,19 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
holder.text1.paintFlags = holder.text1.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
holder.text2.setText(R.string.invalid_tab)
}
val icon = CustomTabUtils.getTabIconDrawable(context, CustomTabUtils.getTabIconObject(iconKey))
val icon = CustomTabUtils.getTabIconDrawable(context, DrawableHolder.parse(iconKey))
holder.icon.visibility = View.VISIBLE
if (icon != null) {
holder.icon.setImageDrawable(icon)
} else {
holder.icon.setImageResource(R.drawable.ic_action_list)
}
holder.icon.setColorFilter(mIconColor, Mode.SRC_ATOP)
holder.icon.setColorFilter(iconColor, Mode.SRC_ATOP)
}
override fun changeCursor(cursor: Cursor?) {
if (cursor != null) {
indices = CursorIndices(cursor)
indices = TabCursorIndices(cursor)
}
super.changeCursor(cursor)
}
@ -311,20 +480,10 @@ class CustomTabsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, Mult
return view
}
internal class CursorIndices(cursor: Cursor) {
val _id: Int
val name: Int
val icon: Int
val type: Int
val arguments: Int
init {
_id = cursor.getColumnIndex(Tabs._ID)
icon = cursor.getColumnIndex(Tabs.ICON)
name = cursor.getColumnIndex(Tabs.NAME)
type = cursor.getColumnIndex(Tabs.TYPE)
arguments = cursor.getColumnIndex(Tabs.ARGUMENTS)
}
fun getTab(position: Int): Tab {
cursor.moveToPosition(position)
return indices!!.newObject(cursor)
}
}

View File

@ -20,13 +20,9 @@
package org.mariotaku.twidere.fragment
import android.os.Bundle
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.SupportTabsAdapter
import org.mariotaku.twidere.fragment.BaseFiltersFragment.FilteredKeywordsFragment
import org.mariotaku.twidere.fragment.BaseFiltersFragment.FilteredLinksFragment
import org.mariotaku.twidere.fragment.BaseFiltersFragment.FilteredSourcesFragment
import org.mariotaku.twidere.fragment.BaseFiltersFragment.FilteredUsersFragment
import org.mariotaku.twidere.fragment.BaseFiltersFragment.*
class FiltersFragment : AbsToolbarTabPagesFragment() {
@ -36,10 +32,10 @@ class FiltersFragment : AbsToolbarTabPagesFragment() {
}
override fun addTabs(adapter: SupportTabsAdapter) {
adapter.addTab(FilteredUsersFragment::class.java, null, getString(R.string.users), null, 0, null)
adapter.addTab(FilteredKeywordsFragment::class.java, null, getString(R.string.keywords), null, 1, null)
adapter.addTab(FilteredSourcesFragment::class.java, null, getString(R.string.sources), null, 2, null)
adapter.addTab(FilteredLinksFragment::class.java, null, getString(R.string.links), null, 3, null)
adapter.addTab(cls = FilteredUsersFragment::class.java, name = getString(R.string.users))
adapter.addTab(cls = FilteredKeywordsFragment::class.java, name = getString(R.string.keywords))
adapter.addTab(cls = FilteredSourcesFragment::class.java, name = getString(R.string.sources))
adapter.addTab(cls = FilteredLinksFragment::class.java, name = getString(R.string.links))
}
}

View File

@ -30,8 +30,8 @@ class GroupFragment : AbsToolbarTabPagesFragment(), LoaderCallbacks<SingleRespon
override fun addTabs(adapter: SupportTabsAdapter) {
val args = arguments
adapter.addTab(GroupTimelineFragment::class.java, args, getString(R.string.statuses), 0, 0, "statuses")
adapter.addTab(GroupMembersFragment::class.java, args, getString(R.string.members), 0, 1, "members")
adapter.addTab(cls = GroupTimelineFragment::class.java, args = args, name = getString(R.string.statuses), tag = "statuses")
adapter.addTab(cls = GroupMembersFragment::class.java, args = args, name = getString(R.string.members), tag = "members")
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -73,7 +73,7 @@ class GroupFragment : AbsToolbarTabPagesFragment(), LoaderCallbacks<SingleRespon
} else {
activity.setTitle(R.string.user_list)
}
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
}

View File

@ -32,7 +32,7 @@ class ListsFragment : AbsToolbarTabPagesFragment() {
}
override fun addTabs(adapter: SupportTabsAdapter) {
adapter.addTab(UserListsFragment::class.java, arguments, getString(R.string.follows), null, 0, null)
adapter.addTab(UserListMembershipsFragment::class.java, arguments, getString(R.string.belongs_to), 0, 1, null)
adapter.addTab(cls = UserListsFragment::class.java, args = arguments, name = getString(R.string.follows))
adapter.addTab(cls = UserListMembershipsFragment::class.java, args = arguments, name = getString(R.string.belongs_to))
}
}

View File

@ -38,6 +38,7 @@ import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCal
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.provider.RecentSearchProvider
import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory
@ -104,8 +105,10 @@ class SearchFragment : AbsToolbarTabPagesFragment(), RefreshScrollTopInterface,
override fun addTabs(adapter: SupportTabsAdapter) {
adapter.addTab(StatusesSearchFragment::class.java, arguments, getString(R.string.statuses), R.drawable.ic_action_twitter, 0, null)
adapter.addTab(SearchUsersFragment::class.java, arguments, getString(R.string.users), R.drawable.ic_action_user, 1, null)
adapter.addTab(cls = StatusesSearchFragment::class.java, args = arguments,
name = getString(R.string.statuses), icon = DrawableHolder.resource(R.drawable.ic_action_twitter))
adapter.addTab(cls = SearchUsersFragment::class.java, args = arguments,
name = getString(R.string.users), icon = DrawableHolder.resource(R.drawable.ic_action_user))
}

View File

@ -446,7 +446,7 @@ class StatusFragment : BaseSupportFragment(), LoaderCallbacks<SingleResponse<Par
setState(STATE_ERROR)
errorText.text = Utils.getErrorMessage(context, data.exception)
}
invalidateOptionsMenu()
activity.supportInvalidateOptionsMenu()
}
override fun onLoaderReset(loader: Loader<SingleResponse<ParcelableStatus>>) {

View File

@ -111,6 +111,7 @@ import org.mariotaku.twidere.model.message.FriendshipTaskEvent
import org.mariotaku.twidere.model.message.FriendshipUpdatedEvent
import org.mariotaku.twidere.model.message.ProfileUpdatedEvent
import org.mariotaku.twidere.model.message.TaskStateChangedEvent
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.model.util.*
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.util.*
@ -163,7 +164,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
private val friendshipLoaderCallbacks = object : LoaderCallbacks<SingleResponse<ParcelableRelationship>> {
override fun onCreateLoader(id: Int, args: Bundle): Loader<SingleResponse<ParcelableRelationship>> {
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
val accountKey = args.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
val user = args.getParcelable<ParcelableUser>(EXTRA_USER)
if (user != null && user.key == accountKey) {
@ -279,7 +280,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
} else {
relationship = userRelationship
}
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
followContainer.follow.isEnabled = userRelationship.blocking || !userRelationship.blocked_by
if (userRelationship.blocked_by) {
pagesErrorContainer!!.visibility = View.GONE
@ -520,7 +521,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
profileBirthdayBanner.visibility = View.GONE
}
updateTitleAlpha()
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
updateSubtitle()
}
@ -586,7 +587,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
@Subscribe
fun notifyTaskStateChanged(event: TaskStateChangedEvent) {
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -1348,16 +1349,16 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
tabArgs.putParcelable(EXTRA_USER_KEY, args.getParcelable<Parcelable>(EXTRA_USER_KEY))
tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME))
}
pagerAdapter!!.addTab(UserTimelineFragment::class.java, tabArgs, getString(R.string.statuses),
R.drawable.ic_action_quote, TAB_TYPE_STATUSES, TAB_POSITION_STATUSES, null)
pagerAdapter!!.addTab(UserMediaTimelineFragment::class.java, tabArgs, getString(R.string.media),
R.drawable.ic_action_gallery, TAB_TYPE_MEDIA, TAB_POSITION_MEDIA, null)
pagerAdapter!!.addTab(cls = UserTimelineFragment::class.java, args = tabArgs, name = getString(R.string.statuses),
icon = DrawableHolder.resource(R.drawable.ic_action_quote), type = TAB_TYPE_STATUSES, position = TAB_POSITION_STATUSES)
pagerAdapter!!.addTab(cls = UserMediaTimelineFragment::class.java, args = tabArgs, name = getString(R.string.media),
icon = DrawableHolder.resource(R.drawable.ic_action_gallery), type = TAB_TYPE_MEDIA, position = TAB_POSITION_MEDIA)
if (preferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) {
pagerAdapter!!.addTab(UserFavoritesFragment::class.java, tabArgs, getString(R.string.favorites),
R.drawable.ic_action_star, TAB_TYPE_FAVORITES, TAB_POSITION_FAVORITES, null)
pagerAdapter!!.addTab(cls = UserFavoritesFragment::class.java, args = tabArgs, name = getString(R.string.favorites),
icon = DrawableHolder.resource(R.drawable.ic_action_star), type = TAB_TYPE_FAVORITES, position = TAB_POSITION_FAVORITES)
} else {
pagerAdapter!!.addTab(UserFavoritesFragment::class.java, tabArgs, getString(R.string.likes),
R.drawable.ic_action_heart, TAB_TYPE_FAVORITES, TAB_POSITION_FAVORITES, null)
pagerAdapter!!.addTab(cls = UserFavoritesFragment::class.java, args = tabArgs, name = getString(R.string.likes),
icon = DrawableHolder.resource(R.drawable.ic_action_heart), type = TAB_TYPE_FAVORITES, position = TAB_POSITION_FAVORITES)
}
}

View File

@ -80,7 +80,7 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
} else {
activity.setTitle(R.string.user_list)
}
invalidateOptionsMenu()
activity.invalidateOptionsMenu()
}
fun getUserListInfo(omitIntentExtra: Boolean) {
@ -150,9 +150,9 @@ class UserListFragment : AbsToolbarTabPagesFragment(), OnClickListener, LoaderCa
tabArgs.putString(EXTRA_LIST_ID, args.getString(EXTRA_LIST_ID))
tabArgs.putString(EXTRA_LIST_NAME, args.getString(EXTRA_LIST_NAME))
}
adapter.addTab(UserListTimelineFragment::class.java, tabArgs, getString(R.string.statuses), null, 0, null)
adapter.addTab(UserListMembersFragment::class.java, tabArgs, getString(R.string.members), null, 1, null)
adapter.addTab(UserListSubscribersFragment::class.java, tabArgs, getString(R.string.subscribers), null, 2, null)
adapter.addTab(cls = UserListTimelineFragment::class.java, args = tabArgs, name = getString(R.string.statuses))
adapter.addTab(cls = UserListMembersFragment::class.java, args = tabArgs, name = getString(R.string.members))
adapter.addTab(cls = UserListSubscribersFragment::class.java, args = tabArgs, name = getString(R.string.subscribers))
}
override fun onStart() {

View File

@ -22,12 +22,13 @@ package org.mariotaku.twidere.model
import android.os.Bundle
import android.support.v4.app.Fragment
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.util.CompareUtils.bundleEquals
import org.mariotaku.twidere.util.CompareUtils.objectEquals
data class SupportTabSpec(
var name: CharSequence? = null,
var icon: Any? = null,
var icon: DrawableHolder? = null,
@CustomTabType val type: String? = null,
var cls: Class<out Fragment>,
var args: Bundle? = null,

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_header_title"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/element_spacing_xsmall"
android:paddingEnd="0dp"
android:paddingStart="@dimen/element_spacing_small"
android:paddingTop="@dimen/element_spacing_xsmall" />

View File

@ -1,158 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:dividerVertical"
android:orientation="vertical"
android:showDividers="middle"
tools:context=".activity.EditCustomTabActivity"
tools:ignore="UselessParent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/element_spacing_large">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Spinner
android:id="@+id/tab_icon_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_normal"
tools:listitem="@layout/spinner_item_custom_tab_icon"/>
<com.rengwuxian.materialedittext.MaterialEditText
android:id="@+id/tab_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_normal"
android:hint="@string/name"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
app:met_baseColor="?android:colorForeground"
app:met_floatingLabelAlwaysShown="true"/>
</LinearLayout>
<LinearLayout
android:id="@+id/account_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="text_color|accent_color"
android:text="@string/account"/>
<Spinner
android:id="@+id/account_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_normal"
tools:listitem="@layout/list_item_two_line_small"/>
</LinearLayout>
<LinearLayout
android:id="@+id/secondary_field_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/secondary_field_label"
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="text_color|accent_color"
android:text="@string/user"/>
<FrameLayout
android:id="@+id/secondary_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:clickable="true"
android:onClick="onClick"
android:padding="@dimen/element_spacing_normal">
<include layout="@layout/list_item_two_line_small"/>
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/extra_configurations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/extra_configurations_label"
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="text_color|accent_color"
android:text="@string/extra_configurations"/>
<LinearLayout
android:id="@+id/extra_configurations_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@android:color/darker_gray"
android:dividerPadding="0.4dp"
android:orientation="vertical"
android:padding="@dimen/element_spacing_normal"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
<Button
android:id="@+id/save"
style="?android:borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/button_bar_height"
android:layout_gravity="center_horizontal"
android:layout_weight="0"
android:gravity="center"
android:onClick="onClick"
android:text="@android:string/ok"/>
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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:orientation="vertical"
android:padding="@dimen/element_spacing_large">
<LinearLayout
android:id="@+id/name_icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Spinner
android:id="@+id/tab_icon_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_normal"
tools:listitem="@layout/spinner_item_custom_tab_icon"/>
<com.rengwuxian.materialedittext.MaterialEditText
android:id="@+id/tabName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_normal"
android:hint="@string/name"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
app:met_baseColor="?android:colorForeground"
app:met_floatingLabelAlwaysShown="true"/>
</LinearLayout>
<LinearLayout
android:id="@+id/account_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/list_item_section_header"/>
<Spinner
android:id="@+id/account_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_normal"
tools:listitem="@layout/list_item_two_line_small"/>
</LinearLayout>
<LinearLayout
android:id="@+id/extra_config_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/element_spacing_normal"/>
</LinearLayout>

View File

@ -17,7 +17,8 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
@ -35,8 +36,9 @@
android:layout_marginStart="8dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:textColorPrimary"/>
<CheckBox
android:id="@android:id/checkbox"
@ -49,6 +51,6 @@
android:layout_weight="0"
android:clickable="false"
android:focusable="false"
android:visibility="gone" />
android:visibility="gone"/>
</LinearLayout>

View File

@ -1,29 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/sectionHeader"
style="?android:listSeparatorTextViewStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list_header_title"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/element_spacing_xsmall"
android:paddingLeft="@dimen/element_spacing_small"
android:paddingRight="0dp"
android:paddingTop="@dimen/element_spacing_xsmall"
tools:ignore="RtlHardcoded" />
android:tag="text_color|accent_color"
tools:showIn="@layout/dialog_custom_tab_editor"/>

View File

@ -23,7 +23,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:listPreferredItemHeightSmall"
android:minHeight="?android:listPreferredItemHeight"
android:orientation="horizontal"
android:padding="@dimen/element_spacing_normal">