MainFragment.kt 9.73 KB
Newer Older
Daniel Wolf's avatar
Daniel Wolf committed
1
2
3
package com.frostnerd.smokescreen.fragment

import android.app.Activity
4
import android.content.ActivityNotFoundException
Daniel Wolf's avatar
Daniel Wolf committed
5
import android.content.BroadcastReceiver
6
import android.content.Context
Daniel Wolf's avatar
Daniel Wolf committed
7
import android.content.Intent
8
import android.net.ConnectivityManager
9
import android.net.Uri
Daniel Wolf's avatar
Daniel Wolf committed
10
import android.net.VpnService
11
import android.os.Build
Daniel Wolf's avatar
Daniel Wolf committed
12
13
import android.os.Bundle
import android.view.LayoutInflater
14
import android.view.MotionEvent
Daniel Wolf's avatar
Daniel Wolf committed
15
16
import android.view.View
import android.view.ViewGroup
17
import android.widget.TextView
18
import android.widget.Toast
Daniel Wolf's avatar
Daniel Wolf committed
19
import androidx.appcompat.app.AppCompatActivity
Daniel Wolf's avatar
Daniel Wolf committed
20
import androidx.fragment.app.Fragment
Daniel Wolf's avatar
Daniel Wolf committed
21
import com.frostnerd.dnstunnelproxy.DnsServerInformation
Daniel Wolf's avatar
Daniel Wolf committed
22
import com.frostnerd.general.service.isServiceRunning
23
import com.frostnerd.smokescreen.*
24
import com.frostnerd.smokescreen.activity.SpeedTestActivity
25
import com.frostnerd.smokescreen.dialog.ServerChoosalDialog
Daniel Wolf's avatar
Daniel Wolf committed
26
27
import com.frostnerd.smokescreen.service.Command
import com.frostnerd.smokescreen.service.DnsVpnService
Daniel Wolf's avatar
Daniel Wolf committed
28
import kotlinx.android.synthetic.main.fragment_main.*
29
30
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
Daniel Wolf's avatar
Daniel Wolf committed
31
import java.net.URL
Daniel Wolf's avatar
Daniel Wolf committed
32
33


Daniel Wolf's avatar
Daniel Wolf committed
34
35
/*
 * Copyright (C) 2019 Daniel Wolf (Ch4t4r)
Daniel Wolf's avatar
Daniel Wolf committed
36
 *
Daniel Wolf's avatar
Daniel Wolf committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 * This program 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.

 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You can contact the developer at daniel.wolf@frostnerd.com.
Daniel Wolf's avatar
Daniel Wolf committed
51
 */
52
53
54
class MainFragment : Fragment() {
    private val vpnRequestCode: Int = 1
    private var proxyRunning: Boolean = false
Daniel Wolf's avatar
Daniel Wolf committed
55
    private var proxyStarting = false
56
    private var vpnStateReceiver: BroadcastReceiver? = null
Daniel Wolf's avatar
Daniel Wolf committed
57

58
59
60
61
62
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
Daniel Wolf's avatar
Daniel Wolf committed
63
64
65
66
67
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onResume() {
        super.onResume()
Daniel Wolf's avatar
Daniel Wolf committed
68
        proxyRunning = requireContext().isServiceRunning(DnsVpnService::class.java)
69
        updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
70
71
72
73
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        startButton.setOnClickListener {
74
            if (proxyRunning) {
Daniel Wolf's avatar
Daniel Wolf committed
75
                DnsVpnService.sendCommand(requireContext(), Command.STOP)
76
                proxyStarting = false
Daniel Wolf's avatar
Daniel Wolf committed
77
78
79
80
81
                proxyRunning = false
            } else {
                startVpn()
                proxyStarting = true
            }
82
            updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
83
        }
84
        startButton.setOnTouchListener { _, event ->
85
            if (proxyRunning || proxyStarting) {
86
87
                false
            } else {
88
89
90
                if (event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0) {
                    if (event.action == MotionEvent.ACTION_UP) {
                        if (VpnService.prepare(context!!) != null) {
91
92
93
94
95
96
97
98
99
100
101
102
103
104
                            showInfoTextDialog(context!!,
                                getString(R.string.dialog_overlaydetected_title),
                                getString(R.string.dialog_overlaydetected_message),
                                positiveButton = null,
                                negativeButton = null,
                                neutralButton = getString(android.R.string.ok) to { dialog, _ ->
                                    dialog.dismiss()
                                    startVpn()
                                    proxyStarting = true
                                }
                            )
                            true
                        } else false
                    } else false
105
                } else false
106
107
            }
        }
108
109
110
        speedTest.setOnClickListener {
            startActivity(Intent(context!!, SpeedTestActivity::class.java))
        }
111
112
113
114
115
116
117
118
        vpnStateReceiver = requireContext().registerLocalReceiver(
            listOf(
                DnsVpnService.BROADCAST_VPN_ACTIVE,
                DnsVpnService.BROADCAST_VPN_INACTIVE
            )
        ) {
            if (it != null && it.action != null) {
                when (it.action) {
Daniel Wolf's avatar
Daniel Wolf committed
119
120
121
122
123
124
125
126
127
                    DnsVpnService.BROADCAST_VPN_ACTIVE -> {
                        proxyStarting = false
                        proxyRunning = true
                    }
                    DnsVpnService.BROADCAST_VPN_INACTIVE -> {
                        proxyRunning = false
                        proxyStarting = false
                    }
                }
128
                updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
129
130
131
            }
        }
        serverButton.setOnClickListener {
132
            ServerChoosalDialog(requireActivity() as AppCompatActivity) { config ->
Daniel Wolf's avatar
Daniel Wolf committed
133
                updatePrivacyPolicyLink(config)
134
                val prefs = requireContext().getPreferences()
135
                prefs.edit {
Daniel Wolf's avatar
Daniel Wolf committed
136
                    prefs.dnsServerConfig = config
137
                }
138
            }.show()
Daniel Wolf's avatar
Daniel Wolf committed
139
        }
140
        privacyStatementText.setOnClickListener {
141
            if (it.tag != null) {
142
143
144
                val i = Intent(Intent.ACTION_VIEW)
                val url = it.tag as URL
                i.data = Uri.parse(url.toURI().toString())
145
146
                try {
                    startActivity(i)
147
148
149
150
151
152
153
                } catch (e: ActivityNotFoundException) {
                    Toast.makeText(
                        context!!,
                        R.string.error_no_webbrowser_installed,
                        Toast.LENGTH_LONG
                    ).show()
                }
154
            }
155
        }
156
        GlobalScope.launch {
157
158
159
            val context = context
            if (isAdded && !isDetached && context != null) {
                val config = context.getPreferences().dnsServerConfig
160
                updatePrivacyPolicyLink(config)
161
162
            }
        }
163
        updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
164
165
166
167
    }

