diff options
Diffstat (limited to 'app/src/main/java/net/lacolaco/smileessence/view')
63 files changed, 3555 insertions, 3894 deletions
diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.java b/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.java deleted file mode 100644 index 33fc05ac..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.lacolaco.smileessence.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.widget.RelativeLayout; -import net.lacolaco.smileessence.R; - - -public class ColoredRelativeLayout extends RelativeLayout { - public static final int HIGHLIGHT_NONE = 0; - private final Paint paint = new Paint(); - private int highlightColors[] = new int[4]; - private boolean showAccent = false; - - public ColoredRelativeLayout(Context context) { - this(context, null); - } - - public ColoredRelativeLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ColoredRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public ColoredRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - - setWillNotDraw(false); - - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColoredRelativeLayout); - highlightColors[0] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_none, R.color.black); - highlightColors[1] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type1, R.color.orange); - highlightColors[2] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type2, R.color.green); - highlightColors[3] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type3, R.color.metro_blue); - setHighlight(HIGHLIGHT_NONE); - paint.setColor(ta.getColor(R.styleable.ColoredRelativeLayout_accent_color, R.color.red)); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(3); - ta.recycle(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (showAccent) - canvas.drawLine(1, 0, 1, getMeasuredHeight(), paint); - } - - public void setAccentVisibility(boolean yes) { - showAccent = yes; - } - - public void setHighlight(int type) { - setBackgroundColor(highlightColors[type]); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.kt b/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.kt new file mode 100644 index 00000000..0bf69378 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/ColoredRelativeLayout.kt @@ -0,0 +1,50 @@ +package net.lacolaco.smileessence.view + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.Canvas +import android.graphics.Paint +import android.util.AttributeSet +import android.widget.RelativeLayout +import net.lacolaco.smileessence.R + + +class ColoredRelativeLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : RelativeLayout(context, attrs, defStyleAttr, defStyleRes) { + private val paint = Paint() + private val highlightColors = IntArray(4) + private var showAccent = false + + init { + + setWillNotDraw(false) + + val ta = context.obtainStyledAttributes(attrs, R.styleable.ColoredRelativeLayout) + highlightColors[0] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_none, R.color.black) + highlightColors[1] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type1, R.color.orange) + highlightColors[2] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type2, R.color.green) + highlightColors[3] = ta.getColor(R.styleable.ColoredRelativeLayout_highlight_type3, R.color.metro_blue) + setHighlight(HIGHLIGHT_NONE) + paint.color = ta.getColor(R.styleable.ColoredRelativeLayout_accent_color, R.color.red) + paint.style = Paint.Style.STROKE + paint.strokeWidth = 3f + ta.recycle() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + if (showAccent) + canvas.drawLine(1f, 0f, 1f, measuredHeight.toFloat(), paint) + } + + fun setAccentVisibility(yes: Boolean) { + showAccent = yes + } + + fun setHighlight(type: Int) { + setBackgroundColor(highlightColors[type]) + } + + companion object { + val HIGHLIGHT_NONE = 0 + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/DialogHelper.java b/app/src/main/java/net/lacolaco/smileessence/view/DialogHelper.kt index 3a0003d6..c9b4a428 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/DialogHelper.java +++ b/app/src/main/java/net/lacolaco/smileessence/view/DialogHelper.kt @@ -22,38 +22,38 @@ * SOFTWARE. */ -package net.lacolaco.smileessence.view; +package net.lacolaco.smileessence.view -import android.app.Activity; -import net.lacolaco.smileessence.view.dialog.StackableDialogFragment; +import android.app.Activity +import net.lacolaco.smileessence.view.dialog.StackableDialogFragment -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; +import java.util.ArrayList +import java.util.LinkedHashSet +import java.util.UUID /** * DialogFragment のタグの一覧を保持し「全てのダイアログを閉じる」機能を提供するヘルパークラス */ -public class DialogHelper { - private static Set<String> dialogStack = new LinkedHashSet<>(); +object DialogHelper { + private val dialogStack = LinkedHashSet<String>() - public synchronized static void closeAll(Activity activity) { - for (String tag : new ArrayList<>(dialogStack)) { - StackableDialogFragment dialogFragment = (StackableDialogFragment) activity.getFragmentManager().findFragmentByTag(tag); - if (dialogFragment != null) { - dialogFragment.dismiss(); - } + @Synchronized + fun closeAll(activity: Activity) { + for (tag in ArrayList(dialogStack)) { + val dialogFragment = activity.fragmentManager.findFragmentByTag(tag) as StackableDialogFragment? + dialogFragment?.dismiss() } } - public synchronized static int showDialog(Activity activity, StackableDialogFragment dialogFragment) { - String tag = "stackingDialog:" + dialogFragment.getClass().getSimpleName() + ":" + UUID.randomUUID(); - dialogStack.add(tag); - return dialogFragment.show(activity.getFragmentManager().beginTransaction(), tag); + @Synchronized + fun showDialog(activity: Activity, dialogFragment: StackableDialogFragment): Int { + val tag = "stackingDialog:" + dialogFragment.javaClass.simpleName + ":" + UUID.randomUUID() + dialogStack.add(tag) + return dialogFragment.show(activity.fragmentManager.beginTransaction(), tag) } - public synchronized static void unregisterDialog(String tag) { - dialogStack.remove(tag); + @Synchronized + fun unregisterDialog(tag: String) { + dialogStack.remove(tag) } } diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.java b/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.java deleted file mode 100644 index e0a4a651..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.lacolaco.smileessence.view; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.ViewGroup; -import android.widget.ListView; - -public class ExpandedListView extends ListView { - public ExpandedListView(Context context) { - super(context); - } - - public ExpandedListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ExpandedListView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); - super.onMeasure(widthMeasureSpec, expandSpec); - - ViewGroup.LayoutParams params = getLayoutParams(); - params.height = getMeasuredHeight(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.kt b/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.kt new file mode 100644 index 00000000..288354ba --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/ExpandedListView.kt @@ -0,0 +1,23 @@ +package net.lacolaco.smileessence.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.ListView + +class ExpandedListView : ListView { + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + public override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val expandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE shr 2, View.MeasureSpec.AT_MOST) + super.onMeasure(widthMeasureSpec, expandSpec) + + val params = layoutParams + params.height = measuredHeight + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/Partials.java b/app/src/main/java/net/lacolaco/smileessence/view/Partials.java deleted file mode 100644 index fc0e35cb..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/Partials.java +++ /dev/null @@ -1,229 +0,0 @@ -package net.lacolaco.smileessence.view; - -import android.app.Activity; -import android.text.Html; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; -import com.android.volley.toolbox.NetworkImageView; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.data.Account; -import net.lacolaco.smileessence.data.ImageCache; -import net.lacolaco.smileessence.entity.DirectMessage; -import net.lacolaco.smileessence.entity.RBinding; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.util.StringUtils; -import net.lacolaco.smileessence.util.UIObserverBundle; -import net.lacolaco.smileessence.view.adapter.CustomListAdapter; -import net.lacolaco.smileessence.view.dialog.MessageDetailDialogFragment; -import net.lacolaco.smileessence.view.dialog.StatusDetailDialogFragment; -import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment; -import net.lacolaco.smileessence.view.listener.ListItemClickListener; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -public class Partials { - public static View getTweetView(Tweet tweet, Activity activity, View convertView, boolean expandEmbeddedTweets) { - if (convertView == null) { - convertView = activity.getLayoutInflater().inflate(R.layout.list_item_status, null); - } - UIObserverBundle bundle = (UIObserverBundle) convertView.getTag(); - if (bundle != null) { - bundle.detachAll(); - } else { - bundle = new UIObserverBundle(); - convertView.setTag(bundle); - } - - convertView.setOnClickListener(new ListItemClickListener(activity, () -> DialogHelper.showDialog(activity, StatusDetailDialogFragment.newInstance(tweet)))); - - updateViewUser(tweet, activity, convertView); - updateViewBody(tweet, activity, convertView); - updateViewFavorited(tweet, convertView); - updateViewEmbeddeds(tweet, activity, convertView, expandEmbeddedTweets); - - final WeakReference<View> weakView = new WeakReference<>(convertView); - final WeakReference<Activity> weakActivity = new WeakReference<>(activity); - bundle.attach(tweet.getOriginalTweet(), changes -> { - View strongView = weakView.get(); - if (strongView != null && changes.contains(RBinding.FAVORITERS)) - updateViewFavorited(tweet, strongView); - }); - bundle.attach(tweet.getUser(), changes -> { - View strongView = weakView.get(); - Activity strongActivity = weakActivity.get(); - if (strongView != null && strongActivity != null && changes.contains(RBinding.BASIC)) - updateViewUser(tweet, strongActivity, strongView); - }); - - return convertView; - } - - - private static void updateViewEmbeddeds(Tweet tweet, Activity activity, View convertView, boolean expandEmbeddedTweets) { - final ListView embeddedStatus = (ListView) convertView.findViewById(R.id.listview_status_embedded_status); - if (expandEmbeddedTweets) { - final List<Tweet> list = new ArrayList<>(); - CustomListAdapter<Tweet> embeddedTweetsAdapter = new CustomListAdapter<Tweet>() { - @Override - protected List<Tweet> getList() { - return list; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return Partials.getTweetView(getItem(position), activity, convertView, false); - } - }; - - Account account = Application.getCurrentWorld().getAccount(); - for (long id : tweet.getEmbeddedStatusIDs()) { - Tweet.fetchTask(id, account).onDone(t -> list.add(t)).execute(); - } - embeddedStatus.setAdapter(embeddedTweetsAdapter); - embeddedStatus.setVisibility(View.VISIBLE); - } else { - embeddedStatus.setAdapter(null); // view may be reused, set null explicitly - embeddedStatus.setVisibility(View.GONE); - } - } - - private static void updateViewUser(Tweet tweet, Activity activity, View convertView) { - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - - NetworkImageView icon = (NetworkImageView) convertView.findViewById(R.id.imageview_status_icon); - String iconUrl = tweet.getOriginalTweet().getUser().getProfileImageUrlOriginal(); - icon.setImageUrl(iconUrl, ImageCache.getImageLoader()); - icon.setOnClickListener(v -> onIconClick(tweet, activity)); - - TextView header = (TextView) convertView.findViewById(R.id.textview_status_header); - header.setTextSize(textSize); - header.setText(tweet.getOriginalTweet().getUser().getFormattedName()); - - ((ColoredRelativeLayout) convertView).setAccentVisibility(tweet.getUser() == Application.getCurrentWorld().getAccount().getUser()); - } - - private static void updateViewBody(Tweet tweet, Activity activity, View convertView) { - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - - TextView content = (TextView) convertView.findViewById(R.id.textview_status_text); - content.setTextSize(textSize); - String rawText = tweet.getOriginalTweet().getText(); - content.setText(rawText); - TextView footer = (TextView) convertView.findViewById(R.id.textview_status_footer); - footer.setTextSize(textSize - 2); - footer.setText(getFooterText(tweet)); - - ColoredRelativeLayout typedView = (ColoredRelativeLayout) convertView; - if (tweet.isRetweet()) { - typedView.setHighlight(2); - } else if (tweet.getOriginalTweet().getMentions().contains(Application.getCurrentWorld().getAccount().getUser().getScreenName())) { - typedView.setHighlight(1); - } else { - typedView.setHighlight(0); - } - } - - private static void updateViewFavorited(Tweet tweet, View convertView) { - ImageView favorited = (ImageView) convertView.findViewById(R.id.imageview_status_favorited); - favorited.setVisibility(tweet.isFavoritedBy(Application.getCurrentWorld().getAccount().getUserId()) ? View.VISIBLE : View.GONE); - } - - private static void onIconClick(Tweet tweet, Activity activity) { - UserDetailDialogFragment dialogFragment = new UserDetailDialogFragment(); - dialogFragment.setUserID(tweet.getOriginalTweet().getUser().getId()); - DialogHelper.showDialog(activity, dialogFragment); - } - - private static String getFooterText(Tweet tweet) { - StringBuilder builder = new StringBuilder(); - if (tweet.isRetweet()) { - builder - .append("(RT: ") - .append(tweet.getUser().getScreenName()) - .append(") "); - } - builder.append(StringUtils.dateToString(tweet.getOriginalTweet().getCreatedAt())); - builder.append(" via "); - builder.append(Html.fromHtml(tweet.getOriginalTweet().getSource())); - return builder.toString(); - } - - public static View getDirectMessageView(DirectMessage directMessage, Activity activity, View convertView) { - if (convertView == null) { - convertView = activity.getLayoutInflater().inflate(R.layout.list_item_status, null); - } - UIObserverBundle bundle = (UIObserverBundle) convertView.getTag(); - if (bundle != null) { - bundle.detachAll(); - } else { - bundle = new UIObserverBundle(); - convertView.setTag(bundle); - } - - convertView.setOnClickListener(new ListItemClickListener(activity, () -> DialogHelper.showDialog(activity, MessageDetailDialogFragment.newInstance(directMessage)))); - - ImageView favorited = (ImageView) convertView.findViewById(R.id.imageview_status_favorited); - favorited.setVisibility(View.GONE); - - updateViewSender(directMessage, activity, convertView); - updateViewBody(directMessage, activity, convertView); - - final WeakReference<View> weakView = new WeakReference<>(convertView); - final WeakReference<MainActivity> weakActivity = new WeakReference<>((MainActivity) activity); - bundle.attach(directMessage.getSender(), changes -> { - View strongView = weakView.get(); - MainActivity strongActivity = weakActivity.get(); - if (strongView != null && strongActivity != null && changes.contains(RBinding.BASIC)) - updateViewSender(directMessage, strongActivity, strongView); - }); - - return convertView; - } - - private static String getFooterText(DirectMessage directMessage, Account account) { - StringBuilder builder = new StringBuilder(); - builder.append(StringUtils.dateToString(directMessage.getCreatedAt())); - if (directMessage.getSender().getId() == account.getUserId()) { - builder.append(" to @").append(directMessage.getRecipient().getScreenName()); - } - return builder.toString(); - } - - private static void updateViewSender(DirectMessage directMessage, Activity activity, View convertView) { - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - - NetworkImageView icon = (NetworkImageView) convertView.findViewById(R.id.imageview_status_icon); - String iconUrl = directMessage.getSender().getProfileImageUrlOriginal(); - icon.setImageUrl(iconUrl, ImageCache.getImageLoader()); - icon.setOnClickListener(v -> { - UserDetailDialogFragment dialogFragment = new UserDetailDialogFragment(); - dialogFragment.setUserID(directMessage.getSender().getId()); - DialogHelper.showDialog(activity, dialogFragment); - }); - - TextView header = (TextView) convertView.findViewById(R.id.textview_status_header); - header.setTextSize(textSize); - header.setText(directMessage.getSender().getFormattedName()); - - ((ColoredRelativeLayout) convertView).setAccentVisibility(directMessage.getSender() == Application.getCurrentWorld().getAccount().getUser()); - } - - private static void updateViewBody(DirectMessage directMessage, Activity activity, View convertView) { - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - - TextView content = (TextView) convertView.findViewById(R.id.textview_status_text); - content.setTextSize(textSize); - content.setText(directMessage.getText()); - TextView footer = (TextView) convertView.findViewById(R.id.textview_status_footer); - footer.setTextSize(textSize - 2); - footer.setText(getFooterText(directMessage, Application.getCurrentWorld().getAccount())); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/Partials.kt b/app/src/main/java/net/lacolaco/smileessence/view/Partials.kt new file mode 100644 index 00000000..27d8c3cd --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/Partials.kt @@ -0,0 +1,220 @@ +package net.lacolaco.smileessence.view + +import android.app.Activity +import android.text.Html +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.ListView +import android.widget.TextView +import com.android.volley.toolbox.NetworkImageView +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.data.Account +import net.lacolaco.smileessence.data.ImageCache +import net.lacolaco.smileessence.entity.DirectMessage +import net.lacolaco.smileessence.entity.RBinding +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.util.StringUtils +import net.lacolaco.smileessence.util.UIObserverBundle +import net.lacolaco.smileessence.view.adapter.CustomListAdapter +import net.lacolaco.smileessence.view.dialog.MessageDetailDialogFragment +import net.lacolaco.smileessence.view.dialog.StatusDetailDialogFragment +import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment +import net.lacolaco.smileessence.view.listener.ListItemClickListener + +import java.lang.ref.WeakReference +import java.util.ArrayList + +object Partials { + fun getTweetView(tweet: Tweet, activity: Activity, convertView: View?, expandEmbeddedTweets: Boolean): View { + var convertView: View = convertView ?: activity.layoutInflater.inflate(R.layout.list_item_status, null) + var bundle: UIObserverBundle? = convertView.tag as UIObserverBundle? + if (bundle != null) { + bundle.detachAll() + } else { + bundle = UIObserverBundle() + convertView.tag = bundle + } + + convertView.setOnClickListener(ListItemClickListener(activity) { DialogHelper.showDialog(activity, StatusDetailDialogFragment.newInstance(tweet)) }) + + updateViewUser(tweet, activity, convertView) + updateViewBody(tweet, activity, convertView) + updateViewFavorited(tweet, convertView) + updateViewEmbeddeds(tweet, activity, convertView, expandEmbeddedTweets) + + val weakView = WeakReference(convertView) + val weakActivity = WeakReference(activity) + bundle.attach(tweet.originalTweet) { changes -> + val strongView = weakView.get() + if (strongView != null && changes.contains(RBinding.FAVORITERS)) + updateViewFavorited(tweet, strongView) + } + bundle.attach(tweet.user) { changes -> + val strongView = weakView.get() + val strongActivity = weakActivity.get() + if (strongView != null && strongActivity != null && changes.contains(RBinding.BASIC)) + updateViewUser(tweet, strongActivity, strongView) + } + + return convertView + } + + + private fun updateViewEmbeddeds(tweet: Tweet, activity: Activity, convertView: View, expandEmbeddedTweets: Boolean) { + val embeddedStatus = convertView.findViewById(R.id.listview_status_embedded_status) as ListView + if (expandEmbeddedTweets) { + val list = ArrayList<Tweet>() + val embeddedTweetsAdapter = object : CustomListAdapter<Tweet>() { + override val list: List<Tweet> + get() = list + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View { + return Partials.getTweetView(getItem(position), activity, convertView, false) + } + } + + val account = Application.currentWorld!!.account + for (id in tweet.embeddedStatusIDs) { + Tweet.fetchTask(id, account).onDone { t -> list.add(t) }.execute() + } + embeddedStatus.adapter = embeddedTweetsAdapter + embeddedStatus.visibility = View.VISIBLE + } else { + embeddedStatus.adapter = null // view may be reused, set null explicitly + embeddedStatus.visibility = View.GONE + } + } + + private fun updateViewUser(tweet: Tweet, activity: Activity, convertView: View) { + val textSize = UserPreferenceHelper.instance.textSize + + val icon = convertView.findViewById(R.id.imageview_status_icon) as NetworkImageView + val iconUrl = tweet.originalTweet.user.profileImageUrl + icon.setImageUrl(iconUrl, ImageCache.getImageLoader()) + icon.setOnClickListener { v -> onIconClick(tweet, activity) } + + val header = convertView.findViewById(R.id.textview_status_header) as TextView + header.textSize = textSize.toFloat() + header.text = tweet.originalTweet.user.formattedName + + (convertView as ColoredRelativeLayout).setAccentVisibility(tweet.user === Application.currentWorld!!.account.user) + } + + private fun updateViewBody(tweet: Tweet, activity: Activity, convertView: View) { + val textSize = UserPreferenceHelper.instance.textSize + + val content = convertView.findViewById(R.id.textview_status_text) as TextView + content.textSize = textSize.toFloat() + val rawText = tweet.originalTweet.text + content.text = rawText + val footer = convertView.findViewById(R.id.textview_status_footer) as TextView + footer.textSize = (textSize - 2).toFloat() + footer.text = getFooterText(tweet) + + val typedView = convertView as ColoredRelativeLayout + if (tweet.isRetweet) { + typedView.setHighlight(2) + } else if (tweet.originalTweet.mentions.contains(Application.currentWorld!!.account.user.screenName)) { + typedView.setHighlight(1) + } else { + typedView.setHighlight(0) + } + } + + private fun updateViewFavorited(tweet: Tweet, convertView: View) { + val favorited = convertView.findViewById(R.id.imageview_status_favorited) as ImageView + favorited.visibility = if (tweet.isFavoritedBy(Application.currentWorld!!.account.userId)) View.VISIBLE else View.GONE + } + + private fun onIconClick(tweet: Tweet, activity: Activity) { + DialogHelper.showDialog(activity, UserDetailDialogFragment.newInstance(tweet.originalTweet.user)) + } + + private fun getFooterText(tweet: Tweet): String { + val builder = StringBuilder() + if (tweet.isRetweet) { + builder + .append("(RT: ") + .append(tweet.user.screenName) + .append(") ") + } + builder.append(StringUtils.dateToString(tweet.originalTweet.createdAt)) + builder.append(" via ") + builder.append(Html.fromHtml(tweet.originalTweet.source)) + return builder.toString() + } + + fun getDirectMessageView(directMessage: DirectMessage, activity: Activity, convertView: View?): View { + var convertView = convertView + if (convertView == null) { + convertView = activity.layoutInflater.inflate(R.layout.list_item_status, null) + } + var bundle: UIObserverBundle? = convertView!!.tag as UIObserverBundle? + if (bundle != null) { + bundle.detachAll() + } else { + bundle = UIObserverBundle() + convertView.tag = bundle + } + + convertView.setOnClickListener(ListItemClickListener(activity) { DialogHelper.showDialog(activity, MessageDetailDialogFragment.newInstance(directMessage)) }) + + val favorited = convertView.findViewById(R.id.imageview_status_favorited) as ImageView + favorited.visibility = View.GONE + + updateViewSender(directMessage, activity, convertView) + updateViewBody(directMessage, activity, convertView) + + val weakView = WeakReference(convertView) + val weakActivity = WeakReference(activity as MainActivity) + bundle.attach(directMessage.sender) { changes -> + val strongView = weakView.get() + val strongActivity = weakActivity.get() + if (strongView != null && strongActivity != null && changes.contains(RBinding.BASIC)) + updateViewSender(directMessage, strongActivity, strongView) + } + + return convertView + } + + private fun getFooterText(directMessage: DirectMessage, account: Account): String { + val builder = StringBuilder() + builder.append(StringUtils.dateToString(directMessage.createdAt)) + if (directMessage.sender.id == account.userId) { + builder.append(" to @").append(directMessage.recipient.screenName) + } + return builder.toString() + } + + private fun updateViewSender(directMessage: DirectMessage, activity: Activity, convertView: View) { + val textSize = UserPreferenceHelper.instance.textSize + + val icon = convertView.findViewById(R.id.imageview_status_icon) as NetworkImageView + val iconUrl = directMessage.sender.profileImageUrl + icon.setImageUrl(iconUrl, ImageCache.getImageLoader()) + icon.setOnClickListener { v -> + DialogHelper.showDialog(activity, UserDetailDialogFragment.newInstance(directMessage.sender)) + } + + val header = convertView.findViewById(R.id.textview_status_header) as TextView + header.textSize = textSize.toFloat() + header.text = directMessage.sender.formattedName + + (convertView as ColoredRelativeLayout).setAccentVisibility(directMessage.sender === Application.currentWorld!!.account.user) + } + + private fun updateViewBody(directMessage: DirectMessage, activity: Activity, convertView: View) { + val textSize = UserPreferenceHelper.instance.textSize + + val content = convertView.findViewById(R.id.textview_status_text) as TextView + content.textSize = textSize.toFloat() + content.text = directMessage.text + val footer = convertView.findViewById(R.id.textview_status_footer) as TextView + footer.textSize = (textSize - 2).toFloat() + footer.text = getFooterText(directMessage, Application.currentWorld!!.account) + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.java deleted file mode 100644 index 7ed9636b..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view; - -import android.app.AlertDialog; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.BuildConfig; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.LicenseActivity; - -import static android.content.SharedPreferences.OnSharedPreferenceChangeListener; - -public class SettingFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener, - Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String newValueStr = String.valueOf(newValue); - if (preference.getKey().contentEquals(getString(R.string.key_setting_text_size))) { - if (TextUtils.isDigitsOnly(newValueStr)) { - return true; - } else { - Application.toast(R.string.error_setting_text_size_not_number); - } - return false; - } else if (preference.getKey().contentEquals(getString(R.string.key_setting_theme))) { - Application.toast(R.string.notice_theme_changed); - } - return true; - } - - // --------------------- Interface OnPreferenceClickListener --------------------- - - @Override - public boolean onPreferenceClick(Preference preference) { - String key = preference.getKey(); - if (key.contentEquals(getString(R.string.key_setting_application_information))) { - openAppInfoDialog(); - } else if (key.contentEquals(getString(R.string.key_setting_licenses))) { - openLicenseActivity(); - } - return true; - } - - // --------------------- Interface OnSharedPreferenceChangeListener --------------------- - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - setSummaryCurrentValue(); - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.setting); - EditTextPreference textSizePreference = (EditTextPreference) findPreference(R.string.key_setting_text_size); - textSizePreference.setSummary(textSizePreference.getText()); - textSizePreference.setOnPreferenceChangeListener(this); - ListPreference themePreference = (ListPreference) findPreference(R.string.key_setting_theme); - themePreference.setSummary(themePreference.getEntry()); - themePreference.setOnPreferenceChangeListener(this); - Preference appInfoPreference = findPreference(R.string.key_setting_application_information); - appInfoPreference.setOnPreferenceClickListener(this); - appInfoPreference.setSummary(BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")"); - Preference license = findPreference(R.string.key_setting_licenses); - license.setOnPreferenceClickListener(this); - } - - @Override - public void onPause() { - super.onPause(); - SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); - } - - @Override - public void onResume() { - super.onResume(); - SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); - sharedPreferences.registerOnSharedPreferenceChangeListener(this); - } - - // -------------------------- OTHER METHODS -------------------------- - - public Preference findPreference(int preferenceResID) { - return findPreference(getString(preferenceResID)); - } - - private void openLicenseActivity() { - Intent intent = new Intent(getActivity(), LicenseActivity.class); - getActivity().startActivity(intent); - } - - private void openAppInfoDialog() { - LayoutInflater inflater = getActivity().getLayoutInflater(); - View contentView = inflater.inflate(R.layout.dialog_app_info, null); - TextView versionTextView = (TextView) contentView.findViewById(R.id.versionTextView); - versionTextView.setText(BuildConfig.VERSION_NAME + " (rev: " + BuildConfig.VERSION_CODE + "; upstream: " + getString(R.string.app_version_full) + ")"); - - new AlertDialog.Builder(getActivity()) - .setTitle(R.string.dialog_title_about) - .setView(contentView) - .setPositiveButton(R.string.alert_dialog_ok, (dialog, which) -> dialog.dismiss()) - .create().show(); - } - - private void setSummaryCurrentValue() { - EditTextPreference textSizePreference = (EditTextPreference) findPreference(R.string.key_setting_text_size); - textSizePreference.setSummary(textSizePreference.getText()); - ListPreference themePreference = (ListPreference) findPreference(R.string.key_setting_theme); - themePreference.setSummary(themePreference.getEntry()); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.kt new file mode 100644 index 00000000..05490f97 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/SettingFragment.kt @@ -0,0 +1,140 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view + +import android.app.AlertDialog +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.preference.EditTextPreference +import android.preference.ListPreference +import android.preference.Preference +import android.preference.PreferenceFragment +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.BuildConfig +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.LicenseActivity + +import android.content.SharedPreferences.OnSharedPreferenceChangeListener + +class SettingFragment : PreferenceFragment(), OnSharedPreferenceChangeListener, Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val newValueStr = newValue.toString() + if (preference.key.contentEquals(getString(R.string.key_setting_text_size))) { + if (TextUtils.isDigitsOnly(newValueStr)) { + return true + } else { + Application.toast(R.string.error_setting_text_size_not_number) + } + return false + } else if (preference.key.contentEquals(getString(R.string.key_setting_theme))) { + Application.toast(R.string.notice_theme_changed) + } + return true + } + + // --------------------- Interface OnPreferenceClickListener --------------------- + + override fun onPreferenceClick(preference: Preference): Boolean { + val key = preference.key + if (key.contentEquals(getString(R.string.key_setting_application_information))) { + openAppInfoDialog() + } else if (key.contentEquals(getString(R.string.key_setting_licenses))) { + openLicenseActivity() + } + return true + } + + // --------------------- Interface OnSharedPreferenceChangeListener --------------------- + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + setSummaryCurrentValue() + } + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + addPreferencesFromResource(R.xml.setting) + val textSizePreference = findPreference(R.string.key_setting_text_size) as EditTextPreference + textSizePreference.summary = textSizePreference.text + textSizePreference.onPreferenceChangeListener = this + val themePreference = findPreference(R.string.key_setting_theme) as ListPreference + themePreference.summary = themePreference.entry + themePreference.onPreferenceChangeListener = this + val appInfoPreference = findPreference(R.string.key_setting_application_information) + appInfoPreference.onPreferenceClickListener = this + appInfoPreference.summary = BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")" + val license = findPreference(R.string.key_setting_licenses) + license.onPreferenceClickListener = this + } + + override fun onPause() { + super.onPause() + val sharedPreferences = preferenceScreen.sharedPreferences + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) + } + + override fun onResume() { + super.onResume() + val sharedPreferences = preferenceScreen.sharedPreferences + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + } + + // -------------------------- OTHER METHODS -------------------------- + + fun findPreference(preferenceResID: Int): Preference { + return findPreference(getString(preferenceResID)) + } + + private fun openLicenseActivity() { + val intent = Intent(activity, LicenseActivity::class.java) + activity.startActivity(intent) + } + + private fun openAppInfoDialog() { + val inflater = activity.layoutInflater + val contentView = inflater.inflate(R.layout.dialog_app_info, null) + val versionTextView = contentView.findViewById(R.id.versionTextView) as TextView + versionTextView.text = BuildConfig.VERSION_NAME + " (rev: " + BuildConfig.VERSION_CODE + "; upstream: " + getString(R.string.app_version_full) + ")" + + AlertDialog.Builder(activity) + .setTitle(R.string.dialog_title_about) + .setView(contentView) + .setPositiveButton(R.string.alert_dialog_ok) { dialog, which -> dialog.dismiss() } + .create().show() + } + + private fun setSummaryCurrentValue() { + val textSizePreference = findPreference(R.string.key_setting_text_size) as EditTextPreference + textSizePreference.summary = textSizePreference.text + val themePreference = findPreference(R.string.key_setting_theme) as ListPreference + themePreference.summary = themePreference.entry + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.java b/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.java deleted file mode 100644 index d5358db5..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.lacolaco.smileessence.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.Button; -import net.lacolaco.smileessence.R; - -public class ThreeStateButton extends Button { - public static final int STATE_OFF = 0; - public static final int STATE_ON = 1; - public static final int STATE_LOCKED = 2; - - private int state = STATE_OFF; - private String texts[] = new String[3]; - private Drawable backgrounds[] = new Drawable[3]; - - public ThreeStateButton(Context context) { - this(context, null); - } - - public ThreeStateButton(Context context, AttributeSet attrs) { - this(context, attrs, android.R.attr.buttonStyle); - } - - public ThreeStateButton(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public ThreeStateButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ThreeStateButton); - texts[STATE_OFF] = ta.getString(R.styleable.ThreeStateButton_off_text); - texts[STATE_ON] = ta.getString(R.styleable.ThreeStateButton_on_text); - texts[STATE_LOCKED] = ta.getString(R.styleable.ThreeStateButton_locked_text); - backgrounds[STATE_OFF] = ta.getDrawable(R.styleable.ThreeStateButton_off_background); - backgrounds[STATE_ON] = ta.getDrawable(R.styleable.ThreeStateButton_on_background); - backgrounds[STATE_LOCKED] = ta.getDrawable(R.styleable.ThreeStateButton_locked_background); - ta.recycle(); - - setState(STATE_OFF); - } - - public int getState() { - return state; - } - - public void setState(int s) { - state = s; - setText(texts[state]); - setBackground(backgrounds[state]); - setEnabled(state != STATE_LOCKED); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.kt b/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.kt new file mode 100644 index 00000000..547aa84d --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/ThreeStateButton.kt @@ -0,0 +1,41 @@ +package net.lacolaco.smileessence.view + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.widget.Button +import net.lacolaco.smileessence.R + +class ThreeStateButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.buttonStyle, defStyleRes: Int = 0) : Button(context, attrs, defStyleAttr, defStyleRes) { + + var state = STATE_OFF + set(s) { + field = s + text = texts[this.state] + background = backgrounds[this.state] + isEnabled = this.state != STATE_LOCKED + } + private val texts = arrayOfNulls<String>(3) + private val backgrounds = arrayOfNulls<Drawable>(3) + + init { + + val ta = context.obtainStyledAttributes(attrs, R.styleable.ThreeStateButton) + texts[STATE_OFF] = ta.getString(R.styleable.ThreeStateButton_off_text) + texts[STATE_ON] = ta.getString(R.styleable.ThreeStateButton_on_text) + texts[STATE_LOCKED] = ta.getString(R.styleable.ThreeStateButton_locked_text) + backgrounds[STATE_OFF] = ta.getDrawable(R.styleable.ThreeStateButton_off_background) + backgrounds[STATE_ON] = ta.getDrawable(R.styleable.ThreeStateButton_on_background) + backgrounds[STATE_LOCKED] = ta.getDrawable(R.styleable.ThreeStateButton_locked_background) + ta.recycle() + + state = STATE_OFF + } + + companion object { + val STATE_OFF = 0 + val STATE_ON = 1 + val STATE_LOCKED = 2 + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.java b/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.java deleted file mode 100644 index b7b2d174..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.lacolaco.smileessence.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.ImageButton; -import net.lacolaco.smileessence.R; - -public class ToggleableImageButton extends ImageButton { - private Drawable onSrc, offSrc; - - public ToggleableImageButton(Context context) { - this(context, null); - } - - public ToggleableImageButton(Context context, AttributeSet attrs) { - this(context, attrs, android.R.attr.imageButtonStyle); - } - - public ToggleableImageButton(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public ToggleableImageButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ToggleableImageButton); - offSrc = ta.getDrawable(R.styleable.ToggleableImageButton_offSrc); - onSrc = ta.getDrawable(R.styleable.ToggleableImageButton_onSrc); - ta.recycle(); - } - - public void setState(boolean isOn) { - setImageDrawable(isOn ? onSrc : offSrc); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.kt b/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.kt new file mode 100644 index 00000000..47a1821a --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/ToggleableImageButton.kt @@ -0,0 +1,25 @@ +package net.lacolaco.smileessence.view + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.widget.ImageButton +import net.lacolaco.smileessence.R + +class ToggleableImageButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.imageButtonStyle, defStyleRes: Int = 0) : ImageButton(context, attrs, defStyleAttr, defStyleRes) { + private val onSrc: Drawable + private val offSrc: Drawable + + init { + + val ta = context.obtainStyledAttributes(attrs, R.styleable.ToggleableImageButton) + offSrc = ta.getDrawable(R.styleable.ToggleableImageButton_offSrc) + onSrc = ta.getDrawable(R.styleable.ToggleableImageButton_onSrc) + ta.recycle() + } + + fun setState(isOn: Boolean) { + setImageDrawable(if (isOn) onSrc else offSrc) + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.java deleted file mode 100644 index 6d7420fb..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.lacolaco.smileessence.view.adapter; - -import android.widget.BaseAdapter; -import net.lacolaco.smileessence.util.UIHandler; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class CustomListAdapter<T> extends BaseAdapter { - private boolean isNotifiable = true; - private List<T> frozenList = new ArrayList<>(); - - public final void setNotifiable(boolean notifiable) { - isNotifiable = notifiable; - } - - @Override - public final int getCount() { - return frozenList.size(); - } - - @Override - public final T getItem(int position) { - return frozenList.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public final void notifyDataSetChanged() { - frozenList = Collections.unmodifiableList(getList()); - super.notifyDataSetChanged(); - } - - protected abstract List<T> getList(); - - public void update() { - if (isNotifiable) { - updateForce(); - } - } - - public void updateForce() { - new UIHandler().post(this::notifyDataSetChanged); - } -}
\ No newline at end of file diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.kt new file mode 100644 index 00000000..3b022012 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/CustomListAdapter.kt @@ -0,0 +1,45 @@ +package net.lacolaco.smileessence.view.adapter + +import android.widget.BaseAdapter +import net.lacolaco.smileessence.util.UIHandler + +import java.util.ArrayList +import java.util.Collections + +abstract class CustomListAdapter<T> : BaseAdapter() { + private var isNotifiable = true + private var frozenList: List<T> = ArrayList() + + fun setNotifiable(notifiable: Boolean) { + isNotifiable = notifiable + } + + override fun getCount(): Int { + return frozenList.size + } + + override fun getItem(position: Int): T { + return frozenList[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun notifyDataSetChanged() { + frozenList = Collections.unmodifiableList(list) + super.notifyDataSetChanged() + } + + protected abstract val list: List<T> + + fun update() { + if (isNotifiable) { + updateForce() + } + } + + fun updateForce() { + UIHandler().post { this.notifyDataSetChanged() } + } +}
\ No newline at end of file diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.java deleted file mode 100644 index 3f98cfaa..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.adapter; - -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import com.android.volley.toolbox.NetworkImageView; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.data.ImageCache; -import net.lacolaco.smileessence.entity.Event; -import net.lacolaco.smileessence.entity.RBinding; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.util.StringUtils; -import net.lacolaco.smileessence.util.UIObserverBundle; -import net.lacolaco.smileessence.view.DialogHelper; -import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment; -import net.lacolaco.smileessence.view.listener.ListItemClickListener; - -import java.lang.ref.WeakReference; -import java.util.List; - -public class EventListAdapter extends CustomListAdapter<Event> { - private final World world; - private final Activity activity; - - public EventListAdapter(World world, Activity activity) { - super(); - this.world = world; - this.activity = activity; - } - - @Override - protected List<Event> getList() { - return world.getEvents(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Event event = getItem(position); - if (convertView == null) { - convertView = activity.getLayoutInflater().inflate(R.layout.list_item_status, null); - } - UIObserverBundle bundle = (UIObserverBundle) convertView.getTag(); - if (bundle != null) { - bundle.detachAll(); - } else { - bundle = new UIObserverBundle(); - convertView.setTag(bundle); - } - - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - - TextView header = (TextView) convertView.findViewById(R.id.textview_status_header); - header.setTextSize(textSize); - - updateViewUser(event, convertView); - - TextView content = (TextView) convertView.findViewById(R.id.textview_status_text); - content.setTextSize(textSize); - content.setText(event.getTargetObject() != null ? event.getTargetObject().getText() : ""); - TextView footer = (TextView) convertView.findViewById(R.id.textview_status_footer); - footer.setTextSize(textSize - 2); - footer.setText(StringUtils.dateToString(event.getCreatedAt())); - ImageView favorited = (ImageView) convertView.findViewById(R.id.imageview_status_favorited); - favorited.setVisibility(View.GONE); - convertView.setOnClickListener(new ListItemClickListener(activity, () -> { - UserDetailDialogFragment fragment = new UserDetailDialogFragment(); - fragment.setUserID(event.getSource().getId()); - DialogHelper.showDialog(activity, fragment); - })); - - final WeakReference<View> weakView = new WeakReference<>(convertView); - bundle.attach(event.getSource(), changes -> { - View strongView = weakView.get(); - if (strongView != null && changes.contains(RBinding.BASIC)) - updateViewUser(event, strongView); - }); - - return convertView; - } - - private void updateViewUser(Event event, View convertedView) { - NetworkImageView icon = (NetworkImageView) convertedView.findViewById(R.id.imageview_status_icon); - String iconUrl = event.getSource().getProfileImageUrlOriginal(); - icon.setImageUrl(iconUrl, ImageCache.getImageLoader()); - - TextView header = (TextView) convertedView.findViewById(R.id.textview_status_header); - header.setText(event.getFormattedString()); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.kt new file mode 100644 index 00000000..1c19d4ec --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/EventListAdapter.kt @@ -0,0 +1,102 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.adapter + +import android.app.Activity +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import com.android.volley.toolbox.NetworkImageView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.data.ImageCache +import net.lacolaco.smileessence.entity.Event +import net.lacolaco.smileessence.entity.RBinding +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.util.StringUtils +import net.lacolaco.smileessence.util.UIObserverBundle +import net.lacolaco.smileessence.view.DialogHelper +import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment +import net.lacolaco.smileessence.view.listener.ListItemClickListener + +import java.lang.ref.WeakReference + +class EventListAdapter(private val world: World, private val activity: Activity) : CustomListAdapter<Event>() { + override val list: List<Event> + get() = world.events + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + val event = getItem(position) + if (convertView == null) { + convertView = activity.layoutInflater.inflate(R.layout.list_item_status, null) + } + var bundle: UIObserverBundle? = convertView!!.tag as UIObserverBundle? + if (bundle != null) { + bundle.detachAll() + } else { + bundle = UIObserverBundle() + convertView.tag = bundle + } + + val textSize = UserPreferenceHelper.instance.textSize + + val header = convertView.findViewById(R.id.textview_status_header) as TextView + header.textSize = textSize.toFloat() + + updateViewUser(event, convertView) + + val content = convertView.findViewById(R.id.textview_status_text) as TextView + content.textSize = textSize.toFloat() + content.text = event.targetObject?.text ?: "" + val footer = convertView.findViewById(R.id.textview_status_footer) as TextView + footer.textSize = (textSize - 2).toFloat() + footer.text = StringUtils.dateToString(event.createdAt) + val favorited = convertView.findViewById(R.id.imageview_status_favorited) as ImageView + favorited.visibility = View.GONE + convertView.setOnClickListener(ListItemClickListener(activity) { + DialogHelper.showDialog(activity, UserDetailDialogFragment.newInstance(event.source)) + }) + + val weakView = WeakReference(convertView) + bundle.attach(event.source) { changes -> + val strongView = weakView.get() + if (strongView != null && changes.contains(RBinding.BASIC)) + updateViewUser(event, strongView) + } + + return convertView + } + + private fun updateViewUser(event: Event, convertedView: View) { + val icon = convertedView.findViewById(R.id.imageview_status_icon) as NetworkImageView + val iconUrl = event.source.profileImageUrl + icon.setImageUrl(iconUrl, ImageCache.getImageLoader()) + + val header = convertedView.findViewById(R.id.textview_status_header) as TextView + header.text = event.formattedString + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/HistoryFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/MessageListAdapter.kt index 5f04114e..d72ab564 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/HistoryFragment.java +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/MessageListAdapter.kt @@ -22,25 +22,22 @@ * SOFTWARE. */ -package net.lacolaco.smileessence.view.page; +package net.lacolaco.smileessence.view.adapter -import android.os.Bundle; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.view.adapter.EventListAdapter; +import android.app.Activity +import android.view.View +import android.view.ViewGroup +import net.lacolaco.smileessence.entity.DirectMessage +import net.lacolaco.smileessence.view.Partials -public class HistoryFragment extends CustomListFragment<EventListAdapter> { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); +class MessageListAdapter(private val activity: Activity) : OrderedCustomListAdapter<DirectMessage>() { + val lastID: Long + get() = if (count > 0) getItem(count - 1).id else -1 - World world = getWorld(); - final EventListAdapter adapter = new EventListAdapter(world, getActivity()); - setAdapter(adapter); - world.addEventNotifier(this, () -> adapter.update()); // XXX - } + val topID: Long + get() = if (count > 0) getItem(0).id else -1 - @Override - public void refresh() { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return Partials.getDirectMessageView(getItem(position), activity, convertView) } } diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.java deleted file mode 100644 index 3de58658..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.lacolaco.smileessence.view.adapter; - -import net.lacolaco.smileessence.entity.IdObject; - -import java.util.*; - -public abstract class OrderedCustomListAdapter<T extends IdObject> extends CustomListAdapter<T> { - private final Map<Long, T> treeMap; - - public OrderedCustomListAdapter() { - this(Long::compare); - } - - public OrderedCustomListAdapter(Comparator<Long> comparator) { - super(); - this.treeMap = new TreeMap<>(Collections.reverseOrder(comparator)); - } - - @Override - protected synchronized List<T> getList() { - return new ArrayList<>(treeMap.values()); - } - - public synchronized void add(T item) { - treeMap.put(item.getId(), item); - } - - public synchronized void addAll(Collection<T> items) { - for (T item : items) { - treeMap.put(item.getId(), item); - } - } - - public synchronized void clear() { - treeMap.clear(); - } - - public synchronized T remove(T item) { - return treeMap.remove(item.getId()); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.kt new file mode 100644 index 00000000..53b1c824 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/OrderedCustomListAdapter.kt @@ -0,0 +1,35 @@ +package net.lacolaco.smileessence.view.adapter + +import net.lacolaco.smileessence.entity.IdObject + +import java.util.* + +abstract class OrderedCustomListAdapter<T : IdObject> +constructor(comparator: Comparator<Long> = Comparator { x, y -> java.lang.Long.compare(x, y) }) : CustomListAdapter<T>() { + private val treeMap: MutableMap<Long, T> = TreeMap(Collections.reverseOrder(comparator)) + + override val list: List<T> + @Synchronized get() = ArrayList(treeMap.values) + + @Synchronized + fun add(item: T) { + treeMap.put(item.id, item) + } + + @Synchronized + fun addAll(items: Collection<T>) { + for (item in items) { + treeMap.put(item.id, item) + } + } + + @Synchronized + fun clear() { + treeMap.clear() + } + + @Synchronized + fun remove(item: T): T { + return treeMap.remove(item.id)!! + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.java deleted file mode 100644 index 76e72093..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.adapter; - -import android.os.Bundle; -import android.support.v13.app.FragmentPagerAdapter; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.logging.Logger; -import net.lacolaco.smileessence.view.page.PageFragment; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class PageListAdapter extends FragmentPagerAdapter { - - // ------------------------------ FIELDS ------------------------------ - - private final List<PageInfo> pages = new ArrayList<>(); - private final Map<Integer, WeakReference<PageFragment>> fragmentCache = new HashMap<>(); - - // --------------------------- FragmentPagerAdapter --------------------------- - - public PageListAdapter(MainActivity _activity) { - super(_activity.getFragmentManager()); - } - - @Override - public synchronized PageFragment getItem(int position) { - PageFragment pf; - PageInfo info = pages.get(position); - try { - pf = info.getFragmentClass().newInstance(); - } catch (Exception e) { - Logger.error("should not happen: fragmentClass is private or Android is broken?"); - e.printStackTrace(); - throw new RuntimeException(e); - } - pf.setArguments(info.getArgs()); - fragmentCache.put(position, new WeakReference<>(pf)); - return pf; - } - - public synchronized PageFragment getCachedFragment(int pos) { - WeakReference<PageFragment> wpf = fragmentCache.get(pos); - if (wpf == null) { - return null; - } else { - return wpf.get(); - } - } - - @Override - public synchronized int getCount() { - return pages.size(); - } - - // ------------------------ INTERFACE METHODS ------------------------ - - // -------------------------- OTHER METHODS -------------------------- - - public void addPage(Class<? extends PageFragment> klass, String name, Bundle args) { - this.addPage(klass, name, args, true); - } - - public void addPage(Class<? extends PageFragment> klass, String name, Bundle args, boolean notifyChanged) { - pages.add(new PageInfo(klass, name, args)); - if (notifyChanged) notifyDataSetChanged(); - } - - public synchronized boolean removePage(int position) { - //if (removePageWithoutNotify(position)) { - // refreshListNavigation(); - // return true; - //} - return pages.remove(position) != null; // TODO - } - - public String getName(int pos) { - return pages.get(pos).getName(); - } - - @Deprecated - public int getIndex(Class<? extends PageFragment> fragmentClass) { - for (int i = 0; i < pages.size(); ++i) { - if (pages.get(i).getFragmentClass() == fragmentClass) { - return i; - } - } - return -1; - } - - private static final class PageInfo { - private final Class<? extends PageFragment> fragmentClass; - private final Bundle args; - private final String name; - - PageInfo(Class<? extends PageFragment> _fragmentClass, String _name, Bundle _args) { - fragmentClass = _fragmentClass; - name = _name; - args = _args; - } - - public Class<? extends PageFragment> getFragmentClass() { - return fragmentClass; - } - - public String getName() { - return name; - } - - public Bundle getArgs() { - return args; - } - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.kt new file mode 100644 index 00000000..34d158f0 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.kt @@ -0,0 +1,110 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.adapter + +import android.os.Bundle +import android.support.v13.app.FragmentPagerAdapter +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.logging.Logger +import net.lacolaco.smileessence.view.page.PageFragment + +import java.lang.ref.WeakReference +import java.util.ArrayList +import java.util.HashMap + +class PageListAdapter +// --------------------------- FragmentPagerAdapter --------------------------- + +(_activity: MainActivity) : FragmentPagerAdapter(_activity.fragmentManager) { + + // ------------------------------ FIELDS ------------------------------ + + private val pages = ArrayList<PageInfo>() + private val fragmentCache = HashMap<Int, WeakReference<PageFragment<*>>>() + + @Synchronized override fun getItem(position: Int): PageFragment<*> { + val pf: PageFragment<*> + val info = pages[position] + try { + pf = info.fragmentClass.newInstance() + } catch (e: Exception) { + Logger.error("should not happen: fragmentClass is private or Android is broken?") + e.printStackTrace() + throw RuntimeException(e) + } + + pf.arguments = info.args + fragmentCache.put(position, WeakReference(pf)) + return pf + } + + @Synchronized + fun getCachedFragment(pos: Int): PageFragment<*>? { + val wpf = fragmentCache[pos] + return wpf?.get() + } + + @Synchronized override fun getCount(): Int { + return pages.size + } + + // ------------------------ INTERFACE METHODS ------------------------ + + // -------------------------- OTHER METHODS -------------------------- + + fun addPage(klass: Class<out PageFragment<*>>, name: String, args: Bundle) { + this.addPage(klass, name, args, true) + } + + fun addPage(klass: Class<out PageFragment<*>>, name: String, args: Bundle, notifyChanged: Boolean) { + pages.add(PageInfo(klass, name, args)) + if (notifyChanged) notifyDataSetChanged() + } + + @Synchronized + fun removePage(position: Int): Boolean { + //if (removePageWithoutNotify(position)) { + // refreshListNavigation(); + // return true; + //} + return pages.removeAt(position) != null // TODO + } + + fun getName(pos: Int): String { + return pages[pos].name + } + + @Deprecated("") + fun getIndex(fragmentClass: Class<out PageFragment<*>>): Int { + for (i in pages.indices) { + if (pages[i].fragmentClass == fragmentClass) { + return i + } + } + return -1 + } + + private class PageInfo internal constructor(val fragmentClass: Class<out PageFragment<*>>, val name: String, val args: Bundle) +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.java deleted file mode 100644 index 0dbe8d53..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.lacolaco.smileessence.view.adapter; - -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.view.Partials; - -import java.util.*; - -public class TimelineAdapter extends OrderedCustomListAdapter<Tweet> { - private final Activity activity; - - public TimelineAdapter(Activity activity) { - super(); - this.activity = activity; - } - - public long getLastID() { - if (getCount() > 0) { - return getItem(getCount() - 1).getId(); - } else { - return -1; - } - } - - public long getTopID() { - if (getCount() > 0) { - return getItem(0).getId(); - } else { - return -1; - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return Partials.getTweetView(getItem(position), activity, convertView, true); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.kt new file mode 100644 index 00000000..750b63a0 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/adapter/TimelineAdapter.kt @@ -0,0 +1,29 @@ +package net.lacolaco.smileessence.view.adapter + +import android.app.Activity +import android.view.View +import android.view.ViewGroup +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.view.Partials + +import java.util.* + +class TimelineAdapter(private val activity: Activity) : OrderedCustomListAdapter<Tweet>() { + val lastID: Long + get() = if (count > 0) { + getItem(count - 1).id + } else { + -1 + } + + val topID: Long + get() = if (count > 0) { + getItem(0).id + } else { + -1 + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return Partials.getTweetView(getItem(position), activity, convertView, true) + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.java deleted file mode 100644 index 74dcc468..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.view.DialogHelper; - -public abstract class ConfirmDialogFragment extends StackableDialogFragment { - - // ------------------------------ FIELDS ------------------------------ - - private static final String ARG_TEXT = "text"; - private final DialogInterface.OnClickListener listener = (dialog, which) -> { - onButtonClick(which); - dialog.dismiss(); - }; - private String text; - - // -------------------------- STATIC METHODS -------------------------- - - public static void show(Activity activity, String text, final Runnable onYes) { - show(activity, text, onYes, null, true); - } - - public static void show(Activity activity, String text, final Runnable onYes, boolean ignorable) { - show(activity, text, onYes, null, ignorable); - } - - public static void show(Activity activity, String text, final Runnable onOK, final Runnable onCancel, boolean ignorable) { - boolean confirm = UserPreferenceHelper.getInstance().get(R.string.key_setting_show_confirm_dialog, true); - if (!confirm && ignorable) { - onOK.run(); - return; - } - final ConfirmDialogFragment fragment = new ConfirmDialogFragment() { - @Override - public void onButtonClick(int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: { - if (onOK != null) { - dismiss(); - onOK.run(); - } - break; - } - case DialogInterface.BUTTON_NEGATIVE: { - if (onCancel != null) { - dismiss(); - onCancel.run(); - } - break; - } - } - } - }; - fragment.setText(text); - DialogHelper.showDialog(activity, fragment); - } - - // --------------------- GETTER / SETTER METHODS --------------------- - - public void setText(String text) { - Bundle args = new Bundle(); - args.putString(ARG_TEXT, text); - setArguments(args); - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle args = getArguments(); - if (args != null) { - text = args.getString(ARG_TEXT); - } - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return new AlertDialog.Builder(getActivity()).setTitle(text) - .setCancelable(false) - .setPositiveButton(R.string.alert_dialog_ok, listener) - .setNegativeButton(R.string.alert_dialog_cancel, listener) - .create(); - } - - // -------------------------- OTHER METHODS -------------------------- - - protected abstract void onButtonClick(int which); -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.kt new file mode 100644 index 00000000..094aa18a --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/ConfirmDialogFragment.kt @@ -0,0 +1,119 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.Activity +import android.app.AlertDialog +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.view.DialogHelper + +abstract class ConfirmDialogFragment : StackableDialogFragment() { + private var text: String? = null + + // --------------------- GETTER / SETTER METHODS --------------------- + + fun setText(text: String) { + val args = Bundle() + args.putString(ARG_TEXT, text) + arguments = args + } + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val args = arguments + if (args != null) { + text = args.getString(ARG_TEXT) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return AlertDialog.Builder(activity).setTitle(text) + .setCancelable(false) + .setPositiveButton(R.string.alert_dialog_ok, { dialog, which -> + onButtonClick(which) + dialog.dismiss() + }) + .setNegativeButton(R.string.alert_dialog_cancel, { dialog, which -> + onButtonClick(which) + dialog.dismiss() + }) + .create() + } + + // -------------------------- OTHER METHODS -------------------------- + + protected abstract fun onButtonClick(which: Int) + + companion object { + + // ------------------------------ FIELDS ------------------------------ + + private val ARG_TEXT = "text" + + // -------------------------- STATIC METHODS -------------------------- + + fun show(activity: Activity, text: String, onYes: () -> Unit) { + show(activity, text, onYes, null, true) + } + + fun show(activity: Activity, text: String, onYes: () -> Unit, ignorable: Boolean) { + show(activity, text, onYes, null, ignorable) + } + + fun show(activity: Activity, text: String, onOK: (() -> Unit)?, onCancel: (() -> Unit)?, ignorable: Boolean) { + val confirm = UserPreferenceHelper.instance.get(R.string.key_setting_show_confirm_dialog, true) + if (!confirm && ignorable) { + onOK!!() + return + } + val fragment = object : ConfirmDialogFragment() { + public override fun onButtonClick(which: Int) { + when (which) { + DialogInterface.BUTTON_POSITIVE -> { + if (onOK != null) { + dismiss() + onOK() + } + } + DialogInterface.BUTTON_NEGATIVE -> { + if (onCancel != null) { + dismiss() + onCancel() + } + } + } + } + } + fragment.setText(text) + DialogHelper.showDialog(activity, fragment) + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/DisposeDialog.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/DisposeDialog.kt index eb3d3613..3cd486bd 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/DisposeDialog.java +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/DisposeDialog.kt @@ -22,22 +22,16 @@ * SOFTWARE. */ -package net.lacolaco.smileessence.view.dialog; +package net.lacolaco.smileessence.view.dialog -import android.app.Dialog; -import android.content.Context; +import android.app.Dialog +import android.content.Context /** * Dialog for dispose soon. */ -public final class DisposeDialog extends Dialog { - - public DisposeDialog(Context context) { - super(context); - } - - @Override - public void show() { - dismiss(); +class DisposeDialog(context: Context) : Dialog(context) { + override fun show() { + dismiss() } } diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.java deleted file mode 100644 index 8ce25145..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.os.Bundle; -import android.view.View; -import android.widget.EditText; -import net.lacolaco.smileessence.R; - -public abstract class EditTextDialogFragment extends DialogFragment { - - // ------------------------------ FIELDS ------------------------------ - - private static final String titleKey = "title"; - private static final String textKey = "text"; - private String title; - private String text; - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle args = getArguments(); - text = args.getString(textKey); - title = args.getString(titleKey); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - View view = getActivity().getLayoutInflater().inflate(R.layout.part_edittext, null); - final EditText editText = (EditText) view.findViewById(R.id.part_edittext); - editText.setText(text); - return new AlertDialog.Builder(getActivity()) - .setTitle(title) - .setView(view) - .setPositiveButton(R.string.alert_dialog_ok, (dialog, which) -> { - onTextInput(editText.getText().toString()); - dialog.dismiss(); - }) - .setNegativeButton(R.string.alert_dialog_cancel, (dialog, which) -> dialog.dismiss()) - .create(); - } - - // -------------------------- OTHER METHODS -------------------------- - - public abstract void onTextInput(String text); - - public void setParams(String title, String text) { - Bundle args = new Bundle(); - args.putString(titleKey, title); - args.putString(textKey, text); - setArguments(args); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.kt new file mode 100644 index 00000000..399b84ad --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/EditTextDialogFragment.kt @@ -0,0 +1,81 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.app.DialogFragment +import android.os.Bundle +import android.view.View +import android.widget.EditText +import net.lacolaco.smileessence.R + +abstract class EditTextDialogFragment : DialogFragment() { + private lateinit var title: String + private lateinit var text: String + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val args = arguments + text = args.getString(textKey) + title = args.getString(titleKey) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val view = activity.layoutInflater.inflate(R.layout.part_edittext, null) + val editText = view.findViewById(R.id.part_edittext) as EditText + editText.setText(text) + return AlertDialog.Builder(activity) + .setTitle(title) + .setView(view) + .setPositiveButton(R.string.alert_dialog_ok) { dialog, which -> + onTextInput(editText.text.toString()) + dialog.dismiss() + } + .setNegativeButton(R.string.alert_dialog_cancel) { dialog, which -> dialog.dismiss() } + .create() + } + + // -------------------------- OTHER METHODS -------------------------- + + abstract fun onTextInput(text: String) + + fun setParams(title: String, text: String) { + val args = Bundle() + args.putString(titleKey, title) + args.putString(textKey, text) + arguments = args + } + + companion object { + + // ------------------------------ FIELDS ------------------------------ + + private val titleKey = "title" + private val textKey = "text" + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.java deleted file mode 100644 index f8a1e2e5..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2015 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.TextView; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.command.Command; -import net.lacolaco.smileessence.command.CommandAddHashtag; -import net.lacolaco.smileessence.command.CommandOpenURL; -import net.lacolaco.smileessence.command.CommandOpenUserDetail; -import net.lacolaco.smileessence.entity.DirectMessage; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.twitter.task.Messages; -import net.lacolaco.smileessence.util.SystemServiceHelper; -import net.lacolaco.smileessence.view.DialogHelper; -import net.lacolaco.smileessence.view.Partials; -import net.lacolaco.smileessence.view.adapter.CustomListAdapter; -import net.lacolaco.smileessence.view.adapter.MessageListAdapter; - -import java.util.ArrayList; -import java.util.List; - -public class MessageDetailDialogFragment extends StackableDialogFragment implements View.OnClickListener { - private static final String KEY_MESSAGE_ID = "message_id"; - private DirectMessage message; - - public static MessageDetailDialogFragment newInstance(DirectMessage message) { - MessageDetailDialogFragment obj = new MessageDetailDialogFragment(); - Bundle args = new Bundle(); - args.putLong(KEY_MESSAGE_ID, message.getId()); - obj.setArguments(args); - return obj; - } - - @Override - public void onClick(final View v) { - switch (v.getId()) { - case R.id.button_status_detail_reply: { - openSendMessageDialog(); - break; - } - case R.id.button_status_detail_delete: { - deleteMessage(); - break; - } - case R.id.button_status_detail_menu: { - openMenu(); - break; - } - default: { - dismiss(); - } - } - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - message = DirectMessage.fetch(getArguments().getLong(KEY_MESSAGE_ID)); - if (message == null) { - getWorld().notifyError(R.string.notice_error_get_messages); - return new DisposeDialog(getActivity()); - } - - View header = getTitleView(); - ListView listView = (ListView) header.findViewById(R.id.listview_status_detail_reply_to); - final MessageListAdapter adapter = new MessageListAdapter(getActivity()); - listView.setAdapter(adapter); - - // TODO: 効率的な探索どうする - DirectMessage replyTo = null; - // FIXME - // for (DirectMessage mes : DirectMessage.cached()) { - // if (message.getId() > mes.getId() && - // message.getRecipient() == mes.getSender() && - // message.getSender() == mes.getRecipient() && - // (replyTo == null || replyTo.getId() < mes.getId())) { - // replyTo = mes; - // } - // } - if (replyTo != null) { - listView.setVisibility(View.VISIBLE); - adapter.add(replyTo); - adapter.updateForce(); - } else { - listView.setVisibility(View.GONE); - } - - return new AlertDialog.Builder(getActivity()).setView(header).create(); - } - - // -------------------------- OTHER METHODS -------------------------- - - private void deleteMessage() { - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), () -> { - new Messages.DestroyTask(getWorld().getAccount(), message.getId()) - .onDone(x -> getWorld().notify(R.string.notice_message_delete_succeeded)) - .onFail(x -> getWorld().notifyError(R.string.notice_message_delete_failed)) - .execute(); - dismiss(); - }); - } - - private void openSendMessageDialog() { - DialogHelper.showDialog(getActivity(), SendMessageDialogFragment.newInstance(message.getSender())); - } - - private View getTitleView() { - MainActivity activity = (MainActivity) getActivity(); - - View view = activity.getLayoutInflater().inflate(R.layout.dialog_status_detail, null); - - View messageHeader = Partials.getDirectMessageView(message, activity, view.findViewById(R.id.layout_status_header)); - messageHeader.setClickable(false); - - view.setBackgroundColor(((ColorDrawable) messageHeader.getBackground()).getColor()); - updateViewButtons(view); - updateViewMenu(view); - - // status only parts - view.findViewById(R.id.detail_dialog_divider_top).setVisibility(View.GONE); - view.findViewById(R.id.button_status_detail_retweet).setVisibility(View.GONE); - view.findViewById(R.id.button_status_detail_favorite).setVisibility(View.GONE); - view.findViewById(R.id.image_status_detail_fav_count).setVisibility(View.GONE); - view.findViewById(R.id.image_status_detail_rt_count).setVisibility(View.GONE); - - return view; - } - - private void updateViewButtons(View view) { - //--- buttons - ImageButton reply = (ImageButton) view.findViewById(R.id.button_status_detail_reply); - reply.setOnClickListener(this); - - ImageButton delete = (ImageButton) view.findViewById(R.id.button_status_detail_delete); - delete.setOnClickListener(this); - delete.setVisibility(View.VISIBLE); - } - - private void updateViewMenu(View view) { - // -- menu dialog - ImageButton menu = (ImageButton) view.findViewById(R.id.button_status_detail_menu); - menu.setOnClickListener(this); - - // -- menu embedded in dialog - View divider = view.findViewById(R.id.detail_dialog_divider_bottom); - ListView listView = (ListView) view.findViewById(R.id.listview_status_detail_menu); - List<Command> commands = getCommands(); - if (commands.size() > 0) { - divider.setVisibility(View.VISIBLE); - listView.setVisibility(View.VISIBLE); - final CustomListAdapter<Command> adapter = new CustomListAdapter<Command>() { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = getActivity().getLayoutInflater().inflate(R.layout.menu_item_simple_text, null); - } - TextView textView = (TextView) convertView.findViewById(R.id.list_item_textview); - textView.setTextSize(UserPreferenceHelper.getInstance().getTextSize()); - textView.setText(getItem(position).getText()); - return convertView; - } - - @Override - protected List<Command> getList() { - return commands; - } - }; - adapter.update(); - listView.setAdapter(adapter); - listView.setOnItemClickListener((parent, view1, position, id) -> { - Command command = (Command) parent.getItemAtPosition(position); - command.execute(); - }); - } else { - divider.setVisibility(View.GONE); - listView.setVisibility(View.GONE); - } - } - - private List<Command> getCommands() { - MainActivity activity = (MainActivity) getActivity(); - List<Command> commands = new ArrayList<>(); - // Mentions - if (message.getSender() != message.getRecipient()) { - commands.add(new CommandOpenUserDetail(activity, message.getRecipient().getScreenName())); - } - for (String screenName : message.getMentions()) { - commands.add(new CommandOpenUserDetail(activity, screenName)); - } - for (String hashtag : message.getHashtags()) { - commands.add(new CommandAddHashtag(activity, hashtag)); - } - // URL - for (String url : message.getUrlsExpanded()) { - commands.add(new CommandOpenURL(activity, url)); - } - for (String url : message.getMediaUrls()) { - commands.add(new CommandOpenURL(activity, url)); - } - return commands; - } - - private void openMenu() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle("@" + message.getSender().getScreenName() + ": " + message.getText()) - .setItems(R.array.message_commands, (dialog, which) -> { - switch (which) { - case 0: - SystemServiceHelper.copyToClipboard(getActivity(), "message text", message.getText()); - getWorld().notify(R.string.notice_copy_clipboard); - break; - default: - throw new IllegalStateException(); - } - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.kt new file mode 100644 index 00000000..36f7584f --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/MessageDetailDialogFragment.kt @@ -0,0 +1,246 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2015 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ListView +import android.widget.TextView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.command.Command +import net.lacolaco.smileessence.command.CommandAddHashtag +import net.lacolaco.smileessence.command.CommandOpenURL +import net.lacolaco.smileessence.command.CommandOpenUserDetail +import net.lacolaco.smileessence.entity.DirectMessage +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.twitter.task.Messages +import net.lacolaco.smileessence.util.SystemServiceHelper +import net.lacolaco.smileessence.view.DialogHelper +import net.lacolaco.smileessence.view.Partials +import net.lacolaco.smileessence.view.adapter.CustomListAdapter +import net.lacolaco.smileessence.view.adapter.MessageListAdapter + +import java.util.ArrayList + +class MessageDetailDialogFragment : StackableDialogFragment(), View.OnClickListener { + private lateinit var message: DirectMessage + + override fun onClick(v: View) { + when (v.id) { + R.id.button_status_detail_reply -> { + openSendMessageDialog() + } + R.id.button_status_detail_delete -> { + deleteMessage() + } + R.id.button_status_detail_menu -> { + openMenu() + } + else -> { + dismiss() + } + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val found = DirectMessage.fetch(arguments.getLong(KEY_MESSAGE_ID)) + if (found == null) { + world.notifyError(R.string.notice_error_get_messages) + return DisposeDialog(activity) + } + message = found + + val header = titleView + val listView = header.findViewById(R.id.listview_status_detail_reply_to) as ListView + val adapter = MessageListAdapter(activity) + listView.adapter = adapter + + // TODO: 効率的な探索どうする + val replyTo: DirectMessage? = null + // FIXME + // for (DirectMessage mes : DirectMessage.cached()) { + // if (message.getId() > mes.getId() && + // message.getRecipient() == mes.getSender() && + // message.getSender() == mes.getRecipient() && + // (replyTo == null || replyTo.getId() < mes.getId())) { + // replyTo = mes; + // } + // } + if (replyTo != null) { + listView.visibility = View.VISIBLE + adapter.add(replyTo) + adapter.updateForce() + } else { + listView.visibility = View.GONE + } + + return AlertDialog.Builder(activity).setView(header).create() + } + + // -------------------------- OTHER METHODS -------------------------- + + private fun deleteMessage() { + ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands)) { + Messages.DestroyTask(world.account, message.id) + .onDone { x -> world.notify(R.string.notice_message_delete_succeeded) } + .onFail { x -> world.notifyError(R.string.notice_message_delete_failed) } + .execute() + dismiss() + } + } + + private fun openSendMessageDialog() { + DialogHelper.showDialog(activity, SendMessageDialogFragment.newInstance(message.sender)) + } + + private // status only parts + val titleView: View + get() { + val activity = activity as MainActivity + + val view = activity.layoutInflater.inflate(R.layout.dialog_status_detail, null) + + val messageHeader = Partials.getDirectMessageView(message, activity, view.findViewById(R.id.layout_status_header)) + messageHeader.isClickable = false + + view.setBackgroundColor((messageHeader.background as ColorDrawable).color) + updateViewButtons(view) + updateViewMenu(view) + view.findViewById(R.id.detail_dialog_divider_top).visibility = View.GONE + view.findViewById(R.id.button_status_detail_retweet).visibility = View.GONE + view.findViewById(R.id.button_status_detail_favorite).visibility = View.GONE + view.findViewById(R.id.image_status_detail_fav_count).visibility = View.GONE + view.findViewById(R.id.image_status_detail_rt_count).visibility = View.GONE + + return view + } + + private fun updateViewButtons(view: View) { + //--- buttons + val reply = view.findViewById(R.id.button_status_detail_reply) as ImageButton + reply.setOnClickListener(this) + + val delete = view.findViewById(R.id.button_status_detail_delete) as ImageButton + delete.setOnClickListener(this) + delete.visibility = View.VISIBLE + } + + private fun updateViewMenu(view: View) { + // -- menu dialog + val menu = view.findViewById(R.id.button_status_detail_menu) as ImageButton + menu.setOnClickListener(this) + + // -- menu embedded in dialog + val divider = view.findViewById(R.id.detail_dialog_divider_bottom) + val listView = view.findViewById(R.id.listview_status_detail_menu) as ListView + val commands = commands + if (commands.size > 0) { + divider.visibility = View.VISIBLE + listView.visibility = View.VISIBLE + val adapter = object : CustomListAdapter<Command>() { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + if (convertView == null) { + convertView = activity.layoutInflater.inflate(R.layout.menu_item_simple_text, null) + } + val textView = convertView!!.findViewById(R.id.list_item_textview) as TextView + textView.textSize = UserPreferenceHelper.instance.textSize.toFloat() + textView.text = getItem(position).text + return convertView + } + + override val list: List<Command> + get() = commands + } + adapter.update() + listView.adapter = adapter + listView.setOnItemClickListener { parent, view1, position, id -> + val command = parent.getItemAtPosition(position) as Command + command.execute() + } + } else { + divider.visibility = View.GONE + listView.visibility = View.GONE + } + } + + private // Mentions + // URL + val commands: List<Command> + get() { + val activity = activity as MainActivity + val commands = ArrayList<Command>() + if (message.sender !== message.recipient) { + commands.add(CommandOpenUserDetail(activity, message.recipient.screenName)) + } + for (screenName in message.mentions) { + commands.add(CommandOpenUserDetail(activity, screenName)) + } + for (hashtag in message.hashtags) { + commands.add(CommandAddHashtag(activity, hashtag)) + } + for (url in message.urlsExpanded) { + commands.add(CommandOpenURL(activity, url)) + } + for (url in message.mediaUrls) { + commands.add(CommandOpenURL(activity, url)) + } + return commands + } + + private fun openMenu() { + val builder = AlertDialog.Builder(activity) + builder.setTitle("@" + message.sender.screenName + ": " + message.text) + .setItems(R.array.message_commands) { dialog, which -> + when (which) { + 0 -> { + SystemServiceHelper.copyToClipboard(activity, "message text", message.text) + world.notify(R.string.notice_copy_clipboard) + } + else -> throw IllegalStateException() + } + } + val dialog = builder.create() + dialog.show() + } + + companion object { + private val KEY_MESSAGE_ID = "message_id" + + fun newInstance(message: DirectMessage): MessageDetailDialogFragment { + val obj = MessageDetailDialogFragment() + val args = Bundle() + args.putLong(KEY_MESSAGE_ID, message.id) + obj.arguments = args + return obj + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.java deleted file mode 100644 index 20917d8f..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.TextView; -import com.twitter.Validator; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.entity.User; -import net.lacolaco.smileessence.twitter.task.Messages; -import net.lacolaco.smileessence.util.SystemServiceHelper; - -public class SendMessageDialogFragment extends StackableDialogFragment implements TextWatcher, View.OnClickListener { - private static final String KEY_RECIPIENT_ID = "KEY_RECIPIENT_ID"; - private User recipient; - private EditText editText; - private TextView textViewCount; - private Button buttonSend; - - public static SendMessageDialogFragment newInstance(User recipient) { - SendMessageDialogFragment obj = new SendMessageDialogFragment(); - Bundle args = new Bundle(); - args.putLong(KEY_RECIPIENT_ID, recipient.getId()); - obj.setArguments(args); - return obj; - } - @Override - public void onClick(View v) { - int id = v.getId(); - switch (id) { - case R.id.button_send_message: { - sendMessage(); - break; - } - case R.id.button_send_message_delete: { - deleteMessage(); - } - } - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - Validator validator = new Validator(); - int remainingCount = 140 - validator.getTweetLength(s.toString()); - textViewCount.setText(String.valueOf(remainingCount)); - if (remainingCount == 140 || remainingCount < 0) { - textViewCount.setTextColor(ContextCompat.getColor(getActivity(), R.color.red)); - } else { - textViewCount.setTextAppearance(getActivity(), android.R.style.TextAppearance_Widget_TextView); - } - } - - @Override - public void afterTextChanged(Editable s) { - - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - recipient = User.fetch(getArguments().getLong(KEY_RECIPIENT_ID)); - View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_send_message, null); - TextView textViewName = (TextView) view.findViewById(R.id.textview_send_message_name); - textViewName.setText("To: @" + recipient.getScreenName()); - textViewCount = (TextView) view.findViewById(R.id.textview_send_message_count); - editText = (EditText) view.findViewById(R.id.edittext_send_message); - editText.addTextChangedListener(this); - buttonSend = (Button) view.findViewById(R.id.button_send_message); - buttonSend.setOnClickListener(this); - ImageButton buttonDelete = (ImageButton) view.findViewById(R.id.button_send_message_delete); - buttonDelete.setOnClickListener(this); - editText.setText(""); - return new AlertDialog.Builder(getActivity()) - .setView(view) - .create(); - } - - private void deleteMessage() { - editText.setText(""); - } - - private void sendMessage() { - SystemServiceHelper.hideIM(getActivity(), editText); - String text = editText.getText().toString(); - new Messages.CreateTask(getWorld().getAccount(), recipient.getId(), text) - .onDoneUI(m -> { - getWorld().addDirectMessage(m); - getWorld().notify(R.string.notice_message_send_succeeded); - dismiss(); - }) - .onFail(e -> getWorld().notifyError(R.string.notice_message_send_failed, e)) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.kt new file mode 100644 index 00000000..fb7e9d2f --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/SendMessageDialogFragment.kt @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.text.Editable +import android.text.TextUtils +import android.text.TextWatcher +import android.view.View +import android.widget.Button +import android.widget.EditText +import android.widget.ImageButton +import android.widget.TextView +import com.twitter.Validator +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.entity.User +import net.lacolaco.smileessence.twitter.task.Messages +import net.lacolaco.smileessence.util.SystemServiceHelper + +class SendMessageDialogFragment : StackableDialogFragment(), TextWatcher, View.OnClickListener { + private var recipient: User? = null + private var editText: EditText? = null + private var textViewCount: TextView? = null + private var buttonSend: Button? = null + override fun onClick(v: View) { + val id = v.id + when (id) { + R.id.button_send_message -> { + sendMessage() + } + R.id.button_send_message_delete -> { + deleteMessage() + } + } + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + val validator = Validator() + val remainingCount = 140 - validator.getTweetLength(s.toString()) + textViewCount!!.text = remainingCount.toString() + if (remainingCount == 140 || remainingCount < 0) { + textViewCount!!.setTextColor(ContextCompat.getColor(activity, R.color.red)) + } else { + textViewCount!!.setTextAppearance(activity, android.R.style.TextAppearance_Widget_TextView) + } + } + + override fun afterTextChanged(s: Editable) { + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + recipient = User.fetch(arguments.getLong(KEY_RECIPIENT_ID)) + val view = activity.layoutInflater.inflate(R.layout.dialog_send_message, null) + val textViewName = view.findViewById(R.id.textview_send_message_name) as TextView + textViewName.text = "To: @" + recipient!!.screenName + textViewCount = view.findViewById(R.id.textview_send_message_count) as TextView + editText = view.findViewById(R.id.edittext_send_message) as EditText + editText!!.addTextChangedListener(this) + buttonSend = view.findViewById(R.id.button_send_message) as Button + buttonSend!!.setOnClickListener(this) + val buttonDelete = view.findViewById(R.id.button_send_message_delete) as ImageButton + buttonDelete.setOnClickListener(this) + editText!!.setText("") + return AlertDialog.Builder(activity) + .setView(view) + .create() + } + + private fun deleteMessage() { + editText!!.setText("") + } + + private fun sendMessage() { + SystemServiceHelper.hideIM(activity, editText) + val text = editText!!.text.toString() + Messages.CreateTask(world.account, recipient!!.id, text) + .onDoneUI { m -> + world.addDirectMessage(m) + world.notify(R.string.notice_message_send_succeeded) + dismiss() + } + .onFail { e -> world.notifyError(R.string.notice_message_send_failed, e) } + .execute() + } + + companion object { + private val KEY_RECIPIENT_ID = "KEY_RECIPIENT_ID" + + fun newInstance(recipient: User): SendMessageDialogFragment { + val obj = SendMessageDialogFragment() + val args = Bundle() + args.putLong(KEY_RECIPIENT_ID, recipient.id) + obj.arguments = args + return obj + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.java deleted file mode 100644 index 848c6465..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.lacolaco.smileessence.view.dialog; - -import android.app.DialogFragment; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.view.DialogHelper; - -/** - * Only used by MainActivity. - */ -public class StackableDialogFragment extends DialogFragment { - @Override - public void dismiss() { - super.dismiss(); - DialogHelper.unregisterDialog(getTag()); - } - - @Override - public void dismissAllowingStateLoss() { - super.dismissAllowingStateLoss(); - DialogHelper.unregisterDialog(getTag()); - } - - protected World getWorld() { - final MainActivity activity = (MainActivity) getActivity(); - return activity.getWorld(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.kt new file mode 100644 index 00000000..7822cfe3 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StackableDialogFragment.kt @@ -0,0 +1,25 @@ +package net.lacolaco.smileessence.view.dialog + +import android.app.DialogFragment +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.view.DialogHelper + +/** + * Only used by MainActivity. + */ +open class StackableDialogFragment : DialogFragment() { + override fun dismiss() { + super.dismiss() + DialogHelper.unregisterDialog(tag) + } + + override fun dismissAllowingStateLoss() { + super.dismissAllowingStateLoss() + DialogHelper.unregisterDialog(tag) + } + + protected val world: World by lazy { + (activity as MainActivity).world + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.java deleted file mode 100644 index 5b2ee58c..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.command.Command; -import net.lacolaco.smileessence.command.CommandAddHashtag; -import net.lacolaco.smileessence.command.CommandOpenURL; -import net.lacolaco.smileessence.command.CommandOpenUserDetail; -import net.lacolaco.smileessence.data.Account; -import net.lacolaco.smileessence.entity.RBinding; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.twitter.task.TweetReactions; -import net.lacolaco.smileessence.twitter.task.Tweets; -import net.lacolaco.smileessence.util.IntentUtils; -import net.lacolaco.smileessence.util.SystemServiceHelper; -import net.lacolaco.smileessence.util.UIObserverBundle; -import net.lacolaco.smileessence.view.DialogHelper; -import net.lacolaco.smileessence.view.Partials; -import net.lacolaco.smileessence.view.ToggleableImageButton; -import net.lacolaco.smileessence.view.adapter.CustomListAdapter; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; - -public class StatusDetailDialogFragment extends StackableDialogFragment implements View.OnClickListener { - private static final String KEY_STATUS_ID = "status_id"; - private Tweet tweet; - - public static StatusDetailDialogFragment newInstance(Tweet tweet) { - StatusDetailDialogFragment obj = new StatusDetailDialogFragment(); - Bundle args = new Bundle(); - args.putLong(KEY_STATUS_ID, tweet.getId()); - obj.setArguments(args); - return obj; - } - - @Override - public void onClick(final View v) { - switch (v.getId()) { - case R.id.button_status_detail_reply: { - Tweet originalTweet = tweet.getOriginalTweet(); - - StringBuilder builder = new StringBuilder(); - builder.append("@" + originalTweet.getUser().getScreenName() + " "); - - for (String screenName : originalTweet.getMentions()) { - if (!screenName.equals(getWorld().getAccount().getUser().getScreenName())) - builder.append("@" + screenName + " "); - } - String text = builder.toString(); - int selStart = originalTweet.getUser().getScreenName().length() + 2; // "@" and " " - - getWorld().getPostState().beginTransaction() - .clear() - .insertText(0, text) - .setInReplyTo(originalTweet) - .setSelection(selStart, text.length()) - .commitWithOpen((MainActivity) getActivity()); - break; - } - case R.id.button_status_detail_retweet: { - Account account = getWorld().getAccount(); - confirm(() -> { - if (tweet.isRetweetedBy(account.getUserId())) { - new Tweets.DestroyTask(account, tweet.getRetweetIdBy(account.getUserId())) - .onDone(t -> getWorld().notify(R.string.notice_status_delete_succeeded)) - .onFail(e -> getWorld().notifyError(R.string.notice_status_delete_failed)) - .execute(); - dismiss(); - } else { - new TweetReactions.RetweetTask(account, tweet.getId()) - .onDone(x -> getWorld().notify(R.string.notice_retweet_succeeded)) - .onFail(x -> getWorld().notifyError(R.string.notice_retweet_failed)) - .execute(); - } - }); - break; - } - case R.id.button_status_detail_favorite: { - Account account = getWorld().getAccount(); - if (tweet.isFavoritedBy(account.getUserId())) { - new TweetReactions.UnfavoriteTask(account, tweet.getId()) - .onDone(x -> getWorld().notify(R.string.notice_unfavorite_succeeded)) - .onFail(x -> getWorld().notifyError(R.string.notice_unfavorite_failed)) - .execute(); - } else { - new TweetReactions.FavoriteTask(account, tweet.getId()) - .onDone(x -> getWorld().notify(R.string.notice_favorite_succeeded)) - .onFail(x -> getWorld().notifyError(R.string.notice_favorite_failed)) - .execute(); - } - break; - } - case R.id.button_status_detail_delete: { - confirm(() -> { - new Tweets.DestroyTask(getWorld().getAccount(), tweet.getOriginalTweet().getId()) - .onDone(t -> getWorld().notify(R.string.notice_status_delete_succeeded)) - .onFail(e -> getWorld().notifyError(R.string.notice_status_delete_failed)) - .execute(); - dismiss(); - }); - break; - } - case R.id.button_status_detail_menu: { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle("@" + tweet.getUser().getScreenName() + ": " + tweet.getText()) - .setItems(R.array.status_commands, (dialog, which) -> { - switch (which) { - case 0: - String text = String.format("@%s ", tweet.getOriginalTweet().getUser().getScreenName()); - getWorld().getPostState().beginTransaction().insertText(0, text).moveCursor(text.length()).commit(); - getWorld().notify(R.string.notice_add_to_reply); - break; - case 1: - TalkChainDialogFragment dialogFragment = new TalkChainDialogFragment(); - dialogFragment.setStatusID(tweet.getOriginalTweet().getId()); - DialogHelper.showDialog(getActivity(), dialogFragment); - break; - case 2: - IntentUtils.openUri(getActivity(), tweet.getOriginalTweet().getTwitterUrl()); - break; - case 3: - SystemServiceHelper.copyToClipboard(getActivity(), "tweet text", tweet.getOriginalTweet().getText()); - getWorld().notify(R.string.notice_copy_clipboard); - break; - case 4: - String statusURL = tweet.getOriginalTweet().getTwitterUrl(); - SystemServiceHelper.copyToClipboard(getActivity(), "tweet url", statusURL); - getWorld().notify(R.string.notice_copy_clipboard); - break; - default: - throw new IllegalStateException(); - } - }); - AlertDialog dialog = builder.create(); - dialog.show(); - break; - } - default: { - dismiss(); - } - } - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - tweet = Tweet.fetch(getArguments().getLong(KEY_STATUS_ID)); - if (tweet == null) { // trying to open deleted tweet? - getWorld().notifyError(R.string.notice_error_show_status); - return new DisposeDialog(getActivity()); - } - - View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_status_detail, null); - UIObserverBundle bundle = new UIObserverBundle(); - view.setTag(bundle); - - View statusHeader = Partials.getTweetView(tweet, getActivity(), view.findViewById(R.id.layout_status_header), true); - statusHeader.setClickable(false); - - view.setBackgroundColor(((ColorDrawable) statusHeader.getBackground()).getColor()); - updateViewReactions(view); - updateViewButtons(view); - setupViewMenu(view); - - final WeakReference<View> weakView = new WeakReference<>(view); - bundle.attach(tweet.getOriginalTweet(), changes -> { - View strongView = weakView.get(); - if (strongView != null && getActivity() != null) { - if (changes.contains(RBinding.REACTION_COUNT)) - updateViewReactions(strongView); - if (changes.contains(RBinding.FAVORITERS) || changes.contains(RBinding.RETWEETERS)) - updateViewButtons(strongView); - } - }); - - ListView listView = (ListView) view.findViewById(R.id.listview_status_detail_reply_to); - final TimelineAdapter adapter = new TimelineAdapter(getActivity()); - listView.setAdapter(adapter); - - View replyDivider = view.findViewById(R.id.detail_dialog_divider_top); - - if (tweet.getInReplyToStatusId() != -1) { - replyDivider.setVisibility(View.VISIBLE); - listView.setVisibility(View.VISIBLE); - Tweet.fetchTask(tweet.getInReplyToStatusId(), getWorld().getAccount()) - .onDoneUI(replyTo -> { - adapter.add(replyTo); - adapter.update(); - }) - .execute(); - } else { - replyDivider.setVisibility(View.GONE); - listView.setVisibility(View.GONE); - } - - return new AlertDialog.Builder(getActivity()).setView(view).create(); - } - - private void updateViewReactions(View view) { - ImageView favCountIcon = (ImageView) view.findViewById(R.id.image_status_detail_fav_count); - TextView favCountText = (TextView) view.findViewById(R.id.textview_status_detail_fav_count); - if (tweet.getFavoriteCount() > 0) { - favCountText.setText(Integer.toString(tweet.getFavoriteCount())); - favCountIcon.setVisibility(View.VISIBLE); - favCountText.setVisibility(View.VISIBLE); - } else { - favCountIcon.setVisibility(View.GONE); - favCountText.setVisibility(View.GONE); - } - - ImageView rtCountIcon = (ImageView) view.findViewById(R.id.image_status_detail_rt_count); - TextView rtCountText = (TextView) view.findViewById(R.id.textview_status_detail_rt_count); - if (tweet.getRetweetCount() > 0) { - rtCountText.setText(Integer.toString(tweet.getRetweetCount())); - rtCountIcon.setVisibility(View.VISIBLE); - rtCountText.setVisibility(View.VISIBLE); - } else { - rtCountIcon.setVisibility(View.GONE); - rtCountText.setVisibility(View.GONE); - } - } - - private void updateViewButtons(View view) { - Account account = getWorld().getAccount(); - - //--- buttons - ImageButton reply = (ImageButton) view.findViewById(R.id.button_status_detail_reply); - reply.setOnClickListener(this); - - ToggleableImageButton retweet = (ToggleableImageButton) view.findViewById(R.id.button_status_detail_retweet); - retweet.setOnClickListener(this); - retweet.setState(tweet.isRetweetedBy(account.getUserId())); - - ToggleableImageButton favorite = (ToggleableImageButton) view.findViewById(R.id.button_status_detail_favorite); - favorite.setOnClickListener(this); - favorite.setState(tweet.isFavoritedBy(account.getUserId())); - - ImageButton delete = (ImageButton) view.findViewById(R.id.button_status_detail_delete); - delete.setOnClickListener(this); - delete.setVisibility(tweet.getOriginalTweet().getUser() == account.getUser() ? View.VISIBLE : View.GONE); - - ImageButton menu = (ImageButton) view.findViewById(R.id.button_status_detail_menu); - menu.setOnClickListener(this); - } - - private void setupViewMenu(View view) { - View divider = view.findViewById(R.id.detail_dialog_divider_bottom); - ListView listView = (ListView) view.findViewById(R.id.listview_status_detail_menu); - List<Command> commands = getCommands(); - if (commands.size() > 0) { - divider.setVisibility(View.VISIBLE); - listView.setVisibility(View.VISIBLE); - final CustomListAdapter<Command> adapter = new CustomListAdapter<Command>() { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = getActivity().getLayoutInflater().inflate(R.layout.menu_item_simple_text, null); - } - TextView textView = (TextView) convertView.findViewById(R.id.list_item_textview); - textView.setTextSize(UserPreferenceHelper.getInstance().getTextSize()); - textView.setText(getItem(position).getText()); - return convertView; - } - - @Override - protected List<Command> getList() { - return commands; - } - }; - adapter.update(); - listView.setAdapter(adapter); - listView.setOnItemClickListener((parent, view1, position, id) -> { - Command command = (Command) parent.getItemAtPosition(position); - command.execute(); - }); - } else { - divider.setVisibility(View.GONE); - listView.setVisibility(View.GONE); - } - } - - private void confirm(Runnable onYes) { - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), onYes); - } - - private List<Command> getCommands() { - MainActivity activity = (MainActivity) getActivity(); - ArrayList<Command> commands = new ArrayList<>(); - // Retweeter - if (tweet.getRetweetedTweet() != null && tweet.getUser() != tweet.getRetweetedTweet().getUser()) - commands.add(new CommandOpenUserDetail(activity, tweet.getUser().getScreenName())); - // Mentions - for (String screenName : new ArrayList<>(new LinkedHashSet<>(tweet.getMentions()))) { // Array#uniq - commands.add(new CommandOpenUserDetail(activity, screenName)); - } - // Hashtags - for (String hashtag : tweet.getHashtags()) { - commands.add(new CommandAddHashtag(activity, hashtag)); - } - // URL - for (String url : tweet.getUrlsExpanded()) { - commands.add(new CommandOpenURL(activity, url)); - } - for (String url : tweet.getMediaUrls()) { - commands.add(new CommandOpenURL(activity, url)); - } - return commands; - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.kt new file mode 100644 index 00000000..c18b6b4d --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.kt @@ -0,0 +1,339 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.ListView +import android.widget.TextView +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.command.Command +import net.lacolaco.smileessence.command.CommandAddHashtag +import net.lacolaco.smileessence.command.CommandOpenURL +import net.lacolaco.smileessence.command.CommandOpenUserDetail +import net.lacolaco.smileessence.data.Account +import net.lacolaco.smileessence.entity.RBinding +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.twitter.task.TweetReactions +import net.lacolaco.smileessence.twitter.task.Tweets +import net.lacolaco.smileessence.util.IntentUtils +import net.lacolaco.smileessence.util.SystemServiceHelper +import net.lacolaco.smileessence.util.UIObserverBundle +import net.lacolaco.smileessence.view.DialogHelper +import net.lacolaco.smileessence.view.Partials +import net.lacolaco.smileessence.view.ToggleableImageButton +import net.lacolaco.smileessence.view.adapter.CustomListAdapter +import net.lacolaco.smileessence.view.adapter.TimelineAdapter + +import java.lang.ref.WeakReference +import java.util.ArrayList +import java.util.LinkedHashSet + +class StatusDetailDialogFragment : StackableDialogFragment(), View.OnClickListener { + private lateinit var tweet: Tweet + + override fun onClick(v: View) { + when (v.id) { + R.id.button_status_detail_reply -> { + val originalTweet = tweet.originalTweet + + val builder = StringBuilder() + builder.append("@" + originalTweet.user.screenName + " ") + + for (screenName in originalTweet.mentions) { + if (screenName != world.account.user.screenName) + builder.append("@$screenName ") + } + val text = builder.toString() + val selStart = originalTweet.user.screenName.length + 2 // "@" and " " + + world.postState.beginTransaction() + .clear() + .insertText(0, text) + .setInReplyTo(originalTweet) + .setSelection(selStart, text.length) + .commitWithOpen(activity as MainActivity) + } + R.id.button_status_detail_retweet -> { + val account = world.account + confirm({ + if (tweet.isRetweetedBy(account.userId)) { + Tweets.DestroyTask(account, tweet.getRetweetIdBy(account.userId)) + .onDone { t -> world.notify(R.string.notice_status_delete_succeeded) } + .onFail { e -> world.notifyError(R.string.notice_status_delete_failed) } + .execute() + dismiss() + } else { + TweetReactions.RetweetTask(account, tweet.id) + .onDone { x -> world.notify(R.string.notice_retweet_succeeded) } + .onFail { x -> world.notifyError(R.string.notice_retweet_failed) } + .execute() + } + }) + } + R.id.button_status_detail_favorite -> { + val account = world.account + if (tweet.isFavoritedBy(account.userId)) { + TweetReactions.UnfavoriteTask(account, tweet.id) + .onDone { x -> world.notify(R.string.notice_unfavorite_succeeded) } + .onFail { x -> world.notifyError(R.string.notice_unfavorite_failed) } + .execute() + } else { + TweetReactions.FavoriteTask(account, tweet.id) + .onDone { x -> world.notify(R.string.notice_favorite_succeeded) } + .onFail { x -> world.notifyError(R.string.notice_favorite_failed) } + .execute() + } + } + R.id.button_status_detail_delete -> { + confirm({ + Tweets.DestroyTask(world.account, tweet.originalTweet.id) + .onDone { t -> world.notify(R.string.notice_status_delete_succeeded) } + .onFail { e -> world.notifyError(R.string.notice_status_delete_failed) } + .execute() + dismiss() + }) + } + R.id.button_status_detail_menu -> { + val builder = AlertDialog.Builder(activity) + builder.setTitle("@" + tweet.user.screenName + ": " + tweet.text) + .setItems(R.array.status_commands) { dialog, which -> + when (which) { + 0 -> { + val text = String.format("@%s ", tweet.originalTweet.user.screenName) + world.postState.beginTransaction().insertText(0, text).moveCursor(text.length).commit() + world.notify(R.string.notice_add_to_reply) + } + 1 -> { + DialogHelper.showDialog(activity, TalkChainDialogFragment.newInstance(tweet)) + } + 2 -> IntentUtils.openUri(activity, tweet.originalTweet.twitterUrl) + 3 -> { + SystemServiceHelper.copyToClipboard(activity, "tweet text", tweet.originalTweet.text) + world.notify(R.string.notice_copy_clipboard) + } + 4 -> { + val statusURL = tweet.originalTweet.twitterUrl + SystemServiceHelper.copyToClipboard(activity, "tweet url", statusURL) + world.notify(R.string.notice_copy_clipboard) + } + else -> throw IllegalStateException() + } + } + val dialog = builder.create() + dialog.show() + } + else -> { + dismiss() + } + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val found = Tweet.fetch(arguments.getLong(KEY_STATUS_ID)) + if (found == null) { // trying to open deleted tweet? + world.notifyError(R.string.notice_error_show_status) + return DisposeDialog(activity) + } + tweet = found + + val view = activity.layoutInflater.inflate(R.layout.dialog_status_detail, null) + val bundle = UIObserverBundle() + view.tag = bundle + + val statusHeader = Partials.getTweetView(tweet, activity, view.findViewById(R.id.layout_status_header), true) + statusHeader.isClickable = false + + view.setBackgroundColor((statusHeader.background as ColorDrawable).color) + updateViewReactions(view) + updateViewButtons(view) + setupViewMenu(view) + + val weakView = WeakReference(view) + bundle.attach(tweet.originalTweet) { changes -> + val strongView = weakView.get() + if (strongView != null && activity != null) { + if (changes.contains(RBinding.REACTION_COUNT)) + updateViewReactions(strongView) + if (changes.contains(RBinding.FAVORITERS) || changes.contains(RBinding.RETWEETERS)) + updateViewButtons(strongView) + } + } + + val listView = view.findViewById(R.id.listview_status_detail_reply_to) as ListView + val adapter = TimelineAdapter(activity) + listView.adapter = adapter + + val replyDivider = view.findViewById(R.id.detail_dialog_divider_top) + + if (tweet.inReplyToStatusId != -1L) { + replyDivider.visibility = View.VISIBLE + listView.visibility = View.VISIBLE + Tweet.fetchTask(tweet.inReplyToStatusId, world.account) + .onDoneUI { replyTo -> + adapter.add(replyTo) + adapter.update() + } + .execute() + } else { + replyDivider.visibility = View.GONE + listView.visibility = View.GONE + } + + return AlertDialog.Builder(activity).setView(view).create() + } + + private fun updateViewReactions(view: View) { + val favCountIcon = view.findViewById(R.id.image_status_detail_fav_count) as ImageView + val favCountText = view.findViewById(R.id.textview_status_detail_fav_count) as TextView + if (tweet.favoriteCount > 0) { + favCountText.text = Integer.toString(tweet.favoriteCount) + favCountIcon.visibility = View.VISIBLE + favCountText.visibility = View.VISIBLE + } else { + favCountIcon.visibility = View.GONE + favCountText.visibility = View.GONE + } + + val rtCountIcon = view.findViewById(R.id.image_status_detail_rt_count) as ImageView + val rtCountText = view.findViewById(R.id.textview_status_detail_rt_count) as TextView + if (tweet.retweetCount > 0) { + rtCountText.text = Integer.toString(tweet.retweetCount) + rtCountIcon.visibility = View.VISIBLE + rtCountText.visibility = View.VISIBLE + } else { + rtCountIcon.visibility = View.GONE + rtCountText.visibility = View.GONE + } + } + + private fun updateViewButtons(view: View) { + val account = world.account + + //--- buttons + val reply = view.findViewById(R.id.button_status_detail_reply) as ImageButton + reply.setOnClickListener(this) + + val retweet = view.findViewById(R.id.button_status_detail_retweet) as ToggleableImageButton + retweet.setOnClickListener(this) + retweet.setState(tweet.isRetweetedBy(account.userId)) + + val favorite = view.findViewById(R.id.button_status_detail_favorite) as ToggleableImageButton + favorite.setOnClickListener(this) + favorite.setState(tweet.isFavoritedBy(account.userId)) + + val delete = view.findViewById(R.id.button_status_detail_delete) as ImageButton + delete.setOnClickListener(this) + delete.visibility = if (tweet.originalTweet.user === account.user) View.VISIBLE else View.GONE + + val menu = view.findViewById(R.id.button_status_detail_menu) as ImageButton + menu.setOnClickListener(this) + } + + private fun setupViewMenu(view: View) { + val divider = view.findViewById(R.id.detail_dialog_divider_bottom) + val listView = view.findViewById(R.id.listview_status_detail_menu) as ListView + val commands = commands + if (commands.size > 0) { + divider.visibility = View.VISIBLE + listView.visibility = View.VISIBLE + val adapter = object : CustomListAdapter<Command>() { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + if (convertView == null) { + convertView = activity.layoutInflater.inflate(R.layout.menu_item_simple_text, null) + } + val textView = convertView!!.findViewById(R.id.list_item_textview) as TextView + textView.textSize = UserPreferenceHelper.instance.textSize.toFloat() + textView.text = getItem(position).text + return convertView + } + + override val list: List<Command> + get() = commands + } + adapter.update() + listView.adapter = adapter + listView.setOnItemClickListener { parent, view1, position, id -> + val command = parent.getItemAtPosition(position) as Command + command.execute() + } + } else { + divider.visibility = View.GONE + listView.visibility = View.GONE + } + } + + private fun confirm(onYes: () -> Unit) { + ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands), onYes) + } + + private // Retweeter + // Mentions + // Array#uniq + // Hashtags + // URL + val commands: List<Command> + get() { + val activity = activity as MainActivity + val commands = ArrayList<Command>() + if (tweet.retweetedTweet != null && tweet.user !== tweet.retweetedTweet!!.user) + commands.add(CommandOpenUserDetail(activity, tweet.user.screenName)) + for (screenName in ArrayList(LinkedHashSet(tweet.mentions))) { + commands.add(CommandOpenUserDetail(activity, screenName)) + } + for (hashtag in tweet.hashtags) { + commands.add(CommandAddHashtag(activity, hashtag)) + } + for (url in tweet.urlsExpanded) { + commands.add(CommandOpenURL(activity, url)) + } + for (url in tweet.mediaUrls) { + commands.add(CommandOpenURL(activity, url)) + } + return commands + } + + companion object { + private val KEY_STATUS_ID = "status_id" + + fun newInstance(tweet: Tweet): StatusDetailDialogFragment { + val obj = StatusDetailDialogFragment() + val args = Bundle() + args.putLong(KEY_STATUS_ID, tweet.id) + obj.arguments = args + return obj + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.java deleted file mode 100644 index 95bbac5c..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.data.Account; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.twitter.task.Tweets; -import net.lacolaco.smileessence.view.Partials; -import net.lacolaco.smileessence.view.adapter.CustomListAdapter; - -import java.util.ArrayList; -import java.util.List; - -public class TalkChainDialogFragment extends StackableDialogFragment { - private static final String KEY_STATUS_ID = "KEY_STATUS_ID"; - - private long getStatusID() { - return getArguments().getLong(KEY_STATUS_ID); - } - - public void setStatusID(long id) { - Bundle bundle = new Bundle(); - bundle.putLong(KEY_STATUS_ID, id); - setArguments(bundle); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Account account = getWorld().getAccount(); - - View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_talk_list, null); - ListView listView = (ListView) view.findViewById(R.id.listview_dialog_talk_list); - final List<Tweet> list = new ArrayList<>(); - final CustomListAdapter<Tweet> adapter = new CustomListAdapter<Tweet>() { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return Partials.getTweetView(getItem(position), getActivity(), convertView, true); - } - - @Override - protected List<Tweet> getList() { - return list; - } - }; - listView.setAdapter(adapter); - - new Tweets.GetTalkTask(account, getStatusID()).onProgressUI(tweet -> { - list.add(tweet); - adapter.updateForce(); - }).execute(); - - return new AlertDialog.Builder(getActivity()) - .setTitle(R.string.dialog_title_talk_chain) - .setView(view) - .setCancelable(true) - .create(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.kt new file mode 100644 index 00000000..1b39e50a --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/TalkChainDialogFragment.kt @@ -0,0 +1,81 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.ListView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.twitter.task.Tweets +import net.lacolaco.smileessence.view.Partials +import net.lacolaco.smileessence.view.adapter.CustomListAdapter + +import java.util.ArrayList + +class TalkChainDialogFragment : StackableDialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val account = world.account + + val view = activity.layoutInflater.inflate(R.layout.dialog_talk_list, null) + val listView = view.findViewById(R.id.listview_dialog_talk_list) as ListView + val list = ArrayList<Tweet>() + val adapter = object : CustomListAdapter<Tweet>() { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return Partials.getTweetView(getItem(position), activity, convertView, true) + } + + override val list: List<Tweet> + get() = list + } + listView.adapter = adapter + + Tweets.GetTalkTask(account, arguments.getLong(KEY_STATUS_ID)).onProgressUI { tweet -> + list.add(tweet) + adapter.updateForce() + }.execute() + + return AlertDialog.Builder(activity) + .setTitle(R.string.dialog_title_talk_chain) + .setView(view) + .setCancelable(true) + .create() + } + + companion object { + private val KEY_STATUS_ID = "KEY_STATUS_ID" + + fun newInstance(tweet: Tweet): TalkChainDialogFragment { + val obj = TalkChainDialogFragment() + val bundle = Bundle() + bundle.putLong(KEY_STATUS_ID, tweet.originalTweet.id) + obj.arguments = bundle + return obj + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.java deleted file mode 100644 index c8d62fa8..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.dialog; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.os.Bundle; -import android.text.Html; -import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.view.View; -import android.widget.AbsListView; -import android.widget.ListView; -import android.widget.TabHost; -import android.widget.TextView; -import com.android.volley.toolbox.NetworkImageView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import com.handmark.pulltorefresh.library.PullToRefreshListView; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.data.Account; -import net.lacolaco.smileessence.data.ImageCache; -import net.lacolaco.smileessence.entity.RBinding; -import net.lacolaco.smileessence.entity.User; -import net.lacolaco.smileessence.twitter.task.Timelines; -import net.lacolaco.smileessence.twitter.task.Users; -import net.lacolaco.smileessence.util.IntentUtils; -import net.lacolaco.smileessence.util.UIHandler; -import net.lacolaco.smileessence.util.UIObserverBundle; -import net.lacolaco.smileessence.view.DialogHelper; -import net.lacolaco.smileessence.view.ThreeStateButton; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; - -public class UserDetailDialogFragment extends StackableDialogFragment implements View.OnClickListener, - PullToRefreshBase.OnRefreshListener2<ListView> { - - // ------------------------------ FIELDS ------------------------------ - - private static final String KEY_USER_ID = "userID"; - private TimelineAdapter adapter; - private TextView textViewScreenName; - private TextView textViewName; - private TextView textViewURL; - private TextView textViewLocate; - private TextView textViewFollowed; - private TextView textViewProtected; - private TextView textViewDescription; - private TextView textViewTweetCount; - private TextView textViewFriendCount; - private TextView textViewFollowerCount; - private TextView textViewFavoriteCount; - private NetworkImageView imageViewIcon; - private NetworkImageView imageViewHeader; - private ThreeStateButton buttonFollow; - private PullToRefreshListView listViewTimeline; - private TabHost tabHost; - private UIObserverBundle observerBundle; - private User user; - - // --------------------- GETTER / SETTER METHODS --------------------- - - public long getUserID() { - return getArguments().getLong(KEY_USER_ID); - } - - public void setUserID(long userID) { - Bundle args = new Bundle(); - args.putLong(KEY_USER_ID, userID); - setArguments(args); - } - - // ------------------------ INTERFACE METHODS ------------------------ - - - // --------------------- Interface OnClickListener --------------------- - - @Override - public void onClick(final View v) { - switch (v.getId()) { - case R.id.imageview_user_detail_menu: { - openUserMenu(); - break; - } - case R.id.imageview_user_detail_icon: { - IntentUtils.openUri(getActivity(), user.getProfileImageUrlOriginal()); - break; - } - case R.id.textview_user_detail_screenname: - case R.id.textview_user_detail_tweet_count: { - IntentUtils.openUri(getActivity(), user.getUserHomeURL()); - break; - } - case R.id.textview_user_detail_friend_count: { - IntentUtils.openUri(getActivity(), String.format("%s/following", user.getUserHomeURL())); - break; - } - case R.id.textview_user_detail_follower_count: { - IntentUtils.openUri(getActivity(), String.format("%s/followers", user.getUserHomeURL())); - break; - } - case R.id.textview_user_detail_favorite_count: { - IntentUtils.openUri(getActivity(), String.format("%s/favorites", user.getUserHomeURL())); - break; - } - case R.id.button_user_detail_follow: { - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), this::toggleFollowing); - break; - } - } - } - - // --------------------- Interface OnRefreshListener2 --------------------- - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - Account currentAccount = getWorld().getAccount(); - new Timelines.UserTimelineTask(currentAccount, getUserID()) - .setCount(200) - .setSinceId(adapter.getTopID()) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_user_timeline)) - .onDoneUI(tweets -> { - adapter.addAll(tweets); - updateListView(refreshView.getRefreshableView(), adapter, true); - refreshView.onRefreshComplete(); - }) - .execute(); - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - Account currentAccount = getWorld().getAccount(); - new Timelines.UserTimelineTask(currentAccount, getUserID()) - .setCount(200) - .setMaxId(adapter.getLastID() - 1) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_user_timeline)) - .onDoneUI(tweets -> { - adapter.addAll(tweets); - updateListView(refreshView.getRefreshableView(), adapter, false); - refreshView.onRefreshComplete(); - }) - .execute(); - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - user = User.fetch(getUserID()); - observerBundle = new UIObserverBundle(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - observerBundle.detachAll(); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - MainActivity activity = (MainActivity) getActivity(); - if (user == null) { - getWorld().notify(R.string.notice_error_show_user); - return new DisposeDialog(activity); - } - - View v = activity.getLayoutInflater().inflate(R.layout.dialog_user_detail, null); - View menu = v.findViewById(R.id.imageview_user_detail_menu); - menu.setOnClickListener(this); - textViewScreenName = (TextView) v.findViewById(R.id.textview_user_detail_screenname); - textViewScreenName.setOnClickListener(this); - textViewName = (TextView) v.findViewById(R.id.textview_user_detail_name); - textViewURL = (TextView) v.findViewById(R.id.textview_user_detail_url); - textViewLocate = (TextView) v.findViewById(R.id.textview_user_detail_locate); - textViewFollowed = (TextView) v.findViewById(R.id.textview_user_detail_followed); - textViewProtected = (TextView) v.findViewById(R.id.texttview_user_detail_protected); - textViewDescription = (TextView) v.findViewById(R.id.textview_user_detail_description); - textViewDescription.setMovementMethod(LinkMovementMethod.getInstance()); - textViewTweetCount = (TextView) v.findViewById(R.id.textview_user_detail_tweet_count); - textViewTweetCount.setOnClickListener(this); - textViewFriendCount = (TextView) v.findViewById(R.id.textview_user_detail_friend_count); - textViewFriendCount.setOnClickListener(this); - textViewFollowerCount = (TextView) v.findViewById(R.id.textview_user_detail_follower_count); - textViewFollowerCount.setOnClickListener(this); - textViewFavoriteCount = (TextView) v.findViewById(R.id.textview_user_detail_favorite_count); - textViewFavoriteCount.setOnClickListener(this); - imageViewIcon = (NetworkImageView) v.findViewById(R.id.imageview_user_detail_icon); - imageViewIcon.setOnClickListener(this); - imageViewHeader = (NetworkImageView) v.findViewById(R.id.imageview_user_detail_header); - buttonFollow = (ThreeStateButton) v.findViewById(R.id.button_user_detail_follow); - buttonFollow.setOnClickListener(this); - listViewTimeline = (PullToRefreshListView) v.findViewById(R.id.listview_user_detail_timeline); - listViewTimeline.setOnRefreshListener(this); - - tabHost = (TabHost) v.findViewById(android.R.id.tabhost); - tabHost.setup(); - TabHost.TabSpec tab1 = tabHost.newTabSpec("tab1").setContent(R.id.tab1).setIndicator(getString(R.string.user_detail_tab_info)); - tabHost.addTab(tab1); - TabHost.TabSpec tab2 = tabHost.newTabSpec("tab2").setContent(R.id.tab2).setIndicator(getString(R.string.user_detail_tab_timeline)); - tabHost.addTab(tab2); - tabHost.setCurrentTab(0); - - initUserData(); - - return new AlertDialog.Builder(activity) - .setView(v) - .setCancelable(true) - .create(); - } - - private void executeUserTimelineTask(final TimelineAdapter adapter) { - Account account = getWorld().getAccount(); - tabHost.getTabWidget().getChildTabViewAt(1).setVisibility(View.GONE); - new Timelines.UserTimelineTask(account, user.getId()) - .setCount(200) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_user_timeline)) - .onDoneUI(tweets -> { - adapter.addAll(tweets); - adapter.updateForce(); - tabHost.getTabWidget().getChildTabViewAt(1).setVisibility(View.VISIBLE); - }) - .execute(); - } - - private String getHtmlDescription() { - String description = user.getDescription(); - if (TextUtils.isEmpty(description)) { - return ""; - } - String html = description; - html = html.replaceAll("https?://[\\w/:%#$&?()~.=+-]+", "<a href=\"$0\">$0</a>"); - html = html.replaceAll("@([a-zA-Z0-9_]+)", "<a href=\"https://twitter.com/$1\">$0</a>"); - html = html.replaceAll("\r\n", "<br />"); - return html; - } - - private void updateUserDataBasic() { - textViewName.setText(user.getName()); - textViewScreenName.setText(user.getScreenName()); - textViewProtected.setVisibility(user.isProtected() ? View.VISIBLE : View.GONE); - imageViewIcon.setImageUrl(user.getProfileImageUrlOriginal(), ImageCache.getImageLoader()); - } - - private void updateUserDataDetail() { - if (TextUtils.isEmpty(user.getLocation())) { - textViewLocate.setVisibility(View.GONE); - } else { - textViewLocate.setText(user.getLocation()); - textViewLocate.setVisibility(View.VISIBLE); - } - if (TextUtils.isEmpty(user.getUrl())) { - textViewURL.setVisibility(View.GONE); - } else { - textViewURL.setText(user.getUrl()); - textViewURL.setVisibility(View.VISIBLE); - } - textViewDescription.setText(Html.fromHtml(getHtmlDescription())); - - textViewTweetCount.setText(String.valueOf(user.getStatusesCount())); - textViewFriendCount.setText(String.valueOf(user.getFriendsCount())); - textViewFollowerCount.setText(String.valueOf(user.getFollowersCount())); - textViewFavoriteCount.setText(String.valueOf(user.getFavoritesCount())); - - imageViewHeader.setImageUrl(user.getProfileBannerUrl(), ImageCache.getImageLoader()); - } - - private void initUserData() { - updateUserDataBasic(); - updateUserDataDetail(); - - MainActivity activity = (MainActivity) getActivity(); - adapter = new TimelineAdapter(activity); - listViewTimeline.setAdapter(adapter); - executeUserTimelineTask(adapter); - updateRelationship(); - - observerBundle.attach(user, changes -> { - if (getActivity() != null) { - if (changes.contains(RBinding.BASIC)) - updateUserDataBasic(); - if (changes.contains(RBinding.DETAIL)) - updateUserDataDetail(); - } - }); - } - - private void openUserMenu() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle("@" + user.getScreenName()) - .setItems(R.array.user_commands, (dialog, which) -> { - // XXX - new UIHandler().postDelayed(() -> { - if (UserDetailDialogFragment.this.isAdded()) { - updateRelationship(); - } - }, 1000); - switch (which) { - case 0: - String text = String.format("@%s ", user.getScreenName()); - getWorld().getPostState().beginTransaction().insertText(0, text).moveCursor(text.length()).commit(); - getWorld().notify(R.string.notice_add_to_reply); - break; - case 1: - DialogHelper.showDialog(getActivity(), SendMessageDialogFragment.newInstance(user)); - break; - case 2: - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), () -> - new Users.BlockTask(getWorld().getAccount(), user.getId()) - .onDone(user -> getWorld().notify(R.string.notice_block_succeeded)) - .onFail(ex -> getWorld().notifyError(R.string.notice_block_failed)) - .execute()); - break; - case 3: - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), () -> - new Users.UnblockTask(getWorld().getAccount(), user.getId()) - .onDone(user -> getWorld().notify(R.string.notice_unblock_succeeded)) - .onFail(x -> getWorld().notifyError(R.string.notice_unblock_failed)) - .execute()); - break; - case 4: - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_commands), () -> - new Users.ReportForSpamTask(getWorld().getAccount(), user.getId()) - .onDone(user -> getWorld().notify(R.string.notice_r4s_succeeded)) - .onFail(ex -> getWorld().notifyError(R.string.notice_r4s_failed)) - .execute()); - break; - case 5: - IntentUtils.openUri(getActivity(), user.getAclogTimelineURL()); - break; - default: - throw new IllegalStateException(); - } - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } - - private void setFollowButtonState(boolean isFollowing) { - MainActivity mainActivity = (MainActivity) getActivity(); - if (mainActivity != null) { - buttonFollow.setState(isFollowing ? ThreeStateButton.STATE_ON : ThreeStateButton.STATE_OFF); - } - } - - private void toggleFollowing() { - Account account = getWorld().getAccount(); - Boolean isFollowing = buttonFollow.getState() == ThreeStateButton.STATE_ON; - buttonFollow.setState(ThreeStateButton.STATE_LOCKED); - if (isFollowing) { - new Users.UnfollowTask(account, user.getId()) - .onDoneUI(result -> { - getWorld().notify(R.string.notice_unfollow_succeeded); - updateRelationship(); - }) - .onFail(x -> - getWorld().notifyError(R.string.notice_unfollow_failed)) - .execute(); - } else { - new Users.FollowTask(account, user.getId()) - .onDoneUI(result -> { - getWorld().notify(R.string.notice_follow_succeeded); - updateRelationship(); - }) - .onFail(x -> getWorld().notifyError(R.string.notice_follow_failed)) - .execute(); - } - } - - private void updateListView(AbsListView absListView, TimelineAdapter adapter, boolean addedToTop) { - int before = adapter.getCount(); - adapter.notifyDataSetChanged(); // synchronized call (not adapter#updateForce()) - int after = adapter.getCount(); - int increments = after - before; - if (increments > 0) { - if (addedToTop) { - absListView.setSelection(increments + 1); - absListView.smoothScrollToPositionFromTop(increments, 0); - absListView.setSelection(increments); - } else { - absListView.smoothScrollToPositionFromTop(before, 0); - } - } - } - - private void updateRelationship() { - Account account = getWorld().getAccount(); - if (user == account.getUser()) { - textViewFollowed.setText(R.string.user_detail_followed_is_me); - buttonFollow.setVisibility(View.GONE); - } else { - buttonFollow.setState(ThreeStateButton.STATE_LOCKED); - textViewFollowed.setText(R.string.user_detail_loading); - new Users.ShowFriendshipTask(account, user.getId()).onDoneUI(relationship -> { - boolean isFollowing = relationship.isSourceFollowingTarget(); - boolean isFollowed = relationship.isSourceFollowedByTarget(); - setFollowButtonState(isFollowing); - textViewFollowed.setText(isFollowed ? R.string.user_detail_followed : R.string.user_detail_not_followed); - }).execute(); - } - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.kt new file mode 100644 index 00000000..228a000a --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.kt @@ -0,0 +1,390 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import android.text.Html +import android.text.TextUtils +import android.text.method.LinkMovementMethod +import android.view.View +import android.widget.AbsListView +import android.widget.ListView +import android.widget.TabHost +import android.widget.TextView +import com.android.volley.toolbox.NetworkImageView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import com.handmark.pulltorefresh.library.PullToRefreshListView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.data.ImageCache +import net.lacolaco.smileessence.entity.RBinding +import net.lacolaco.smileessence.entity.User +import net.lacolaco.smileessence.twitter.task.Timelines +import net.lacolaco.smileessence.twitter.task.Users +import net.lacolaco.smileessence.util.IntentUtils +import net.lacolaco.smileessence.util.UIHandler +import net.lacolaco.smileessence.util.UIObserverBundle +import net.lacolaco.smileessence.view.DialogHelper +import net.lacolaco.smileessence.view.ThreeStateButton +import net.lacolaco.smileessence.view.adapter.TimelineAdapter + +class UserDetailDialogFragment : StackableDialogFragment(), View.OnClickListener, PullToRefreshBase.OnRefreshListener2<ListView> { + private lateinit var adapter: TimelineAdapter + private lateinit var textViewScreenName: TextView + private lateinit var textViewName: TextView + private lateinit var textViewURL: TextView + private lateinit var textViewLocate: TextView + private lateinit var textViewFollowed: TextView + private lateinit var textViewProtected: TextView + private lateinit var textViewDescription: TextView + private lateinit var textViewTweetCount: TextView + private lateinit var textViewFriendCount: TextView + private lateinit var textViewFollowerCount: TextView + private lateinit var textViewFavoriteCount: TextView + private lateinit var imageViewIcon: NetworkImageView + private lateinit var imageViewHeader: NetworkImageView + private lateinit var buttonFollow: ThreeStateButton + private lateinit var listViewTimeline: PullToRefreshListView + private lateinit var tabHost: TabHost + private lateinit var observerBundle: UIObserverBundle + private lateinit var user: User + + override fun onClick(v: View) { + when (v.id) { + R.id.imageview_user_detail_menu -> { + openUserMenu() + } + R.id.imageview_user_detail_icon -> { + IntentUtils.openUri(activity, user.profileImageUrl!!) + } + R.id.textview_user_detail_screenname, R.id.textview_user_detail_tweet_count -> { + IntentUtils.openUri(activity, user.userHomeURL) + } + R.id.textview_user_detail_friend_count -> { + IntentUtils.openUri(activity, String.format("%s/following", user.userHomeURL)) + } + R.id.textview_user_detail_follower_count -> { + IntentUtils.openUri(activity, String.format("%s/followers", user.userHomeURL)) + } + R.id.textview_user_detail_favorite_count -> { + IntentUtils.openUri(activity, String.format("%s/favorites", user.userHomeURL)) + } + R.id.button_user_detail_follow -> { + ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands)) { this.toggleFollowing() } + } + } + } + + // --------------------- Interface OnRefreshListener2 --------------------- + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + val currentAccount = world.account + Timelines.UserTimelineTask(currentAccount, user.id) + .setCount(200) + .setSinceId(adapter.topID) + .onFail { x -> world.notifyError(R.string.notice_error_get_user_timeline) } + .onDoneUI { tweets -> + adapter.addAll(tweets) + updateListView(refreshView.refreshableView, adapter, true) + refreshView.onRefreshComplete() + } + .execute() + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + val currentAccount = world.account + Timelines.UserTimelineTask(currentAccount, user.id) + .setCount(200) + .setMaxId(adapter.lastID - 1) + .onFail { x -> world.notifyError(R.string.notice_error_get_user_timeline) } + .onDoneUI { tweets -> + adapter.addAll(tweets) + updateListView(refreshView.refreshableView, adapter, false) + refreshView.onRefreshComplete() + } + .execute() + } + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreate(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + observerBundle = UIObserverBundle() + } + + override fun onDestroy() { + super.onDestroy() + observerBundle.detachAll() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val activity = activity as MainActivity + val found = User.fetch(arguments.getLong(KEY_USER_ID)) + if (found == null) { + world.notify(R.string.notice_error_show_user) + return DisposeDialog(activity) + } + user = found + + val v = activity.layoutInflater.inflate(R.layout.dialog_user_detail, null) + val menu = v.findViewById(R.id.imageview_user_detail_menu) + menu.setOnClickListener(this) + textViewScreenName = v.findViewById(R.id.textview_user_detail_screenname) as TextView + textViewScreenName.setOnClickListener(this) + textViewName = v.findViewById(R.id.textview_user_detail_name) as TextView + textViewURL = v.findViewById(R.id.textview_user_detail_url) as TextView + textViewLocate = v.findViewById(R.id.textview_user_detail_locate) as TextView + textViewFollowed = v.findViewById(R.id.textview_user_detail_followed) as TextView + textViewProtected = v.findViewById(R.id.texttview_user_detail_protected) as TextView + textViewDescription = v.findViewById(R.id.textview_user_detail_description) as TextView + textViewDescription.movementMethod = LinkMovementMethod.getInstance() + textViewTweetCount = v.findViewById(R.id.textview_user_detail_tweet_count) as TextView + textViewTweetCount.setOnClickListener(this) + textViewFriendCount = v.findViewById(R.id.textview_user_detail_friend_count) as TextView + textViewFriendCount.setOnClickListener(this) + textViewFollowerCount = v.findViewById(R.id.textview_user_detail_follower_count) as TextView + textViewFollowerCount.setOnClickListener(this) + textViewFavoriteCount = v.findViewById(R.id.textview_user_detail_favorite_count) as TextView + textViewFavoriteCount.setOnClickListener(this) + imageViewIcon = v.findViewById(R.id.imageview_user_detail_icon) as NetworkImageView + imageViewIcon.setOnClickListener(this) + imageViewHeader = v.findViewById(R.id.imageview_user_detail_header) as NetworkImageView + buttonFollow = v.findViewById(R.id.button_user_detail_follow) as ThreeStateButton + buttonFollow.setOnClickListener(this) + listViewTimeline = v.findViewById(R.id.listview_user_detail_timeline) as PullToRefreshListView + listViewTimeline.setOnRefreshListener(this) + + tabHost = v.findViewById(android.R.id.tabhost) as TabHost + tabHost.setup() + val tab1 = tabHost.newTabSpec("tab1").setContent(R.id.tab1).setIndicator(getString(R.string.user_detail_tab_info)) + tabHost.addTab(tab1) + val tab2 = tabHost.newTabSpec("tab2").setContent(R.id.tab2).setIndicator(getString(R.string.user_detail_tab_timeline)) + tabHost.addTab(tab2) + tabHost.currentTab = 0 + + initUserData() + + return AlertDialog.Builder(activity) + .setView(v) + .setCancelable(true) + .create() + } + + private fun executeUserTimelineTask(adapter: TimelineAdapter) { + val account = world.account + tabHost.tabWidget.getChildTabViewAt(1).visibility = View.GONE + Timelines.UserTimelineTask(account, user.id) + .setCount(200) + .onFail { x -> world.notifyError(R.string.notice_error_get_user_timeline) } + .onDoneUI { tweets -> + adapter.addAll(tweets) + adapter.updateForce() + tabHost.tabWidget.getChildTabViewAt(1).visibility = View.VISIBLE + } + .execute() + } + + private val htmlDescription: String? + get() { + val description = user.description + if (TextUtils.isEmpty(description)) { + return "" + } + var html = description + html = html!!.replace("https?://[\\w/:%#$&?()~.=+-]+".toRegex(), "<a href=\"$0\">$0</a>") + html = html.replace("@([a-zA-Z0-9_]+)".toRegex(), "<a href=\"https://twitter.com/$1\">$0</a>") + html = html.replace("\r\n".toRegex(), "<br />") + return html + } + + private fun updateUserDataBasic() { + textViewName.text = user.name + textViewScreenName.text = user.screenName + textViewProtected.visibility = if (user.isProtected) View.VISIBLE else View.GONE + imageViewIcon.setImageUrl(user.profileImageUrl, ImageCache.getImageLoader()) + } + + private fun updateUserDataDetail() { + if (TextUtils.isEmpty(user.location)) { + textViewLocate.visibility = View.GONE + } else { + textViewLocate.text = user.location + textViewLocate.visibility = View.VISIBLE + } + if (TextUtils.isEmpty(user.url)) { + textViewURL.visibility = View.GONE + } else { + textViewURL.text = user.url + textViewURL.visibility = View.VISIBLE + } + textViewDescription.text = Html.fromHtml(htmlDescription) + + textViewTweetCount.text = user.statusesCount.toString() + textViewFriendCount.text = user.friendsCount.toString() + textViewFollowerCount.text = user.followersCount.toString() + textViewFavoriteCount.text = user.favoritesCount.toString() + + imageViewHeader.setImageUrl(user.profileBannerUrl, ImageCache.getImageLoader()) + } + + private fun initUserData() { + updateUserDataBasic() + updateUserDataDetail() + + val activity = activity as MainActivity + adapter = TimelineAdapter(activity) + listViewTimeline.setAdapter(adapter) + executeUserTimelineTask(adapter) + updateRelationship() + + observerBundle.attach(user) { changes -> + if (getActivity() != null) { + if (changes.contains(RBinding.BASIC)) + updateUserDataBasic() + if (changes.contains(RBinding.DETAIL)) + updateUserDataDetail() + } + } + } + + private fun openUserMenu() { + val builder = AlertDialog.Builder(activity) + builder.setTitle("@" + user.screenName) + .setItems(R.array.user_commands) { dialog, which -> + // XXX + UIHandler().postDelayed({ + if (this@UserDetailDialogFragment.isAdded) { + updateRelationship() + } + }, 1000) + when (which) { + 0 -> { + val text = String.format("@%s ", user.screenName) + world.postState.beginTransaction().insertText(0, text).moveCursor(text.length).commit() + world.notify(R.string.notice_add_to_reply) + } + 1 -> DialogHelper.showDialog(activity, SendMessageDialogFragment.newInstance(user)) + 2 -> ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands)) { + Users.BlockTask(world.account, user.id) + .onDone { user -> world.notify(R.string.notice_block_succeeded) } + .onFail { ex -> world.notifyError(R.string.notice_block_failed) } + .execute() + } + 3 -> ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands)) { + Users.UnblockTask(world.account, user.id) + .onDone { user -> world.notify(R.string.notice_unblock_succeeded) } + .onFail { x -> world.notifyError(R.string.notice_unblock_failed) } + .execute() + } + 4 -> ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_commands)) { + Users.ReportForSpamTask(world.account, user.id) + .onDone { user -> world.notify(R.string.notice_r4s_succeeded) } + .onFail { ex -> world.notifyError(R.string.notice_r4s_failed) } + .execute() + } + 5 -> IntentUtils.openUri(activity, user.aclogTimelineURL) + else -> throw IllegalStateException() + } + } + val dialog = builder.create() + dialog.show() + } + + private fun setFollowButtonState(isFollowing: Boolean) { + if (activity != null) { + buttonFollow.state = if (isFollowing) ThreeStateButton.STATE_ON else ThreeStateButton.STATE_OFF + } + } + + private fun toggleFollowing() { + val account = world.account + val isFollowing = buttonFollow.state == ThreeStateButton.STATE_ON + buttonFollow.state = ThreeStateButton.STATE_LOCKED + if (isFollowing) { + Users.UnfollowTask(account, user.id) + .onDoneUI { result -> + world.notify(R.string.notice_unfollow_succeeded) + updateRelationship() + } + .onFail { x -> world.notifyError(R.string.notice_unfollow_failed) } + .execute() + } else { + Users.FollowTask(account, user.id) + .onDoneUI { result -> + world.notify(R.string.notice_follow_succeeded) + updateRelationship() + } + .onFail { x -> world.notifyError(R.string.notice_follow_failed) } + .execute() + } + } + + private fun updateListView(absListView: AbsListView, adapter: TimelineAdapter, addedToTop: Boolean) { + val before = adapter.count + adapter.notifyDataSetChanged() // synchronized call (not adapter#updateForce()) + val after = adapter.count + val increments = after - before + if (increments > 0) { + if (addedToTop) { + absListView.setSelection(increments + 1) + absListView.smoothScrollToPositionFromTop(increments, 0) + absListView.setSelection(increments) + } else { + absListView.smoothScrollToPositionFromTop(before, 0) + } + } + } + + private fun updateRelationship() { + val account = world.account + if (user === account.user) { + textViewFollowed.setText(R.string.user_detail_followed_is_me) + buttonFollow.visibility = View.GONE + } else { + buttonFollow.state = ThreeStateButton.STATE_LOCKED + textViewFollowed.setText(R.string.user_detail_loading) + Users.ShowFriendshipTask(account, user.id).onDoneUI { relationship -> + val isFollowing = relationship.isSourceFollowingTarget + val isFollowed = relationship.isSourceFollowedByTarget + setFollowButtonState(isFollowing) + textViewFollowed.setText(if (isFollowed) R.string.user_detail_followed else R.string.user_detail_not_followed) + }.execute() + } + } + + companion object { + private val KEY_USER_ID = "userID" + + fun newInstance(user: User): UserDetailDialogFragment { + val obj = UserDetailDialogFragment() + val bundle = Bundle() + bundle.putLong(KEY_USER_ID, user.id) + obj.arguments = bundle + return obj + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/listener/ListItemClickListener.java b/app/src/main/java/net/lacolaco/smileessence/view/listener/ListItemClickListener.kt index 22d6180f..20404c63 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/listener/ListItemClickListener.java +++ b/app/src/main/java/net/lacolaco/smileessence/view/listener/ListItemClickListener.kt @@ -22,42 +22,35 @@ * SOFTWARE. */ -package net.lacolaco.smileessence.view.listener; +package net.lacolaco.smileessence.view.listener -import android.app.Activity; -import android.graphics.drawable.ColorDrawable; -import android.support.v4.content.ContextCompat; -import android.view.View; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.util.UIHandler; +import android.app.Activity +import android.graphics.drawable.ColorDrawable +import android.support.v4.content.ContextCompat +import android.view.View +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.util.UIHandler -public class ListItemClickListener implements View.OnClickListener { +class ListItemClickListener +// --------------------------- CONSTRUCTORS --------------------------- - // ------------------------------ FIELDS ------------------------------ +( + // ------------------------------ FIELDS ------------------------------ - private final Activity activity; - private final Runnable callback; - - // --------------------------- CONSTRUCTORS --------------------------- - - public ListItemClickListener(Activity activity, Runnable callback) { - this.activity = activity; - this.callback = callback; - } + private val activity: Activity, private val callback: () -> Unit) : View.OnClickListener { // ------------------------ INTERFACE METHODS ------------------------ // --------------------- Interface OnClickListener --------------------- - @Override - public void onClick(final View v) { - final int currentBgColor = ((ColorDrawable) v.getBackground()).getColor(); - v.setBackgroundColor(ContextCompat.getColor(activity, R.color.metro_blue)); - v.invalidate(); - new UIHandler().post(() -> { - v.setBackgroundColor(currentBgColor); - callback.run(); - }); + override fun onClick(v: View) { + val currentBgColor = (v.background as ColorDrawable).color + v.setBackgroundColor(ContextCompat.getColor(activity, R.color.metro_blue)) + v.invalidate() + UIHandler().post { + v.setBackgroundColor(currentBgColor) + callback() + } } } diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.java deleted file mode 100644 index 0dd24e67..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.ListView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import com.handmark.pulltorefresh.library.PullToRefreshListView; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.view.adapter.CustomListAdapter; - -public abstract class CustomListFragment<T extends CustomListAdapter> extends PageFragment<T> implements AbsListView.OnScrollListener, - PullToRefreshBase.OnRefreshListener2<ListView> { - - // --------------------- GETTER / SETTER METHODS --------------------- - - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.DISABLED; - } - - // ------------------------ INTERFACE METHODS ------------------------ - - - // --------------------- Interface OnRefreshListener2 --------------------- - - @Override - public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) { - } - - @Override - public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) { - } - - // --------------------- Interface OnScrollListener --------------------- - - @Override - public void onScrollStateChanged(AbsListView absListView, int scrollState) { - T adapter = getAdapter(); - if (scrollState != SCROLL_STATE_TOUCH_SCROLL && - absListView.getFirstVisiblePosition() == 0 && (absListView.getChildAt(0) == null || absListView.getChildAt(0).getTop() == 0)) { - adapter.setNotifiable(true); - updateListViewWithNotice(absListView, true); - } else { - adapter.setNotifiable(false); - } - } - - @Override - public void onScroll(AbsListView absListView, int i, int i2, int i3) { - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View page = inflater.inflate(R.layout.fragment_list, container, false); - PullToRefreshListView listView = getListView(page); - listView.setAdapter(getAdapter()); - listView.setOnScrollListener(this); - listView.setOnRefreshListener(this); - listView.setMode(getRefreshMode()); - return page; - } - - protected PullToRefreshListView getListView(View page) { - return (PullToRefreshListView) page.findViewById(R.id.fragment_list_listview); - } - - protected void updateListViewWithNotice(AbsListView absListView, boolean addedToTop) { - T adapter = getAdapter(); - int before = adapter.getCount(); - adapter.notifyDataSetChanged(); // synchronized call (not adapter#updateForce()) - int after = adapter.getCount(); - int increments = after - before; - if (increments > 0) { - if (addedToTop) { - absListView.setSelection(increments + 1); - absListView.smoothScrollToPositionFromTop(increments, 0); - absListView.setSelection(increments); - } else { - absListView.smoothScrollToPositionFromTop(before, 0); - } - - if (increments > 1) { - adapter.setNotifiable(false); - } - } - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.kt new file mode 100644 index 00000000..d8536263 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/CustomListFragment.kt @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AbsListView +import android.widget.ListView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import com.handmark.pulltorefresh.library.PullToRefreshListView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.view.adapter.CustomListAdapter + +abstract class CustomListFragment<T : CustomListAdapter<*>> : PageFragment<T>(), AbsListView.OnScrollListener, PullToRefreshBase.OnRefreshListener2<ListView> { + + // --------------------- GETTER / SETTER METHODS --------------------- + + protected open val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.DISABLED + + // ------------------------ INTERFACE METHODS ------------------------ + + + // --------------------- Interface OnRefreshListener2 --------------------- + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) {} + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) {} + + // --------------------- Interface OnScrollListener --------------------- + + override fun onScrollStateChanged(absListView: AbsListView, scrollState: Int) { + val adapter = adapter + if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL && + absListView.firstVisiblePosition == 0 && (absListView.getChildAt(0) == null || absListView.getChildAt(0).top == 0)) { + adapter.setNotifiable(true) + updateListViewWithNotice(absListView, true) + } else { + adapter.setNotifiable(false) + } + } + + override fun onScroll(absListView: AbsListView, i: Int, i2: Int, i3: Int) {} + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { + val page = inflater.inflate(R.layout.fragment_list, container, false) + val listView = getListView(page) + listView.setAdapter(adapter) + listView.setOnScrollListener(this) + listView.setOnRefreshListener(this) + listView.mode = refreshMode + return page + } + + protected open fun getListView(page: View): PullToRefreshListView { + return page.findViewById(R.id.fragment_list_listview) as PullToRefreshListView + } + + protected fun updateListViewWithNotice(absListView: AbsListView, addedToTop: Boolean) { + val adapter = adapter + val before = adapter.count + adapter.notifyDataSetChanged() // synchronized call (not adapter#updateForce()) + val after = adapter.count + val increments = after - before + if (increments > 0) { + if (addedToTop) { + absListView.setSelection(increments + 1) + absListView.smoothScrollToPositionFromTop(increments, 0) + absListView.setSelection(increments) + } else { + absListView.smoothScrollToPositionFromTop(before, 0) + } + + if (increments > 1) { + adapter.setNotifiable(false) + } + } + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/adapter/MessageListAdapter.java b/app/src/main/java/net/lacolaco/smileessence/view/page/HistoryFragment.kt index 9f8e5039..70cae02f 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/MessageListAdapter.java +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/HistoryFragment.kt @@ -22,32 +22,20 @@ * SOFTWARE. */ -package net.lacolaco.smileessence.view.adapter; +package net.lacolaco.smileessence.view.page -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; -import net.lacolaco.smileessence.entity.DirectMessage; -import net.lacolaco.smileessence.view.Partials; +import android.os.Bundle +import com.handmark.pulltorefresh.library.PullToRefreshBase +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.view.adapter.EventListAdapter -public class MessageListAdapter extends OrderedCustomListAdapter<DirectMessage> { - private final Activity activity; +class HistoryFragment : CustomListFragment<EventListAdapter>() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) - public MessageListAdapter(Activity activity) { - super(); - this.activity = activity; + adapter = EventListAdapter(world, activity) + world.addEventNotifier(this) { adapter.update() } // XXX } - public long getLastID() { - return getCount() > 0 ? getItem(getCount() - 1).getId() : -1; - } - - public long getTopID() { - return getCount() > 0 ? getItem(0).getId() : -1; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return Partials.getDirectMessageView(getItem(position), activity, convertView); - } + override fun refresh() {} } diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.java deleted file mode 100644 index e00f69c6..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.os.Bundle; -import android.widget.ListView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.logging.Logger; -import net.lacolaco.smileessence.twitter.task.TimelineTask; -import net.lacolaco.smileessence.twitter.task.Timelines; -import net.lacolaco.smileessence.util.UIHandler; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; - -public class HomeFragment extends CustomListFragment<TimelineAdapter> { - @Override - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.BOTH; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - TimelineAdapter adapter = new TimelineAdapter(getActivity()); - setAdapter(adapter); - - getWorld().addTimeline(this, tweet -> { - adapter.add(tweet); - adapter.update(); - }); - - refresh(); - } - - @Override - public void refresh() { - runRefreshTask(new Timelines.HomeTimelineTask(getWorld().getAccount()), () -> getAdapter().updateForce()); - } - - // --------------------- Interface OnRefreshListener2 --------------------- - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - if (getWorld().isStreaming()) { - new UIHandler().post(() -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); - } else { - runRefreshTask( - new Timelines.HomeTimelineTask(getWorld().getAccount()) - .setSinceId(getAdapter().getTopID()), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); - } - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - runRefreshTask( - new Timelines.HomeTimelineTask(getWorld().getAccount()) - .setMaxId(getAdapter().getLastID() - 1), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), false); - refreshView.onRefreshComplete(); - }); - } - - private void runRefreshTask(TimelineTask<Tweet> task, Runnable onFinish) { - task - .setCount(200) - .onFail(e -> getWorld().notifyError(R.string.notice_error_get_home)) - .onDoneUI(tweets -> getWorld().addTweetAll(tweets)) - .onFinishUI(onFinish) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.kt new file mode 100644 index 00000000..a9bd8fe4 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/HomeFragment.kt @@ -0,0 +1,97 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.os.Bundle +import android.widget.ListView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.logging.Logger +import net.lacolaco.smileessence.twitter.task.TimelineTask +import net.lacolaco.smileessence.twitter.task.Timelines +import net.lacolaco.smileessence.util.UIHandler +import net.lacolaco.smileessence.view.adapter.TimelineAdapter + +class HomeFragment : CustomListFragment<TimelineAdapter>() { + override val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.BOTH + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + adapter = TimelineAdapter(activity) + + world.addTimeline(this) { tweet -> + adapter.add(tweet) + adapter.update() + } + + refresh() + } + + override fun refresh() { + runRefreshTask(Timelines.HomeTimelineTask(world.account), { adapter.updateForce() }) + } + + // --------------------- Interface OnRefreshListener2 --------------------- + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + if (world.isStreaming) { + UIHandler().post { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + } + } else { + runRefreshTask( + Timelines.HomeTimelineTask(world.account) + .setSinceId(adapter.topID), + { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + }) + } + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + runRefreshTask( + Timelines.HomeTimelineTask(world.account) + .setMaxId(adapter.lastID - 1), + { + updateListViewWithNotice(refreshView.refreshableView, false) + refreshView.onRefreshComplete() + }) + } + + private fun runRefreshTask(task: TimelineTask<Tweet>, onFinish: () -> Unit) { + task + .setCount(200) + .onFail { e -> world.notifyError(R.string.notice_error_get_home) } + .onDoneUI { tweets -> world.addTweetAll(tweets) } + .onFinishUI(onFinish) + .execute() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.java deleted file mode 100644 index 21ae1169..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.os.Bundle; -import android.widget.ListView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.data.ExtractionWord; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.entity.User; -import net.lacolaco.smileessence.twitter.task.TimelineTask; -import net.lacolaco.smileessence.twitter.task.Timelines; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; - -public class MentionsFragment extends CustomListFragment<TimelineAdapter> { - - // --------------------- GETTER / SETTER METHODS --------------------- - - @Override - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.BOTH; - } - - // ------------------------ INTERFACE METHODS ------------------------ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - final TimelineAdapter adapter = new TimelineAdapter(getActivity()); - setAdapter(adapter); - - // Prevent calling getWorld() on null - final User self = getWorld().getAccount().getUser(); - getWorld().addTimeline(this, tweet -> { - if (tweet.getMentions().contains(self.getScreenName())) { - adapter.add(tweet); - adapter.update(); - } else { - for (ExtractionWord word : ExtractionWord.cached()) { - if (word.getPattern().matcher(tweet.getOriginalTweet().getText()).find()) { - adapter.add(tweet); - adapter.update(); - } - } - } - }); - - refresh(); - } - - @Override - public void refresh() { - runRefreshTask(new Timelines.MentionsTimelineTask(getWorld().getAccount()), () -> getAdapter().updateForce()); - } - - // --------------------- Interface OnRefreshListener2 --------------------- - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - runRefreshTask( - new Timelines.MentionsTimelineTask(getWorld().getAccount()) - .setSinceId(getAdapter().getTopID()), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - runRefreshTask( - new Timelines.MentionsTimelineTask(getWorld().getAccount()) - .setMaxId(getAdapter().getLastID() - 1), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), false); - refreshView.onRefreshComplete(); - }); - } - - private void runRefreshTask(TimelineTask<Tweet> task, Runnable onFinish) { - task - .setCount(200) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_mentions)) - .onDoneUI(tweets -> getWorld().addTweetAll(tweets)) - .onFinishUI(onFinish) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.kt new file mode 100644 index 00000000..d8442ac7 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/MentionsFragment.kt @@ -0,0 +1,102 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.os.Bundle +import android.widget.ListView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.data.ExtractionWord +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.entity.User +import net.lacolaco.smileessence.twitter.task.TimelineTask +import net.lacolaco.smileessence.twitter.task.Timelines +import net.lacolaco.smileessence.view.adapter.TimelineAdapter + +class MentionsFragment : CustomListFragment<TimelineAdapter>() { + override val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.BOTH + + // ------------------------ INTERFACE METHODS ------------------------ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + adapter = TimelineAdapter(activity) + + // Prevent calling getWorld() on null + val self = world.account.user + world.addTimeline(this) { tweet -> + if (tweet.mentions.contains(self.screenName)) { + adapter.add(tweet) + adapter.update() + } else { + for (word in ExtractionWord.cached()) { + if (word.pattern.matcher(tweet.originalTweet.text).find()) { + adapter.add(tweet) + adapter.update() + } + } + } + } + + refresh() + } + + override fun refresh() { + runRefreshTask(Timelines.MentionsTimelineTask(world.account), { adapter.updateForce() }) + } + + // --------------------- Interface OnRefreshListener2 --------------------- + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + runRefreshTask( + Timelines.MentionsTimelineTask(world.account) + .setSinceId(adapter.topID), + { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + }) + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + runRefreshTask( + Timelines.MentionsTimelineTask(world.account) + .setMaxId(adapter.lastID - 1), + { + updateListViewWithNotice(refreshView.refreshableView, false) + refreshView.onRefreshComplete() + }) + } + + private fun runRefreshTask(task: TimelineTask<Tweet>, onFinish: () -> Unit) { + task + .setCount(200) + .onFail { x -> world.notifyError(R.string.notice_error_get_mentions) } + .onDoneUI { tweets -> world.addTweetAll(tweets) } + .onFinishUI(onFinish) + .execute() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.java deleted file mode 100644 index 291f4cfc..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.os.Bundle; -import android.widget.ListView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.entity.DirectMessage; -import net.lacolaco.smileessence.twitter.task.Messages; -import net.lacolaco.smileessence.twitter.task.TimelineTask; -import net.lacolaco.smileessence.view.adapter.MessageListAdapter; - -public class MessagesFragment extends CustomListFragment<MessageListAdapter> { - @Override - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.BOTH; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - MessageListAdapter adapter = new MessageListAdapter(getActivity()); - setAdapter(adapter); - - getWorld().addDirectMessageTimeline(this, message -> { - adapter.add(message); - adapter.update(); - }); - - refresh(); - } - - @Override - public void refresh() { - runRefreshTask(new Messages.GetAllReceived(getWorld().getAccount()), () -> getAdapter().updateForce()); - runRefreshTask(new Messages.GetAllSent(getWorld().getAccount()), () -> getAdapter().updateForce()); - } - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - runRefreshTask( - new Messages.GetAllReceived(getWorld().getAccount()) - .setSinceId(getAdapter().getTopID()), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); // TODO: sent? - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - runRefreshTask( - new Messages.GetAllReceived(getWorld().getAccount()) - .setMaxId(getAdapter().getLastID() - 1), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), false); - refreshView.onRefreshComplete(); - }); // TODO: sent? - } - - private void runRefreshTask(TimelineTask<DirectMessage> task, Runnable onFinish) { - task - .setCount(200) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_messages)) - .onDoneUI(messages -> { - for (DirectMessage message : messages) { - getWorld().addDirectMessage(message); - } - }) - .onFinishUI(onFinish) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.kt new file mode 100644 index 00000000..ea756d09 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/MessagesFragment.kt @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.os.Bundle +import android.widget.ListView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.entity.DirectMessage +import net.lacolaco.smileessence.twitter.task.Messages +import net.lacolaco.smileessence.twitter.task.TimelineTask +import net.lacolaco.smileessence.view.adapter.MessageListAdapter + +class MessagesFragment : CustomListFragment<MessageListAdapter>() { + override val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.BOTH + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + adapter = MessageListAdapter(activity) + + world.addDirectMessageTimeline(this) { message -> + adapter.add(message) + adapter.update() + } + + refresh() + } + + override fun refresh() { + runRefreshTask(Messages.GetAllReceived(world.account), { adapter.updateForce() }) + runRefreshTask(Messages.GetAllSent(world.account), { adapter.updateForce() }) + } + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + runRefreshTask( + Messages.GetAllReceived(world.account) + .setSinceId(adapter.topID), + { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + }) // TODO: sent? + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + runRefreshTask( + Messages.GetAllReceived(world.account) + .setMaxId(adapter.lastID - 1), + { + updateListViewWithNotice(refreshView.refreshableView, false) + refreshView.onRefreshComplete() + }) // TODO: sent? + } + + private fun runRefreshTask(task: TimelineTask<DirectMessage>, onFinish: () -> Unit) { + task + .setCount(200) + .onFail { x -> world.notifyError(R.string.notice_error_get_messages) } + .onDoneUI { messages -> + for (message in messages) { + world.addDirectMessage(message) + } + } + .onFinishUI(onFinish) + .execute() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.java deleted file mode 100644 index c1299298..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.lacolaco.smileessence.view.page; - -import android.app.Fragment; -import android.os.Bundle; -import android.widget.Adapter; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.activity.MainActivity; - -public abstract class PageFragment<T extends Adapter> extends Fragment { - public static final String KEY_USER_ID = "KEY_USER_ID"; - private T adapter; - private World world; - - protected final T getAdapter() { - if (adapter == null) throw new IllegalStateException("adapter is not initialized"); - return adapter; - } - - protected final void setAdapter(T _adapter) { - adapter = _adapter; - } - - public abstract void refresh(); - - protected final World getWorld() { - if (world == null) { - world = Application.getWorld(getArguments().getLong(KEY_USER_ID)); - } - return world; - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.kt new file mode 100644 index 00000000..596f4107 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.kt @@ -0,0 +1,25 @@ +package net.lacolaco.smileessence.view.page + +import android.app.Fragment +import android.os.Bundle +import android.widget.Adapter +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.activity.MainActivity + +abstract class PageFragment<T : Adapter> : Fragment() { + private var _adapter: T? = null + protected var adapter: T + get() = _adapter ?: throw IllegalStateException("adapter is not initialized") + set(value) { _adapter = value } + + protected val world: World by lazy { + Application.getWorld(arguments.getLong(KEY_USER_ID)) + } + + abstract fun refresh() + + companion object { + val KEY_USER_ID = "KEY_USER_ID" + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.java deleted file mode 100644 index c8426383..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.text.Editable; -import android.text.Spannable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.ArrowKeyMovementMethod; -import android.view.*; -import android.widget.*; -import com.twitter.Validator; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.World; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.data.PostState; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.logging.Logger; -import net.lacolaco.smileessence.preference.UserPreferenceHelper; -import net.lacolaco.smileessence.twitter.task.Tweets; -import net.lacolaco.smileessence.util.BitmapThumbnailTask; -import net.lacolaco.smileessence.util.IntentUtils; -import net.lacolaco.smileessence.util.SystemServiceHelper; -import net.lacolaco.smileessence.util.UIHandler; -import net.lacolaco.smileessence.view.Partials; - -import java.io.File; - -public class PostFragment extends PageFragment implements TextWatcher, View.OnFocusChangeListener, View.OnClickListener, - PostState.OnPostStateChangeListener { - // ------------------------------ FIELDS ------------------------------ - - private EditText editText; - private TextView textViewCount; - private Button buttonTweet; - private ViewGroup viewGroupReply; - private ViewGroup viewGroupMedia; - - @Override - public void refresh() { - } - - // --------------------- Interface OnClickListener --------------------- - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.button_post_delete: { - deletePost(); - break; - } - case R.id.button_post_media: { - setImage(); - break; - } - case R.id.button_post_tweet: { - submitPost(); - break; - } - case R.id.button_post_reply_delete: { - deleteReply(); - break; - } - case R.id.button_post_media_delete: { - removeImage(); - break; - } - case R.id.image_post_media: { - displayImage(); - break; - } - } - } - - // --------------------- Interface OnFocusChangeListener --------------------- - - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - SystemServiceHelper.showIM(getActivity(), editText); - } else { - SystemServiceHelper.hideIM(getActivity(), editText); - } - } - - // --------------------- Interface OnPostStateChangeListener --------------------- - - - @Override - public void onPostStateChange(final PostState postState) { - Logger.debug("onPostStateChange"); - final MainActivity activity = (MainActivity) getActivity(); - if (editText != null) { - final int start = postState.getSelectionStart(); - final int end = postState.getSelectionEnd(); - editText.removeTextChangedListener(this); - editText.setTextKeepState(postState.getText()); - editText.addTextChangedListener(this); - updateTextCount(editText.getText()); - new UIHandler().postAtFrontOfQueue(() -> editText.setSelection(start, end)); - } - if (viewGroupReply != null) { - if (postState.getInReplyTo() != null) { - viewGroupReply.setVisibility(View.VISIBLE); - ImageButton imageButtonDeleteReply = (ImageButton) viewGroupReply.findViewById(R.id.button_post_reply_delete); - imageButtonDeleteReply.setOnClickListener(this); - - Tweet tweet = postState.getInReplyTo(); - View header = viewGroupReply.findViewById(R.id.layout_post_reply_status); - header = Partials.getTweetView(tweet, activity, header, true); - header.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.transparent)); - header.setClickable(false); - } else { - viewGroupReply.setVisibility(View.GONE); - } - } - if (viewGroupMedia != null) { - ImageView imageViewMedia = (ImageView) viewGroupMedia.findViewById(R.id.image_post_media); - if (TextUtils.isEmpty(postState.getMediaFilePath())) { - viewGroupMedia.setVisibility(View.GONE); - } else { - viewGroupMedia.setVisibility(View.VISIBLE); - - } - new BitmapThumbnailTask(postState.getMediaFilePath(), imageViewMedia).execute(); - } - } - - // --------------------- Interface TextWatcher --------------------- - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - updateTextCount(s); - } - - private void updateTextCount(CharSequence s) { - Validator validator = new Validator(); - int remainingCount = 140 - validator.getTweetLength(s.toString()); - if (!TextUtils.isEmpty(getWorld().getPostState().getMediaFilePath())) { - remainingCount -= validator.getShortUrlLength(); - } - textViewCount.setText(String.valueOf(remainingCount)); - if (remainingCount == 140 || remainingCount < 0) { - textViewCount.setTextColor(ContextCompat.getColor(getActivity(), R.color.red)); - } else { - textViewCount.setTextAppearance(getActivity(), android.R.style.TextAppearance_Widget_TextView); - } - setStateFromView(); - } - - @Override - public void afterTextChanged(Editable s) { - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - Logger.debug("onCreate"); - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - SystemServiceHelper.showIM(getActivity(), editText); - } - - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Logger.debug("onCreateView"); - getWorld().getPostState().setListener(this); - View v = inflater.inflate(R.layout.fragment_post, null); - buttonTweet = getTweetButton(v); - buttonTweet.setOnClickListener(this); - editText = getEditText(v); - textViewCount = getCountTextView(v); - int textSize = UserPreferenceHelper.getInstance().getTextSize(); - editText.addTextChangedListener(this); - editText.setOnFocusChangeListener(this); - editText.setTextSize(textSize + 4); - editText.setMovementMethod(new ArrowKeyMovementMethod() { - @Override - protected boolean right(@NonNull TextView widget, Spannable buffer) { - //Don't back to Home - return widget.getSelectionEnd() == widget.length() || super.right(widget, buffer); - } - }); - ImageButton imageButtonDeleteText = (ImageButton) v.findViewById(R.id.button_post_delete); - imageButtonDeleteText.setOnClickListener(this); - ImageButton imageButtonMedia = (ImageButton) v.findViewById(R.id.button_post_media); - imageButtonMedia.setOnClickListener(this); - //Reply view - viewGroupReply = getReplyViewGroup(v); - ImageButton imageButtonDeleteReply = (ImageButton) viewGroupReply.findViewById(R.id.button_post_reply_delete); - imageButtonDeleteReply.setOnClickListener(this); - //Media view - viewGroupMedia = getMediaViewGroup(v); - ImageView imageViewMedia = (ImageView) viewGroupMedia.findViewById(R.id.image_post_media); - ImageButton imageButtonDeleteMedia = (ImageButton) viewGroupMedia.findViewById(R.id.button_post_media_delete); - imageViewMedia.setOnClickListener(this); - imageButtonDeleteMedia.setOnClickListener(this); - editText.requestFocus(); - return v; - } - - @Override - public void onDestroyView() { - Logger.debug("onDestroyView"); - super.onDestroyView(); - setStateFromView(); - getWorld().getPostState().removeListener(); - } - - @Override - public void onViewStateRestored(Bundle savedInstanceState) { - Logger.debug("onViewStateRestored"); - super.onViewStateRestored(savedInstanceState); - PostState state = getWorld().getPostState(); - onPostStateChange(state); - } - - private void deletePost() { - editText.setText(""); - getWorld().getPostState().beginTransaction().setText("").setCursor(0).commit(); - deleteReply(); - } - - private void deleteReply() { - viewGroupReply.setVisibility(View.GONE); - getWorld().getPostState().beginTransaction().setInReplyTo(null).commit(); - } - - private void displayImage() { - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setDataAndType(Uri.fromFile(new File(getWorld().getPostState().getMediaFilePath())), "image/*"); - IntentUtils.startActivityIfFound(getActivity(), intent); - } - - private TextView getCountTextView(View v) { - return (TextView) v.findViewById(R.id.post_text_count); - } - - private EditText getEditText(View v) { - return (EditText) v.findViewById(R.id.post_edit_text); - } - - private ViewGroup getMediaViewGroup(View v) { - return (ViewGroup) v.findViewById(R.id.post_media_parent); - } - - private ViewGroup getReplyViewGroup(View v) { - return (ViewGroup) v.findViewById(R.id.post_inreplyto_parent); - } - - private Button getTweetButton(View v) { - return (Button) v.findViewById(R.id.button_post_tweet); - } - - private void removeImage() { - SystemServiceHelper.hideIM(getActivity(), editText); - viewGroupMedia.setVisibility(View.GONE); - ((ImageView) viewGroupMedia.findViewById(R.id.image_post_media)).setImageBitmap(null); - getWorld().getPostState().beginTransaction().setMediaFilePath("").commit(); - } - - private void setImage() { - setStateFromView(); - SystemServiceHelper.hideIM(getActivity(), editText); - - Intent intent = new Intent(Intent.ACTION_PICK); - intent.setType("image/*"); - IntentUtils.startActivityForResultIfFound(getActivity(), intent, MainActivity.REQUEST_GET_PICTURE_FROM_GALLERY); - } - - private void setStateFromView() { - PostState state = getWorld().getPostState(); - state.removeListener(); - state.beginTransaction() - .setText(editText.getText().toString()) - .setSelection(editText.getSelectionStart(), editText.getSelectionEnd()) - .commit(); - state.setListener(this); - } - - private void submitPost() { - SystemServiceHelper.hideIM(getActivity(), editText); - setStateFromView(); - PostState state = getWorld().getPostState(); - MainActivity mainActivity = (MainActivity) getActivity(); - boolean resizeFlag = UserPreferenceHelper.getInstance().get(R.string.key_setting_resize_post_image, false); - new Tweets.CreateTask(getWorld().getAccount(), state.toStatusUpdate(), state.getMediaFilePath(), resizeFlag) - .onDoneUI(t -> { - getWorld().notify(R.string.notice_tweet_succeeded); - getWorld().getPostState().beginTransaction().clear().commit(); - }) - .onFail(e -> getWorld().notifyError(R.string.notice_tweet_failed, e)) - .execute(); - mainActivity.openHomePage(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.kt new file mode 100644 index 00000000..8afeadd1 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.kt @@ -0,0 +1,304 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.text.Editable +import android.text.Spannable +import android.text.TextUtils +import android.text.TextWatcher +import android.text.method.ArrowKeyMovementMethod +import android.view.* +import android.widget.* +import com.twitter.Validator +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.World +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.data.PostState +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.logging.Logger +import net.lacolaco.smileessence.preference.UserPreferenceHelper +import net.lacolaco.smileessence.twitter.task.Tweets +import net.lacolaco.smileessence.util.BitmapThumbnailTask +import net.lacolaco.smileessence.util.IntentUtils +import net.lacolaco.smileessence.util.SystemServiceHelper +import net.lacolaco.smileessence.util.UIHandler +import net.lacolaco.smileessence.view.Partials + +import java.io.File + +class PostFragment : PageFragment<BaseAdapter>(), TextWatcher, View.OnFocusChangeListener, View.OnClickListener, PostState.OnPostStateChangeListener { + private var editText: EditText? = null + private var textViewCount: TextView? = null + private var buttonTweet: Button? = null + private var viewGroupReply: ViewGroup? = null + private var viewGroupMedia: ViewGroup? = null + + override fun refresh() {} + + // --------------------- Interface OnClickListener --------------------- + + override fun onClick(v: View) { + when (v.id) { + R.id.button_post_delete -> { + deletePost() + } + R.id.button_post_media -> { + setImage() + } + R.id.button_post_tweet -> { + submitPost() + } + R.id.button_post_reply_delete -> { + deleteReply() + } + R.id.button_post_media_delete -> { + removeImage() + } + R.id.image_post_media -> { + displayImage() + } + } + } + + // --------------------- Interface OnFocusChangeListener --------------------- + + override fun onFocusChange(v: View, hasFocus: Boolean) { + if (hasFocus) { + SystemServiceHelper.showIM(activity, editText) + } else { + SystemServiceHelper.hideIM(activity, editText) + } + } + + // --------------------- Interface OnPostStateChangeListener --------------------- + + + override fun onPostStateChange(postState: PostState) { + Logger.debug("onPostStateChange") + val activity = activity as MainActivity + if (editText != null) { + val start = postState.selectionStart + val end = postState.selectionEnd + editText!!.removeTextChangedListener(this) + editText!!.setTextKeepState(postState.text) + editText!!.addTextChangedListener(this) + updateTextCount(editText!!.text) + UIHandler().postAtFrontOfQueue { editText!!.setSelection(start, end) } + } + if (viewGroupReply != null) { + if (postState.inReplyTo != null) { + viewGroupReply!!.visibility = View.VISIBLE + val imageButtonDeleteReply = viewGroupReply!!.findViewById(R.id.button_post_reply_delete) as ImageButton + imageButtonDeleteReply.setOnClickListener(this) + + val tweet = postState.inReplyTo + var header = viewGroupReply!!.findViewById(R.id.layout_post_reply_status) + header = Partials.getTweetView(tweet!!, activity, header, true) + header.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.transparent)) + header.isClickable = false + } else { + viewGroupReply!!.visibility = View.GONE + } + } + if (viewGroupMedia != null) { + val imageViewMedia = viewGroupMedia!!.findViewById(R.id.image_post_media) as ImageView + if (TextUtils.isEmpty(postState.mediaFilePath)) { + viewGroupMedia!!.visibility = View.GONE + } else { + viewGroupMedia!!.visibility = View.VISIBLE + + } + BitmapThumbnailTask(postState.mediaFilePath, imageViewMedia).execute() + } + } + + // --------------------- Interface TextWatcher --------------------- + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + updateTextCount(s) + } + + private fun updateTextCount(s: CharSequence) { + val validator = Validator() + var remainingCount = 140 - validator.getTweetLength(s.toString()) + if (!TextUtils.isEmpty(world.postState.mediaFilePath)) { + remainingCount -= validator.shortUrlLength + } + textViewCount!!.text = remainingCount.toString() + if (remainingCount == 140 || remainingCount < 0) { + textViewCount!!.setTextColor(ContextCompat.getColor(activity, R.color.red)) + } else { + textViewCount!!.setTextAppearance(activity, android.R.style.TextAppearance_Widget_TextView) + } + setStateFromView() + } + + override fun afterTextChanged(s: Editable) {} + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + SystemServiceHelper.showIM(activity, editText) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + Logger.debug("onCreateView") + setHasOptionsMenu(true) + world.postState.setListener(this) + val v = inflater.inflate(R.layout.fragment_post, container, false) + buttonTweet = getTweetButton(v) + buttonTweet!!.setOnClickListener(this) + editText = getEditText(v) + textViewCount = getCountTextView(v) + val textSize = UserPreferenceHelper.instance.textSize + editText!!.addTextChangedListener(this) + editText!!.onFocusChangeListener = this + editText!!.textSize = (textSize + 4).toFloat() + editText!!.movementMethod = object : ArrowKeyMovementMethod() { + override fun right(widget: TextView, buffer: Spannable): Boolean { + //Don't back to Home + return widget.selectionEnd == widget.length() || super.right(widget, buffer) + } + } + val imageButtonDeleteText = v.findViewById(R.id.button_post_delete) as ImageButton + imageButtonDeleteText.setOnClickListener(this) + val imageButtonMedia = v.findViewById(R.id.button_post_media) as ImageButton + imageButtonMedia.setOnClickListener(this) + //Reply view + viewGroupReply = getReplyViewGroup(v) + val imageButtonDeleteReply = viewGroupReply!!.findViewById(R.id.button_post_reply_delete) as ImageButton + imageButtonDeleteReply.setOnClickListener(this) + //Media view + viewGroupMedia = getMediaViewGroup(v) + val imageViewMedia = viewGroupMedia!!.findViewById(R.id.image_post_media) as ImageView + val imageButtonDeleteMedia = viewGroupMedia!!.findViewById(R.id.button_post_media_delete) as ImageButton + imageViewMedia.setOnClickListener(this) + imageButtonDeleteMedia.setOnClickListener(this) + editText!!.requestFocus() + return v + } + + override fun onDestroyView() { + Logger.debug("onDestroyView") + super.onDestroyView() + setStateFromView() + world.postState.removeListener() + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + Logger.debug("onViewStateRestored") + super.onViewStateRestored(savedInstanceState) + val state = world.postState + onPostStateChange(state) + } + + private fun deletePost() { + editText!!.setText("") + world.postState.beginTransaction().setText("").setCursor(0).commit() + deleteReply() + } + + private fun deleteReply() { + viewGroupReply!!.visibility = View.GONE + world.postState.beginTransaction().setInReplyTo(null).commit() + } + + private fun displayImage() { + val intent = Intent() + intent.action = Intent.ACTION_VIEW + intent.addCategory(Intent.CATEGORY_DEFAULT) + intent.setDataAndType(Uri.fromFile(File(world.postState.mediaFilePath)), "image/*") + IntentUtils.startActivityIfFound(activity, intent) + } + + private fun getCountTextView(v: View): TextView { + return v.findViewById(R.id.post_text_count) as TextView + } + + private fun getEditText(v: View): EditText { + return v.findViewById(R.id.post_edit_text) as EditText + } + + private fun getMediaViewGroup(v: View): ViewGroup { + return v.findViewById(R.id.post_media_parent) as ViewGroup + } + + private fun getReplyViewGroup(v: View): ViewGroup { + return v.findViewById(R.id.post_inreplyto_parent) as ViewGroup + } + + private fun getTweetButton(v: View): Button { + return v.findViewById(R.id.button_post_tweet) as Button + } + + private fun removeImage() { + SystemServiceHelper.hideIM(activity, editText) + viewGroupMedia!!.visibility = View.GONE + (viewGroupMedia!!.findViewById(R.id.image_post_media) as ImageView).setImageBitmap(null) + world.postState.beginTransaction().setMediaFilePath("").commit() + } + + private fun setImage() { + setStateFromView() + SystemServiceHelper.hideIM(activity, editText) + + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + IntentUtils.startActivityForResultIfFound(activity, intent, MainActivity.REQUEST_GET_PICTURE_FROM_GALLERY) + } + + private fun setStateFromView() { + val state = world.postState + state.removeListener() + state.beginTransaction() + .setText(editText!!.text.toString()) + .setSelection(editText!!.selectionStart, editText!!.selectionEnd) + .commit() + state.setListener(this) + } + + private fun submitPost() { + SystemServiceHelper.hideIM(activity, editText) + setStateFromView() + val state = world.postState + val mainActivity = activity as MainActivity + val resizeFlag = UserPreferenceHelper.instance.get(R.string.key_setting_resize_post_image, false) + Tweets.CreateTask(world.account, state.toStatusUpdate(), state.mediaFilePath, resizeFlag) + .onDoneUI { t -> + world.notify(R.string.notice_tweet_succeeded) + world.postState.beginTransaction().clear().commit() + } + .onFail { e -> world.notifyError(R.string.notice_tweet_failed, e) } + .execute() + mainActivity.openHomePage() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.java deleted file mode 100644 index 84d10c21..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.app.AlertDialog; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.text.Spannable; -import android.text.TextUtils; -import android.text.method.ArrowKeyMovementMethod; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.TextView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import com.handmark.pulltorefresh.library.PullToRefreshListView; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.entity.SavedSearch; -import net.lacolaco.smileessence.preference.InternalPreferenceHelper; -import net.lacolaco.smileessence.twitter.task.Searches; -import net.lacolaco.smileessence.util.ListUtils; -import net.lacolaco.smileessence.util.SystemServiceHelper; -import net.lacolaco.smileessence.util.UIHandler; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; -import net.lacolaco.smileessence.view.dialog.ConfirmDialogFragment; -import twitter4j.Query; - -import java.util.ArrayList; -import java.util.List; - -public class SearchFragment extends CustomListFragment<TimelineAdapter> implements View.OnClickListener, View.OnLongClickListener, - View.OnFocusChangeListener { - - // ------------------------------ FIELDS ------------------------------ - - private String queryString; - private TimelineAdapter adapter; - private EditText editText; - - // --------------------- GETTER / SETTER METHODS --------------------- - - @Override - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.BOTH; - } - - // ------------------------ INTERFACE METHODS ------------------------ - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - - queryString = InternalPreferenceHelper.getInstance().get(R.string.key_last_used_search_query, ""); - android.app.Activity activity = getActivity(); - adapter = new TimelineAdapter(activity); - setAdapter(adapter); - - refresh(); - } - - @Override - public void refresh() { //TODO - if (!TextUtils.isEmpty(queryString)) { - startSearch(queryString); - } - } - - // --------------------- Interface OnClickListener --------------------- - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.button_search_queries: { - openSearchQueryDialog(); - break; - } - case R.id.button_search_execute: { - search(); - break; - } - case R.id.button_search_save: { - saveQuery(); - } - } - } - - @Override - public boolean onLongClick(View v) { - switch (v.getId()) { - case R.id.button_search_save: { - String text = editText.getText().toString(); - for (SavedSearch ss : getWorld().getSavedSearches()) { - if (ss.getQuery().equals(text)) { - ConfirmDialogFragment.show(getActivity(), getString(R.string.dialog_confirm_delete_query), () -> new Searches.DestroySavedSearchTask(getWorld().getAccount(), ss.getId()) - .onDoneUI(x -> { - getWorld().notify(R.string.notice_search_query_deleted); - getWorld().refreshSavedSearches(); - }) - .onFailUI(x -> getWorld().notify("unable to delete search query")) - .execute(), false); - break; - } - } - return true; - } - } - return false; - } - - // --------------------- Interface OnFocusChangeListener --------------------- - - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus) { - SystemServiceHelper.hideIM(getActivity(), editText); - } - } - - // --------------------- Interface OnRefreshListener2 --------------------- - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - if (TextUtils.isEmpty(queryString)) { - new UIHandler().post(() -> { - notifyTextEmpty(); - refreshView.onRefreshComplete(); - }); - return; - } - final Query query = new Query(); - query.setQuery(queryString); - query.setCount(200); - query.setResultType(Query.RECENT); - if (adapter.getCount() > 0) { - query.setSinceId(adapter.getTopID()); - } - runRefreshTask(query, () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - if (TextUtils.isEmpty(queryString)) { - new UIHandler().post(() -> { - notifyTextEmpty(); - refreshView.onRefreshComplete(); - }); - return; - } - final Query query = new Query(); - query.setQuery(queryString); - query.setCount(200); - query.setResultType(Query.RECENT); - if (adapter.getCount() > 0) { - query.setMaxId(adapter.getLastID() - 1); - } - runRefreshTask(query, () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), false); - refreshView.onRefreshComplete(); - }); - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - protected PullToRefreshListView getListView(View page) { - return (PullToRefreshListView) page.findViewById(R.id.listview_search); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View page = inflater.inflate(R.layout.fragment_search, container, false); - PullToRefreshListView listView = getListView(page); - listView.setAdapter(adapter); - listView.setOnScrollListener(this); - listView.setOnRefreshListener(this); - listView.setMode(getRefreshMode()); - ImageButton buttonQueries = getQueriesButton(page); - buttonQueries.setOnClickListener(this); - ImageButton buttonExecute = getExecuteButton(page); - buttonExecute.setOnClickListener(this); - ImageButton buttonSave = getSaveButton(page); - buttonSave.setOnClickListener(this); - editText = getEditText(page); - editText.setOnFocusChangeListener(this); - editText.setText(queryString); - editText.setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_SEARCH || - keyEvent != null && - keyEvent.getAction() == KeyEvent.ACTION_DOWN && - keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER) { - search(); - } - return true; - }); - editText.setMovementMethod(new ArrowKeyMovementMethod() { - @Override - protected boolean right(@NonNull TextView widget, Spannable buffer) { - //Don't move page - return widget.getSelectionEnd() == widget.length() || super.right(widget, buffer); - } - - @Override - protected boolean left(@NonNull TextView widget, Spannable buffer) { - //Don't move page - return widget.getSelectionStart() == 0 || super.left(widget, buffer); - } - }); - return page; - } - - private EditText getEditText(View page) { - return (EditText) page.findViewById(R.id.edittext_search); - } - - private ImageButton getExecuteButton(View page) { - return (ImageButton) page.findViewById(R.id.button_search_execute); - } - - private ImageButton getQueriesButton(View page) { - return (ImageButton) page.findViewById(R.id.button_search_queries); - } - - private ImageButton getSaveButton(View page) { - return (ImageButton) page.findViewById(R.id.button_search_save); - } - - private void notifyTextEmpty() { - getWorld().notifyError(R.string.notice_search_text_empty); - } - - private void openSearchQueryDialog() { - List<SavedSearch> sss = new ArrayList<>(getWorld().getSavedSearches()); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setItems(ListUtils.map(sss, SavedSearch::getQuery).toArray(new String[]{}), (dialog, which) -> { - SavedSearch ss = sss.get(which); - ((MainActivity) getActivity()).openSearchPage(ss.getQuery()); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } - - private void saveQuery() { - String text = editText.getText().toString(); - if (TextUtils.isEmpty(text)) { - getWorld().notifyError(R.string.notice_query_is_empty); - } else { - new Searches.CreateSavedSearchTask(getWorld().getAccount(), text).onDoneUI(cb -> { - getWorld().notify(R.string.notice_query_saved); - getWorld().refreshSavedSearches(); - }).onFailUI(ex -> getWorld().notifyError("Query is not saved")).execute(); - } - } - - private void search() { - if (editText != null) { - String text = editText.getText().toString(); - if (TextUtils.isEmpty(text)) { - getWorld().notifyError(R.string.notice_query_is_empty); - } else { - startSearch(text); - SystemServiceHelper.hideIM(getActivity(), editText); - } - } - } - - public void startSearch(final String queryString) { - InternalPreferenceHelper.getInstance().set(R.string.key_last_used_search_query, queryString); - if (editText != null) - editText.setText(queryString); - this.queryString = queryString; - adapter.clear(); - adapter.updateForce(); - if (!TextUtils.isEmpty(queryString)) { - final Query query = new Query(); - query.setQuery(queryString); - query.setCount(200); - query.setResultType(Query.RECENT); - runRefreshTask(query, adapter::updateForce); - } - } - - private void runRefreshTask(Query query, Runnable onFinish) { - new Searches.SearchTask(getWorld().getAccount(), query) - .onFail(x -> getWorld().notifyError(R.string.notice_error_search)) - .onDoneUI(tweets -> adapter.addAll(ListUtils.filter(tweets, t -> !t.isRetweet()))) - .onFinishUI(onFinish) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.kt new file mode 100644 index 00000000..de06a133 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/SearchFragment.kt @@ -0,0 +1,299 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.app.AlertDialog +import android.os.Bundle +import android.text.Spannable +import android.text.TextUtils +import android.text.method.ArrowKeyMovementMethod +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.ImageButton +import android.widget.ListView +import android.widget.TextView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import com.handmark.pulltorefresh.library.PullToRefreshListView +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.preference.InternalPreferenceHelper +import net.lacolaco.smileessence.twitter.task.Searches +import net.lacolaco.smileessence.util.SystemServiceHelper +import net.lacolaco.smileessence.util.UIHandler +import net.lacolaco.smileessence.view.adapter.TimelineAdapter +import net.lacolaco.smileessence.view.dialog.ConfirmDialogFragment +import twitter4j.Query + +import java.util.ArrayList + +class SearchFragment : CustomListFragment<TimelineAdapter>(), View.OnClickListener, View.OnLongClickListener, View.OnFocusChangeListener { + + // ------------------------------ FIELDS ------------------------------ + + private lateinit var queryString: String + private var editText: EditText? = null + + // --------------------- GETTER / SETTER METHODS --------------------- + + override val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.BOTH + + // ------------------------ INTERFACE METHODS ------------------------ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + + queryString = InternalPreferenceHelper.instance.get(R.string.key_last_used_search_query, "") + adapter = TimelineAdapter(activity) + + refresh() + } + + override fun refresh() { //TODO + if (!TextUtils.isEmpty(queryString)) { + startSearch(queryString) + } + } + + // --------------------- Interface OnClickListener --------------------- + + override fun onClick(v: View) { + when (v.id) { + R.id.button_search_queries -> { + openSearchQueryDialog() + } + R.id.button_search_execute -> { + search() + } + R.id.button_search_save -> { + saveQuery() + } + } + } + + override fun onLongClick(v: View): Boolean { + when (v.id) { + R.id.button_search_save -> { + val text = editText!!.text.toString() + for (ss in world.savedSearches.values) { + if (ss.query == text) { + ConfirmDialogFragment.show(activity, getString(R.string.dialog_confirm_delete_query), { + Searches.DestroySavedSearchTask(world.account, ss.id) + .onDoneUI { x -> + world.notify(R.string.notice_search_query_deleted) + world.refreshSavedSearches() + } + .onFailUI { x -> world.notify("unable to delete search query") } + .execute() + }, false) + break + } + } + return true + } + } + return false + } + + // --------------------- Interface OnFocusChangeListener --------------------- + + override fun onFocusChange(v: View, hasFocus: Boolean) { + if (!hasFocus) { + SystemServiceHelper.hideIM(activity, editText) + } + } + + // --------------------- Interface OnRefreshListener2 --------------------- + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + if (TextUtils.isEmpty(queryString)) { + UIHandler().post { + notifyTextEmpty() + refreshView.onRefreshComplete() + } + return + } + val query = Query() + query.query = queryString + query.count = 200 + query.resultType = Query.RECENT + if (adapter.count > 0) { + query.sinceId = adapter.topID + } + runRefreshTask(query, { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + }) + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + if (TextUtils.isEmpty(queryString)) { + UIHandler().post { + notifyTextEmpty() + refreshView.onRefreshComplete() + } + return + } + val query = Query() + query.query = queryString + query.count = 200 + query.resultType = Query.RECENT + if (adapter.count > 0) { + query.maxId = adapter.lastID - 1 + } + runRefreshTask(query, { + updateListViewWithNotice(refreshView.refreshableView, false) + refreshView.onRefreshComplete() + }) + } + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun getListView(page: View): PullToRefreshListView { + return page.findViewById(R.id.listview_search) as PullToRefreshListView + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { + val page = inflater.inflate(R.layout.fragment_search, container, false) + val listView = getListView(page) + listView.setAdapter(adapter) + listView.setOnScrollListener(this) + listView.setOnRefreshListener(this) + listView.mode = refreshMode + val buttonQueries = getQueriesButton(page) + buttonQueries.setOnClickListener(this) + val buttonExecute = getExecuteButton(page) + buttonExecute.setOnClickListener(this) + val buttonSave = getSaveButton(page) + buttonSave.setOnClickListener(this) + editText = getEditText(page) + editText!!.onFocusChangeListener = this + editText!!.setText(queryString) + editText!!.setOnEditorActionListener { textView, i, keyEvent -> + if (i == EditorInfo.IME_ACTION_SEARCH || keyEvent != null && + keyEvent.action == KeyEvent.ACTION_DOWN && + keyEvent.keyCode == KeyEvent.KEYCODE_ENTER) { + search() + } + true + } + editText!!.movementMethod = object : ArrowKeyMovementMethod() { + override fun right(widget: TextView, buffer: Spannable): Boolean { + //Don't move page + return widget.selectionEnd == widget.length() || super.right(widget, buffer) + } + + override fun left(widget: TextView, buffer: Spannable): Boolean { + //Don't move page + return widget.selectionStart == 0 || super.left(widget, buffer) + } + } + return page + } + + private fun getEditText(page: View): EditText { + return page.findViewById(R.id.edittext_search) as EditText + } + + private fun getExecuteButton(page: View): ImageButton { + return page.findViewById(R.id.button_search_execute) as ImageButton + } + + private fun getQueriesButton(page: View): ImageButton { + return page.findViewById(R.id.button_search_queries) as ImageButton + } + + private fun getSaveButton(page: View): ImageButton { + return page.findViewById(R.id.button_search_save) as ImageButton + } + + private fun notifyTextEmpty() { + world.notifyError(R.string.notice_search_text_empty) + } + + private fun openSearchQueryDialog() { + val sss = ArrayList(world.savedSearches.values) + val builder = AlertDialog.Builder(activity) + builder.setItems(sss.map { it.query }.toTypedArray()) { dialog, which -> + val ss = sss[which] + (activity as MainActivity).openSearchPage(ss.query) + } + val dialog = builder.create() + dialog.show() + } + + private fun saveQuery() { + val text = editText!!.text.toString() + if (TextUtils.isEmpty(text)) { + world.notifyError(R.string.notice_query_is_empty) + } else { + Searches.CreateSavedSearchTask(world.account, text).onDoneUI { cb -> + world.notify(R.string.notice_query_saved) + world.refreshSavedSearches() + }.onFailUI { ex -> world.notifyError("Query is not saved") }.execute() + } + } + + private fun search() { + if (editText != null) { + val text = editText!!.text.toString() + if (TextUtils.isEmpty(text)) { + world.notifyError(R.string.notice_query_is_empty) + } else { + startSearch(text) + SystemServiceHelper.hideIM(activity, editText) + } + } + } + + fun startSearch(queryString: String) { + InternalPreferenceHelper.instance.set(R.string.key_last_used_search_query, queryString) + if (editText != null) + editText!!.setText(queryString) + this.queryString = queryString + adapter.clear() + adapter.updateForce() + if (!TextUtils.isEmpty(queryString)) { + val query = Query() + query.query = queryString + query.count = 200 + query.resultType = Query.RECENT + runRefreshTask(query) { adapter.updateForce() } + } + } + + private fun runRefreshTask(query: Query, onFinish: () -> Unit) { + Searches.SearchTask(world.account, query) + .onFail { x -> world.notifyError(R.string.notice_error_search) } + .onDoneUI { tweets -> adapter.addAll(tweets.filter { t -> !t.isRetweet }) } + .onFinishUI(onFinish) + .execute() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.java b/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.java deleted file mode 100644 index 3d19c5d6..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012-2014 lacolaco.net - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.lacolaco.smileessence.view.page; - -import android.app.AlertDialog; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.TextView; -import com.handmark.pulltorefresh.library.PullToRefreshBase; -import com.handmark.pulltorefresh.library.PullToRefreshListView; -import net.lacolaco.smileessence.Application; -import net.lacolaco.smileessence.R; -import net.lacolaco.smileessence.activity.MainActivity; -import net.lacolaco.smileessence.entity.Tweet; -import net.lacolaco.smileessence.preference.InternalPreferenceHelper; -import net.lacolaco.smileessence.twitter.task.TimelineTask; -import net.lacolaco.smileessence.twitter.task.Timelines; -import net.lacolaco.smileessence.util.UIHandler; -import net.lacolaco.smileessence.view.adapter.TimelineAdapter; - -public class UserListFragment extends CustomListFragment<TimelineAdapter> implements View.OnClickListener { - private TextView textListName; - private String listFullName; - - @Override - protected PullToRefreshBase.Mode getRefreshMode() { - return PullToRefreshBase.Mode.BOTH; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - TimelineAdapter adapter = new TimelineAdapter(getActivity()); - setAdapter(adapter); - - refresh(); - } - - @Override - public void refresh() {//TODO - String lastUserList = InternalPreferenceHelper.getInstance().get(R.string.key_last_used_user_list, ""); - if (!TextUtils.isEmpty(lastUserList)) { - startUserList(lastUserList); - } - } - - @Override - public void onClick(View v) { - int id = v.getId(); - switch (id) { - case R.id.button_userlist_lists: { - openUserListsDialog(); - break; - } - } - } - - @Override - public void onPullDownToRefresh(final PullToRefreshBase<ListView> refreshView) { - if (listFullName == null) { - new UIHandler().post(() -> { - notifyTextEmpty(); - refreshView.onRefreshComplete(); - }); - return; - } - runRefreshTask( - new Timelines.UserListStatusesTask(getWorld().getAccount(), listFullName) - .setSinceId(getAdapter().getTopID()), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), true); - refreshView.onRefreshComplete(); - }); - } - - @Override - public void onPullUpToRefresh(final PullToRefreshBase<ListView> refreshView) { - if (listFullName == null) { - new UIHandler().post(() -> { - notifyTextEmpty(); - refreshView.onRefreshComplete(); - }); - return; - } - runRefreshTask( - new Timelines.UserListStatusesTask(getWorld().getAccount(), listFullName) - .setMaxId(getAdapter().getLastID() - 1), - () -> { - updateListViewWithNotice(refreshView.getRefreshableView(), false); - refreshView.onRefreshComplete(); - }); - } - - // ------------------------ OVERRIDE METHODS ------------------------ - - @Override - protected PullToRefreshListView getListView(View page) { - return (PullToRefreshListView) page.findViewById(R.id.listview_userlist); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View page = inflater.inflate(R.layout.fragment_userlist, container, false); - PullToRefreshListView listView = getListView(page); - TimelineAdapter adapter = getAdapter(); - listView.setAdapter(adapter); - listView.setOnScrollListener(this); - listView.setOnRefreshListener(this); - listView.setMode(getRefreshMode()); - ImageButton buttonUserLists = getUserListsButton(page); - buttonUserLists.setOnClickListener(this); - textListName = getTextListName(page); - textListName.setText(listFullName != null ? listFullName : "<none>"); - return page; - } - - private TextView getTextListName(View page) { - return (TextView) page.findViewById(R.id.textview_userlist_name); - } - - private ImageButton getUserListsButton(View page) { - return (ImageButton) page.findViewById(R.id.button_userlist_lists); - } - - private void notifyTextEmpty() { - getWorld().notifyError(R.string.notice_userlist_not_selected); - } - - private void openUserListsDialog() { - final String[] ary = getWorld().getListSubscriptions().toArray(new String[]{}); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.dialog_title_select_userlist) - .setItems(ary, (dialog, which) -> { - MainActivity activity = (MainActivity) getActivity(); - activity.openUserListPage(ary[which]); - textListName.setText(ary[which]); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } - - public void startUserList(String listFullName) { - InternalPreferenceHelper.getInstance().set(R.string.key_last_used_user_list, listFullName); - final TimelineAdapter adapter = getAdapter(); - this.listFullName = listFullName; - adapter.clear(); - adapter.updateForce(); - runRefreshTask( - new Timelines.UserListStatusesTask(getWorld().getAccount(), listFullName), - adapter::updateForce); - } - - private void runRefreshTask(TimelineTask<Tweet> task, Runnable onFinish) { - final TimelineAdapter adapter = getAdapter(); - task - .setCount(200) - .onFail(x -> getWorld().notifyError(R.string.notice_error_get_list)) - .onDoneUI(tweets -> { - getWorld().addTweetAll(tweets); - adapter.addAll(tweets); - }) - .onFinishUI(onFinish) - .execute(); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.kt b/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.kt new file mode 100644 index 00000000..3fb7ef49 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.kt @@ -0,0 +1,179 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2012-2014 lacolaco.net + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.lacolaco.smileessence.view.page + +import android.app.AlertDialog +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ListView +import android.widget.TextView +import com.handmark.pulltorefresh.library.PullToRefreshBase +import com.handmark.pulltorefresh.library.PullToRefreshListView +import net.lacolaco.smileessence.Application +import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.preference.InternalPreferenceHelper +import net.lacolaco.smileessence.twitter.task.TimelineTask +import net.lacolaco.smileessence.twitter.task.Timelines +import net.lacolaco.smileessence.util.UIHandler +import net.lacolaco.smileessence.view.adapter.TimelineAdapter + +class UserListFragment : CustomListFragment<TimelineAdapter>(), View.OnClickListener { + private var textListName: TextView? = null + private var listFullName: String? = null + + override val refreshMode: PullToRefreshBase.Mode + get() = PullToRefreshBase.Mode.BOTH + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + adapter = TimelineAdapter(activity) + + refresh() + } + + override fun refresh() {//TODO + val lastUserList = InternalPreferenceHelper.instance.get(R.string.key_last_used_user_list, "") + if (!TextUtils.isEmpty(lastUserList)) { + startUserList(lastUserList) + } + } + + override fun onClick(v: View) { + val id = v.id + when (id) { + R.id.button_userlist_lists -> { + openUserListsDialog() + } + } + } + + override fun onPullDownToRefresh(refreshView: PullToRefreshBase<ListView>) { + if (listFullName == null) { + UIHandler().post { + notifyTextEmpty() + refreshView.onRefreshComplete() + } + return + } + runRefreshTask( + Timelines.UserListStatusesTask(world.account, listFullName!!) + .setSinceId(adapter.topID), + { + updateListViewWithNotice(refreshView.refreshableView, true) + refreshView.onRefreshComplete() + }) + } + + override fun onPullUpToRefresh(refreshView: PullToRefreshBase<ListView>) { + if (listFullName == null) { + UIHandler().post { + notifyTextEmpty() + refreshView.onRefreshComplete() + } + return + } + runRefreshTask( + Timelines.UserListStatusesTask(world.account, listFullName!!) + .setMaxId(adapter.lastID - 1), + { + updateListViewWithNotice(refreshView.refreshableView, false) + refreshView.onRefreshComplete() + }) + } + + // ------------------------ OVERRIDE METHODS ------------------------ + + override fun getListView(page: View): PullToRefreshListView { + return page.findViewById(R.id.listview_userlist) as PullToRefreshListView + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { + val page = inflater.inflate(R.layout.fragment_userlist, container, false) + val listView = getListView(page) + val adapter = adapter + listView.setAdapter(adapter) + listView.setOnScrollListener(this) + listView.setOnRefreshListener(this) + listView.mode = refreshMode + val buttonUserLists = getUserListsButton(page) + buttonUserLists.setOnClickListener(this) + textListName = getTextListName(page) + textListName!!.text = if (listFullName != null) listFullName else "<none>" + return page + } + + private fun getTextListName(page: View): TextView { + return page.findViewById(R.id.textview_userlist_name) as TextView + } + + private fun getUserListsButton(page: View): ImageButton { + return page.findViewById(R.id.button_userlist_lists) as ImageButton + } + + private fun notifyTextEmpty() { + world.notifyError(R.string.notice_userlist_not_selected) + } + + private fun openUserListsDialog() { + val ary = world.listSubscriptions.toTypedArray<String>() + val builder = AlertDialog.Builder(activity) + builder.setTitle(R.string.dialog_title_select_userlist) + .setItems(ary) { dialog, which -> + val activity = activity as MainActivity + activity.openUserListPage(ary[which]) + textListName!!.text = ary[which] + } + val dialog = builder.create() + dialog.show() + } + + fun startUserList(listFullName: String) { + InternalPreferenceHelper.instance.set(R.string.key_last_used_user_list, listFullName) + val adapter = adapter + this.listFullName = listFullName + adapter.clear() + adapter.updateForce() + runRefreshTask(Timelines.UserListStatusesTask(world.account, listFullName)) { adapter.updateForce() } + } + + private fun runRefreshTask(task: TimelineTask<Tweet>, onFinish: () -> Unit) { + val adapter = adapter + task + .setCount(200) + .onFail { x -> world.notifyError(R.string.notice_error_get_list) } + .onDoneUI { tweets -> + world.addTweetAll(tweets) + adapter.addAll(tweets) + } + .onFinishUI(onFinish) + .execute() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.java b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.java deleted file mode 100644 index 88ad0879..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.lacolaco.smileessence.view.preference; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.preference.EditTextPreference; -import android.util.AttributeSet; - -public class IntegerEditTextPreference extends EditTextPreference { - public IntegerEditTextPreference(Context context) { - super(context); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public IntegerEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public IntegerEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public IntegerEditTextPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected boolean persistString(String value) { - return persistInt(Integer.valueOf(value)); - } - - @Override - protected String getPersistedString(String defaultReturnValue) { - return String.valueOf(getPersistedInt(-1)); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.kt b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.kt new file mode 100644 index 00000000..e6a23696 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerEditTextPreference.kt @@ -0,0 +1,26 @@ +package net.lacolaco.smileessence.view.preference + +import android.annotation.TargetApi +import android.content.Context +import android.os.Build +import android.preference.EditTextPreference +import android.util.AttributeSet + +class IntegerEditTextPreference : EditTextPreference { + constructor(context: Context) : super(context) + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + override fun persistString(value: String): Boolean { + return persistInt(Integer.valueOf(value)!!) + } + + override fun getPersistedString(defaultReturnValue: String?): String { + return getPersistedInt(-1).toString() + } +} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.java b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.java deleted file mode 100644 index f04a3b89..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.lacolaco.smileessence.view.preference; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.preference.ListPreference; -import android.util.AttributeSet; - -public class IntegerListPreference extends ListPreference { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public IntegerListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public IntegerListPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public IntegerListPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public IntegerListPreference(Context context) { - super(context); - } - - @Override - protected boolean persistString(String value) { - return persistInt(Integer.valueOf(value)); - } - - @Override - protected String getPersistedString(String defaultReturnValue) { - return String.valueOf(getPersistedInt(-1)); - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.kt b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.kt new file mode 100644 index 00000000..7dbe3297 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/preference/IntegerListPreference.kt @@ -0,0 +1,27 @@ +package net.lacolaco.smileessence.view.preference + +import android.annotation.TargetApi +import android.content.Context +import android.os.Build +import android.preference.ListPreference +import android.util.AttributeSet + +class IntegerListPreference : ListPreference { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context) : super(context) + + override fun persistString(value: String): Boolean { + return persistInt(Integer.valueOf(value)!!) + } + + override fun getPersistedString(defaultReturnValue: String?): String { + return getPersistedInt(-1).toString() + } +} |