1+ package com.cubanopensource.todo
2+
3+ import android.app.Activity
4+ import android.app.Service
5+ import android.content.Context
6+ import android.content.Intent
7+ import android.content.SharedPreferences
8+ import android.graphics.Color
9+ import android.graphics.PixelFormat
10+ import android.net.TrafficStats
11+ import android.os.Build
12+ import android.os.Handler
13+ import android.os.IBinder
14+ import android.provider.Settings
15+ import android.util.DisplayMetrics
16+ import android.view.*
17+ import android.widget.RelativeLayout
18+ import android.widget.TextView
19+
20+
21+ class FloatingWindow : Service () {
22+
23+ private var mLastRxBytes: Long = 0
24+ private var mLastTxBytes: Long = 0
25+ private var mLastTime: Long = 0
26+
27+ private val mHandler = Handler ()
28+
29+ private lateinit var tv: TextView
30+
31+ lateinit var preferences: SharedPreferences
32+
33+ private val mHandlerRunnable = object : Runnable {
34+ override fun run () {
35+ val currentRxBytes = TrafficStats .getTotalRxBytes()
36+ val currentTxBytes = TrafficStats .getTotalTxBytes()
37+ val usedRxBytes = currentRxBytes - mLastRxBytes
38+ val usedTxBytes = currentTxBytes - mLastTxBytes
39+ val currentTime = System .currentTimeMillis()
40+ val usedTime = currentTime - mLastTime
41+
42+ mLastRxBytes = currentRxBytes
43+ mLastTxBytes = currentTxBytes
44+ mLastTime = currentTime
45+
46+ tv.text = calcSpeed(usedTime, usedRxBytes, usedTxBytes)
47+
48+ mHandler.postDelayed(this , 1000 )
49+ }
50+ }
51+
52+ fun calcSpeed (timeTaken : Long , downBytes : Long , upBytes : Long ): String {
53+ var downSpeed: Long = 0
54+ var upSpeed: Long = 0
55+
56+ if (timeTaken > 0 ) {
57+ downSpeed = downBytes * 1000 / timeTaken
58+ upSpeed = upBytes * 1000 / timeTaken
59+ }
60+
61+ val mDownSpeed = downSpeed
62+ val mUpSpeed = upSpeed
63+
64+ val down = setSpeed(mDownSpeed)
65+ val up = setSpeed(mUpSpeed)
66+
67+ return " ↑$up ↓$down "
68+ }
69+
70+ private fun setSpeed (speed : Long ): String {
71+ if (speed < 1000000 ) {
72+ return " %.1f KB" .format((speed / 1000.0 ))
73+ } else if (speed >= 1000000 ) {
74+ if (speed < 10000000 ) {
75+ return " %.1f MB" .format((speed / 1000000.0 ))
76+ } else if (speed < 100000000 ) {
77+ return " %.1f MB" .format((speed / 1000000.0 ))
78+ } else {
79+ return " +99 MB"
80+ }
81+ } else {
82+ return " -"
83+ }
84+ }
85+
86+ override fun onCreate () {
87+ super .onCreate()
88+
89+ mLastRxBytes = TrafficStats .getTotalRxBytes()
90+ mLastTxBytes = TrafficStats .getTotalTxBytes()
91+ mLastTime = System .currentTimeMillis()
92+
93+ showFloatWidget()
94+ }
95+
96+ private fun showFloatWidget () {
97+ if (getDrawPermissionState()) {
98+ preferences = applicationContext.getSharedPreferences(" ${packageName} _preferences" , Activity .MODE_PRIVATE )
99+
100+ val displayMetrics = DisplayMetrics ()
101+ (getSystemService(Context .WINDOW_SERVICE ) as WindowManager ).defaultDisplay.getMetrics(displayMetrics)
102+ val height = displayMetrics.heightPixels
103+ val width = displayMetrics.widthPixels
104+
105+ // Close traffic stats widget
106+ val closeView = LayoutInflater .from(this ).inflate(R .layout.close_float_widget, null )
107+
108+ val closeWM = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
109+
110+ val parameters_close: WindowManager .LayoutParams
111+
112+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O )
113+ parameters_close = WindowManager .LayoutParams (
114+ width,
115+ WindowManager .LayoutParams .WRAP_CONTENT ,
116+ WindowManager .LayoutParams .TYPE_APPLICATION_OVERLAY ,
117+ WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE ,
118+ PixelFormat .TRANSLUCENT
119+ )
120+ else
121+ parameters_close = WindowManager .LayoutParams (
122+ width,
123+ WindowManager .LayoutParams .WRAP_CONTENT ,
124+ WindowManager .LayoutParams .TYPE_PHONE ,
125+ WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE ,
126+ PixelFormat .TRANSLUCENT
127+ )
128+
129+ parameters_close.gravity = Gravity .BOTTOM
130+
131+ // Traffic Stats widget
132+ val widgetView = LayoutInflater .from(this ).inflate(R .layout.float_window, null ) as RelativeLayout
133+
134+ /*
135+ <TextView
136+ android:padding="2dp"
137+ android:id="@+id/traffic_text"
138+ android:layout_width="wrap_content"
139+ android:layout_height="wrap_content"
140+ android:layout_centerInParent="true"
141+ android:text="up 0 | down 0"
142+ android:textColor="@android:color/white"
143+ android:textStyle="bold" />
144+ */
145+ tv = TextView (this )
146+ tv.setPadding(2 , 4 , 2 , 4 )
147+ tv.setTextColor(Color .WHITE )
148+ tv.textSize = 11f
149+ tv.text = " up 0 | down 0"
150+
151+ widgetView.addView(tv)
152+
153+ val wm = getSystemService(Context .WINDOW_SERVICE ) as WindowManager
154+
155+ val parameters: WindowManager .LayoutParams
156+
157+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O )
158+ parameters = WindowManager .LayoutParams (
159+ WindowManager .LayoutParams .WRAP_CONTENT ,
160+ WindowManager .LayoutParams .WRAP_CONTENT ,
161+ WindowManager .LayoutParams .TYPE_APPLICATION_OVERLAY ,
162+ WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE ,
163+ PixelFormat .TRANSLUCENT
164+ )
165+ else
166+ parameters = WindowManager .LayoutParams (
167+ WindowManager .LayoutParams .WRAP_CONTENT ,
168+ WindowManager .LayoutParams .WRAP_CONTENT ,
169+ WindowManager .LayoutParams .TYPE_PHONE ,
170+ WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE ,
171+ PixelFormat .TRANSLUCENT
172+ )
173+
174+
175+ val fx = preferences.getInt(" float_widget_x" , 0 )
176+ val fy = preferences.getInt(" float_widget_y" , 0 )
177+
178+ println (fx)
179+ println (fy)
180+
181+ parameters.x = fx
182+ parameters.y = fy
183+ parameters.gravity = Gravity .CENTER
184+ wm.addView(widgetView, parameters)
185+
186+ // Traffic stats on touch handler
187+ widgetView.setOnTouchListener(object : View .OnTouchListener {
188+ private var updatedParameters: WindowManager .LayoutParams = parameters
189+ var x: Int = 0
190+ var y: Int = 0
191+
192+ var touchedX: Float = 0.0f
193+ var touchedY: Float = 0.0f
194+
195+ override fun onTouch (v : View ? , event : MotionEvent ? ): Boolean {
196+ when (event?.action) {
197+ MotionEvent .ACTION_DOWN -> {
198+ x = updatedParameters.x
199+ y = updatedParameters.y
200+
201+ touchedX = event.rawX
202+ touchedY = event.rawY
203+
204+ closeWM.addView(closeView, parameters_close)
205+ }
206+ MotionEvent .ACTION_MOVE -> {
207+ updatedParameters.x = (x + (event.rawX - touchedX)).toInt()
208+ updatedParameters.y = (y + (event.rawY - touchedY)).toInt()
209+
210+ wm.updateViewLayout(widgetView, updatedParameters)
211+ }
212+
213+ MotionEvent .ACTION_UP -> {
214+ if (event.rawY.toInt() >= height - 100 ) {
215+ wm.removeView(widgetView)
216+ } else {
217+ with (preferences.edit()) {
218+ putInt(" float_widget_x" , updatedParameters.x)
219+ putInt(" float_widget_y" , updatedParameters.y)
220+
221+ apply ()
222+ }
223+ }
224+
225+ closeWM.removeView(closeView)
226+ }
227+ }
228+
229+ return false
230+ }
231+ })
232+
233+ mHandler.post(mHandlerRunnable)
234+ }
235+ }
236+
237+ private fun getDrawPermissionState (): Boolean {
238+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M )
239+ return (Settings .canDrawOverlays(this ))
240+
241+ return true
242+ }
243+
244+ override fun onBind (intent : Intent ? ): IBinder ? {
245+ return null
246+ }
247+ }
0 commit comments