    override fun onDestroy() {
        super.onDestroy()
168
        if (vpnStateReceiver != null) requireContext().unregisterLocalReceiver(vpnStateReceiver!!)
Daniel Wolf's avatar
Daniel Wolf committed
169
170
    }

Daniel Wolf's avatar
Daniel Wolf committed
171
    private fun startVpn() {
172
173
174
        val prepare = VpnService.prepare(requireContext()).apply {
            this?.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
        }
Daniel Wolf's avatar
Daniel Wolf committed
175

176
        if (prepare == null) {
Daniel Wolf's avatar
Daniel Wolf committed
177
            requireContext().startService(Intent(requireContext(), DnsVpnService::class.java))
178
            getPreferences().vpnInformationShown = true
Daniel Wolf's avatar
Daniel Wolf committed
179
        } else {
180
            if (getPreferences().vpnInformationShown) {
181
                startActivityForResult(prepare, vpnRequestCode)
182
183
184
185
186
187
188
            } else {
                showInfoTextDialog(requireContext(),
                    getString(R.string.dialog_vpninformation_title),
                    getString(R.string.dialog_vpninformation_message),
                    neutralButton = getString(android.R.string.ok) to { dialog, _ ->
                        startActivityForResult(prepare, vpnRequestCode)
                        dialog.dismiss()
189
190
191
                    }, withDialog = {
                        setCancelable(false)
                    })
192
193
                getPreferences().vpnInformationShown = true
            }
Daniel Wolf's avatar
Daniel Wolf committed
194
195
196
197
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
198
        if (requestCode == vpnRequestCode && resultCode == Activity.RESULT_OK) {
Daniel Wolf's avatar
Daniel Wolf committed
199
            startVpn()
200
        } else if (requestCode == vpnRequestCode) {
Daniel Wolf's avatar
Daniel Wolf committed
201
            proxyStarting = false
202
            updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
203
204
205
        }
    }

206
    private fun updateVpnIndicators() {
207
        val privateDnsActive = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
208
209
210
            false
        } else {
            (context!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).let {
211
                if (it.activeNetwork == null) false
212
213
214
                else it.getLinkProperties(it.activeNetwork)?.isPrivateDnsActive ?: false
            }
        }
Daniel Wolf's avatar
Daniel Wolf committed
215
216
        when {
            proxyRunning -> {
217
                privateDnsInfo.visibility = View.INVISIBLE
Daniel Wolf's avatar
Daniel Wolf committed
218
219
220
221
222
                statusImage.setImageResource(R.drawable.ic_lock)
                statusImage.clearAnimation()
                startButton.setText(R.string.all_stop)
            }
            proxyStarting -> {
223
                privateDnsInfo.visibility = View.INVISIBLE
Daniel Wolf's avatar
Daniel Wolf committed
224
                startButton.setText(R.string.all_stop)
225
                statusImage.setImageResource(R.drawable.ic_lock_half_open)
Daniel Wolf's avatar
Daniel Wolf committed
226
227
228
            }
            else -> {
                startButton.setText(R.string.all_start)
229
                if (privateDnsActive) {
230
231
232
233
234
235
236
237
                    statusImage.setImageResource(R.drawable.ic_lock)
                    statusImage.clearAnimation()
                    privateDnsInfo.visibility = View.VISIBLE
                } else {
                    statusImage.setImageResource(R.drawable.ic_lock_open)
                    statusImage.clearAnimation()
                    privateDnsInfo.visibility = View.INVISIBLE
                }
Daniel Wolf's avatar
Daniel Wolf committed
238
239
240
            }
        }
    }
241

242
    private fun updatePrivacyPolicyLink(serverInfo: DnsServerInformation<*>) {
243
        activity?.runOnUiThread {
244
            val url = serverInfo.specification.privacyPolicyURL
245
246
            val text = view?.findViewById<TextView>(R.id.privacyStatementText)
            if (url != null && text != null) {
247
248
                text.text =
                    getString(R.string.main_dnssurveillance_privacystatement, serverInfo.name)
249
250
                text.tag = url
                text.visibility = View.VISIBLE
251
            } else if (text != null) {
252
                text.visibility = View.GONE
253
            }
254
255
        }
    }
Daniel Wolf's avatar
Daniel Wolf committed
256
}