feat: Add tablet landscape orientation support (#490)

- Tablets now support landscape mode, phones remain portrait-only
- Add OrientationAwareActivity base class for orientation management
- Add DeviceUtils.isTablet() for runtime device detection
- Update MainActivity and GeohashPickerActivity to extend OrientationAwareActivity
- Uses multiple detection criteria (screen size, density, configuration)
Fixes #480
This commit is contained in:
Developer Chunk
2025-10-20 20:14:50 +02:00
committed by GitHub
parent f633509848
commit 9535ca8940
5 changed files with 71 additions and 8 deletions
-2
View File
@@ -64,14 +64,12 @@
android:name=".ui.GeohashPickerActivity" android:name=".ui.GeohashPickerActivity"
android:exported="false" android:exported="false"
android:theme="@style/Theme.BitchatAndroid" android:theme="@style/Theme.BitchatAndroid"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.BitchatAndroid" android:theme="@style/Theme.BitchatAndroid"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:launchMode="singleTop"> android:launchMode="singleTop">
<intent-filter> <intent-filter>
@@ -3,7 +3,6 @@ package com.bitchat.android
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@@ -15,7 +14,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
@@ -39,13 +37,14 @@ import com.bitchat.android.onboarding.PermissionExplanationScreen
import com.bitchat.android.onboarding.PermissionManager import com.bitchat.android.onboarding.PermissionManager
import com.bitchat.android.ui.ChatScreen import com.bitchat.android.ui.ChatScreen
import com.bitchat.android.ui.ChatViewModel import com.bitchat.android.ui.ChatViewModel
import com.bitchat.android.ui.OrientationAwareActivity
import com.bitchat.android.ui.theme.BitchatTheme import com.bitchat.android.ui.theme.BitchatTheme
import com.bitchat.android.nostr.PoWPreferenceManager import com.bitchat.android.nostr.PoWPreferenceManager
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : OrientationAwareActivity() {
private lateinit var permissionManager: PermissionManager private lateinit var permissionManager: PermissionManager
private lateinit var onboardingCoordinator: OnboardingCoordinator private lateinit var onboardingCoordinator: OnboardingCoordinator
private lateinit var bluetoothStatusManager: BluetoothStatusManager private lateinit var bluetoothStatusManager: BluetoothStatusManager
@@ -11,7 +11,6 @@ import android.webkit.WebChromeClient
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -45,7 +44,7 @@ import com.bitchat.android.geohash.LocationChannelManager
import com.bitchat.android.ui.theme.BASE_FONT_SIZE import com.bitchat.android.ui.theme.BASE_FONT_SIZE
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
class GeohashPickerActivity : ComponentActivity() { class GeohashPickerActivity : OrientationAwareActivity() {
companion object { companion object {
const val EXTRA_INITIAL_GEOHASH = "initial_geohash" const val EXTRA_INITIAL_GEOHASH = "initial_geohash"
@@ -0,0 +1,28 @@
package com.bitchat.android.ui
import android.content.pm.ActivityInfo
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.bitchat.android.utils.DeviceUtils
/**
* Base activity that automatically sets orientation based on device type.
* Tablets can rotate to landscape, phones are locked to portrait.
*/
abstract class OrientationAwareActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setOrientationBasedOnDeviceType()
}
private fun setOrientationBasedOnDeviceType() {
requestedOrientation = if (DeviceUtils.isTablet(this)) {
// Allow all orientations on tablets
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} else {
// Lock to portrait on phones
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
}
@@ -0,0 +1,39 @@
package com.bitchat.android.utils
import android.content.Context
import android.content.res.Configuration
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.core.content.getSystemService
import kotlin.math.sqrt
object DeviceUtils {
/**
* Determines if the current device is a tablet based on screen size and density.
* Uses multiple criteria to accurately detect tablets vs phones.
*/
fun isTablet(context: Context): Boolean {
val windowManager = context.getSystemService<WindowManager>()
val displayMetrics = DisplayMetrics()
windowManager?.defaultDisplay?.getMetrics(displayMetrics)
// Calculate screen size in inches
val widthInches = displayMetrics.widthPixels / displayMetrics.xdpi
val heightInches = displayMetrics.heightPixels / displayMetrics.ydpi
val diagonalInches = sqrt((widthInches * widthInches) + (heightInches * heightInches))
// Check if device has tablet configuration
val configuration = context.resources.configuration
val isLargeScreen = (configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
val isXLargeScreen = (configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE
// A device is considered a tablet if:
// 1. Screen diagonal is 7 inches or larger, OR
// 2. Configuration indicates large or xlarge screen, OR
// 3. Smallest width is 600dp or more (sw600dp)
val smallestWidthDp = context.resources.configuration.smallestScreenWidthDp
return diagonalInches >= 7.0 || isLargeScreen || isXLargeScreen || smallestWidthDp >= 600
}
}