MainFragment.kt 8.89 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
63

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onResume() {
        super.onResume()
Daniel Wolf's avatar
Daniel Wolf committed
64
        proxyRunning = requireContext().isServiceRunning(DnsVpnService::class.java)
65
        updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
66
67
68
69
    }

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

    override fun onDestroy() {
        super.onDestroy()
159
        if (vpnStateReceiver != null) requireContext().unregisterLocalReceiver(vpnStateReceiver!!)
Daniel Wolf's avatar
Daniel Wolf committed
160
161
    }

Daniel Wolf's avatar
Daniel Wolf committed
162
    private fun startVpn() {
163
164
165
        val prepare = VpnService.prepare(requireContext()).apply {
            this?.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
        }
Daniel Wolf's avatar
Daniel Wolf committed
166

167
        if (prepare == null) {
Daniel Wolf's avatar
Daniel Wolf committed
168
169
170
171
172
173
174
            requireContext().startService(Intent(requireContext(), DnsVpnService::class.java))
        } else {
            startActivityForResult(prepare, vpnRequestCode)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
175
        if (requestCode == vpnRequestCode && resultCode == Activity.RESULT_OK) {
Daniel Wolf's avatar
Daniel Wolf committed
176
            startVpn()
177
        } else if (requestCode == vpnRequestCode) {
Daniel Wolf's avatar
Daniel Wolf committed
178
            proxyStarting = false
179
            updateVpnIndicators()
Daniel Wolf's avatar
Daniel Wolf committed
180
181
182
        }
    }

183
    private fun updateVpnIndicators() {
184
185
186
187
188
189
190
191
        val privateDnsActive = if(Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            false
        } else {
            (context!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).let {
                if(it.activeNetwork == null) false
                else it.getLinkProperties(it.activeNetwork)?.isPrivateDnsActive ?: false
            }
        }
Daniel Wolf's avatar
Daniel Wolf committed
192
193
        when {
            proxyRunning -> {
194
                privateDnsInfo.visibility = View.INVISIBLE
Daniel Wolf's avatar
Daniel Wolf committed
195
196
197
198
199
                statusImage.setImageResource(R.drawable.ic_lock)
                statusImage.clearAnimation()
                startButton.setText(R.string.all_stop)
            }
            proxyStarting -> {
200
                privateDnsInfo.visibility = View.INVISIBLE
Daniel Wolf's avatar
Daniel Wolf committed
201
                startButton.setText(R.string.all_stop)
202
                statusImage.setImageResource(R.drawable.ic_lock_half_open)
Daniel Wolf's avatar
Daniel Wolf committed
203
204
205
            }
            else -> {
                startButton.setText(R.string.all_start)
206
207
208
209
210
211
212
213
214
                if(privateDnsActive) {
                    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
215
216
217
            }
        }
    }
218

219
    private fun updatePrivacyPolicyLink(serverInfo: DnsServerInformation<*>) {
220
        activity?.runOnUiThread {
221
            val url = serverInfo.specification.privacyPolicyURL
222
223
224
225
226
227
228
            val text = view?.findViewById<TextView>(R.id.privacyStatementText)
            if (url != null && text != null) {
                text.text = getString(R.string.main_dnssurveillance_privacystatement, serverInfo.name)
                text.tag = url
                text.visibility = View.VISIBLE
            } else if(text != null){
                text.visibility = View.GONE
229
            }
230
231
        }
    }
Daniel Wolf's avatar
Daniel Wolf committed
232
}