/* * This file is part of AndroidIDE. * * AndroidIDE is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * AndroidIDE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ package com.itsaky.androidide.activities import android.content.Context import android.content.Intent import android.graphics.Rect import android.os.Bundle import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan import android.view.View import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.view.updatePaddingRelative import com.blankj.utilcode.util.ClipboardUtils import com.blankj.utilcode.util.SizeUtils import com.itsaky.androidide.BuildConfig import com.itsaky.androidide.R import com.itsaky.androidide.adapters.SimpleIconTitleDescriptionAdapter import com.itsaky.androidide.app.BaseApplication import com.itsaky.androidide.app.LimitlessIDEActivity import com.itsaky.androidide.app.configuration.IDEBuildConfigProvider import com.itsaky.androidide.buildinfo.BuildInfo import com.itsaky.androidide.databinding.ActivityAboutBinding import com.itsaky.androidide.models.IconTitleDescriptionItem import com.itsaky.androidide.models.SimpleIconTitleDescriptionItem import com.itsaky.androidide.utils.BuildInfoUtils import com.itsaky.androidide.utils.flashSuccess import com.itsaky.androidide.utils.resolveAttr class AboutActivity : LimitlessIDEActivity() { private var _binding: ActivityAboutBinding? = null private val binding: ActivityAboutBinding get() = checkNotNull(_binding) { "Activity has been destroyed" } override fun bindLayout(): View { _binding = ActivityAboutBinding.inflate(layoutInflater) return _binding!!.root } companion object { private var id = 0 private val ACTION_WEBSITE = id++ private val ACTION_EMAIL = id++ private val ACTION_TG_CHANNEL = id++ private val ACTION_TG_GROUP = id++ private val ACTION_CONTRIBUTE = id++ private val ACTION_CONTRIBUTORS = id++ } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding.apply { setSupportActionBar(toolbar) supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setTitle(R.string.about) toolbar.setNavigationOnClickListener { finish() } aboutHeader.apply { ideVersion.text = createVersionText() ideVersion.isClickable = true ideVersion.isFocusable = true ideVersion.setBackgroundResource(R.drawable.bg_ripple) ideVersion.setOnClickListener { ClipboardUtils.copyText(BuildInfoUtils.getBuildInfoHeader()) flashSuccess(R.string.copied) } } socials.apply { sectionTitle.setText(R.string.title_socials) sectionItems.adapter = AboutSocialItemsAdapter(createSocialItems(), ::handleActionClick) } misc.apply { sectionTitle.setText(R.string.title_misc) sectionItems.adapter = AboutSocialItemsAdapter(createMiscItems(), ::handleActionClick) } } } override fun onInsetsUpdated(insets: Rect) { super.onInsetsUpdated(insets) binding.toolbar.apply { setPaddingRelative( paddingStart + insets.left, paddingTop, paddingEnd + insets.right, paddingBottom ) } } private fun handleActionClick(action: SimpleIconTitleDescriptionItem) { when (action.id) { ACTION_WEBSITE -> app.openWebsite() ACTION_EMAIL -> app.emailUs() ACTION_TG_GROUP -> app.openTelegramGroup() ACTION_TG_CHANNEL -> app.openTelegramChannel() ACTION_CONTRIBUTE -> app.openUrl(BaseApplication.CONTRIBUTOR_GUIDE_URL) ACTION_CONTRIBUTORS -> startActivity(Intent(this, ContributorsActivity::class.java)) } } private fun createSocialItems(): List { return mutableListOf().apply { add(createSimpleIconTextItem( this@AboutActivity, ACTION_WEBSITE, R.drawable.ic_website, R.string.about_option_website, BuildInfo.PROJECT_SITE )) add(createSimpleIconTextItem( this@AboutActivity, ACTION_EMAIL, R.drawable.ic_email, R.string.about_option_email, BaseApplication.EMAIL )) add(createSimpleIconTextItem( this@AboutActivity, ACTION_TG_GROUP, R.drawable.ic_telegram, R.string.discussions_on_telegram, BaseApplication.TELEGRAM_GROUP_URL )) add(createSimpleIconTextItem( this@AboutActivity, ACTION_TG_CHANNEL, R.drawable.ic_telegram, R.string.official_tg_channel, BaseApplication.TELEGRAM_CHANNEL_URL )) } } private fun createMiscItems(): List { return mutableListOf().apply { add(SimpleIconTitleDescriptionItem.create( this@AboutActivity, ACTION_CONTRIBUTE, R.drawable.ic_code, R.string.title_contribute, R.string.summary_contribute )) add(SimpleIconTitleDescriptionItem.create( this@AboutActivity, ACTION_CONTRIBUTORS, R.drawable.ic_heart_outline, R.string.title_contributors, R.string.summary_contributors )) } } private fun createSimpleIconTextItem( context: Context, id: Int, @DrawableRes icon: Int, @StringRes title: Int, description: CharSequence ): SimpleIconTitleDescriptionItem { return SimpleIconTitleDescriptionItem( id, ContextCompat.getDrawable(context, icon), ContextCompat.getString(context, title), description ) } /** * Create the version name string that should be displayed to the user. * * Format of the version name string is : * * `v[version-name]-[variant] ([build-type]/[[UN]OFFICIAL])` */ @Suppress("KDocUnresolvedReference") private fun createVersionText(): CharSequence { val builder = SpannableStringBuilder() builder.append("v") builder.append(BuildInfo.VERSION_NAME_SIMPLE) builder.append("-") builder.append(IDEBuildConfigProvider.getInstance().cpuAbiName) builder.append(" ") val colorPositive = ContextCompat.getColor(this, R.color.color_success) val colorNegative = ContextCompat.getColor(this, R.color.color_error) appendBuildType(builder, colorPositive, colorNegative) return builder } private fun appendBuildType( builder: SpannableStringBuilder, @ColorInt colorPositive: Int, @ColorInt colorNegative: Int ) { @Suppress("KotlinConstantConditions") var color = if (BuildConfig.BUILD_TYPE != "release") { colorNegative } else { colorPositive } builder.append("(") appendForegroundSpan(builder, BuildConfig.BUILD_TYPE, color) val isOfficialBuild = BuildInfoUtils.isOfficialBuild(this) color = if (isOfficialBuild) { colorPositive } else { colorNegative } builder.append("/") appendForegroundSpan( builder, BuildInfoUtils.getBuildType(this).lowercase(), color) builder.append(")") } private fun appendForegroundSpan( builder: SpannableStringBuilder, text: CharSequence, color: Int ) { builder.append(text, ForegroundColorSpan(color), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE ) } override fun onDestroy() { super.onDestroy() _binding = null } class AboutSocialItemsAdapter( items: List, private val onClickListener: (SimpleIconTitleDescriptionItem) -> Unit ) : SimpleIconTitleDescriptionAdapter(items) { override fun onBindViewHolder(holder: ViewHolder, position: Int) { super.onBindViewHolder(holder, position) val binding = holder.binding val item = getItem(position) as SimpleIconTitleDescriptionItem val dp8 = SizeUtils.dp2px(8f) binding.icon.updatePaddingRelative(dp8, dp8, dp8, dp8) binding.title.setTextAppearance(R.style.TextAppearance_Material3_TitleSmall) binding.description.maxLines = 3 binding.description.setTextAppearance(R.style.TextAppearance_Material3_BodySmall) binding.description.setTextColor(binding.description.context.resolveAttr(R.attr.colorPrimary)) binding.root.isClickable = true binding.root.isFocusable = true binding.root.setBackgroundResource(R.drawable.bg_ripple) binding.root.setOnClickListener { onClickListener(item) } } } }