diff options
| author | Arne Schwabe <arne@rfc2549.org> | 2025-11-06 07:41:17 +0100 |
|---|---|---|
| committer | Arne Schwabe <arne@rfc2549.org> | 2025-11-16 01:30:14 +0100 |
| commit | 5ec86bba571661c70d27368881db797f3a1d926e (patch) | |
| tree | 00e8ab64bf01c35984283623100fc2fda90438da /main/src/ui/java | |
| parent | dda0a2b1eb3ed42fea4c14fea6806600734d1b41 (diff) | |
Implement minimal UI that allows only connect/disconnect
Diffstat (limited to 'main/src/ui/java')
5 files changed, 201 insertions, 8 deletions
diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt index 9e6dd462..9157c7d2 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt @@ -11,6 +11,7 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.Window +import android.widget.Toast import androidx.activity.SystemBarStyle import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity @@ -19,6 +20,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import de.blinkt.openvpn.R +import de.blinkt.openvpn.core.GlobalPreferences import de.blinkt.openvpn.core.LocaleHelper abstract class BaseActivity : AppCompatActivity() { @@ -28,6 +30,13 @@ abstract class BaseActivity : AppCompatActivity() { return uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION } + protected fun checkMinimalUIDisabled() { + if (GlobalPreferences.getMinimalUi()) { + Toast.makeText(this, R.string.minimal_ui_not_available, Toast.LENGTH_LONG).show() + finish() + } + } + override fun onCreate(savedInstanceState: Bundle?) { if (isAndroidTV) { requestWindowFeature(Window.FEATURE_OPTIONS_PANEL) diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt index 88aa9839..85336e17 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt @@ -32,6 +32,7 @@ import de.blinkt.openvpn.R import de.blinkt.openvpn.VpnProfile import de.blinkt.openvpn.core.ConfigParser import de.blinkt.openvpn.core.ConfigParser.ConfigParseError +import de.blinkt.openvpn.core.GlobalPreferences import de.blinkt.openvpn.core.ProfileManager import de.blinkt.openvpn.fragments.Utils import de.blinkt.openvpn.views.FileSelectLayout @@ -824,6 +825,7 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener override fun onStart() { super.onStart() + checkMinimalUIDisabled() } private fun log(logmessage: String?) { diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt index a15de114..e941d76c 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt @@ -14,6 +14,7 @@ import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import de.blinkt.openvpn.R +import de.blinkt.openvpn.core.GlobalPreferences import de.blinkt.openvpn.fragments.* import de.blinkt.openvpn.fragments.ImportRemoteConfig.Companion.newInstance import de.blinkt.openvpn.views.ScreenSlidePagerAdapter @@ -22,6 +23,7 @@ class MainActivity : BaseActivity() { private lateinit var mPager: ViewPager2 private lateinit var mPagerAdapter: ScreenSlidePagerAdapter + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val view = layoutInflater.inflate(R.layout.main_activity, null) @@ -32,16 +34,26 @@ class MainActivity : BaseActivity() { mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, lifecycle, this) - /* Toolbar and slider should have the same elevation */disableToolbarElevation() - mPagerAdapter.addTab(R.string.vpn_list_title, VPNProfileList::class.java) - mPagerAdapter.addTab(R.string.graph, GraphFragment::class.java) - mPagerAdapter.addTab(R.string.generalsettings, GeneralSettings::class.java) - mPagerAdapter.addTab(R.string.faq, FaqFragment::class.java) - if (SendDumpFragment.getLastestDump(this) != null) { - mPagerAdapter.addTab(R.string.crashdump, SendDumpFragment::class.java) + /* Toolbar and slider should have the same elevation */ + disableToolbarElevation() + + val minimalUi = GlobalPreferences.getMinimalUi(); + if (minimalUi ) { + mPagerAdapter.addTab(R.string.minimal_ui, MinimalUI::class.java) + } else { + + mPagerAdapter.addTab(R.string.vpn_list_title, VPNProfileList::class.java) + mPagerAdapter.addTab(R.string.graph, GraphFragment::class.java) + mPagerAdapter.addTab(R.string.generalsettings, GeneralSettings::class.java) + mPagerAdapter.addTab(R.string.faq, FaqFragment::class.java) + if (SendDumpFragment.getLastestDump(this) != null) { + mPagerAdapter.addTab(R.string.crashdump, SendDumpFragment::class.java) + } + } - if (isAndroidTV) + if (isAndroidTV || minimalUi) mPagerAdapter.addTab(R.string.openvpn_log, LogFragment::class.java) + mPagerAdapter.addTab(R.string.about, AboutFragment::class.java) mPager.setAdapter(mPagerAdapter) diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt index a60c7567..91806b5e 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt @@ -87,6 +87,8 @@ class VPNPreferences : BaseActivity(), VpnStatus.ProfileNotifyListener { } override fun onCreate(savedInstanceState: Bundle?) { + checkMinimalUIDisabled() + mProfileUUID = intent.getStringExtra("$packageName.profileUUID") if (savedInstanceState != null) { val savedUUID = savedInstanceState.getString("$packageName.profileUUID") diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/MinimalUI.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/MinimalUI.kt new file mode 100644 index 00000000..b57fdea7 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/MinimalUI.kt @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2012-2025 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.fragments + +import android.Manifest +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.os.IBinder +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CompoundButton +import android.widget.TextView +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import de.blinkt.openvpn.LaunchVPN +import de.blinkt.openvpn.R +import de.blinkt.openvpn.VpnProfile +import de.blinkt.openvpn.core.ConnectionStatus +import de.blinkt.openvpn.core.IOpenVPNServiceInternal +import de.blinkt.openvpn.core.OpenVPNService +import de.blinkt.openvpn.core.ProfileManager +import de.blinkt.openvpn.core.VpnStatus + +class MinimalUI: Fragment(), View.OnClickListener, VpnStatus.StateListener { + override fun onClick(v: View?) { + TODO("Not yet implemented") + } + + private var mPermReceiver: ActivityResultLauncher<String>? = null + private lateinit var profileManger: ProfileManager + private var mService: IOpenVPNServiceInternal? = null + private lateinit var vpnstatus: TextView + private lateinit var vpntoggle: CompoundButton + + private lateinit var view: View + + private val mConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected( + className: ComponentName, + service: IBinder + ) { + mService = IOpenVPNServiceInternal.Stub.asInterface(service) + } + + override fun onServiceDisconnected(arg0: ComponentName) { + mService = null + } + } + + private fun registerPermissionReceiver() { + mPermReceiver = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { result: Boolean? -> + checkForNotificationPermission( + requireView() + ) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + registerPermissionReceiver() + + profileManger = ProfileManager.getInstance(requireContext()); + } + + override fun onResume() { + super.onResume() + VpnStatus.addStateListener(this) + + val intent = Intent(requireActivity(), OpenVPNService::class.java) + intent.action = OpenVPNService.START_SERVICE + requireActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE) + + /* Check the default VPN */ + val alwaysOnVPN: VpnProfile? = ProfileManager.getAlwaysOnVPN(requireContext()) + if (alwaysOnVPN == null) { + vpnstatus.text = "Default VPN is not configured." + } + } + + override fun onPause() { + super.onPause() + VpnStatus.removeStateListener(this) + + requireActivity().unbindService(mConnection) + } + + private fun checkForNotificationPermission(v: View) { + val permissionView = v.findViewById<View>(R.id.notification_permission) + val permissionGranted = + requireActivity().checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED + permissionView.setVisibility(if (permissionGranted) View.GONE else View.VISIBLE) + permissionView.setOnClickListener({ view: View? -> + mPermReceiver?.launch( + Manifest.permission.POST_NOTIFICATIONS + ) + }) + } + + override fun updateState( + state: String?, + logmessage: String?, + localizedResId: Int, + level: ConnectionStatus?, + Intent: Intent? + ) { + val cleanLogMessage = VpnStatus.getLastCleanLogMessage(activity) + + requireActivity().runOnUiThread { + vpnstatus.setText(localizedResId) + val connected = level == ConnectionStatus.LEVEL_CONNECTED; + vpntoggle.isChecked = connected + } + } + + override fun setConnectedVPN(uuid: String?) { + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + view = inflater.inflate(R.layout.minimalui, container, false) + vpntoggle = view.findViewById(R.id.vpntoggle) + vpnstatus = view.findViewById(R.id.vpnstatus) + + vpntoggle.setOnClickListener { view -> + toggleSwitchPressed(view as CompoundButton) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + checkForNotificationPermission(view) + return view + } + + fun toggleSwitchPressed(view: CompoundButton) { + + val alwaysOnVPN = ProfileManager.getAlwaysOnVPN(requireContext()) + if (alwaysOnVPN == null) { + Toast.makeText( + requireContext(), + R.string.cannot_start_vpn_not_configured, + Toast.LENGTH_SHORT + ).show(); + view.setChecked(false) + return + } + val intent = Intent(requireContext(), LaunchVPN::class.java) + intent.putExtra(LaunchVPN.EXTRA_KEY, alwaysOnVPN.uuidString) + intent.putExtra(OpenVPNService.EXTRA_START_REASON, "VPN started from homescreen.") + intent.action = Intent.ACTION_MAIN + startActivity(intent) + } + +}
\ No newline at end of file |
