diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48611fa..76cf219 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -166,9 +166,9 @@ android.productFlavors.forEach { flavor -> apply(plugin = "com.google.firebase.crashlytics") dependencies { - implementation("com.android.billingclient:billing:5.1.0") + implementation("com.android.billingclient:billing:6.0.1") implementation("com.google.android.play:core-ktx:1.8.1") - implementation("com.google.firebase:firebase-crashlytics:18.3.5") + implementation("com.google.firebase:firebase-crashlytics:18.4.1") } } } diff --git a/app/src/free/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt b/app/src/free/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt index 2bff236..d39f9be 100644 --- a/app/src/free/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt +++ b/app/src/free/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt @@ -1,9 +1,6 @@ package com.wbrawner.simplemarkdown.utility -import android.app.Activity -import androidx.lifecycle.MutableLiveData -import com.google.android.material.button.MaterialButton +import androidx.compose.runtime.Composable -class SupportLinkProvider(@Suppress("unused") private val activity: Activity) { - val supportLinks = MutableLiveData>() -} \ No newline at end of file +@Composable +fun SupportLinks() {} \ No newline at end of file diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/ui/MainScreen.kt b/app/src/main/java/com/wbrawner/simplemarkdown/ui/MainScreen.kt index 1169cd5..231ecf6 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/ui/MainScreen.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/ui/MainScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Menu @@ -26,6 +27,7 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Tab @@ -42,9 +44,13 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity import androidx.navigation.NavController @@ -101,7 +107,9 @@ fun MainScreen(navController: NavController, viewModel: MarkdownViewModel) { DropdownMenuItem(text = { Row(verticalAlignment = Alignment.CenterVertically) { Text("Lock Swiping") - Checkbox(checked = lockSwiping, onCheckedChange = { lockSwiping = !lockSwiping }) + Checkbox( + checked = lockSwiping, + onCheckedChange = { lockSwiping = !lockSwiping }) } }, onClick = { lockSwiping = !lockSwiping @@ -138,7 +146,9 @@ fun MainScreen(navController: NavController, viewModel: MarkdownViewModel) { .padding(8.dp), value = markdown, onValueChange = { viewModel.updateMarkdown(it) }, - textStyle = TextStyle.Default.copy(fontFamily = FontFamily.Monospace) + textStyle = TextStyle.Default.copy(fontFamily = FontFamily.Monospace, color = MaterialTheme.colorScheme.onSurface), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences), + cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface) ) } else { MarkdownPreview(modifier = Modifier.fillMaxSize(), markdown) @@ -154,18 +164,26 @@ fun MarkdownNavigationDrawer( navigate: (Route) -> Unit, content: @Composable (drawerState: DrawerState) -> Unit ) { val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val coroutineScope = rememberCoroutineScope() DismissibleNavigationDrawer(drawerState = drawerState, drawerContent = { DismissibleDrawerSheet { Route.entries.forEach { route -> if (route == Route.EDITOR) { return@forEach } - NavigationDrawerItem(icon = { - Icon(imageVector = route.icon, contentDescription = null) - }, + NavigationDrawerItem( + icon = { + Icon(imageVector = route.icon, contentDescription = null) + }, label = { Text(route.title) }, selected = false, - onClick = { navigate(route) }) + onClick = { + navigate(route) + coroutineScope.launch { + drawerState.close() + } + } + ) } } }) { diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/ui/MarkdownInfoScreen.kt b/app/src/main/java/com/wbrawner/simplemarkdown/ui/MarkdownInfoScreen.kt index bf63a78..d9f7a44 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/ui/MarkdownInfoScreen.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/ui/MarkdownInfoScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavController import com.wbrawner.simplemarkdown.utility.readAssetToString import com.wbrawner.simplemarkdown.utility.toHtml -import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel @Composable fun MarkdownInfoScreen( diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/ui/SupportScreen.kt b/app/src/main/java/com/wbrawner/simplemarkdown/ui/SupportScreen.kt index c6a85c4..96f85f7 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/ui/SupportScreen.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/ui/SupportScreen.kt @@ -1,2 +1,104 @@ package com.wbrawner.simplemarkdown.ui +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent +import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_ON +import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat.startActivity +import androidx.navigation.NavController +import com.wbrawner.simplemarkdown.R +import com.wbrawner.simplemarkdown.utility.SupportLinks + +@Composable +fun SupportScreen(navController: NavController) { + Scaffold(topBar = { + MarkdownTopAppBar(title = "Support SimpleMarkdown", navController = navController) + }) { paddingValues -> + val context = LocalContext.current + Column( + modifier = Modifier + .padding(paddingValues) + .padding(16.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + modifier = Modifier.size(100.dp), + imageVector = Icons.Default.Favorite, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + Text(context.getString(R.string.support_info), textAlign = TextAlign.Center) + Spacer(modifier = Modifier.height(8.dp)) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + CustomTabsIntent.Builder() + .setShareState(SHARE_STATE_ON) + .build() + .launchUrl(context, Uri.parse("https://github.com/wbrawner/SimpleMarkdown")) + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color(context.getColor(R.color.colorBackgroundGitHub)), + contentColor = Color.White + ) + ) { + Text(context.getString(R.string.action_view_github)) + } + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + val playStoreIntent = Intent(Intent.ACTION_VIEW) + .apply { + data = Uri.parse("market://details?id=${context.packageName}") + addFlags( + Intent.FLAG_ACTIVITY_NO_HISTORY or + Intent.FLAG_ACTIVITY_NEW_DOCUMENT or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK + ) + } + try { + startActivity(context, playStoreIntent, null) + } catch (ignored: ActivityNotFoundException) { + playStoreIntent.data = + Uri.parse("https://play.google.com/store/apps/details?id=${context.packageName}") + startActivity(context, playStoreIntent, null) + } + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color(context.getColor(R.color.colorBackgroundPlayStore)), + contentColor = Color.White + ) + ) { + Text(context.getString(R.string.action_rate)) + } + SupportLinks() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/DisableableViewPager.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/DisableableViewPager.kt deleted file mode 100644 index 229fd2a..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/DisableableViewPager.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.wbrawner.simplemarkdown.view - -import android.annotation.SuppressLint -import android.content.Context -import android.util.AttributeSet -import android.view.MotionEvent -import androidx.viewpager.widget.ViewPager - -class DisableableViewPager : ViewPager { - private var isSwipeLocked = false - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - - override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { - return !isSwipeLocked && super.onInterceptTouchEvent(ev) - } - - @SuppressLint("ClickableViewAccessibility") - override fun onTouchEvent(ev: MotionEvent): Boolean { - return !isSwipeLocked && super.onTouchEvent(ev) - } - - fun setSwipeLocked(locked: Boolean) { - this.isSwipeLocked = locked - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/ViewPagerPage.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/ViewPagerPage.kt deleted file mode 100644 index d978d44..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/ViewPagerPage.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.wbrawner.simplemarkdown.view - -interface ViewPagerPage { - fun onSelected() - fun onDeselected() -} \ No newline at end of file diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt index 6c1bf17..1ee9ca0 100644 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt +++ b/app/src/main/java/com/wbrawner/simplemarkdown/view/activity/MainActivity.kt @@ -38,6 +38,7 @@ import com.wbrawner.simplemarkdown.R import com.wbrawner.simplemarkdown.ui.MainScreen import com.wbrawner.simplemarkdown.ui.MarkdownInfoScreen import com.wbrawner.simplemarkdown.ui.SettingsScreen +import com.wbrawner.simplemarkdown.ui.SupportScreen import com.wbrawner.simplemarkdown.ui.theme.SimpleMarkdownTheme import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel import kotlinx.coroutines.Dispatchers @@ -126,7 +127,7 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes SettingsScreen(navController = navController) } composable(Route.SUPPORT.path) { - Text("To do") + SupportScreen(navController = navController) } composable(Route.HELP.path) { MarkdownInfoScreen(title = Route.HELP.title, file = "Cheatsheet.md", navController = navController) @@ -141,12 +142,6 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes } } } - - override fun onBackPressed() { - if (!findNavController(R.id.content).navigateUp()) { - super.onBackPressed() - } - } } enum class Route( diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.kt deleted file mode 100644 index d2003a3..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/adapter/EditPagerAdapter.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.wbrawner.simplemarkdown.view.adapter - -import android.content.Context -import android.content.res.Configuration -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter -import androidx.viewpager.widget.ViewPager -import com.wbrawner.simplemarkdown.R -import com.wbrawner.simplemarkdown.view.fragment.EditFragment -import com.wbrawner.simplemarkdown.view.fragment.PreviewFragment - -class EditPagerAdapter(fm: FragmentManager, private val context: Context) - : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), ViewPager.OnPageChangeListener { - - private val editFragment = EditFragment() - private val previewFragment = PreviewFragment() - - override fun getItem(position: Int): Fragment { - return when (position) { - FRAGMENT_EDIT -> editFragment - FRAGMENT_PREVIEW -> previewFragment - else -> throw IllegalStateException("Attempting to get fragment for invalid page number") - } - } - - override fun getCount(): Int { - return NUM_PAGES - } - - override fun getPageTitle(position: Int): CharSequence? { - var stringId = 0 - when (position) { - FRAGMENT_EDIT -> stringId = R.string.action_edit - FRAGMENT_PREVIEW -> stringId = R.string.action_preview - } - return context.getString(stringId) - } - - override fun getPageWidth(position: Int): Float { - return if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - 0.5f - } else { - super.getPageWidth(position) - } - } - - override fun onPageScrollStateChanged(state: Int) { - } - - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - } - - override fun onPageSelected(position: Int) { - when (position) { - FRAGMENT_EDIT -> { - editFragment.onSelected() - } - FRAGMENT_PREVIEW -> { - editFragment.onDeselected() - } - } - } - - companion object { - const val FRAGMENT_EDIT = 0 - const val FRAGMENT_PREVIEW = 1 - const val NUM_PAGES = 2 - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt deleted file mode 100644 index 29a82ae..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/MarkdownInfoFragment.kt +++ /dev/null @@ -1,99 +0,0 @@ -//package com.wbrawner.simplemarkdown.view.fragment -// -//import android.content.res.Configuration -//import android.os.Bundle -//import android.view.LayoutInflater -//import android.view.MenuItem -//import android.view.View -//import android.view.ViewGroup -//import android.widget.Toast -//import androidx.appcompat.app.AppCompatDelegate -//import androidx.fragment.app.Fragment -//import androidx.lifecycle.lifecycleScope -//import androidx.navigation.fragment.findNavController -//import androidx.navigation.ui.setupWithNavController -//import com.wbrawner.plausible.android.Plausible -//import com.wbrawner.simplemarkdown.R -//import com.wbrawner.simplemarkdown.databinding.FragmentMarkdownInfoBinding -//import com.wbrawner.simplemarkdown.utility.* -//import kotlinx.coroutines.launch -// -//class MarkdownInfoFragment : Fragment() { -// private val errorHandler: ErrorHandler by errorHandlerImpl() -// private var _binding: FragmentMarkdownInfoBinding? = null -// private val binding: FragmentMarkdownInfoBinding -// get() = _binding!! -// -// override fun onCreate(savedInstanceState: Bundle?) { -// super.onCreate(savedInstanceState) -// setHasOptionsMenu(true) -// } -// -// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { -// _binding = FragmentMarkdownInfoBinding.inflate(inflater, container, false) -// return binding.root -// } -// -// override fun onDestroyView() { -// super.onDestroyView() -// _binding = null -// } -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// val fileName = arguments?.getString(EXTRA_FILE) -// if (fileName.isNullOrBlank()) { -// findNavController().navigateUp() -// return -// } -// binding.toolbar.setupWithNavController(findNavController()) -// -// val isNightMode = AppCompatDelegate.getDefaultNightMode() == -// AppCompatDelegate.MODE_NIGHT_YES -// || resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES -// val defaultCssId = if (isNightMode) { -// R.string.pref_custom_css_default_dark -// } else { -// R.string.pref_custom_css_default -// } -// val css: String? = getString(defaultCssId) -// lifecycleScope.launch { -// try { -// val html = view.context.assets?.readAssetToString(fileName) -// ?.toHtml() -// ?: throw RuntimeException("Unable to open stream to $fileName") -// binding.infoWebview.loadDataWithBaseURL(null, -// String.format(FORMAT_CSS, css) + html, -// "text/html", -// "UTF-8", null -// ) -// } catch (e: Exception) { -// errorHandler.reportException(e) -// Toast.makeText(view.context, R.string.file_load_error, Toast.LENGTH_SHORT).show() -// findNavController().navigateUp() -// } -// } -// } -// -// override fun onOptionsItemSelected(item: MenuItem): Boolean { -// if (item.itemId == android.R.id.home) { -// findNavController().navigateUp() -// return true -// } -// return super.onOptionsItemSelected(item) -// } -// -// override fun onStart() { -// super.onStart() -// arguments?.getString(EXTRA_FILE)?.let { -// Plausible.pageView(it) -// } -// } -// -// companion object { -// const val FORMAT_CSS = "" -// const val EXTRA_TITLE = "title" -// const val EXTRA_FILE = "file" -// } -//} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt deleted file mode 100644 index e61c6b7..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsContainerFragment.kt +++ /dev/null @@ -1,45 +0,0 @@ -//package com.wbrawner.simplemarkdown.view.fragment -// -//import android.os.Bundle -//import android.view.LayoutInflater -//import android.view.MenuItem -//import android.view.View -//import android.view.ViewGroup -//import androidx.fragment.app.Fragment -//import androidx.navigation.fragment.findNavController -//import androidx.navigation.ui.setupWithNavController -//import com.wbrawner.plausible.android.Plausible -//import com.wbrawner.simplemarkdown.R -//import com.wbrawner.simplemarkdown.databinding.FragmentSettingsBinding -// -//class SettingsContainerFragment : Fragment() { -// private var _binding: FragmentSettingsBinding? = null -// private val binding: FragmentSettingsBinding -// get() = _binding!! -// -// override fun onCreate(savedInstanceState: Bundle?) { -// super.onCreate(savedInstanceState) -// setHasOptionsMenu(true) -// } -// -// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { -// _binding = FragmentSettingsBinding.inflate(inflater, container, false) -// return binding.root -// } -// -// override fun onDestroyView() { -// super.onDestroyView() -// _binding = null -// } -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// binding.toolbar.setupWithNavController(findNavController()) -// } -// -// override fun onOptionsItemSelected(item: MenuItem): Boolean = findNavController().navigateUp() -// -// override fun onStart() { -// super.onStart() -// Plausible.pageView("Settings") -// } -//} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsFragment.kt deleted file mode 100644 index 0f7c1dc..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SettingsFragment.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.wbrawner.simplemarkdown.view.fragment - -import android.content.SharedPreferences -import android.os.Build -import android.os.Bundle -import androidx.appcompat.app.AppCompatDelegate -import androidx.lifecycle.lifecycleScope -import androidx.preference.ListPreference -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager -import com.wbrawner.simplemarkdown.BuildConfig -import com.wbrawner.simplemarkdown.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class SettingsFragment - : PreferenceFragmentCompat(), - SharedPreferences.OnSharedPreferenceChangeListener { - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.pref_general) - if (BuildConfig.DEBUG) { - preferenceScreen.addPreference(Preference(preferenceScreen.context).apply { - title = "Force crash" - onPreferenceClickListener = Preference.OnPreferenceClickListener { - throw RuntimeException("Forced crash from settings") - } - }) - } - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - lifecycleScope.launch(context = Dispatchers.IO) { - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - sharedPreferences.registerOnSharedPreferenceChangeListener(this@SettingsFragment) - (findPreference(getString(R.string.pref_key_dark_mode)) as? ListPreference)?.let { - setListPreferenceSummary(sharedPreferences, it) - } - @Suppress("ConstantConditionIf") - if (!BuildConfig.ENABLE_CUSTOM_CSS) { - findPreference(getString(R.string.pref_custom_css))?.let { - preferenceScreen.removePreference(it) - } - } - } - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - if (!isAdded || sharedPreferences == null || key == null) return - val preference = findPreference(key) as? ListPreference ?: return - setListPreferenceSummary(sharedPreferences, preference) - if (preference.key != getString(R.string.pref_key_dark_mode)) { - return - } - var darkMode: Int = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { - AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY - } else { - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - } - val darkModeValue = sharedPreferences.getString(preference.key, null) - if (!darkModeValue.isNullOrEmpty()) { - if (darkModeValue.equals(getString(R.string.pref_value_light), ignoreCase = true)) { - darkMode = AppCompatDelegate.MODE_NIGHT_NO - } else if (darkModeValue.equals(getString(R.string.pref_value_dark), ignoreCase = true)) { - darkMode = AppCompatDelegate.MODE_NIGHT_YES - } - } - AppCompatDelegate.setDefaultNightMode(darkMode) - activity?.recreate() - } - - private fun setListPreferenceSummary(sharedPreferences: SharedPreferences, preference: ListPreference) { - val storedValue = sharedPreferences.getString( - preference.key, - null - ) ?: return - val index = preference.findIndexOfValue(storedValue) - if (index < 0) return - preference.summary = preference.entries[index].toString() - } -} diff --git a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt b/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt deleted file mode 100644 index 6910da7..0000000 --- a/app/src/main/java/com/wbrawner/simplemarkdown/view/fragment/SupportFragment.kt +++ /dev/null @@ -1,69 +0,0 @@ -//package com.wbrawner.simplemarkdown.view.fragment -// -//import android.content.ActivityNotFoundException -//import android.content.Intent -//import android.net.Uri -//import android.os.Bundle -//import android.view.LayoutInflater -//import android.view.View -//import android.view.ViewGroup -//import androidx.browser.customtabs.CustomTabsIntent -//import androidx.fragment.app.Fragment -//import androidx.lifecycle.Observer -//import androidx.navigation.fragment.findNavController -//import androidx.navigation.ui.setupWithNavController -//import com.wbrawner.plausible.android.Plausible -//import com.wbrawner.simplemarkdown.databinding.FragmentSupportBinding -//import com.wbrawner.simplemarkdown.utility.SupportLinkProvider -// -//class SupportFragment : Fragment() { -// private var _binding: FragmentSupportBinding? = null -// private val binding: FragmentSupportBinding -// get() = _binding!! -// -// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { -// _binding = FragmentSupportBinding.inflate(inflater, container, false) -// return binding.root -// } -// -// override fun onDestroyView() { -// super.onDestroyView() -// _binding = null -// } -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// binding.toolbar.setupWithNavController(findNavController()) -// binding.githubButton.setOnClickListener { -// CustomTabsIntent.Builder() -// .addDefaultShareMenuItem() -// .build() -// .launchUrl(view.context, Uri.parse("https://github" + -// ".com/wbrawner/SimpleMarkdown")) -// } -// binding.rateButton.setOnClickListener { -// val playStoreIntent = Intent(Intent.ACTION_VIEW) -// .apply { -// data = Uri.parse("market://details?id=${view.context.packageName}") -// addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or -// Intent.FLAG_ACTIVITY_NEW_DOCUMENT or -// Intent.FLAG_ACTIVITY_MULTIPLE_TASK) -// } -// try { -// startActivity(playStoreIntent) -// } catch (ignored: ActivityNotFoundException) { -// playStoreIntent.data = Uri.parse("https://play.google.com/store/apps/details?id=${view.context.packageName}") -// startActivity(playStoreIntent) -// } -// } -// SupportLinkProvider(requireActivity()).supportLinks.observe(viewLifecycleOwner, Observer { links -> -// links.forEach { -// binding.supportButtons.addView(it) -// } -// }) -// } -// -// override fun onStart() { -// super.onStart() -// Plausible.pageView("Support") -// } -//} \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_main.xml b/app/src/main/res/layout-land/fragment_main.xml deleted file mode 100644 index fe45a2b..0000000 --- a/app/src/main/res/layout-land/fragment_main.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 51f7c60..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit.xml b/app/src/main/res/layout/fragment_edit.xml deleted file mode 100644 index a03d90e..0000000 --- a/app/src/main/res/layout/fragment_edit.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml deleted file mode 100644 index 6c77982..0000000 --- a/app/src/main/res/layout/fragment_main.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_markdown_info.xml b/app/src/main/res/layout/fragment_markdown_info.xml deleted file mode 100644 index 987b398..0000000 --- a/app/src/main/res/layout/fragment_markdown_info.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_preview.xml b/app/src/main/res/layout/fragment_preview.xml deleted file mode 100644 index 047600d..0000000 --- a/app/src/main/res/layout/fragment_preview.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml deleted file mode 100644 index d6d7abe..0000000 --- a/app/src/main/res/layout/fragment_settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_support.xml b/app/src/main/res/layout/fragment_support.xml deleted file mode 100644 index 25dacb3..0000000 --- a/app/src/main/res/layout/fragment_support.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_edit.xml b/app/src/main/res/menu/menu_edit.xml deleted file mode 100644 index 6798380..0000000 --- a/app/src/main/res/menu/menu_edit.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml deleted file mode 100644 index 30f818c..0000000 --- a/app/src/main/res/menu/menu_main.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml deleted file mode 100644 index 522612a..0000000 --- a/app/src/main/res/navigation/nav_graph.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml deleted file mode 100644 index aa50523..0000000 --- a/app/src/main/res/xml/pref_general.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - diff --git a/app/src/play/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt b/app/src/play/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt index 8f8f3db..768d19d 100644 --- a/app/src/play/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt +++ b/app/src/play/java/com/wbrawner/simplemarkdown/utility/SupportLinkProvider.kt @@ -1,105 +1,107 @@ package com.wbrawner.simplemarkdown.utility import android.app.Activity -import android.app.Application -import android.os.Bundle import android.widget.Toast -import androidx.lifecycle.MutableLiveData -import com.android.billingclient.api.* -import com.google.android.material.button.MaterialButton +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import com.android.billingclient.api.BillingClient +import com.android.billingclient.api.BillingClientStateListener +import com.android.billingclient.api.BillingFlowParams +import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams +import com.android.billingclient.api.BillingResult +import com.android.billingclient.api.ConsumeParams +import com.android.billingclient.api.ProductDetails +import com.android.billingclient.api.QueryProductDetailsParams import com.wbrawner.simplemarkdown.R -class SupportLinkProvider(private val activity: Activity) : BillingClientStateListener, - PurchasesUpdatedListener { - val supportLinks = MutableLiveData>() - - private val billingClient: BillingClient = BillingClient.newBuilder(activity.applicationContext) - .setListener(this) +@Composable +fun SupportLinks() { + val context = LocalContext.current + var products by remember { mutableStateOf(emptyList()) } + var billingClient by remember { mutableStateOf(null) } + DisposableEffect(context) { + billingClient = BillingClient.newBuilder(context.applicationContext) + .setListener { _, purchases -> + purchases?.forEach { purchase -> + val consumeParams = ConsumeParams.newBuilder() + .setPurchaseToken(purchase.purchaseToken) + .build() + billingClient?.consumeAsync(consumeParams) { _, _ -> + Toast.makeText( + context, + context.getString(R.string.support_thank_you), + Toast.LENGTH_SHORT + ).show() + } + } + } .enablePendingPurchases() .build() - init { - billingClient.startConnection(this) - activity.application.registerActivityLifecycleCallbacks( - object : Application.ActivityLifecycleCallbacks { - override fun onActivityPaused(activity: Activity) { - } + billingClient?.startConnection(object : BillingClientStateListener { + override fun onBillingServiceDisconnected() { + billingClient?.startConnection(this) + } - override fun onActivityStarted(activity: Activity) { - } - - override fun onActivityDestroyed(activity: Activity) { - billingClient.endConnection() - } - - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { - } - - override fun onActivityStopped(activity: Activity) { - } - - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { - } - - override fun onActivityResumed(activity: Activity) { - } + override fun onBillingSetupFinished(result: BillingResult) { + if (result.responseCode != BillingClient.BillingResponseCode.OK) { + return } - ) - } - - override fun onBillingSetupFinished(result: BillingResult) { - if (result.responseCode != BillingClient.BillingResponseCode.OK) { - return - } - - val skuDetails = SkuDetailsParams.newBuilder() - .setSkusList(listOf("support_the_developer", "tip_coffee", "tip_beer")) - .setType(BillingClient.SkuType.INAPP) - .build() - billingClient.querySkuDetailsAsync(skuDetails) { skuDetailsResponse, skuDetailsList -> - // Process the result. - if (skuDetailsResponse.responseCode != BillingClient.BillingResponseCode.OK || skuDetailsList.isNullOrEmpty()) { - return@querySkuDetailsAsync - } - - skuDetailsList.sortedBy { it.priceAmountMicros } - .map { skuDetails -> - val supportButton = MaterialButton(activity) - supportButton.text = activity.getString( - R.string.support_button_purchase, - skuDetails.title, - skuDetails.price - ) - supportButton.setOnClickListener { - val flowParams = BillingFlowParams.newBuilder() - .setSkuDetails(skuDetails) - .build() - billingClient.launchBillingFlow(activity, flowParams) - } - supportButton + val productsQuery = listOf("support_the_developer", "tip_coffee", "tip_beer") + .map { + QueryProductDetailsParams.Product.newBuilder() + .setProductId(it) + .setProductType(BillingClient.ProductType.INAPP) + .build() } - .let { - supportLinks.postValue(it) - } - } - } - - override fun onBillingServiceDisconnected() { - billingClient.startConnection(this) - } - - override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList?) { - purchases?.forEach { purchase -> - val consumeParams = ConsumeParams.newBuilder() - .setPurchaseToken(purchase.purchaseToken) + val productDetailsQuery = QueryProductDetailsParams.newBuilder() + .setProductList(productsQuery) .build() - billingClient.consumeAsync(consumeParams) { _, _ -> - Toast.makeText( - activity, - activity.getString(R.string.support_thank_you), - Toast.LENGTH_SHORT - ).show() + billingClient?.queryProductDetailsAsync(productDetailsQuery) { result, productDetails -> + if (result.responseCode != BillingClient.BillingResponseCode.OK || productDetails.isEmpty()) { + return@queryProductDetailsAsync + } + products = + productDetails.sortedBy { it.oneTimePurchaseOfferDetails?.priceAmountMicros } + .toList() + } } + }) + + onDispose { + billingClient?.endConnection() + } + } + + products.forEach { product -> + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + val productDetails = ProductDetailsParams.newBuilder() + .setProductDetails(product) + .build() + val flowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(listOf(productDetails)) + .build() + billingClient?.launchBillingFlow(context as Activity, flowParams) + } + ) { + Text( + context.getString( + R.string.support_button_purchase, + product.name, + product.oneTimePurchaseOfferDetails?.formattedPrice + ) + ) } } } \ No newline at end of file