diff --git a/app/build.gradle b/app/build.gradle index f9bf50f..7b72933 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId = "partisan.weforge.xyz.pulse" minSdk = 29 targetSdk = 34 - versionCode = 15 - versionName = "2.0.1" + versionCode = 16 + versionName = "2.0.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/ContactsFragment.kt b/app/src/main/java/partisan/weforge/xyz/pulse/ContactsFragment.kt index 370281d..39d236a 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/ContactsFragment.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/ContactsFragment.kt @@ -89,7 +89,9 @@ class ContactsFragment : Fragment() { } private fun getContacts(): List { + val results = mutableListOf() val resolver: ContentResolver = requireContext().contentResolver + val projection = arrayOf( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER @@ -103,16 +105,16 @@ class ContactsFragment : Fragment() { "${ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME} ASC" ) - val results = mutableListOf() - cursor?.use { - val nameIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME) - val numberIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + val nameIndex = it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME) + val numberIndex = it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER) while (it.moveToNext()) { - val name = it.getString(nameIndex) ?: continue - val number = it.getString(numberIndex) ?: continue - results.add(ContactEntry(name, number)) + val name = it.getString(nameIndex) + val number = it.getString(numberIndex) + if (!name.isNullOrBlank() && !number.isNullOrBlank()) { + results.add(ContactEntry(name, number)) + } } } diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/MainFragment.kt b/app/src/main/java/partisan/weforge/xyz/pulse/MainFragment.kt index 9e8c3b4..72cc529 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/MainFragment.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/MainFragment.kt @@ -1,17 +1,20 @@ package partisan.weforge.xyz.pulse +import android.net.ConnectivityManager +import android.net.NetworkCapabilities import android.os.Bundle import android.os.SystemClock import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView import androidx.fragment.app.Fragment import com.google.android.material.button.MaterialButton +import java.util.concurrent.TimeUnit import nl.dionsegijn.konfetti.core.Party import nl.dionsegijn.konfetti.core.Position import nl.dionsegijn.konfetti.core.emitter.Emitter import nl.dionsegijn.konfetti.xml.KonfettiView -import java.util.concurrent.TimeUnit class MainFragment : Fragment() { @@ -24,9 +27,9 @@ class MainFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { val view = inflater.inflate(R.layout.fragment_main, container, false) prefs = Preferences(requireContext()) @@ -44,14 +47,16 @@ class MainFragment : Fragment() { if (isNowChecked && SystemClock.elapsedRealtime() - lastConfettiTime > 500) { konfetti.start( - Party( - emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).perSecond(100), - speed = 30f, - maxSpeed = 40f, - damping = 0.85f, - spread = 360, - position = Position.Relative(0.5, 0.5) - ) + Party( + emitter = + Emitter(duration = 100, TimeUnit.MILLISECONDS) + .perSecond(100), + speed = 30f, + maxSpeed = 40f, + damping = 0.85f, + spread = 360, + position = Position.Relative(0.5, 0.5) + ) ) lastConfettiTime = SystemClock.elapsedRealtime() } @@ -62,6 +67,49 @@ class MainFragment : Fragment() { } } + val warningText = view.findViewById(R.id.warningText) + val warnings = mutableListOf() + + // 1. Check if contacts are available + val contactCursor = + requireContext() + .contentResolver + .query( + android.provider.ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + null, + null, + null, + null + ) + + if (contactCursor != null) { + contactCursor.use { + if (!it.moveToFirst()) { + warnings.add(getString(R.string.warn_no_contacts)) + } + } + } else { + warnings.add(getString(R.string.warn_no_contacts)) + } + + // 2. Check internet connectivity + if (!hasInternet()) { + warnings.add(getString(R.string.warn_no_internet)) + } + + // Show warning if needed + if (warnings.isNotEmpty()) { + warningText.text = warnings.joinToString("\n") + warningText.visibility = View.VISIBLE + } + return view } + + private fun hasInternet(): Boolean { + val cm = requireContext().getSystemService(ConnectivityManager::class.java) + val capabilities = cm?.getNetworkCapabilities(cm.activeNetwork) ?: return false + return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } } diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt b/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt index 64efcce..759e8aa 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt @@ -127,10 +127,13 @@ class SecretView @JvmOverloads constructor( com.google.android.material.R.attr.colorPrimaryVariant, com.google.android.material.R.attr.colorSecondary ) - context.obtainStyledAttributes(colorAttrs).use { - playerPaint.color = it.getColor(0, Color.CYAN) - enemyPaint.color = it.getColor(0, Color.CYAN) - colorSecondary = it.getColor(1, Color.GREEN) + val ta = context.obtainStyledAttributes(colorAttrs) + try { + playerPaint.color = ta.getColor(0, Color.CYAN) + enemyPaint.color = ta.getColor(0, Color.CYAN) + colorSecondary = ta.getColor(1, Color.GREEN) + } finally { + ta.recycle() } Choreographer.getInstance().postFrameCallback(this) diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 86b4dee..741fc5e 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -34,4 +34,17 @@ app:iconGravity="textTop" app:iconPadding="0dp" app:cornerRadius="48dp" /> + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ebeab5..19b99a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,10 @@ Random + + Unable to access contacts. + No internet connection. + Support Pulse Development 💖 Pulse is free and open-source. You can support future development through Ko-fi. As a thank-you, donors get special popup animation effects! diff --git a/fastlane/metadata/android/en-US/changelogs/17.txt b/fastlane/metadata/android/en-US/changelogs/17.txt new file mode 100644 index 0000000..d18ecdb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/17.txt @@ -0,0 +1,3 @@ +v2.0.2 +- Added warning text in case the app does not have sufficient permissions +- Fixed a bug related to tapping the Pulse logo in the About section, specific to MIUI \ No newline at end of file