PixelDroid-App-Android/app/src/main/java/com/h/pixeldroid/MainActivity.kt

329 lines
12 KiB
Kotlin

package com.h.pixeldroid
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.fragment.app.Fragment
import androidx.paging.ExperimentalPagingApi
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.Glide
import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.db.entities.HomeStatusDatabaseEntity
import com.h.pixeldroid.db.entities.PublicFeedStatusDatabaseEntity
import com.h.pixeldroid.db.entities.UserDatabaseEntity
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.fragments.CameraFragment
import com.h.pixeldroid.fragments.SearchDiscoverFragment
import com.h.pixeldroid.fragments.feeds.cachedFeeds.postFeeds.PostFeedFragment
import com.h.pixeldroid.fragments.feeds.cachedFeeds.notifications.NotificationsFragment
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.Utils.Companion.hasInternet
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.widget.AccountHeaderView
import kotlinx.android.synthetic.main.activity_main.*
import org.ligi.tracedroid.sending.TraceDroidEmailSender
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.lang.IllegalArgumentException
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
private lateinit var header: AccountHeaderView
private var user: UserDatabaseEntity? = null
companion object {
const val ADD_ACCOUNT_IDENTIFIER: Long = -13
}
@ExperimentalPagingApi
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme_NoActionBar)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
TraceDroidEmailSender.sendStackTraces("contact@pixeldroid.org", this)
(this.application as Pixeldroid).getAppComponent().inject(this)
//get the currently active user
user = db.userDao().getActiveUser()
//Check if we have logged in and gotten an access token
if (user == null) {
launchActivity(LoginActivity(), firstTime = true)
} else {
setupDrawer()
val tabs: List<Fragment> = listOf(
PostFeedFragment<HomeStatusDatabaseEntity>()
.apply {
arguments = Bundle().apply { putBoolean("home", true) }
},
SearchDiscoverFragment(),
CameraFragment(),
NotificationsFragment(),
PostFeedFragment<PublicFeedStatusDatabaseEntity>()
.apply {
arguments = Bundle().apply { putBoolean("home", false) }
}
)
setupTabs(tabs)
}
}
private fun setupDrawer() {
main_drawer_button.setOnClickListener{
drawer_layout.open()
}
header = AccountHeaderView(this).apply {
headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP
currentHiddenInList = true
onAccountHeaderListener = { _: View?, profile: IProfile, current: Boolean ->
clickProfile(profile, current)
}
addProfile(ProfileSettingDrawerItem().apply {
identifier = ADD_ACCOUNT_IDENTIFIER
nameRes = R.string.add_account_name
descriptionRes = R.string.add_account_description
iconicsIcon = GoogleMaterial.Icon.gmd_add
}, 0)
attachToSliderView(drawer)
dividerBelowHeader = false
closeDrawerOnProfileListClick = true
}
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
Glide.with(imageView.context)
.load(uri)
.placeholder(placeholder)
.into(imageView)
}
override fun cancel(imageView: ImageView) {
Glide.with(imageView.context).clear(imageView)
}
override fun placeholder(ctx: Context, tag: String?): Drawable {
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) {
return ContextCompat.getDrawable(ctx, R.drawable.ic_default_user)!!
}
return super.placeholder(ctx, tag)
}
})
fillDrawerAccountInfo(user!!.user_id)
//after setting with the values in the db, we make sure to update the database and apply
//with the received one. This happens asynchronously.
getUpdatedAccount()
drawer.itemAdapter.add(
primaryDrawerItem {
nameRes = R.string.menu_account
iconicsIcon = GoogleMaterial.Icon.gmd_person
},
primaryDrawerItem {
nameRes = R.string.menu_settings
iconicsIcon = GoogleMaterial.Icon.gmd_settings
},
primaryDrawerItem {
nameRes = R.string.logout
iconicsIcon = GoogleMaterial.Icon.gmd_close
})
drawer.onDrawerItemClickListener = { v, drawerItem, position ->
when (position){
1 -> launchActivity(ProfileActivity())
2 -> launchActivity(SettingsActivity())
3 -> logOut()
}
false
}
}
private fun logOut(){
db.userDao().deleteActiveUsers()
val remainingUsers = db.userDao().getAll()
if (remainingUsers.isEmpty()){
//no more users, start first-time login flow
launchActivity(LoginActivity(), firstTime = true)
} else {
val newActive = remainingUsers.first()
db.userDao().activateUser(newActive.user_id)
//relaunch the app
launchActivity(MainActivity(), firstTime = true)
}
}
private fun getUpdatedAccount() {
if (hasInternet(applicationContext)) {
val domain = user?.instance_uri.orEmpty()
val accessToken = user?.accessToken.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
api.verifyCredentials("Bearer $accessToken")
.enqueue(object : Callback<Account> {
override fun onResponse(
call: Call<Account>,
response: Response<Account>
) {
if (response.body() != null && response.isSuccessful) {
val account = response.body() as Account
DBUtils.addUser(db, account, domain, accessToken = accessToken)
fillDrawerAccountInfo(account.id!!)
}
}
override fun onFailure(call: Call<Account>, t: Throwable) {
Log.e("DRAWER ACCOUNT:", t.toString())
}
})
}
}
//called when switching profiles, or when clicking on current profile
private fun clickProfile(profile: IProfile, current: Boolean): Boolean {
if(current){
launchActivity(ProfileActivity())
return false
}
//Clicked on add new account
if(profile.identifier == ADD_ACCOUNT_IDENTIFIER){
launchActivity(LoginActivity())
return false
}
db.userDao().deActivateActiveUsers()
db.userDao().activateUser(profile.identifier.toString())
apiHolder.setDomainToCurrentUser(db)
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
return false
}
private inline fun primaryDrawerItem(block: PrimaryDrawerItem.() -> Unit): PrimaryDrawerItem {
return PrimaryDrawerItem()
.apply {
isSelectable = false
isIconTinted = true
}
.apply(block)
}
private fun fillDrawerAccountInfo(account: String) {
val users = db.userDao().getAll().toMutableList()
users.sortWith(Comparator { l, r ->
when {
l.isActive && !r.isActive -> -1
r.isActive && !l.isActive -> 1
else -> 0
}
})
val profiles: MutableList<IProfile> = users.map { user ->
ProfileDrawerItem().apply {
isSelected = user.isActive
nameText = user.display_name
iconUrl = user.avatar_static
isNameShown = true
identifier = user.user_id.toLong()
descriptionText = "@${user.username}@${user.instance_uri.removePrefix("https://")}"
}
}.toMutableList()
// reuse the already existing "add account" item
header.profiles.orEmpty()
.filter { it.identifier == ADD_ACCOUNT_IDENTIFIER }
.take(1)
.forEach { profiles.add(it) }
header.clear()
header.profiles = profiles
header.setActiveProfile(account.toLong())
}
private fun setupTabs(tab_array: List<Fragment>){
view_pager.adapter = object : FragmentStateAdapter(this) {
override fun createFragment(position: Int): Fragment {
return tab_array[position]
}
override fun getItemCount(): Int {
return tab_array.size
}
}
//Keep the tabs active to prevent reloads and stutters
view_pager.offscreenPageLimit = tab_array.size - 1
TabLayoutMediator(tabs, view_pager) { tab, position ->
tab.icon = ContextCompat.getDrawable(applicationContext,
when(position){
0 -> R.drawable.ic_home_white_24dp
1 -> R.drawable.ic_search_white_24dp
2 -> R.drawable.ic_photo_camera_white_24dp
3 -> R.drawable.ic_heart
4 -> R.drawable.ic_filter_black_24dp
else -> throw IllegalArgumentException()
})
}.attach()
}
/**
* Launches the given activity and put it as the current one
* @param firstTime to true means the task history will be reset (as if the app were
* launched anew into this activity)
*/
private fun launchActivity(activity: AppCompatActivity, firstTime: Boolean = false) {
val intent = Intent(this, activity::class.java)
if(firstTime){
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
}
/**
* Closes the drawer if it is open, when we press the back button
*/
override fun onBackPressed() {
if(drawer_layout.isDrawerOpen(GravityCompat.START)){
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
}