OSDN Git Service

ラップタイムリストをスクロールできるようにする。
[gokigen/JoggingTimer.git] / wear / src / main / java / net / osdn / gokigen / joggingtimer / stopwatch / MainActivity.kt
1 package net.osdn.gokigen.joggingtimer.stopwatch
2
3 import android.content.Intent
4 import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
5 import android.graphics.Color
6 import android.os.Bundle
7 import android.util.Log
8 import android.view.KeyEvent
9 import android.view.View
10 import android.widget.ImageButton
11 import android.widget.ListView
12 import android.widget.TextView
13 import android.widget.Toast
14 import androidx.appcompat.app.AppCompatActivity
15 import androidx.constraintlayout.widget.ConstraintLayout
16 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
17 import androidx.core.widget.NestedScrollView
18 import androidx.wear.ambient.AmbientModeSupport
19 import net.osdn.gokigen.joggingtimer.R
20 import net.osdn.gokigen.joggingtimer.recordlist.ListActivity
21 import net.osdn.gokigen.joggingtimer.stopwatch.MyTimerCounter.ICounterStatusNotify
22 import net.osdn.gokigen.joggingtimer.stopwatch.MyTimerTrigger.ITimeoutReceiver
23 import net.osdn.gokigen.joggingtimer.stopwatch.graphview.LapTimeGraphView
24 import net.osdn.gokigen.joggingtimer.utilities.SelectReferenceViewModeDialog
25 import net.osdn.gokigen.joggingtimer.utilities.SelectReferenceViewModeDialog.SelectReferenceCallback
26 import net.osdn.gokigen.joggingtimer.utilities.TimeStringConvert
27 import java.text.SimpleDateFormat
28 import java.util.Date
29 import java.util.Locale
30
31 /**
32  *
33  *
34  */
35 class MainActivity : AppCompatActivity(), IClickCallback, ITimeoutReceiver, ICounterStatusNotify, AmbientModeSupport.AmbientCallbackProvider, SelectReferenceCallback
36 {
37     private val controller: IWearableActivityControl = WearableActivityController()
38     private val counter = MyTimerCounter()
39     private var isCounterLapTime = true
40     private var isLaptimeView = true
41     private var pendingStart = false
42     private var currentLapCount = 0
43     private var currentReferenceId = 0
44     private var stopTrigger: ITimerStopTrigger? = null
45
46     /**
47      *
48      */
49     override fun onCreate(savedInstanceState: Bundle?)
50     {
51         try
52         {
53             ///////// SHOW SPLASH SCREEN : call before super.onCreate() /////////
54             installSplashScreen()
55         }
56         catch (e: Exception)
57         {
58             e.printStackTrace()
59         }
60         super.onCreate(savedInstanceState)
61         Log.v(TAG, "onCreate()")
62
63         try
64         {
65             setContentView(R.layout.activity_main)
66             controller.setup(this, this, counter)
67         }
68         catch (e: Exception)
69         {
70             e.printStackTrace()
71         }
72
73         try
74         {
75             val ambientController = AmbientModeSupport.attach(this)
76             ambientController.setAutoResumeEnabled(true)
77         }
78         catch (e: Exception)
79         {
80             e.printStackTrace()
81         }
82
83     }
84
85     private fun importReceivedIntent()
86     {
87         try
88         {
89             val thread = Thread {
90                 // 取得したSENDインテントを処理する
91                 IntentSendImporter(this.applicationContext, intent).start()
92                 val title = intent.getStringExtra(Intent.EXTRA_SUBJECT)
93                 runOnUiThread {
94                     Toast.makeText(this, getString(R.string.data_imported) + title, Toast.LENGTH_SHORT).show()
95                     //setResult(RESULT_OK, intent)
96                     finish()
97                 }
98             }
99             thread.start()
100         }
101         catch (e: Exception)
102         {
103             e.printStackTrace()
104         }
105     }
106
107     /**
108      *
109      */
110     override fun onResume()
111     {
112         super.onResume()
113
114         // インテントを取得する
115         val intent = intent
116         val action = intent.action
117         Log.v(TAG, "onResume() : $action")
118         var isStartTimer = false
119
120         if (action != null)
121         {
122             try
123             {
124                 if (Intent.ACTION_SEND == action)
125                 {
126                     val flags = intent.flags
127                     val checkFlags = FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY // + FLAG_ACTIVITY_BROUGHT_TO_FRONT
128                     Log.v(TAG, "INTENT : $intent")
129                     if ((flags.and(checkFlags)) == 0)
130                     {
131                         Log.v(TAG, " IMPORT DATA ")
132                         importReceivedIntent()
133                     }
134                 }
135                 else if (action == "com.google.android.wearable.action.STOPWATCH")
136                 {
137                     isStartTimer = true
138                 }
139                 else if (action == "vnd.google.fitness.TRACK")
140                 {
141                     if (intent.getStringExtra("actionStatus") == "ActiveActionStatus")
142                     {
143                         isStartTimer = true
144                     }
145                 }
146             }
147             catch (e: Exception)
148             {
149                 e.printStackTrace()
150             }
151         }
152
153         isLaptimeView = controller.displayMode
154         currentReferenceId = controller.referenceTimerSelection
155         controller.setupReferenceData()
156         if (isStartTimer)
157         {
158             pendingStart = true
159         }
160     }
161
162     /**
163      *
164      */
165     override fun onPause()
166     {
167         super.onPause()
168         Log.v(TAG, "onPause()")
169     }
170
171     /**
172      *
173      *
174      */
175     public override fun onStart()
176     {
177         super.onStart()
178         Log.v(TAG, "onStart()")
179
180         // データベースのセットアップ
181         counter.setCallback(this)
182         controller.setupDatabase(this, false)
183     }
184
185     /**
186      *
187      *
188      */
189     public override fun onStop()
190     {
191         super.onStop()
192         Log.v(TAG, "onStop()")
193         stopTrigger?.forceStop()
194         controller.exitApplication(this)
195     }
196
197     /**
198      *
199      */
200     private fun updateTimerLabel()
201     {
202         val timerCounter: ITimerCounter = counter
203         val bgColor: Int
204         val insetLayout = findViewById<NestedScrollView>(R.id.top_layout)
205         val layout = findViewById<ConstraintLayout>(R.id.main_layout)
206         val btn1 = findViewById<ImageButton>(R.id.btn1)
207         val btn2 = findViewById<ImageButton>(R.id.btn2)
208         val btn3 = findViewById<ImageButton>(R.id.btn3)
209         updateMainSubCounter()
210         if (timerCounter.isStarted)
211         {
212             bgColor = Color.BLACK
213             insetLayout.setBackgroundColor(bgColor)
214             insetLayout.invalidate()
215             layout.setBackgroundColor(bgColor)
216             layout.invalidate()
217             btn1.setImageResource(R.drawable.ic_flag_black_24dp)
218             btn1.setBackgroundColor(bgColor)
219
220             // チャタリング防止(ラップタイムとして、3秒以内は記録しないようボタンを消しておく)
221             val currentElapsedTime = timerCounter.currentElapsedTime
222             btn1.visibility = if (currentElapsedTime > 3000) View.VISIBLE else View.INVISIBLE
223             btn1.invalidate()
224             btn2.setImageResource(R.drawable.ic_stop_black_24dp)
225             btn2.setBackgroundColor(bgColor)
226             btn2.visibility = View.VISIBLE
227             btn2.invalidate()
228             btn3.setImageResource(R.drawable.ic_block_black_24dp)
229             btn3.setBackgroundColor(bgColor)
230             btn3.visibility = View.INVISIBLE
231             btn3.invalidate()
232         }
233         else if (timerCounter.isReset)
234         {
235             bgColor = Color.BLACK
236             insetLayout.setBackgroundColor(bgColor)
237             insetLayout.invalidate()
238             layout.setBackgroundColor(bgColor)
239             layout.invalidate()
240             btn1.setImageResource(R.drawable.ic_play_arrow_black_24dp)
241             btn1.setBackgroundColor(bgColor)
242             btn1.visibility = View.VISIBLE
243             btn1.invalidate()
244             btn2.setImageResource(R.drawable.ic_format_list_bulleted_black_24dp)
245             btn2.setBackgroundColor(bgColor)
246             btn2.visibility = View.VISIBLE
247             btn2.invalidate()
248             btn3.setImageResource(R.drawable.ic_refresh_black_24dp)
249             btn3.setBackgroundColor(bgColor)
250             btn3.visibility = View.INVISIBLE
251             btn3.invalidate()
252         }
253         else
254         {
255             bgColor = Color.BLACK
256             insetLayout.setBackgroundColor(bgColor)
257             insetLayout.invalidate()
258             layout.setBackgroundColor(bgColor)
259             layout.invalidate()
260             btn1.setImageResource(R.drawable.ic_play_arrow_black_24dp)
261             btn1.visibility = View.VISIBLE
262             btn1.setBackgroundColor(bgColor)
263             btn1.invalidate()
264             btn2.setImageResource(R.drawable.ic_format_list_bulleted_black_24dp)
265             btn2.visibility = View.VISIBLE
266             btn2.setBackgroundColor(bgColor)
267             btn2.invalidate()
268             btn3.setImageResource(R.drawable.ic_refresh_black_24dp)
269             btn3.visibility = View.VISIBLE
270             btn3.setBackgroundColor(bgColor)
271             btn3.invalidate()
272         }
273         updateElapsedTimes()
274     }
275
276     override fun clickedCounter()
277     {
278         // 表示順番を変える
279         isCounterLapTime = !isCounterLapTime
280     }
281
282     /**
283      *
284      */
285     override fun clickedBtn1()
286     {
287         startTimer()
288     }
289
290     /**
291      *
292      *
293      */
294     private fun startTimer()
295     {
296         try
297         {
298             val timerCounter: ITimerCounter = counter
299             val graphView = findViewById<LapTimeGraphView>(R.id.graph_area)
300             if (timerCounter.isStarted)
301             {
302                 Log.v(TAG, "startTimer() LAP TIME")
303                 val currentElapsedTime = timerCounter.currentElapsedTime
304                 if (currentElapsedTime > 3000) // チャタリング防止(ラップタイムとして、3秒以内は記録しないようにする)
305                 {
306                     currentLapCount++
307                     val lapTime = timerCounter.timeStamp()
308                     val refLapTime = timerCounter.getReferenceLapTime(currentLapCount)
309                     val diffTime = if (refLapTime == 0L) 0 else currentElapsedTime - refLapTime
310                     controller.vibrate(50)
311                     controller.dataEntry.appendTimeData(lapTime)
312                     controller.addTimeStamp(currentLapCount.toLong(), currentElapsedTime, diffTime)
313                     graphView?.notifyLapTime()
314                 }
315             } else {
316                 Log.v(TAG, "startTimer() START")
317                 controller.clearTimeStamp()
318                 timerCounter.start()
319                 val trigger = MyTimerTrigger(this, 100, timerCounter)
320                 trigger.startTimer()
321                 currentLapCount = 0
322                 stopTrigger = trigger
323                 controller.timerStarted(true)
324                 controller.vibrate(120)
325                 val date = Date()
326                 val sdf1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
327                 val title = sdf1.format(date)
328                 val startTime = timerCounter.startTime
329                 controller.dataEntry.createIndex(title, startTime)
330                 graphView?.notifyStarted(startTime)
331             }
332             updateTimerLabel()
333         }
334         catch (e: Exception)
335         {
336             e.printStackTrace()
337         }
338     }
339
340     /**
341      *
342      *
343      */
344     private fun stopTimer(): Boolean
345     {
346         var ret = false
347         try
348         {
349             val timerCounter: ITimerCounter = counter
350             if (timerCounter.isStarted)
351             {
352                 timerCounter.stop()
353                 controller.timerStarted(false)
354                 controller.vibrate(120)
355                 controller.dataEntry.finishTimeData(timerCounter.startTime, timerCounter.stopTime)
356                 val lapCount = currentLapCount + 1
357                 val currentElapsedTime = timerCounter.lastElapsedTime - timerCounter.getElapsedTime(currentLapCount)
358                 val refLapTime = timerCounter.getReferenceLapTime(lapCount)
359                 val diffTime = if (refLapTime == 0L) 0 else currentElapsedTime - refLapTime
360                 controller.addTimeStamp(lapCount.toLong(), currentElapsedTime, diffTime)
361                 ret = true
362                 val graphView = findViewById<LapTimeGraphView>(R.id.graph_area)
363                 graphView?.notifyStopped()
364             }
365             updateTimerLabel()
366         }
367         catch (e: Exception)
368         {
369             e.printStackTrace()
370         }
371         return ret
372     }
373
374     /**
375      *
376      */
377     override fun clickedBtn2()
378     {
379         if (!(counter as ITimerCounter).isStarted)
380         {
381             // 停止中は、記録一覧を呼び出す
382             launchListActivity()
383
384             // ぶるぶる
385             controller.vibrate(35)
386         }
387         updateTimerLabel()
388     }
389
390     /**
391      *
392      */
393     override fun clickedBtn3()
394     {
395         val timerCounter: ITimerCounter = counter
396         if (!timerCounter.isStarted)
397         {
398             timerCounter.reset()
399             controller.vibrate(50)
400             controller.clearTimeStamp()
401             currentLapCount = 0
402             val graphView = findViewById<LapTimeGraphView>(R.id.graph_area)
403             graphView?.notifyReset()
404         }
405         updateTimerLabel()
406     }
407
408     override fun clickedArea()
409     {
410         Log.v(TAG, "clickedArea()")
411     }
412
413     override fun pushedBtn1(): Boolean
414     {
415         return false
416     }
417
418     override fun pushedBtn2(): Boolean
419     {
420         return stopTimer()
421     }
422
423     override fun pushedBtn3(): Boolean
424     {
425         return false
426     }
427
428     override fun pushedArea(): Boolean
429     {
430         try
431         {
432             // 基準値の設定ダイアログを表示する
433             val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(
434                 applicationContext
435             )
436             val viewMode = preferences.getBoolean(
437                 SelectReferenceViewModeDialog.PREF_KEY_DISPLAY_LAPGRAPHIC,
438                 false
439             )
440             val selectionId = preferences.getInt(
441                 SelectReferenceViewModeDialog.PREF_KEY_REFERENCE_TIME_SELECTION,
442                 0
443             )
444             val callback: SelectReferenceCallback = this
445             runOnUiThread {
446                 try {
447                     // 基準値&表示モード設定ダイアログを表示する
448                     val dialog =
449                         SelectReferenceViewModeDialog.newInstance(viewMode, selectionId, callback)
450                     val manager = supportFragmentManager
451                     dialog.show(manager, "dialog")
452                 } catch (e: Exception) {
453                     e.printStackTrace()
454                 }
455             }
456         } catch (e: Exception) {
457             e.printStackTrace()
458         }
459         return true
460     }
461
462     /**
463      *
464      *
465      */
466     override fun timeout()
467     {
468         try
469         {
470             runOnUiThread { updateTimerLabel() }
471         }
472         catch (e: Exception)
473         {
474             e.printStackTrace()
475         }
476     }
477
478     /**
479      *
480      *
481      */
482     private fun updateMainSubCounter()
483     {
484         val main = findViewById<TextView>(R.id.main_counter)
485         val sub = findViewById<TextView>(R.id.sub_counter1)
486         val timerCounter: ITimerCounter = counter
487         val time1 = timerCounter.pastTime
488         val str1 = TimeStringConvert.getTimeString(time1)
489         var str2: CharSequence = ""
490         if (timerCounter.isStarted)
491         {
492             val time2 = timerCounter.currentElapsedTime
493             val lapCount = timerCounter.elapsedCount
494             if (time2 >= 100 &&(lapCount > 1))
495             {
496                 str2 = "[" + lapCount + "] " + TimeStringConvert.getTimeString(time2)
497             }
498         }
499         if (str2.isNotEmpty() && this.isCounterLapTime)
500         {
501             // ラップタイムの方を大きく表示する
502             main.text = str2
503             sub.text = str1
504         }
505         else
506         {
507             main.text = str1
508             sub.text = str2
509         }
510         main.invalidate()
511         sub.invalidate()
512     }
513
514     /**
515      *
516      *
517      */
518     private fun updateElapsedTimes()
519     {
520         if (isLaptimeView)
521         {
522             updateElapsedTimesGraph()
523         }
524         else
525         {
526             updateElapsedTimesText()
527         }
528     }
529
530     /**
531      *
532      *
533      */
534     private fun updateElapsedTimesGraph()
535     {
536         val view = findViewById<LapTimeGraphView>(R.id.graph_area)
537         view.invalidate()
538     }
539
540     /**
541      *
542      *
543      */
544     private fun updateElapsedTimesText()
545     {
546         // Log.v(TAG, "updateElapsedTimesText()");
547     }
548
549     /**
550      * Launch ListActivity
551      *
552      */
553     private fun launchListActivity()
554     {
555         Log.v(TAG, "launchListActivity()")
556         try
557         {
558             val intent = Intent(this, ListActivity::class.java)
559             startActivity(intent)
560         }
561         catch (e: Exception)
562         {
563             e.printStackTrace()
564         }
565     }
566
567     /**
568      *
569      *
570      */
571     override fun onUserLeaveHint()
572     {
573         Log.v(TAG, "onUserLeaveHint() ")
574         // ハードキー(ホームボタン)が押されたとき、これがひろえるが...
575     }
576
577     /**
578      *
579      *
580      */
581     override fun dispatchKeyEvent(event: KeyEvent): Boolean
582     {
583         Log.v(TAG, "dispatchKeyEvent() : " + event.action + " (" + event.keyCode + ")")
584         return super.dispatchKeyEvent(event)
585     }
586
587     /**
588      *
589      *
590      */
591     override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean
592     {
593         Log.v(TAG, "onKeyDown() : " + event.action + " (" + event.keyCode + ")" + keyCode)
594         if (event.repeatCount == 0)
595         {
596             when (keyCode) {
597                 KeyEvent.KEYCODE_STEM_1 -> {
598                     startTimer()
599                     return true
600                 }
601                 KeyEvent.KEYCODE_STEM_2 -> {
602                     startTimer()
603                     return true
604                 }
605                 KeyEvent.KEYCODE_STEM_3 -> {
606                     startTimer()
607                     return true
608                 }
609             }
610         }
611         return super.onKeyDown(keyCode, event)
612     }
613
614     /**
615      *
616      *
617      */
618     override fun counterStatusChanged(forceStartTimer: Boolean)
619     {
620         if (forceStartTimer)
621         {
622             try
623             {
624                 val graphView = findViewById<LapTimeGraphView>(R.id.graph_area)
625                 val trigger = MyTimerTrigger(this, 100, counter)
626                 trigger.startTimer()
627                 stopTrigger = trigger
628                 graphView?.notifyLapTime()
629             }
630             catch (e: Exception)
631             {
632                 e.printStackTrace()
633             }
634         }
635         runOnUiThread {
636             // 自動スタート時の処理。。
637             if (pendingStart) {
638                 startTimer()
639                 pendingStart = false
640             }
641
642             // ラップタイム表示状態の更新
643             reloadLapTimeList(forceStartTimer)
644
645             // 表示ビューの切り替え
646             changeGraphicView(isLaptimeView)
647
648             // 表示のボタン状態を変更
649             updateTimerLabel()
650         }
651     }
652
653     /**
654      *
655      *
656      */
657     private fun reloadLapTimeList(forceStartTimer: Boolean)
658     {
659         if (!forceStartTimer)
660         {
661             return
662         }
663
664         // Adapter と TimerCounterの整合性を確認
665         try
666         {
667             val lapTimeList: List<Long> = (counter as ITimerCounter).lapTimeList
668             val lapCount = lapTimeList.size
669             val listCount = controller.lapTimeCount
670             if (lapCount != listCount)
671             {
672                 Log.v(TAG, "LAP COUNT IS MISMATCH!!! lap:$lapCount vs list:$listCount")
673                 var index = 0
674                 controller.clearTimeStamp()
675                 var prevTime = lapTimeList[0]
676                 for (lapTime in lapTimeList)
677                 {
678                     index++
679                     if (prevTime != lapTime)
680                     {
681                         val refLapTime = counter.getReferenceLapTime(index - 1)
682                         val curLapTime = lapTime - prevTime
683                         val calcRefLapTime = if (refLapTime == 0L) 0 else curLapTime - refLapTime
684                         controller.addTimeStamp((index - 1).toLong(), curLapTime, calcRefLapTime)
685                     }
686                     prevTime = lapTime
687                 }
688                 currentLapCount = lapCount - 1
689             }
690         }
691         catch (e: Exception)
692         {
693             e.printStackTrace()
694         }
695     }
696
697     /**
698      *
699      *
700      */
701     private fun changeGraphicView(isGraphics: Boolean)
702     {
703         try
704         {
705             val graphView = findViewById<LapTimeGraphView>(R.id.graph_area)
706             val listView = findViewById<ListView>(R.id.laptime_list_area)
707             if (isGraphics)
708             {
709                 graphView.setITimerCounter(counter)
710                 graphView.visibility = View.VISIBLE
711                 listView.visibility = View.INVISIBLE
712             }
713             else
714             {
715                 graphView.visibility = View.INVISIBLE
716                 listView.visibility = View.VISIBLE
717             }
718             //controller.vibrate(30);
719         }
720         catch (e: Exception)
721         {
722             e.printStackTrace()
723         }
724     }
725
726     override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback
727     {
728         return object : AmbientModeSupport.AmbientCallback() {
729             override fun onEnterAmbient(ambientDetails: Bundle) {
730                 Log.v(TAG, "onEnterAmbient()")
731             }
732         }
733     }
734
735     override fun selectedReferenceViewMode(referenceId: Int, viewMode: Int)
736     {
737         isLaptimeView = viewMode != 0
738         currentReferenceId = referenceId
739         controller.displayMode = isLaptimeView
740         controller.referenceTimerSelection = currentReferenceId
741         controller.setupReferenceData()
742         Log.v(TAG, "pushedArea() : $isLaptimeView REF: $currentReferenceId")
743         runOnUiThread {
744             // ラップタイム表示状態の更新
745             reloadLapTimeList(true)
746
747             // 表示ビューの切り替え
748             changeGraphicView(isLaptimeView)
749
750             // 表示のボタン状態を変更
751             updateTimerLabel()
752         }
753     }
754
755     companion object
756     {
757         private val TAG = MainActivity::class.java.simpleName
758         //private const val REQUEST_CODE_PERMISSIONS = 10
759         //private val REQUIRED_PERMISSIONS = arrayOf(
760         //    Manifest.permission.VIBRATE,
761         //    Manifest.permission.WAKE_LOCK,
762         //)
763     }
764 }