BarInsta/app/src/main/java/awais/instagrabber/activities/MainActivity.kt

858 lines
35 KiB
Kotlin

package awais.instagrabber.activities
import android.animation.LayoutTransition
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import android.provider.DocumentsContract.EXTRA_INITIAL_URI
import android.text.Editable
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.NotificationManagerCompat
import androidx.core.provider.FontRequest
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.EmojiCompat.InitCallback
import androidx.emoji.text.FontRequestEmojiCompatConfig
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavController.OnDestinationChangedListener
import androidx.navigation.NavDestination
import androidx.navigation.ui.NavigationUI
import awais.instagrabber.BuildConfig
import awais.instagrabber.R
import awais.instagrabber.customviews.emoji.EmojiVariantManager
import awais.instagrabber.customviews.helpers.RootViewDeferringInsetsCallback
import awais.instagrabber.customviews.helpers.TextWatcherAdapter
import awais.instagrabber.databinding.ActivityMainBinding
import awais.instagrabber.fragments.PostViewV2Fragment
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections
import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.IntentModel
import awais.instagrabber.models.Resource
import awais.instagrabber.models.Tab
import awais.instagrabber.models.enums.IntentModelType
import awais.instagrabber.services.ActivityCheckerService
import awais.instagrabber.services.DMSyncAlarmReceiver
import awais.instagrabber.utils.*
import awais.instagrabber.utils.AppExecutors.tasksThread
import awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException
import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.utils.TextUtils.shortcodeToId
import awais.instagrabber.utils.emoji.EmojiParser
import awais.instagrabber.viewmodels.AppStateViewModel
import awais.instagrabber.viewmodels.DirectInboxViewModel
import awais.instagrabber.webservices.GraphQLRepository
import awais.instagrabber.webservices.MediaRepository
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textfield.TextInputLayout
import com.google.common.collect.ImmutableList
import com.google.common.collect.Iterators
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import java.util.stream.Collectors
class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedListener {
private lateinit var binding: ActivityMainBinding
private var currentNavControllerLiveData: LiveData<NavController>? = null
private var searchMenuItem: MenuItem? = null
private var firstFragmentGraphIndex = 0
private var lastSelectedNavMenuId = 0
private var isActivityCheckerServiceBound = false
private var isBackStackEmpty = false
private var isLoggedIn = false
private var deviceUuid: String? = null
private var csrfToken: String? = null
private var userId: Long = 0
// private var behavior: HideBottomViewOnScrollBehavior<BottomNavigationView>? = null
var currentTabs: List<Tab> = emptyList()
private set
private var showBottomViewDestinations: List<Int> = emptyList()
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// final ActivityCheckerService.LocalBinder binder = (ActivityCheckerService.LocalBinder) service;
// final ActivityCheckerService activityCheckerService = binder.getService();
isActivityCheckerServiceBound = true
}
override fun onServiceDisconnected(name: ComponentName) {
isActivityCheckerServiceBound = false
}
}
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }
override fun onCreate(savedInstanceState: Bundle?) {
try {
DownloadUtils.init(
this,
Utils.settingsHelper.getString(PreferenceKeys.PREF_BARINSTA_DIR_URI)
)
} catch (e: ReselectDocumentTreeException) {
super.onCreate(savedInstanceState)
val intent = Intent(this, DirectorySelectActivity::class.java)
intent.putExtra(EXTRA_INITIAL_URI, e.initialUri)
startActivity(intent)
finish()
return
}
super.onCreate(savedInstanceState)
instance = this
binding = ActivityMainBinding.inflate(layoutInflater)
setupCookie()
if (Utils.settingsHelper.getBoolean(PreferenceKeys.FLAG_SECURE)) {
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
setupInsetsCallback()
createNotificationChannels()
// try {
// val layoutParams = binding.bottomNavView.layoutParams as CoordinatorLayout.LayoutParams
// @Suppress("UNCHECKED_CAST")
// behavior = layoutParams.behavior as HideBottomViewOnScrollBehavior<BottomNavigationView>
// } catch (e: Exception) {
// Log.e(TAG, "onCreate: ", e)
// }
if (savedInstanceState == null) {
setupBottomNavigationBar(true)
}
if (!BuildConfig.isPre) {
val checkUpdates = Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_UPDATES)
if (checkUpdates) FlavorTown.updateCheck(this)
}
FlavorTown.changelogCheck(this)
ViewModelProvider(this).get(AppStateViewModel::class.java) // Just initiate the App state here
handleIntent(intent)
if (isLoggedIn && Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_ACTIVITY)) {
bindActivityCheckerService()
}
supportFragmentManager.addOnBackStackChangedListener(this)
// Initialise the internal map
tasksThread.execute {
EmojiParser.getInstance(this)
EmojiVariantManager.getInstance()
}
initEmojiCompat()
// initDmService();
initDmUnreadCount()
initSearchInput()
}
private fun setupInsetsCallback() {
val deferringInsetsCallback = RootViewDeferringInsetsCallback(
WindowInsetsCompat.Type.systemBars(),
WindowInsetsCompat.Type.ime()
)
ViewCompat.setWindowInsetsAnimationCallback(binding.root, deferringInsetsCallback)
ViewCompat.setOnApplyWindowInsetsListener(binding.root, deferringInsetsCallback)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
private fun setupCookie() {
val cookie = Utils.settingsHelper.getString(Constants.COOKIE)
userId = 0
csrfToken = null
if (cookie.isNotBlank()) {
userId = getUserIdFromCookie(cookie)
csrfToken = getCsrfTokenFromCookie(cookie)
}
if (cookie.isBlank() || userId == 0L || csrfToken.isNullOrBlank()) {
isLoggedIn = false
return
}
deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)
if (isEmpty(deviceUuid)) {
Utils.settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString())
}
setupCookies(cookie)
isLoggedIn = true
}
@Suppress("unused")
private fun initDmService() {
if (!isLoggedIn) return
val enabled = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH)
if (!enabled) return
DMSyncAlarmReceiver.setAlarm(this)
}
private fun initDmUnreadCount() {
if (!isLoggedIn) return
val directInboxViewModel = ViewModelProvider(this).get(DirectInboxViewModel::class.java)
directInboxViewModel.unseenCount.observe(this, { unseenCountResource: Resource<Int?>? ->
if (unseenCountResource == null) return@observe
val unseenCount = unseenCountResource.data
setNavBarDMUnreadCountBadge(unseenCount ?: 0)
})
}
private fun initSearchInput() {
binding.searchInputLayout.setEndIconOnClickListener {
val editText = binding.searchInputLayout.editText ?: return@setEndIconOnClickListener
editText.setText("")
}
binding.searchInputLayout.addOnEditTextAttachedListener { textInputLayout: TextInputLayout ->
textInputLayout.isEndIconVisible = false
val editText = textInputLayout.editText ?: return@addOnEditTextAttachedListener
editText.addTextChangedListener(object : TextWatcherAdapter() {
override fun afterTextChanged(s: Editable) {
binding.searchInputLayout.isEndIconVisible = !isEmpty(s)
}
})
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
searchMenuItem = menu.findItem(R.id.search)
val navController = currentNavControllerLiveData?.value
if (navController != null) {
val currentDestination = navController.currentDestination
if (currentDestination != null) {
@SuppressLint("RestrictedApi") val backStack = navController.backStack
setupMenu(backStack.size, currentDestination.id)
}
}
// if (binding.searchInputLayout.getVisibility() == View.VISIBLE) {
// searchMenuItem.setVisible(false).setEnabled(false);
// return true;
// }
// searchMenuItem.setVisible(true).setEnabled(true);
// if (showSearch && currentNavControllerLiveData != null) {
// final NavController navController = currentNavControllerLiveData.getValue();
// if (navController != null) {
// final NavDestination currentDestination = navController.getCurrentDestination();
// if (currentDestination != null) {
// final int destinationId = currentDestination.getId();
// showSearch = destinationId == R.id.profileFragment;
// }
// }
// }
// if (!showSearch) {
// searchMenuItem.setVisible(false);
// return true;
// }
// return setupSearchView();
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.search) {
val navController = currentNavControllerLiveData?.value ?: return false
try {
navController.navigate(R.id.action_global_search)
return true
} catch (e: Exception) {
Log.e(TAG, "onOptionsItemSelected: ", e)
}
return false
}
return super.onOptionsItemSelected(item)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, firstFragmentGraphIndex.toString())
outState.putString(LAST_SELECT_NAV_MENU_ID, binding.bottomNavView.selectedItemId.toString())
super.onSaveInstanceState(outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
val key = savedInstanceState[FIRST_FRAGMENT_GRAPH_INDEX_KEY] as String?
if (key != null) {
try {
firstFragmentGraphIndex = key.toInt()
} catch (ignored: NumberFormatException) {
}
}
val lastSelected = savedInstanceState[LAST_SELECT_NAV_MENU_ID] as String?
if (lastSelected != null) {
try {
lastSelectedNavMenuId = lastSelected.toInt()
} catch (ignored: NumberFormatException) {
}
}
setupBottomNavigationBar(false)
}
override fun onSupportNavigateUp(): Boolean {
if (currentNavControllerLiveData == null) return false
val navController = currentNavControllerLiveData?.value ?: return false
return navController.navigateUp()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}
override fun onDestroy() {
try {
super.onDestroy()
} catch (e: Exception) {
Log.e(TAG, "onDestroy: ", e)
}
unbindActivityCheckerService()
// try {
// RetrofitFactory.getInstance().destroy()
// } catch (e: Exception) {
// Log.e(TAG, "onDestroy: ", e)
// }
DownloadUtils.destroy()
instance = null
}
override fun onBackPressed() {
var currentNavControllerBackStack = 2
currentNavControllerLiveData?.let {
val navController = it.value
if (navController != null) {
@SuppressLint("RestrictedApi") val backStack = navController.backStack
currentNavControllerBackStack = backStack.size
}
}
if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) {
finishAfterTransition()
return
}
if (!isFinishing) {
try {
super.onBackPressed()
} catch (e: Exception) {
Log.e(TAG, "onBackPressed: ", e)
finish()
}
}
}
override fun onBackStackChanged() {
val backStackEntryCount = supportFragmentManager.backStackEntryCount
isBackStackEmpty = backStackEntryCount == 0
}
private fun createNotificationChannels() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val notificationManager = NotificationManagerCompat.from(applicationContext)
notificationManager.createNotificationChannel(
NotificationChannel(
Constants.DOWNLOAD_CHANNEL_ID,
Constants.DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
)
)
notificationManager.createNotificationChannel(
NotificationChannel(
Constants.ACTIVITY_CHANNEL_ID,
Constants.ACTIVITY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
)
)
notificationManager.createNotificationChannel(
NotificationChannel(
Constants.DM_UNREAD_CHANNEL_ID,
Constants.DM_UNREAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
)
)
val silentNotificationChannel = NotificationChannel(
Constants.SILENT_NOTIFICATIONS_CHANNEL_ID,
Constants.SILENT_NOTIFICATIONS_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
)
silentNotificationChannel.setSound(null, null)
notificationManager.createNotificationChannel(silentNotificationChannel)
}
private fun setupBottomNavigationBar(setDefaultTabFromSettings: Boolean) {
currentTabs = if (!isLoggedIn) setupAnonBottomNav() else setupMainBottomNav()
val mainNavList = currentTabs.stream()
.map(Tab::navigationResId)
.collect(Collectors.toList())
showBottomViewDestinations = currentTabs.asSequence().map {
it.startDestinationFragmentId
}.toMutableList().apply {
add(R.id.postViewFragment)
add(R.id.favoritesFragment)
}
if (setDefaultTabFromSettings) {
setSelectedTab(currentTabs)
} else {
binding.bottomNavView.selectedItemId = lastSelectedNavMenuId
}
val navControllerLiveData = NavigationExtensions.setupWithNavController(
binding.bottomNavView,
mainNavList,
supportFragmentManager,
R.id.main_nav_host,
intent,
firstFragmentGraphIndex
)
navControllerLiveData.observe(this, { navController: NavController? -> setupNavigation(binding.toolbar, navController) })
currentNavControllerLiveData = navControllerLiveData
}
private fun setSelectedTab(tabs: List<Tab>) {
val defaultTabResNameString = Utils.settingsHelper.getString(Constants.DEFAULT_TAB)
try {
var navId = 0
if (!isEmpty(defaultTabResNameString)) {
navId = resources.getIdentifier(defaultTabResNameString, "navigation", packageName)
}
val navGraph = if (isLoggedIn) R.navigation.feed_nav_graph else R.navigation.profile_nav_graph
val defaultNavId = if (navId <= 0) navGraph else navId
var index = Iterators.indexOf(tabs.iterator()) { tab: Tab? ->
if (tab == null) return@indexOf false
tab.navigationResId == defaultNavId
}
if (index < 0 || index >= tabs.size) index = 0
firstFragmentGraphIndex = index
setBottomNavSelectedTab(tabs[index])
} catch (e: Exception) {
Log.e(TAG, "Error parsing id", e)
}
}
private fun setupAnonBottomNav(): List<Tab> {
val selectedItemId = binding.bottomNavView.selectedItemId
val favoriteTab = Tab(
R.drawable.ic_star_24,
getString(R.string.title_favorites),
false,
"favorites_nav_graph",
R.navigation.favorites_nav_graph,
R.id.favorites_nav_graph,
R.id.favoritesFragment
)
val profileTab = Tab(
R.drawable.ic_person_24,
getString(R.string.profile),
false,
"profile_nav_graph",
R.navigation.profile_nav_graph,
R.id.profile_nav_graph,
R.id.profileFragment
)
val moreTab = Tab(
R.drawable.ic_more_horiz_24,
getString(R.string.more),
false,
"more_nav_graph",
R.navigation.more_nav_graph,
R.id.more_nav_graph,
R.id.morePreferencesFragment
)
val menu = binding.bottomNavView.menu
menu.clear()
menu.add(0, favoriteTab.navigationRootId, 0, favoriteTab.title).setIcon(favoriteTab.iconResId)
menu.add(0, profileTab.navigationRootId, 0, profileTab.title).setIcon(profileTab.iconResId)
menu.add(0, moreTab.navigationRootId, 0, moreTab.title).setIcon(moreTab.iconResId)
if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph && selectedItemId != R.id.favorites_nav_graph) {
setBottomNavSelectedTab(profileTab)
}
return ImmutableList.of(favoriteTab, profileTab, moreTab)
}
private fun setupMainBottomNav(): List<Tab> {
val menu = binding.bottomNavView.menu
menu.clear()
val navTabList = Utils.getNavTabList(this).first
for ((iconResId, title, _, _, _, navigationRootId) in navTabList) {
menu.add(0, navigationRootId, 0, title).setIcon(iconResId)
}
return navTabList
}
private fun setBottomNavSelectedTab(tab: Tab) {
binding.bottomNavView.selectedItemId = tab.navigationRootId
}
private fun setBottomNavSelectedTab(@IdRes navGraphRootId: Int) {
binding.bottomNavView.selectedItemId = navGraphRootId
}
private fun setupNavigation(toolbar: Toolbar, navController: NavController?) {
if (navController == null) return
NavigationUI.setupWithNavController(toolbar, navController)
navController.addOnDestinationChangedListener(OnDestinationChangedListener { _: NavController?, destination: NavDestination, arguments: Bundle? ->
if (destination.id == R.id.directMessagesThreadFragment && arguments != null) {
// Set the thread title earlier for better ux
val title = arguments.getString("title")
if (!title.isNullOrBlank()) {
supportActionBar?.title = title
}
}
if (destination.id == R.id.profileFragment && arguments != null) {
// Set the title to username
val username = arguments.getString("username")
if (!username.isNullOrBlank()) {
supportActionBar?.title = username.substringAfter("@")
}
}
// below is a hack to check if we are at the end of the current stack, to setup the search view
binding.appBarLayout.setExpanded(true, true)
val destinationId = destination.id
@SuppressLint("RestrictedApi") val backStack = navController.backStack
setupMenu(backStack.size, destinationId)
val contains = showBottomViewDestinations.contains(destinationId)
binding.root.post {
binding.bottomNavView.visibility = if (contains) View.VISIBLE else View.GONE
// if (contains) {
// behavior?.slideUp(binding.bottomNavView)
// }
}
// explicitly hide keyboard when we navigate
val view = currentFocus
Utils.hideKeyboard(view)
})
}
private fun setupMenu(backStackSize: Int, destinationId: Int) {
val searchMenuItem = searchMenuItem ?: return
if (backStackSize >= 2 && SEARCH_VISIBLE_DESTINATIONS.contains(destinationId)) {
searchMenuItem.isVisible = true
return
}
searchMenuItem.isVisible = false
}
private fun setScrollingBehaviour() {
val layoutParams = binding.mainNavHost.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.behavior = ScrollingViewBehavior()
binding.mainNavHost.requestLayout()
}
private fun removeScrollingBehaviour() {
val layoutParams = binding.mainNavHost.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.behavior = null
binding.mainNavHost.requestLayout()
}
private fun handleIntent(intent: Intent?) {
if (intent == null) return
val action = intent.action
val type = intent.type
// Log.d(TAG, action + " " + type);
if (Intent.ACTION_MAIN == action) return
if (Constants.ACTION_SHOW_ACTIVITY == action) {
showActivityView()
return
}
if (Constants.ACTION_SHOW_DM_THREAD == action) {
showThread(intent)
return
}
if (Intent.ACTION_SEND == action && type != null) {
if (type == "text/plain") {
handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT))
}
return
}
if (Intent.ACTION_VIEW == action) {
val data = intent.data ?: return
handleUrl(data.toString())
}
}
private fun showThread(intent: Intent) {
val threadId = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID)
val threadTitle = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE)
navigateToThread(threadId, threadTitle)
}
fun navigateToThread(threadId: String?, threadTitle: String?) {
if (threadId == null || threadTitle == null) return
currentNavControllerLiveData?.observe(this, object : Observer<NavController?> {
override fun onChanged(navController: NavController?) {
if (navController == null) return
if (navController.graph.id != R.id.direct_messages_nav_graph) return
try {
val currentDestination = navController.currentDestination
if (currentDestination != null && currentDestination.id == R.id.directMessagesInboxFragment) {
// if we are already on the inbox page, navigate to the thread
// need handler.post() to wait for the fragment manager to be ready to navigate
Handler(Looper.getMainLooper()).post {
val action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle)
navController.navigate(action)
}
return
}
// add a destination change listener to navigate to thread once we are on the inbox page
navController.addOnDestinationChangedListener(object : OnDestinationChangedListener {
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?,
) {
if (destination.id == R.id.directMessagesInboxFragment) {
val action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle)
controller.navigate(action)
controller.removeOnDestinationChangedListener(this)
}
}
})
// pop back stack until we reach the inbox page
navController.popBackStack(R.id.directMessagesInboxFragment, false)
} finally {
currentNavControllerLiveData?.removeObserver(this)
}
}
})
val selectedItemId = binding.bottomNavView.selectedItemId
if (selectedItemId != R.navigation.direct_messages_nav_graph) {
setBottomNavSelectedTab(R.id.direct_messages_nav_graph)
}
}
private fun handleUrl(url: String?) {
if (url == null) return
// Log.d(TAG, url);
val intentModel = IntentUtils.parseUrl(url) ?: return
showView(intentModel)
}
private fun showView(intentModel: IntentModel) {
when (intentModel.type) {
IntentModelType.USERNAME -> showProfileView(intentModel)
IntentModelType.POST -> showPostView(intentModel)
IntentModelType.LOCATION -> showLocationView(intentModel)
IntentModelType.HASHTAG -> showHashtagView(intentModel)
IntentModelType.UNKNOWN -> Log.w(TAG, "Unknown model type received!")
// else -> Log.w(TAG, "Unknown model type received!")
}
}
private fun showProfileView(intentModel: IntentModel) {
val username = intentModel.text
// Log.d(TAG, "username: " + username);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putString("username", "@$username")
try {
navController?.navigate(R.id.action_global_profileFragment, bundle)
} catch (e: Exception) {
Log.e(TAG, "showProfileView: ", e)
}
}
private fun showPostView(intentModel: IntentModel) {
val shortCode = intentModel.text
// Log.d(TAG, "shortCode: " + shortCode);
val alertDialog = AlertDialog.Builder(this)
.setCancelable(false)
.setView(R.layout.dialog_opening_post)
.create()
alertDialog.show()
lifecycleScope.launch(Dispatchers.IO) {
try {
val media = if (isLoggedIn) mediaRepository.fetch(shortcodeToId(shortCode)) else graphQLRepository.fetchPost(shortCode)
withContext(Dispatchers.Main) {
if (media == null) {
Toast.makeText(applicationContext, R.string.post_not_found, Toast.LENGTH_SHORT).show()
return@withContext
}
val currentNavControllerLiveData = currentNavControllerLiveData ?: return@withContext
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media)
try {
navController?.navigate(R.id.action_global_post_view, bundle)
} catch (e: Exception) {
Log.e(TAG, "showPostView: ", e)
}
}
} catch (e: Exception) {
Log.e(TAG, "showPostView: ", e)
} finally {
withContext(Dispatchers.Main) {
alertDialog.dismiss()
}
}
}
}
private fun showLocationView(intentModel: IntentModel) {
val locationId = intentModel.text
// Log.d(TAG, "locationId: " + locationId);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putLong("locationId", locationId.toLong())
navController?.navigate(R.id.action_global_locationFragment, bundle)
}
private fun showHashtagView(intentModel: IntentModel) {
val hashtag = intentModel.text
// Log.d(TAG, "hashtag: " + hashtag);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putString("hashtag", hashtag)
navController?.navigate(R.id.action_global_hashTagFragment, bundle)
}
private fun showActivityView() {
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putString("type", "notif")
navController?.navigate(R.id.action_global_notificationsViewerFragment, bundle)
}
private fun bindActivityCheckerService() {
bindService(Intent(this, ActivityCheckerService::class.java), serviceConnection, BIND_AUTO_CREATE)
isActivityCheckerServiceBound = true
}
private fun unbindActivityCheckerService() {
if (!isActivityCheckerServiceBound) return
unbindService(serviceConnection)
isActivityCheckerServiceBound = false
}
val bottomNavView: BottomNavigationView
get() = binding.bottomNavView
fun setCollapsingView(view: View) {
try {
binding.collapsingToolbarLayout.addView(view, 0)
} catch (e: Exception) {
Log.e(TAG, "setCollapsingView: ", e)
}
}
fun removeCollapsingView(view: View) {
try {
binding.collapsingToolbarLayout.removeView(view)
} catch (e: Exception) {
Log.e(TAG, "removeCollapsingView: ", e)
}
}
fun resetToolbar() {
binding.appBarLayout.visibility = View.VISIBLE
setScrollingBehaviour()
setSupportActionBar(binding.toolbar)
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
setupNavigation(binding.toolbar, currentNavControllerLiveData.value)
}
val collapsingToolbarView: CollapsingToolbarLayout
get() = binding.collapsingToolbarLayout
val appbarLayout: AppBarLayout
get() = binding.appBarLayout
fun removeLayoutTransition() {
binding.root.layoutTransition = null
}
fun setLayoutTransition() {
binding.root.layoutTransition = LayoutTransition()
}
private fun initEmojiCompat() {
// Use a downloadable font for EmojiCompat
val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)
val config: EmojiCompat.Config = FontRequestEmojiCompatConfig(applicationContext, fontRequest)
config.setReplaceAll(true) // .setUseEmojiAsDefaultStyle(true)
.registerInitCallback(object : InitCallback() {
override fun onInitialized() {
Log.i(TAG, "EmojiCompat initialized")
}
override fun onFailed(throwable: Throwable?) {
Log.e(TAG, "EmojiCompat initialization failed", throwable)
}
})
EmojiCompat.init(config)
}
var toolbar: Toolbar
get() = binding.toolbar
set(toolbar) {
binding.appBarLayout.visibility = View.GONE
removeScrollingBehaviour()
setSupportActionBar(toolbar)
if (currentNavControllerLiveData == null) return
setupNavigation(toolbar, currentNavControllerLiveData?.value)
}
val rootView: View
get() = binding.root
private fun setNavBarDMUnreadCountBadge(unseenCount: Int) {
val badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph)
if (unseenCount == 0) {
badge.isVisible = false
badge.clearNumber()
return
}
if (badge.verticalOffset != 10) {
badge.verticalOffset = 10
}
badge.number = unseenCount
badge.isVisible = true
}
fun showSearchView(): TextInputLayout {
binding.searchInputLayout.visibility = View.VISIBLE
return binding.searchInputLayout
}
fun hideSearchView() {
binding.searchInputLayout.visibility = View.GONE
}
companion object {
private const val TAG = "MainActivity"
private const val FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"
private const val LAST_SELECT_NAV_MENU_ID = "lastSelectedNavMenuId"
private val SEARCH_VISIBLE_DESTINATIONS: List<Int> = ImmutableList.of(
R.id.feedFragment,
R.id.profileFragment,
R.id.directMessagesInboxFragment,
R.id.discoverFragment,
R.id.favoritesFragment,
R.id.hashTagFragment,
R.id.locationFragment
)
@JvmStatic
var instance: MainActivity? = null
private set
}
}