66import android .view .ViewGroup ;
77
88import androidx .annotation .NonNull ;
9- import androidx .core .content .ContextCompat ;
109import androidx .fragment .app .Fragment ;
11- import androidx .lifecycle .LiveData ;
10+ import androidx .lifecycle .ViewModelProvider ;
1211
13- import com .google .common .util .concurrent .ListenableFuture ;
14- import com .jackpf .locationhistory .PingResponse ;
15- import com .jackpf .locationhistory .client .MainActivity ;
12+ import com .google .android .material .dialog .MaterialAlertDialogBuilder ;
1613import com .jackpf .locationhistory .client .R ;
17- import com .jackpf .locationhistory .client .client .BeaconClientFactory ;
1814import com .jackpf .locationhistory .client .client .ssl .SSLPrompt ;
19- import com .jackpf .locationhistory .client .client .ssl .TrustedCertStorage ;
20- import com .jackpf .locationhistory .client .client .ssl .UntrustedCertException ;
21- import com .jackpf .locationhistory .client .client .util .GrpcFutureWrapper ;
22- import com .jackpf .locationhistory .client .config .ConfigRepository ;
2315import com .jackpf .locationhistory .client .databinding .FragmentSettingsBinding ;
24- import com .jackpf .locationhistory .client .grpc .BeaconClient ;
2516import com .jackpf .locationhistory .client .push .Ntfy ;
2617import com .jackpf .locationhistory .client .push .ObservableUnifiedPushState ;
27- import com .jackpf .locationhistory .client .push .UnifiedPushService ;
28- import com .jackpf .locationhistory .client .util .Logger ;
2918
30- import org .unifiedpush .android .connector .UnifiedPush ;
31-
32- import java .io .IOException ;
33- import java .util .Arrays ;
3419import java .util .List ;
3520
3621import javax .annotation .Nullable ;
3722
3823public class SettingsFragment extends Fragment {
24+
3925 private FragmentSettingsBinding binding ;
40- @ Nullable
41- private ConfigRepository configRepository ;
26+ private SettingsViewModel viewModel ;
27+
4228 @ Nullable
4329 private SSLPrompt sslPrompt ;
4430
45- private final Logger log = new Logger (this );
46-
4731 @ Nullable
4832 @ Override
4933 public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState ) {
@@ -55,22 +39,17 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
5539 public void onViewCreated (@ NonNull View view , @ Nullable Bundle savedInstanceState ) {
5640 super .onViewCreated (view , savedInstanceState );
5741
58- configRepository = new ConfigRepository ( requireContext () );
59- sslPrompt = new SSLPrompt (getActivity ());
42+ viewModel = new ViewModelProvider ( this ). get ( SettingsViewModel . class );
43+ sslPrompt = new SSLPrompt (requireActivity ());
6044
45+ setupInputs ();
6146 setupUnifiedPushListener ();
47+ observeEvents ();
6248 }
6349
6450 @ Override
6551 public void onResume () {
6652 super .onResume ();
67-
68- binding .serverHostInput .setText (configRepository .getServerHost ());
69- binding .serverPortInput .setText (Integer .toString (configRepository .getServerPort ()));
70- binding .updateFrequencyInput .setText (Integer .toString (configRepository .getUpdateIntervalMinutes ()));
71-
72- binding .testButton .setOnClickListener (view -> handleTestClick ());
73- binding .saveButton .setOnClickListener (view -> handleSaveClick ());
7453 }
7554
7655 @ Override
@@ -84,95 +63,80 @@ public void onDestroyView() {
8463 binding = null ;
8564 }
8665
87- private void handleTestClick () {
88- if (getActivity () == null ) {
89- return ;
90- }
91-
92- try {
93- // Create a temp client with current (non-saved) settings
94- BeaconClient tempClient = BeaconClientFactory .createClient (
95- new BeaconClientFactory .BeaconClientParams (
96- binding .serverHostInput .getText ().toString (),
97- Integer .parseInt (binding .serverPortInput .getText ().toString ()),
98- false ,
99- 3000 // Smaller timeout for pings
100- ),
101- new TrustedCertStorage (getActivity ())
102- );
103-
104- ListenableFuture <PingResponse > pingResponse = tempClient .ping (new GrpcFutureWrapper <>(
105- response -> getActivity ().runOnUiThread (() -> {
106- if (BeaconClient .isPongResponse (response )) {
107- Toasts .show (getActivity (), R .string .toast_connection_successful );
108- } else {
109- Toasts .show (getActivity (), R .string .toast_invalid_response , response .getMessage ());
110- }
111- }),
112- e -> {
113- if (UntrustedCertException .isCauseOf (e )) {
114- sslPrompt .show (UntrustedCertException .getCauseFrom (e ).getFingerprint (), true );
115- } else {
116- requireActivity ().runOnUiThread (() ->
117- Toasts .show (getActivity (), R .string .toast_connection_failed , e .getMessage ())
118- );
119- }
120- }
121- ));
122-
123- pingResponse .addListener (tempClient ::close , ContextCompat .getMainExecutor (requireContext ()));
124- } catch (NumberFormatException | IOException e ) {
125- Toasts .show (requireContext (), R .string .toast_invalid_settings , e .getMessage ());
126- }
66+ private void setupInputs () {
67+ binding .serverHostInput .setText (viewModel .getConfig ().getServerHost ());
68+ binding .serverPortInput .setText (String .valueOf (viewModel .getConfig ().getServerPort ()));
69+ binding .updateFrequencyInput .setText (Long .toString (viewModel .getConfig ().getUpdateIntervalMinutes ()));
70+
71+ binding .testButton .setOnClickListener (v -> viewModel .testConnection (
72+ binding .serverHostInput .getText ().toString (),
73+ binding .serverPortInput .getText ().toString ()
74+ ));
75+
76+ binding .saveButton .setOnClickListener (v -> viewModel .saveSettings (
77+ binding .serverHostInput .getText ().toString (),
78+ binding .serverPortInput .getText ().toString (),
79+ binding .updateFrequencyInput .getText ().toString ()
80+ ));
12781 }
12882
129- private void handleSaveClick () {
130- if ( getActivity () instanceof MainActivity ) {
131- try {
132- configRepository . setServerHost ( binding . serverHostInput . getText (). toString ());
133- configRepository . setServerPort ( Integer . parseInt ( binding .serverPortInput . getText (). toString ()));
134- configRepository . setUpdateIntervalMinutes ( Integer . parseInt ( binding .updateFrequencyInput . getText (). toString ()) );
135-
136- Toasts . show ( requireContext (), R . string . toast_saved );
137- } catch ( NumberFormatException e ) {
138- Toasts . show ( requireContext (), R . string . toast_invalid_settings , e . getMessage ());
139- }
140- }
83+ private void setupUnifiedPushListener () {
84+ ObservableUnifiedPushState . getInstance ( requireContext ())
85+ . observeEnabled ()
86+ . observe ( getViewLifecycleOwner (), isEnabled -> {
87+ if ( binding .pushRegisterSwitch . isChecked () != isEnabled ) {
88+ binding .pushRegisterSwitch . setOnCheckedChangeListener ( null );
89+ binding . pushRegisterSwitch . setChecked ( isEnabled );
90+ }
91+ binding . pushRegisterSwitch . setOnCheckedChangeListener (( v , isChecked ) ->
92+ viewModel . handleUnifiedPushToggle ( isChecked )
93+ );
94+ });
14195 }
14296
143- private void setupUnifiedPushListener () {
144- isUnifiedPushEnabled ().observe (getViewLifecycleOwner (), isEnabled -> {
145- // Prevent infinite loops
146- if (binding .pushRegisterSwitch .isChecked () != isEnabled ) {
147- binding .pushRegisterSwitch .setOnCheckedChangeListener (null );
148- binding .pushRegisterSwitch .setChecked (isEnabled );
97+ private void observeEvents () {
98+ viewModel .getEvents ().observe (getViewLifecycleOwner (), event -> {
99+ if (event == null ) return ;
100+
101+ switch (event .getType ()) {
102+ case TOAST :
103+ SettingsViewEvent .Toast toast = (SettingsViewEvent .Toast ) event ;
104+ Toasts .show (requireContext (), toast .getMessageResId (), toast .getFormatArgs ());
105+ break ;
106+ case SSL_PROMPT :
107+ SettingsViewEvent .SslPrompt sslPromptEvent = (SettingsViewEvent .SslPrompt ) event ;
108+ if (sslPrompt != null ) sslPrompt .show (sslPromptEvent .getFingerprint (), true );
109+ break ;
110+ case SET_UNIFIED_PUSH_CHECKED :
111+ SettingsViewEvent .SetUnifiedPushChecked checkedEvent = (SettingsViewEvent .SetUnifiedPushChecked ) event ;
112+ binding .pushRegisterSwitch .setChecked (checkedEvent .isChecked ());
113+ break ;
114+ case SHOW_DISTRIBUTOR_PICKER :
115+ SettingsViewEvent .ShowDistributorPicker pickerEvent = (SettingsViewEvent .ShowDistributorPicker ) event ;
116+ showDistributorPicker (pickerEvent .getDistributors ());
117+ break ;
118+ case PROMPT_NTFY_INSTALL :
119+ Ntfy .promptInstall (requireContext ());
120+ binding .pushRegisterSwitch .setChecked (false ); // Make sure we un-check
121+ break ;
149122 }
150- binding .pushRegisterSwitch .setOnCheckedChangeListener ((v , isChecked ) -> handleUnifiedPushCheck (isChecked ));
151123 });
152124 }
153125
154- private LiveData <Boolean > isUnifiedPushEnabled () {
155- return ObservableUnifiedPushState
156- .getInstance (requireContext ())
157- .observeEnabled ();
158- }
159-
160- private void handleUnifiedPushCheck (boolean isChecked ) {
161- if (getActivity () instanceof MainActivity ) {
162- if (isChecked ) {
163- List <String > distributors = UnifiedPush .getDistributors (requireContext ());
164- if (!distributors .isEmpty ()) {
165- log .d ("Found distributors: %s" , Arrays .toString (distributors .toArray ()));
166- // TODO Select distributor properly from list
167- UnifiedPushService .register (requireContext (), distributors .get (0 ));
168- } else {
169- log .d ("No push distributors" );
170- Ntfy .promptInstall (requireContext ());
171- binding .pushRegisterSwitch .setChecked (false );
172- }
173- } else {
174- UnifiedPushService .unregister (requireContext ());
175- }
176- }
126+ private void showDistributorPicker (List <String > distributors ) {
127+ String [] distributorsArray = distributors .toArray (new String [0 ]);
128+
129+ new MaterialAlertDialogBuilder (requireContext ())
130+ .setTitle (R .string .select_distributor )
131+ .setItems (distributorsArray , (dialog , which ) ->
132+ viewModel .registerUnifiedPush (distributorsArray [which ])
133+ )
134+ .setNegativeButton (R .string .cancel , (dialog , which ) ->
135+ binding .pushRegisterSwitch .setChecked (false )
136+ )
137+ .setOnCancelListener (dialog ->
138+ binding .pushRegisterSwitch .setChecked (false )
139+ )
140+ .show ();
177141 }
178142}
0 commit comments