11package com .nutomic .syncthingandroid .service ;
22
33import android .app .ActivityManager ;
4+ import android .content .ComponentName ;
45import android .content .Context ;
56import android .content .Intent ;
7+ import android .content .ServiceConnection ;
68import android .content .SharedPreferences ;
79import android .content .res .Resources ;
810import android .os .Build ;
11+ import android .os .IBinder ;
912import android .service .quicksettings .Tile ;
1013import android .service .quicksettings .TileService ;
11-
14+ import android . util . Log ;
1215import androidx .annotation .RequiresApi ;
1316import androidx .localbroadcastmanager .content .LocalBroadcastManager ;
1417import androidx .preference .PreferenceManager ;
15-
1618import com .nutomic .syncthingandroid .R ;
1719
1820import static com .nutomic .syncthingandroid .service .RunConditionMonitor .ACTION_SYNC_TRIGGER_FIRED ;
1921import static com .nutomic .syncthingandroid .service .RunConditionMonitor .EXTRA_BEGIN_ACTIVE_TIME_WINDOW ;
2022
2123@ RequiresApi (api = Build .VERSION_CODES .N )
22- public class QuickSettingsTileSchedule extends TileService {
24+ public class QuickSettingsTileSchedule extends TileService implements ServiceConnection , SyncthingService .OnServiceStateChangeListener {
25+
26+ private static final String TAG = "QuickSettingsTileSchedule" ;
27+
2328 public QuickSettingsTileSchedule () {
2429
2530 }
31+
2632 private Context mContext ;
2733 private SharedPreferences mPreferences ; // Manually initialized - no injection needed
34+ private SyncthingService mSyncthingService ;
35+
36+ private int tile_active_state = Tile .STATE_INACTIVE ;
2837
2938 @ Override
3039 public void onStartListening () {
40+ Log .d (TAG , "onStartListening()" );
3141 Tile tile = getQsTile ();
3242 if (tile != null ) {
3343 mContext = getApplication ().getApplicationContext ();
@@ -43,16 +53,27 @@ public void onStartListening() {
4353 break ;
4454 }
4555 }
56+
4657 // disable tile if app is not running, schedule is off, or syncthing is force-started/stopped
47- if (!syncthingRunning || !mPreferences .getBoolean (Constants .PREF_RUN_ON_TIME_SCHEDULE ,false )
58+ if (!syncthingRunning || !mPreferences .getBoolean (Constants .PREF_RUN_ON_TIME_SCHEDULE , false )
4859 || mPreferences .getInt (Constants .PREF_BTNSTATE_FORCE_START_STOP , Constants .BTNSTATE_NO_FORCE_START_STOP ) != Constants .BTNSTATE_NO_FORCE_START_STOP ) {
4960 tile .setState (Tile .STATE_UNAVAILABLE );
5061 tile .setLabel (res .getString (R .string .qs_schedule_disabled ));
5162 tile .updateTile ();
5263 return ;
5364 }
5465
55- tile .setState (Tile .STATE_ACTIVE );
66+ // try to bind to SyncthingService if it's running and not already bound
67+ if (mSyncthingService == null ) {
68+ try {
69+ Intent bindIntent = new Intent (mContext , SyncthingService .class );
70+ mContext .bindService (bindIntent , this , Context .BIND_AUTO_CREATE );
71+ } catch (Exception e ) {
72+ Log .w (TAG , "Failed to bind to SyncthingService" , e );
73+ }
74+ }
75+
76+ tile .setState (tile_active_state );
5677 tile .setLabel (res .getString (
5778 R .string .qs_schedule_label_minutes ,
5879 Integer .parseInt (mPreferences .getString (Constants .PREF_SYNC_DURATION_MINUTES , "5" ))
@@ -64,11 +85,61 @@ public void onStartListening() {
6485
6586 @ Override
6687 public void onClick () {
67- if (getQsTile ().getState () == Tile .STATE_ACTIVE ) {
68- LocalBroadcastManager localBroadcastManager = LocalBroadcastManager .getInstance (mContext );
69- Intent intent = new Intent (ACTION_SYNC_TRIGGER_FIRED );
70- intent .putExtra (EXTRA_BEGIN_ACTIVE_TIME_WINDOW , true );
71- localBroadcastManager .sendBroadcast (intent );
88+ if (getQsTile ().getState () == Tile .STATE_UNAVAILABLE ) {
89+ return ;
7290 }
91+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager .getInstance (mContext );
92+ Intent intent = new Intent (ACTION_SYNC_TRIGGER_FIRED );
93+ intent .putExtra (EXTRA_BEGIN_ACTIVE_TIME_WINDOW , true );
94+ localBroadcastManager .sendBroadcast (intent );
95+ }
96+
97+
98+ @ Override
99+ public void onServiceConnected (ComponentName name , IBinder service ) {
100+ Log .d (TAG , String .format ("onServiceConnected(ComponentName=%s, IBinder=%s)" , name .toString (), service .toString ()));
101+ SyncthingServiceBinder syncthingServiceBinder = (SyncthingServiceBinder ) service ;
102+ mSyncthingService = syncthingServiceBinder .getService ();
103+ mSyncthingService .registerOnServiceStateChangeListener (this );
73104 }
105+
106+ @ Override
107+ public void onServiceDisconnected (ComponentName componentName ) {
108+ mSyncthingService = null ;
109+ }
110+
111+ @ Override
112+ public void onDestroy () {
113+ Log .d (TAG , "onDestroy()" );
114+ if (mSyncthingService != null ) {
115+ mSyncthingService .unregisterOnServiceStateChangeListener (this );
116+ }
117+ try {
118+ if (mContext != null ) {
119+ mContext .unbindService (this );
120+ }
121+ } catch (IllegalArgumentException | IllegalStateException e ) {
122+ Log .d (TAG , "Service not bound or already unbound" );
123+ }
124+ mSyncthingService = null ;
125+ tile_active_state = Tile .STATE_INACTIVE ;
126+ super .onDestroy ();
127+ }
128+
129+ @ Override
130+ public void onServiceStateChange (SyncthingService .State currentState ) {
131+ Log .d (TAG , String .format ("onServiceStateChange: %s" , currentState .toString ()));
132+
133+ int tile_active_state = Tile .STATE_INACTIVE ;
134+ if (currentState == SyncthingService .State .STARTING || currentState == SyncthingService .State .ACTIVE ) {
135+ tile_active_state = Tile .STATE_ACTIVE ;
136+ }
137+ if (tile_active_state == this .tile_active_state ) return ;
138+
139+ Tile tile = getQsTile ();
140+ this .tile_active_state = tile_active_state ;
141+ tile .setState (tile_active_state );
142+ tile .updateTile ();
143+ }
144+
74145}
0 commit comments