package net.lacolaco.smileessence.activity import android.app.ActivityManager import android.content.Intent import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.support.v4.view.ViewPager import android.support.v7.app.AppCompatActivity import android.support.v7.graphics.Palette import android.text.TextUtils import android.view.Menu import android.view.MenuItem import kotlinx.android.synthetic.main.activity_main.* import net.lacolaco.smileessence.Application import net.lacolaco.smileessence.Logger import net.lacolaco.smileessence.R import net.lacolaco.smileessence.World import net.lacolaco.smileessence.data.PageInfo import net.lacolaco.smileessence.entity.Tweet import net.lacolaco.smileessence.entity.User import net.lacolaco.smileessence.twitter.TwitterTaskException import net.lacolaco.smileessence.twitter.getTweetAsync import net.lacolaco.smileessence.twitter.getUserAsync import net.lacolaco.smileessence.util.bg import net.lacolaco.smileessence.util.getMainActivityOrCancel import net.lacolaco.smileessence.util.launchUi import net.lacolaco.smileessence.view.DialogHelper import net.lacolaco.smileessence.view.MainFragmentPagerAdapter import net.lacolaco.smileessence.view.confirm import net.lacolaco.smileessence.view.dialog.AppInfoDialogFragment import net.lacolaco.smileessence.view.dialog.StatusDetailDialogFragment import net.lacolaco.smileessence.view.dialog.UserDetailDialogFragment import net.lacolaco.smileessence.view.page.ComposePageFragment import java.util.regex.Pattern class MainActivity : AppCompatActivity() { val world by lazy { val uri = intent.data ?: throw IllegalStateException("[BUG] data not set") val userIdValue = uri.getQueryParameter("user_id") ?: throw IllegalStateException("[BUG] user_id not set") World[userIdValue.toLong()] } private lateinit var pagerAdapter: MainFragmentPagerAdapter fun openHomePage() { view_pager.setCurrentItem(world.pages.indexOfFirst { it is PageInfo.TweetsPageInfo }, true) } private fun setSelectedPageIndex(position: Int, smooth: Boolean = true) { view_pager.setCurrentItem(position, smooth) } fun openPostPageAndReplyTo(tweet: Tweet, prefix: String) { val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment).setInReplyTo(tweet, prefix) setSelectedPageIndex(postPagePosition, true) } fun openPostPageAndReplyTo(user: User) { val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment).setInReplyTo(user) setSelectedPageIndex(postPagePosition, true) } fun openPostPageAndAppendText(text: String) { val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment).appendText(text) setSelectedPageIndex(postPagePosition, true) } private fun setTitle() { val label = getString(R.string.app_name) + " - @" + world.user.screenName if (android.os.Build.VERSION.SDK_INT >= 21) { setTaskDescription(ActivityManager.TaskDescription(label, null, world.themeColor)) window.statusBarColor = world.themeColor toolbar.setBackgroundColor(world.themeColor) } launchUi { val bitmap = world.getProfileImageBitmap((32 * resources.displayMetrics.density).toInt()) val palette = bg { Palette.from(bitmap).generate() }.await() if (isDestroyed) return@launchUi toolbar.navigationIcon = BitmapDrawable(resources, bitmap) val swatch = if (world.useDarkTheme) palette.darkVibrantSwatch ?: palette.darkMutedSwatch ?: palette.dominantSwatch else palette.vibrantSwatch ?: palette.mutedSwatch ?: palette.dominantSwatch if (swatch == null) { Logger.info("Unable to get a theme color of @${world.user.screenName}") } else { world.themeColor = swatch.rgb toolbar.setBackgroundColor(world.themeColor) if (android.os.Build.VERSION.SDK_INT >= 21) { setTaskDescription(ActivityManager.TaskDescription(label, bitmap, world.themeColor)) window.statusBarColor = world.themeColor } } } } private fun processIntent(intent: Intent) { val uri = intent.data if (uri != null) { if (uri.host == "twitter.com") { // /share and /intent/tweet: don't accept status parameter val postMatcher = TWITTER_POST_PATTERN.matcher(uri.path) if (postMatcher.find()) { var text = uri.getQueryParameter("text") ?: "" val url = uri.getQueryParameter("url") if (!TextUtils.isEmpty(url)) text += " " + url val hashtags = uri.getQueryParameter("hashtags") if (!TextUtils.isEmpty(hashtags)) text += " " + hashtags.trim { it <= ' ' }.replace(",".toRegex(), " #") val via = uri.getQueryParameter("via") if (!TextUtils.isEmpty(via)) text += " via @" + via val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment).setText(text) setSelectedPageIndex(postPagePosition, true) return } val statusMatcher = TWITTER_STATUS_PATTERN.matcher(uri.path) if (statusMatcher.find()) { return launchUi { try { val tweet = world.getTweetAsync(statusMatcher.group(1).toLong()).await() DialogHelper.showDialog(world.getMainActivityOrCancel(), StatusDetailDialogFragment.newInstance(tweet)) } catch (e: TwitterTaskException) { world.notifyError(R.string.error_intent_status_cannot_load) } } } val userMatcher = TWITTER_USER_PATTERN.matcher(uri.path) if (userMatcher.find()) { return launchUi { try { val user = world.getUserAsync(userMatcher.group(1)).await() DialogHelper.showDialog(world.getMainActivityOrCancel(), UserDetailDialogFragment.newInstance(user)) } catch (e: TwitterTaskException) { world.notifyError(R.string.notice_error_show_user) } } } } } else when (intent.action) { Intent.ACTION_SEND -> { val type = intent.type if (type == "text/plain") { val extra = intent.extras if (extra != null) { var text = extra.getCharSequence(Intent.EXTRA_TEXT).toString() if (!TextUtils.isEmpty(extra.getCharSequence(Intent.EXTRA_SUBJECT))) { text = extra.getCharSequence(Intent.EXTRA_SUBJECT).toString() + " " + text } val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment).setText(text) setSelectedPageIndex(postPagePosition, true) return } } else if (type != null && type.startsWith("image/")) { val postPagePosition = world.pages.indexOfFirst { it is PageInfo.ComposePageInfo } assert(postPagePosition != -1) (pagerAdapter.getPageFragmentAt(postPagePosition) as ComposePageFragment) .setMediaFile(intent.getParcelableExtra(Intent.EXTRA_STREAM)) } } } } override fun onBackPressed() { val homeIndex = world.pages.indexOfFirst { it is PageInfo.TweetsPageInfo } if (view_pager.currentItem != homeIndex) view_pager.setCurrentItem(homeIndex, true) else confirm(R.string.dialog_confirm_exit) { finish() } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { REQUEST_CODE_MANAGE_PAGES -> { if (resultCode == RESULT_OK) { Application.toast("Restarting activity...") recreate() } } else -> super.onActivityResult(requestCode, resultCode, data) } } override fun onCreate(savedInstanceState: Bundle?) { Logger.debug("onCreate") super.onCreate(savedInstanceState) world.setMainActivity(this) setTheme(if (world.useDarkTheme) R.style.AppTheme_Dark else R.style.AppTheme_Light) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) pagerAdapter = MainFragmentPagerAdapter(world, view_pager, fragmentManager) view_pager.offscreenPageLimit = pagerAdapter.count view_pager.adapter = pagerAdapter view_pager.setCurrentItem(world.pages.indexOfFirst { it is PageInfo.TweetsPageInfo }, false) view_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageSelected(position: Int) { Logger.verbose("Page #$position selected (${world.pages[position].name})") title = "${world.user.screenName} / ${world.pages[position].name}" } }) // Set application title setTitle() // Fetch necessary data & start streaming API if needed world.setup() } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.main, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> { val intent = Intent(this, ManageProfilesActivity::class.java) intent.putExtra(ManageProfilesActivity.INTENT_KEY_NOINIT, true) startActivity(intent) } R.id.actionbar_manage_pages -> { val intent = Intent(this, ManagePagesActivity::class.java) intent.putExtra(ManagePagesActivity.INTENT_KEY_WORLD_ID, world.id) startActivityForResult(intent, REQUEST_CODE_MANAGE_PAGES) } R.id.open_version_dialog -> DialogHelper.showDialog(this, AppInfoDialogFragment.newInstance()) else -> return super.onOptionsItemSelected(item) } return true } override fun onPause() { Logger.debug("onPause") super.onPause() world.setMainActivityActive(false) world.checkpoint() } override fun onResume() { Logger.debug("onResume") super.onResume() Application.currentWorld = world world.setMainActivityActive(true) // With the current releases (~ 7) of Android, onNewIntent() doesn't seem to be called // properly when resuming an already running Activity that has configured as // documentLaunchMode=intoExisting. So, as a workaround, store the actual Intent in // the World associated and check it in onResume() for now. See also // ManageProfilesActivity::goToWorld(). val intent = world.mainActivityIntent if (intent != null) { world.mainActivityIntent = null processIntent(intent) } } companion object { private val REQUEST_CODE_MANAGE_PAGES = 12 private val TWITTER_POST_PATTERN = Pattern.compile("\\A/(intent/tweet|share)\\z") private val TWITTER_STATUS_PATTERN = Pattern.compile("\\A(?:/#!)?/(?:\\w{1,15})/status(?:es)?/(\\d+)\\z") private val TWITTER_USER_PATTERN = Pattern.compile("\\A(?:/#!)?/(\\w{1,15})/?\\z") } }