diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2017-10-15 23:19:50 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2017-10-15 23:23:10 +0900 |
commit | 3d7252fd5337d9d1acbca2db92ed88b638986f1e (patch) | |
tree | 06e4f06572e4300ed6e1d91ac816d5991268eb2f /app/src/main/java/net | |
parent | 95de1cbdd911f0f003ca19ef1421004d315d0ef4 (diff) | |
download | SmileEssence-3d7252fd5337d9d1acbca2db92ed88b638986f1e.tar.gz |
poststate moved to postfragmetn
Diffstat (limited to 'app/src/main/java/net')
11 files changed, 271 insertions, 453 deletions
diff --git a/app/src/main/java/net/lacolaco/smileessence/World.kt b/app/src/main/java/net/lacolaco/smileessence/World.kt index 4c38c653..e74c9fa8 100644 --- a/app/src/main/java/net/lacolaco/smileessence/World.kt +++ b/app/src/main/java/net/lacolaco/smileessence/World.kt @@ -7,7 +7,6 @@ import kotlinx.android.synthetic.main.layout_main.* import net.lacolaco.smileessence.activity.MainActivity import net.lacolaco.smileessence.compat.Twitter4J import net.lacolaco.smileessence.data.Account -import net.lacolaco.smileessence.data.PostState import net.lacolaco.smileessence.entity.DirectMessage import net.lacolaco.smileessence.entity.Event import net.lacolaco.smileessence.entity.SavedSearch @@ -30,10 +29,7 @@ import java.util.concurrent.ConcurrentHashMap * World contains data that are specific to an account. */ class World(val account: Account) { - // XXX: Move to MainActivity - val postState = PostState() // XXX: Workaround for a bug in Android - @Deprecated("A temporary workaround") var mainActivityIntent: Intent? = null // Timelines private val tweets = ArrayList<Tweet>() diff --git a/app/src/main/java/net/lacolaco/smileessence/activity/MainActivity.kt b/app/src/main/java/net/lacolaco/smileessence/activity/MainActivity.kt index b286003a..d738d6b4 100644 --- a/app/src/main/java/net/lacolaco/smileessence/activity/MainActivity.kt +++ b/app/src/main/java/net/lacolaco/smileessence/activity/MainActivity.kt @@ -20,13 +20,15 @@ import net.lacolaco.smileessence.Application import net.lacolaco.smileessence.R import net.lacolaco.smileessence.World import net.lacolaco.smileessence.data.ExtractionWord +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.entity.User import net.lacolaco.smileessence.logging.Logger import net.lacolaco.smileessence.twitter.TwitterTaskException import net.lacolaco.smileessence.twitter.task.getTweetAsync import net.lacolaco.smileessence.twitter.task.getUserAsync import net.lacolaco.smileessence.util.* import net.lacolaco.smileessence.view.DialogHelper -import net.lacolaco.smileessence.view.adapter.PageListAdapter +import net.lacolaco.smileessence.view.MainFragmentPagerAdapter import net.lacolaco.smileessence.view.confirm import net.lacolaco.smileessence.view.dialog.StatusDetailDialogFragment import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment @@ -42,18 +44,32 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { val userIdValue = uri.getQueryParameter("user_id") ?: throw IllegalStateException("[BUG] user_id not set") Application.getWorld(userIdValue.toLong()) } - private val pagerAdapter by lazy { PageListAdapter(this) } + private lateinit var pagerAdapter: MainFragmentPagerAdapter + + fun openHomePage() { + viewPager.setCurrentItem(pagerAdapter.getPageIndex(TAG_PAGE_HOME), true) + } private fun setSelectedPageIndex(position: Int, smooth: Boolean = true) { viewPager.setCurrentItem(position, smooth) } - fun openHomePage() { - setSelectedPageIndex(pagerAdapter.getIndex(HomeFragment::class.java)) + fun openPostPageAndReplyTo(tweet: Tweet, prefix: String) { + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.setInReplyTo(tweet, prefix) + setSelectedPageIndex(pagerAdapter.getPageIndex(TAG_PAGE_POST), true) + } + + fun openPostPageAndReplyTo(user: User) { + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.setInReplyTo(user) + setSelectedPageIndex(pagerAdapter.getPageIndex(TAG_PAGE_POST), true) } - fun openPostPage() { - setSelectedPageIndex(pagerAdapter.getIndex(PostFragment::class.java)) + fun openPostPageAndAppendText(text: String) { + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.appendText(text) + setSelectedPageIndex(pagerAdapter.getPageIndex(TAG_PAGE_POST), true) } private fun openPostPageWithImage(uri: Uri) { @@ -61,9 +77,8 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { val c = contentResolver.query(uri, null, null, null, null)!! c.moveToFirst() val path = c.getString(c.getColumnIndex(MediaStore.MediaColumns.DATA)) - world.postState.beginTransaction() - .setMediaFilePath(path) - .commitWithOpen(this) + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.setMediaFilePath(path) world.notify(R.string.notice_select_image_succeeded) c.close() } catch (e: Exception) { @@ -74,23 +89,13 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { } fun openSearchPage(query: String) { - val fragment = pagerAdapter.getCachedFragment(pagerAdapter.getIndex(SearchFragment::class.java)) as SearchFragment? - if (fragment != null) { - fragment.startSearch(query) - setSelectedPageIndex(pagerAdapter.getIndex(SearchFragment::class.java)) - } - } - - fun openUserListPage(listFullName: String) { - val fragment = pagerAdapter.getCachedFragment(pagerAdapter.getIndex(UserListFragment::class.java)) as UserListFragment? - if (fragment != null) { - fragment.startUserList(listFullName) - setSelectedPageIndex(pagerAdapter.getIndex(UserListFragment::class.java)) - } + val fragment = pagerAdapter.getPageFragment(TAG_PAGE_SEARCH) as SearchFragment + fragment.startSearch(query) + setSelectedPageIndex(pagerAdapter.getPageIndex(TAG_PAGE_SEARCH)) } private fun setTitle() { - title = String.format("%s / %s", world.account.user.screenName, pagerAdapter.getName(viewPager.currentItem)) + //title = String.format("%s / %s", world.account.user.screenName, pagerAdapter.getName(viewPager.currentItem)) val label = getString(R.string.app_name) + " - @" + world.account.user.screenName if (android.os.Build.VERSION.SDK_INT >= 21) { setTaskDescription(ActivityManager.TaskDescription(label, null, world.account.themeColor)) @@ -168,7 +173,9 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { val via = uri.getQueryParameter("via") if (!TextUtils.isEmpty(via)) text += " via @" + via - world.postState.beginTransaction().setText(text).commitWithOpen(this) + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.setText(text) + viewPager.setCurrentItem(pagerAdapter.getPageIndex(TAG_PAGE_POST), true) return } val statusMatcher = TWITTER_STATUS_PATTERN.matcher(uri.path) @@ -204,7 +211,9 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { if (!TextUtils.isEmpty(extra.getCharSequence(Intent.EXTRA_SUBJECT))) { text = extra.getCharSequence(Intent.EXTRA_SUBJECT).toString() + " " + text } - world.postState.beginTransaction().setText(text).commitWithOpen(this) + val postPage = pagerAdapter.getPageFragment(TAG_PAGE_POST) as PostFragment + postPage.setText(text) + viewPager.setCurrentItem(pagerAdapter.getPageIndex(TAG_PAGE_POST), true) return } } else if (type != null && type.startsWith("image/")) { @@ -217,16 +226,15 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { // ------------------------ OVERRIDE METHODS ------------------------ override fun onBackPressed() { - this.finish() + finish() } override fun finish() { - val homeIndex = pagerAdapter.getIndex(HomeFragment::class.java) - if (viewPager.currentItem != homeIndex) { + val homeIndex = pagerAdapter.getPageIndex(TAG_PAGE_HOME) + if (viewPager.currentItem != homeIndex) viewPager.setCurrentItem(homeIndex, true) - } else { + else confirm(R.string.dialog_confirm_finish_app) { super.finish() } - } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -255,23 +263,31 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { setContentView(R.layout.layout_main) setSupportActionBar(toolbar) - viewPager.addOnPageChangeListener(this) - //actionBar.setDisplayHomeAsUpEnabled(true) - - // TODO: tab order? val args = Bundle() args.putLong(PageFragment.KEY_WORLD_USER_ID, account.id) - pagerAdapter.addPage(PostFragment::class.java, getString(R.string.page_name_post), args, false) - pagerAdapter.addPage(HomeFragment::class.java, getString(R.string.page_name_home), args, false) - pagerAdapter.addPage(MentionsFragment::class.java, getString(R.string.page_name_mentions), args, false) - pagerAdapter.addPage(HistoryFragment::class.java, getString(R.string.page_name_history), args, false) - pagerAdapter.addPage(MessagesFragment::class.java, getString(R.string.page_name_messages), args, false) - pagerAdapter.addPage(SearchFragment::class.java, getString(R.string.page_name_search), args, false) - pagerAdapter.addPage(UserListFragment::class.java, getString(R.string.page_name_list), args, false) + pagerAdapter = MainFragmentPagerAdapter(fragmentManager, viewPager) + pagerAdapter.addPage(TAG_PAGE_POST, PostFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_HOME, HomeFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_MENTIONS, MentionsFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_EVENTS, HistoryFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_MESSAGES, MessagesFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_SEARCH, SearchFragment::class.java, args) + pagerAdapter.addPage(TAG_PAGE_LIST, UserListFragment::class.java, args) pagerAdapter.notifyDataSetChanged() + + // TODO: tab order? + getString(R.string.page_name_post) + getString(R.string.page_name_home) + getString(R.string.page_name_mentions) + getString(R.string.page_name_history) + getString(R.string.page_name_messages) + getString(R.string.page_name_search) + getString(R.string.page_name_list) + + viewPager.addOnPageChangeListener(this) viewPager.offscreenPageLimit = pagerAdapter.count viewPager.adapter = pagerAdapter - setSelectedPageIndex(pagerAdapter.getIndex(HomeFragment::class.java), false) + viewPager.setCurrentItem(pagerAdapter.getPageIndex(TAG_PAGE_HOME), false) ExtractionWord.load() @@ -281,7 +297,7 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { world.refreshSavedSearches() launchUi { - val user = try { + try { world.getUserAsync(world.account.id) } catch (e: TwitterTaskException) { return@launchUi @@ -292,15 +308,6 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { // Set application title setTitle() - // refresh all pages - for (i in 0 until pagerAdapter.count) { - val pf = pagerAdapter.getCachedFragment(i) - if (pf != null && pf.isAdded) { - Logger.debug(String.format("PageFragment %s is already attached; refreshing", pf.javaClass.name)) - pf.refresh() - } - } - // start user stream world.setupStreaming() @@ -357,7 +364,8 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { override fun onPageSelected(position: Int) { Logger.debug("Page selected: " + position) - setTitle() + //title = String.format("%s / %s", world.account.user.screenName, pagerAdapter.getName(viewPager.currentItem)) + //setTitle() } override fun onPageScrollStateChanged(state: Int) {} @@ -370,5 +378,12 @@ class MainActivity : AppCompatActivity(), ViewPager.OnPageChangeListener { private val TWITTER_STATUS_PATTERN = Pattern.compile("\\A(?:/#!)?/(?:\\w{1,15})/status(?:es)?/(\\d+)\\z", Pattern.CASE_INSENSITIVE) private val TWITTER_USER_PATTERN = Pattern.compile("\\A(?:/#!)?/(\\w{1,15})/?\\z", Pattern.CASE_INSENSITIVE) + private val TAG_PAGE_POST = "PAGE_POST" + private val TAG_PAGE_HOME = "PAGE_HOME" + private val TAG_PAGE_MENTIONS = "PAGE_MENTIONS" + private val TAG_PAGE_EVENTS = "PAGE_EVENTS" + private val TAG_PAGE_MESSAGES = "PAGE_MESSAGES" + private val TAG_PAGE_SEARCH = "PAGE_SEARCH" + private val TAG_PAGE_LIST = "PAGE_LIST" } } diff --git a/app/src/main/java/net/lacolaco/smileessence/command/CommandAddHashtag.kt b/app/src/main/java/net/lacolaco/smileessence/command/CommandAddHashtag.kt index 9565f4c0..4012891c 100644 --- a/app/src/main/java/net/lacolaco/smileessence/command/CommandAddHashtag.kt +++ b/app/src/main/java/net/lacolaco/smileessence/command/CommandAddHashtag.kt @@ -18,7 +18,7 @@ class CommandAddHashtag // -------------------------- OTHER METHODS -------------------------- override fun execute(): Boolean { - activity.world.postState.beginTransaction().appendText(" #" + hashtag).commit() + activity.openPostPageAndAppendText(" #" + hashtag) return true } } diff --git a/app/src/main/java/net/lacolaco/smileessence/data/PostState.kt b/app/src/main/java/net/lacolaco/smileessence/data/PostState.kt deleted file mode 100644 index 74a09179..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/data/PostState.kt +++ /dev/null @@ -1,143 +0,0 @@ -package net.lacolaco.smileessence.data - -import net.lacolaco.smileessence.activity.MainActivity -import net.lacolaco.smileessence.entity.Tweet -import net.lacolaco.smileessence.view.DialogHelper -import twitter4j.StatusUpdate - -class PostState { - var text = "" - private set - var inReplyTo: Tweet? = null - private set - var mediaFilePath = "" - private set - private var listener: OnPostStateChangeListener? = null - var selectionStart: Int = 0 - get() { - return if (field < 0) { - text.length - } else field - } - var selectionEnd: Int = 0 - get() { - return if (field < 0) { - text.length - } else field - } - - fun setListener(listener: OnPostStateChangeListener): PostState { - this.listener = listener - return this - } - - fun beginTransaction(): PostStateTransaction { - return PostStateTransaction(this) - } - - fun removeListener() { - this.listener = null - } - - /** - * Convert to StatusUpdate for tweet. - * - * @return StatusUpdate - */ - fun toStatusUpdate(): StatusUpdate { - val su = StatusUpdate(text) - if (inReplyTo != null) { - su.inReplyToStatusId = inReplyTo!!.id - } - return su - } - - private fun copy(another: PostState): PostState { - this.text = another.text - this.inReplyTo = another.inReplyTo - this.mediaFilePath = another.mediaFilePath - this.selectionStart = another.selectionStart - this.selectionEnd = another.selectionEnd - this.listener = another.listener - return this - } - - private fun postStateChange() { - if (listener != null) { - listener!!.onPostStateChange(this) - } - } - - // -------------------------- INNER CLASSES -------------------------- - - interface OnPostStateChangeListener { - - fun onPostStateChange(postState: PostState) - } - - class PostStateTransaction constructor(private val origState: PostState) { - private val state: PostState = PostState().copy(origState) - - fun setText(text: String): PostStateTransaction { - state.text = text - return this - } - - fun appendText(text: String): PostStateTransaction { - state.text = state.text + text - return this - } - - fun insertText(index: Int, text: String): PostStateTransaction { - val builder = StringBuilder(state.text) - builder.insert(index, text) - state.text = builder.toString() - return this - } - - fun setInReplyTo(inReplyTo: Tweet?): PostStateTransaction { - state.inReplyTo = inReplyTo - return this - } - - fun setMediaFilePath(mediaFilePath: String): PostStateTransaction { - state.mediaFilePath = mediaFilePath - return this - } - - fun setCursor(cursor: Int): PostStateTransaction { - state.selectionEnd = cursor - state.selectionStart = state.selectionEnd - return this - } - - fun setSelection(start: Int, end: Int): PostStateTransaction { - state.selectionStart = start - state.selectionEnd = end - return this - } - - fun clear(): PostStateTransaction { - state.text = "" - state.selectionEnd = 0 - state.selectionStart = state.selectionEnd - state.inReplyTo = null - return this - } - - fun commit() { - origState.copy(state).postStateChange() - } - - fun commitWithOpen(activity: MainActivity) { - DialogHelper.closeAll(activity) - origState.copy(state).postStateChange() - activity.openPostPage() - } - - fun moveCursor(length: Int): PostStateTransaction { - val cursor = state.selectionEnd + length - return setCursor(cursor) - } - } -} diff --git a/app/src/main/java/net/lacolaco/smileessence/view/MainFragmentPagerAdapter.kt b/app/src/main/java/net/lacolaco/smileessence/view/MainFragmentPagerAdapter.kt new file mode 100644 index 00000000..add4c958 --- /dev/null +++ b/app/src/main/java/net/lacolaco/smileessence/view/MainFragmentPagerAdapter.kt @@ -0,0 +1,61 @@ +package net.lacolaco.smileessence.view + +import android.app.Fragment +import android.app.FragmentManager +import android.os.Bundle +import android.support.v13.app.FragmentPagerAdapter +import android.support.v4.util.ArrayMap +import android.support.v4.view.ViewPager +import android.view.ViewGroup +import net.lacolaco.smileessence.view.page.PageFragment + +class MainFragmentPagerAdapter(fm: FragmentManager, private val viewPager: ViewPager) : + FragmentPagerAdapter(fm) { + private val pageTags = ArrayList<String>() + private val pages = ArrayMap<String, PageInfo>() + + fun addPage(tag: String, clazz: Class<out PageFragment>, args: Bundle? = null) { + assert(pages[tag] == null) + pages[tag] = PageInfo(clazz, args) + pageTags.add(tag) + } + + fun getPageFragment(tag: String): PageFragment { + // XXX: This is really an ugly workaround. + return pages[tag]!!.fragment ?: instantiateItem(viewPager, getPageIndex(tag)) as PageFragment + } + + fun getPageIndex(tag: String): Int { + return pageTags.indexOf(tag) + } + + override fun getItem(position: Int): Fragment { + val info = pages[pageTags[position]]!! + assert(info.fragment == null) + val fragment = info.clazz.newInstance() + if (info.args != null) + fragment.arguments = info.args + return fragment + } + + override fun getCount(): Int { + return pages.size + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val info = pages[pageTags[position]]!! + val fragment = super.instantiateItem(container, position) + info.fragment = fragment as PageFragment + return fragment + } + + override fun destroyItem(container: ViewGroup?, position: Int, `object`: Any?) { + throw IllegalStateException("Fragments must not be destroyed by ViewPager") + } + + private class PageInfo( + val clazz: Class<out PageFragment>, + val args: Bundle?, + var fragment: PageFragment? = null + ) +} 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 deleted file mode 100644 index 9589ffef..00000000 --- a/app/src/main/java/net/lacolaco/smileessence/view/adapter/PageListAdapter.kt +++ /dev/null @@ -1,140 +0,0 @@ -package net.lacolaco.smileessence.view.adapter - -import android.app.Fragment -import android.app.FragmentManager -import android.app.FragmentTransaction -import android.os.Bundle -import android.support.v4.view.PagerAdapter -import android.view.View -import android.view.ViewGroup -import net.lacolaco.smileessence.BuildConfig -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.* - -class PageListAdapter(_activity: MainActivity) : PagerAdapter() { - private val mFragmentManager: FragmentManager = _activity.fragmentManager - private var mCurTransaction: FragmentTransaction? = null - private var mCurrentPrimaryItem: Fragment? = null - private val pages = ArrayList<PageInfo>() - private val fragmentCache = HashMap<Int, WeakReference<PageFragment>>() - - @Synchronized - 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 - } - - fun addPage(klass: Class<out PageFragment>, name: String, args: Bundle, notifyChanged: Boolean) { - pages.add(PageInfo(klass, name, args)) - if (notifyChanged) notifyDataSetChanged() - } - - 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) - - override fun startUpdate(container: ViewGroup) { - assert(container.id != View.NO_ID) - } - - override fun instantiateItem(container: ViewGroup, position: Int): Any { - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction() - } - - val itemId = position.toLong() - - // Do we already have this fragment? - val name = "android:switcher:${container.id}:$itemId" - var fragment: Fragment? = mFragmentManager.findFragmentByTag(name) - if (fragment != null) { - if (BuildConfig.DEBUG) - Logger.verbose("Attaching item #$itemId: f=$fragment") - mCurTransaction!!.attach(fragment) - } else { - fragment = getItem(position) - if (BuildConfig.DEBUG) - Logger.verbose("Adding item #$itemId: f=$fragment") - mCurTransaction!!.add(container.id, fragment, "android:switcher:${container.id}:$itemId") - } - if (fragment !== mCurrentPrimaryItem) { - fragment.setMenuVisibility(false) - fragment.userVisibleHint = false - } - - return fragment - } - - override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction() - } - if (BuildConfig.DEBUG) - Logger.verbose("Detaching item #" + position + ": f=" + `object` - + " v=" + (`object` as Fragment).view) - mCurTransaction!!.detach(`object` as Fragment) - } - - override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) { - val fragment = `object` as Fragment? - if (fragment !== mCurrentPrimaryItem) { - if (mCurrentPrimaryItem != null) { - mCurrentPrimaryItem!!.setMenuVisibility(false) - mCurrentPrimaryItem!!.userVisibleHint = false - } - if (fragment != null) { - fragment.setMenuVisibility(true) - fragment.userVisibleHint = true - } - mCurrentPrimaryItem = fragment - } - } - - override fun finishUpdate(container: ViewGroup) { - if (mCurTransaction != null) { - mCurTransaction!!.commitAllowingStateLoss() - mCurTransaction = null - mFragmentManager.executePendingTransactions() - } - } - - override fun isViewFromObject(view: View, `object`: Any): Boolean { - return (`object` as Fragment).view === view - } -} 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 index 97134ce4..78c67ce6 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.kt +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/StatusDetailDialogFragment.kt @@ -6,7 +6,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import kotlinx.android.synthetic.main.dialog_status_detail.view.* -import kotlinx.android.synthetic.main.menu_item_simple_text.view.* import net.lacolaco.smileessence.R import net.lacolaco.smileessence.activity.MainActivity import net.lacolaco.smileessence.command.Command @@ -109,12 +108,7 @@ class StatusDetailDialogFragment : StackableDialogFragment() { 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) + (activity as MainActivity).openPostPageAndReplyTo(originalTweet, text) } view.button_status_detail_retweet.setOnClickListener { val account = world.account @@ -181,7 +175,7 @@ class StatusDetailDialogFragment : StackableDialogFragment() { val popup = PopupMenu(activity, view.button_status_detail_menu) popup.add(R.string.command_status_add_to_reply) { val text = String.format("@%s ", tweet.originalTweet.user.screenName) - world.postState.beginTransaction().insertText(0, text).moveCursor(text.length).commit() + (activity as MainActivity).openPostPageAndAppendText(text) world.notify(R.string.notice_add_to_reply) } popup.add(R.string.command_status_open_talk_view) { @@ -222,7 +216,7 @@ class StatusDetailDialogFragment : StackableDialogFragment() { val adapter = object : UnorderedCustomListAdapter<Command>(commands) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val itemView = convertView ?: activity.layoutInflater.inflate(R.layout.menu_item_simple_text, parent, false) - itemView.list_item_textview.text = getItem(position).text + TODO("not implemented") //To change body of created functions use File | Settings | File Templates.itemView.list_item_textview.text = getItem(position).text return itemView } } 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 index e68a38a1..d4bea45f 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.kt +++ b/app/src/main/java/net/lacolaco/smileessence/view/dialog/UserDetailDialogFragment.kt @@ -11,6 +11,7 @@ import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirec import kotlinx.android.synthetic.main.dialog_user_detail.* import kotlinx.android.synthetic.main.dialog_user_detail.view.* import net.lacolaco.smileessence.R +import net.lacolaco.smileessence.activity.MainActivity import net.lacolaco.smileessence.data.ImageCache import net.lacolaco.smileessence.entity.User import net.lacolaco.smileessence.twitter.TwitterTaskException @@ -30,8 +31,7 @@ class UserDetailDialogFragment : StackableDialogFragment() { private fun showPopupMenu() { val popup = PopupMenu(activity, imageview_user_detail_menu) popup.add(R.string.command_user_add_to_reply) { - val text = String.format("@%s ", user.screenName) - world.postState.beginTransaction().insertText(0, text).moveCursor(text.length).commit() + (activity as MainActivity).openPostPageAndReplyTo(user) world.notify(R.string.notice_add_to_reply) } popup.add(R.string.command_user_send_message) { 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 index 81db9412..f23a9edf 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.kt +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/PageFragment.kt @@ -3,6 +3,9 @@ package net.lacolaco.smileessence.view.page import android.app.Fragment import net.lacolaco.smileessence.Application +/** + * A PageFragment is always attached to a MainActivity. + */ abstract class PageFragment : Fragment() { protected val world by lazy { Application.getWorld(arguments.getLong(KEY_WORLD_USER_ID)) 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 index 044261f4..93cb670f 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.kt +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/PostFragment.kt @@ -1,8 +1,10 @@ package net.lacolaco.smileessence.view.page +import android.annotation.SuppressLint import android.content.Intent import android.net.Uri import android.os.Bundle +import android.os.Parcelable import android.support.v4.content.ContextCompat import android.text.Editable import android.text.Spannable @@ -12,12 +14,13 @@ import android.text.method.ArrowKeyMovementMethod import android.view.* import android.widget.TextView import com.twitter.Validator +import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_post.* import net.lacolaco.smileessence.R import net.lacolaco.smileessence.activity.MainActivity -import net.lacolaco.smileessence.data.PostState +import net.lacolaco.smileessence.entity.Tweet +import net.lacolaco.smileessence.entity.User import net.lacolaco.smileessence.logging.Logger -import net.lacolaco.smileessence.preference.UserPreferenceHelper import net.lacolaco.smileessence.twitter.TwitterTaskException import net.lacolaco.smileessence.twitter.task.createTweetAsync import net.lacolaco.smileessence.util.BitmapThumbnailTask @@ -25,28 +28,60 @@ import net.lacolaco.smileessence.util.SystemServiceHelper import net.lacolaco.smileessence.util.UIHandler import net.lacolaco.smileessence.util.launchUi import net.lacolaco.smileessence.view.Partials +import twitter4j.StatusUpdate import java.io.File -class PostFragment : PageFragment(), TextWatcher, PostState.OnPostStateChangeListener { +class PostFragment : PageFragment(), TextWatcher { + private lateinit var postState: PostState + override fun refresh() {} - override fun onPostStateChange(postState: PostState) { + fun setMediaFilePath(path: String) { + postState.mediaFilePath = path + onPostStateChange() + } + + fun setInReplyTo(tweet: Tweet, prefix: String) { + postState.inReplyTo = tweet + postState.text = prefix + postState.text + postState.selectionStart = prefix.length + postState.selectionEnd = prefix.length + onPostStateChange() + } + + fun setInReplyTo(user: User) { + val prefix = "@${user.screenName} " + postState.text = prefix + postState.text + postState.selectionStart += prefix.length + postState.selectionEnd += prefix.length + onPostStateChange() + } + + fun setText(text: String) { + postState.text = text + onPostStateChange() + } + + fun appendText(text: String) { + postState.text += text + onPostStateChange() + } + + private fun onPostStateChange() { Logger.debug("onPostStateChange") - val activity = activity as MainActivity - val start = postState.selectionStart - val end = postState.selectionEnd post_edit_text.removeTextChangedListener(this) post_edit_text.setTextKeepState(postState.text) post_edit_text.addTextChangedListener(this) updateTextCount(post_edit_text.text) - UIHandler().postAtFrontOfQueue { post_edit_text.setSelection(start, end) } + UIHandler().postAtFrontOfQueue { + post_edit_text.setSelection(postState.selectionStart, postState.selectionEnd) + } if (postState.inReplyTo != null) { post_inreplyto_parent.visibility = View.VISIBLE - - val tweet = postState.inReplyTo - val header = Partials.getTweetView(tweet!!, world, activity, layout_post_reply_status) - header.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.transparent)) + val header = Partials.getTweetView(postState.inReplyTo!!, world, activity, + layout_post_reply_status) + header.setBackgroundColor(ContextCompat.getColor(activity, R.color.transparent)) header.isClickable = false } else { post_inreplyto_parent.visibility = View.GONE @@ -60,18 +95,19 @@ class PostFragment : PageFragment(), TextWatcher, PostState.OnPostStateChangeLis BitmapThumbnailTask(postState.mediaFilePath, image_post_media).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) + postState.text = s.toString() } + override fun afterTextChanged(s: Editable) {} + private fun updateTextCount(s: CharSequence) { val validator = Validator() var length = validator.getTweetLength(s.toString()) - if (!TextUtils.isEmpty(world.postState.mediaFilePath)) + if (!TextUtils.isEmpty(postState.mediaFilePath)) length += validator.shortUrlLength post_text_count.text = length.toString() @@ -81,12 +117,20 @@ class PostFragment : PageFragment(), TextWatcher, PostState.OnPostStateChangeLis } else { post_text_count.setTextAppearance(activity, android.R.style.TextAppearance_Widget_TextView) } - setStateFromView() } - override fun afterTextChanged(s: Editable) {} + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (savedInstanceState != null) + postState = savedInstanceState.getParcelable(KEY_POST_STATE) + else + postState = PostState() + } - // ------------------------ OVERRIDE METHODS ------------------------ + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putParcelable(KEY_POST_STATE, postState) + } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) @@ -94,14 +138,11 @@ class PostFragment : PageFragment(), TextWatcher, PostState.OnPostStateChangeLis } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - setHasOptionsMenu(true) return inflater.inflate(R.layout.fragment_post, container, false) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - world.postState.setListener(this) - button_post_tweet.setOnClickListener { submitPost() } post_edit_text.addTextChangedListener(this) post_edit_text.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) @@ -111,92 +152,84 @@ class PostFragment : PageFragment(), TextWatcher, PostState.OnPostStateChangeLis } post_edit_text.movementMethod = object : ArrowKeyMovementMethod() { override fun right(widget: TextView, buffer: Spannable): Boolean { - //Don't back to Home + // Don't back to Home return widget.selectionEnd == widget.length() || super.right(widget, buffer) } } - button_post_delete.setOnClickListener { deletePost() } - button_post_media.setOnClickListener { setImage() } - //Reply view - button_post_reply_delete.setOnClickListener { deleteReply() } - //Media view - image_post_media.setOnClickListener { displayImage() } - button_post_media_delete.setOnClickListener { removeImage() } - } - - 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() { - post_edit_text.setText("") - world.postState.beginTransaction().setText("").setCursor(0).commit() - deleteReply() - } - - private fun deleteReply() { - post_inreplyto_parent.visibility = View.GONE - world.postState.beginTransaction().setInReplyTo(null).commit() - } - - private fun displayImage() { - val intent = Intent(Intent.ACTION_VIEW) - intent.addCategory(Intent.CATEGORY_DEFAULT) - intent.setDataAndType(Uri.fromFile(File(world.postState.mediaFilePath)), "image/*") - startActivity(intent) - } + button_post_tweet.setOnClickListener { + SystemServiceHelper.hideIM(activity, post_edit_text) + launchUi { + try { + val su = StatusUpdate(postState.text) + if (postState.inReplyTo != null) { + su.inReplyToStatusId = postState.inReplyTo!!.id + } + world.createTweetAsync(su, postState.mediaFilePath, false).await() + world.notify(R.string.notice_tweet_succeeded) + postState.clear() + } catch (e: TwitterTaskException) { + world.notifyError(R.string.notice_tweet_failed, e) + } + } + (activity as MainActivity).openHomePage() + } + button_post_reply_delete.setOnClickListener { + postState.inReplyTo = null + onPostStateChange() + } + button_post_delete.setOnClickListener { + postState.clear() + onPostStateChange() + } + button_post_media.setOnClickListener { + SystemServiceHelper.hideIM(activity, post_edit_text) - private fun removeImage() { - SystemServiceHelper.hideIM(activity, post_edit_text) - post_media_parent.visibility = View.GONE - image_post_media.setImageBitmap(null) - world.postState.beginTransaction().setMediaFilePath("").commit() + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + startActivityForResult(intent, MainActivity.REQUEST_GET_PICTURE_FROM_GALLERY) + } + image_post_media.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW) + intent.addCategory(Intent.CATEGORY_DEFAULT) + intent.setDataAndType(Uri.fromFile(File(postState.mediaFilePath)), "image/*") + startActivity(intent) + } + button_post_media_delete.setOnClickListener { + SystemServiceHelper.hideIM(activity, post_edit_text) + post_media_parent.visibility = View.GONE + image_post_media.setImageBitmap(null) + postState.mediaFilePath = "" + onPostStateChange() + } + onPostStateChange() } - private fun setImage() { - setStateFromView() - SystemServiceHelper.hideIM(activity, post_edit_text) - - val intent = Intent(Intent.ACTION_PICK) - intent.type = "image/*" - startActivityForResult(intent, MainActivity.REQUEST_GET_PICTURE_FROM_GALLERY) - } + // FIXME: Remove @SuppressList + @SuppressLint("ParcelCreator") + @Parcelize + private class PostState( + var text: String = "", + private var inReplyToId: Long = -1, + var mediaFilePath: String = "", + var selectionStart: Int = 0, + var selectionEnd: Int = 0 + ) : Parcelable { + var inReplyTo: Tweet? + get() = if (inReplyToId != -1L) Tweet.fetch(inReplyToId) else null + set(value) { + inReplyToId = value?.id ?: -1 + } - private fun setStateFromView() { - val state = world.postState - state.removeListener() - state.beginTransaction() - .setText(post_edit_text.text.toString()) - .setSelection(post_edit_text.selectionStart, post_edit_text.selectionEnd) - .commit() - state.setListener(this) + fun clear() { + text = "" + inReplyTo = null + mediaFilePath = "" + selectionStart = 0 + selectionEnd = 0 + } } - private fun submitPost() { - SystemServiceHelper.hideIM(activity, post_edit_text) - setStateFromView() - launchUi { - val state = world.postState - val resizeFlag = UserPreferenceHelper.instance[R.string.key_setting_resize_post_image, false] - try { - val t = world.createTweetAsync(state.toStatusUpdate(), state.mediaFilePath, resizeFlag).await() - world.notify(R.string.notice_tweet_succeeded) - world.postState.beginTransaction().clear().commit() - } catch (e: TwitterTaskException) { - world.notifyError(R.string.notice_tweet_failed, e) - } - } - val mainActivity = activity as MainActivity - mainActivity.openHomePage() + companion object { + val KEY_POST_STATE = "POST_STATE" } } 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 index 2bedf71d..55193aea 100644 --- a/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.kt +++ b/app/src/main/java/net/lacolaco/smileessence/view/page/UserListFragment.kt @@ -9,7 +9,6 @@ import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout import kotlinx.android.synthetic.main.fragment_userlist.* import kotlinx.coroutines.experimental.Deferred import net.lacolaco.smileessence.R -import net.lacolaco.smileessence.activity.MainActivity import net.lacolaco.smileessence.entity.Tweet import net.lacolaco.smileessence.preference.UserPreferenceHelper import net.lacolaco.smileessence.twitter.TwitterTaskException @@ -75,7 +74,7 @@ class UserListFragment : CustomListFragment<TimelineAdapter>() { for (name in world.listSubscriptions) { popup.add(name) { textview_userlist_name.text = name - (activity as MainActivity).openUserListPage(name) + startUserList(name) } } popup.show() |