diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6ef7a91..8da5a23 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,148 +1,124 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
index 517627b..a95a538 100644
--- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
@@ -1,918 +1,924 @@
/*
BusTO - Arrival times for Turin public transport.
Copyright (C) 2021 Fabio Mazza
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 .
*/
package it.reyboz.bustorino;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.*;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
import androidx.core.view.*;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.work.WorkInfo;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.Consumer;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.DBUpdateCheckWorker;
import it.reyboz.bustorino.data.DBUpdateWorker;
import it.reyboz.bustorino.data.PreferencesHolder;
import it.reyboz.bustorino.fragments.*;
import it.reyboz.bustorino.middleware.GeneralActivity;
import it.reyboz.bustorino.viewmodels.ServiceAlertsViewModel;
import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri;
import static it.reyboz.bustorino.backend.utils.openIceweasel;
public class ActivityPrincipal extends GeneralActivity implements FragmentListenerMain {
private DrawerLayout mDrawer;
private NavigationView mNavView;
private ActionBarDrawerToggle drawerToggle;
private final static String DEBUG_TAG="BusTO Act Principal";
private final static String TAG_FAVORITES="favorites_frag";
private Snackbar snackbar;
-
+ private boolean startedFromIntent = false;
private ServiceAlertsViewModel serviceAlertsViewModel;
+
+ private final DrawerLayout.DrawerListener drawerListener = new DrawerLayout.DrawerListener() {
+ @Override
+ public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
+
+ }
+
+ @Override
+ public void onDrawerOpened(@NonNull View drawerView) {
+ hideKeyboard();
+ }
+
+ @Override
+ public void onDrawerClosed(@NonNull View drawerView) {
+
+ }
+
+ @Override
+ public void onDrawerStateChanged(int newState) {
+ }
+ };
//private FragmentKind showingFragmentKind;
private final Map menuActions = Map.of(
R.id.drawer_action_settings, () -> {
Log.d("MAINBusTO", "Pressed button preferences");
startActivity(new Intent(this, ActivitySettings.class));
},
R.id.nav_favorites_item, () ->
checkAndShowFavoritesFragment(getSupportFragmentManager(), true),
R.id.nav_home, this::showHomeMainFragmentFromClick,
R.id.nav_map_item, () ->
requestMapFragment(true),
R.id.nav_lines_item, () ->
showLinesFragment(getSupportFragmentManager(), true, null),
R.id.drawer_action_info, () ->
startActivity(new Intent(this, ActivityAbout.class)),
R.id.nav_nearby, this::openNearbyStopsFragment
);
private long lastClosingAttempt = -1L;
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
boolean isResolved = activityCustomBackPressed();
Log.d(DEBUG_TAG, "backpress resolved: " + isResolved);
if(!isResolved){
- long currentTime = System.currentTimeMillis();
- if(currentTime - lastClosingAttempt < 2000){
- finish();
- } else{
- lastClosingAttempt = currentTime;
- Toast.makeText(getApplicationContext(),R.string.back_again_to_close,Toast.LENGTH_SHORT).show();
- }
+ //if(startedFromIntent){
+ // finish();
+ //} else{
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastClosingAttempt < 2000)
+ finish();
+ else {
+ lastClosingAttempt = currentTime;
+ Toast.makeText(getApplicationContext(), R.string.back_again_to_close, Toast.LENGTH_SHORT).show();
+ }
+ //}
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(DEBUG_TAG, "onCreate, savedInstanceState is: "+savedInstanceState);
setContentView(R.layout.activity_principal);
serviceAlertsViewModel = new ViewModelProvider(this).get(ServiceAlertsViewModel.class);
//Use LiveModel to sync fragment state
//onBackPressed solution required from Android 16
backPressedCallback.setEnabled(true);
this.getOnBackPressedDispatcher().addCallback(backPressedCallback);
- boolean showingArrivalsFromIntent = false;
final Toolbar mToolbar = findViewById(R.id.default_toolbar);
setSupportActionBar(mToolbar);
if (getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
else Log.w(DEBUG_TAG, "NO ACTION BAR");
mToolbar.setOnMenuItemClickListener(new ToolbarItemClickListener(this));
mDrawer = findViewById(R.id.drawer_layout);
drawerToggle = setupDrawerToggle(mToolbar);
// Setup toggle to display hamburger icon with nice animation
drawerToggle.setDrawerIndicatorEnabled(true);
drawerToggle.syncState();
mDrawer.addDrawerListener(drawerToggle);
- mDrawer.addDrawerListener(new DrawerLayout.DrawerListener() {
- @Override
- public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
-
- }
-
- @Override
- public void onDrawerOpened(@NonNull View drawerView) {
- hideKeyboard();
- }
-
- @Override
- public void onDrawerClosed(@NonNull View drawerView) {
-
- }
-
- @Override
- public void onDrawerStateChanged(int newState) {
- }
- });
-
+ mDrawer.addDrawerListener(drawerListener);
mNavView = findViewById(R.id.nvView);
setupDrawerContent(mNavView);
/*View header = mNavView.getHeaderView(0);
*/
//mNavView.getMenu().findItem(R.id.versionFooter).
/// LEGACY CODE
//---------------------------- START INTENT CHECK QUEUE ------------------------------------
// Intercept calls from URL intent
boolean tryedFromIntent = false;
- String busStopID = null;
- Uri data = getIntent().getData();
- if (data != null) {
+ String initialBusStopID = null;
+ var intent = getIntent();
+ if(intent != null){
+ var data = intent.getData();
+ if(data != null){
+ //var rest = data.getSchemeSpecificPart();
+ //Log.d(DEBUG_TAG, "the rest is: "+rest);
- busStopID = getBusStopIDFromUri(data);
- Log.d(DEBUG_TAG, "Opening Intent: busStopID: "+busStopID);
- tryedFromIntent = true;
- }
+ initialBusStopID = getBusStopIDFromUri(data);
+ tryedFromIntent = true;
+ Log.d(DEBUG_TAG, "Opening Intent: initialBusStopID: "+initialBusStopID);
- // Intercept calls from other activities
- if (!tryedFromIntent) {
- Bundle b = getIntent().getExtras();
- if (b != null) {
- busStopID = b.getString("bus-stop-ID");
-
- /*
- * I'm not very sure if you are coming from an Intent.
- * Some launchers work in strange ways.
- */
- tryedFromIntent = busStopID != null;
+ }
+ // Intercept calls from other activities
+ if(!tryedFromIntent){
+ Bundle b =intent.getExtras();
+ if (b != null) {
+ initialBusStopID = b.getString("bus-stop-ID");
+
+ /*
+ * I'm not very sure if you are coming from an Intent.
+ * Some launchers work in strange ways.
+ */
+ tryedFromIntent = initialBusStopID != null;
+ }
}
}
+
//---------------------------- END INTENT CHECK QUEUE --------------------------------------
- if (busStopID == null) {
+ if (initialBusStopID == null) {
// Show keyboard if can't start from intent
// JUST DON'T
// showKeyboard();
// You haven't obtained anything... from an intent?
if (tryedFromIntent) {
// This shows a luser warning
Toast.makeText(getApplicationContext(),
R.string.insert_bus_stop_number_error, Toast.LENGTH_SHORT).show();
}
- } else {
- // If you are here an intent has worked successfully
- //setBusStopSearchByIDEditText(busStopID);
- //Log.d(DEBUG_TAG, "Requesting arrivals for stop "+busStopID+" from intent");
- requestArrivalsForStopID(busStopID); //this shows the fragment, too
- showingArrivalsFromIntent = true;
}
- //database check
+ //save whether we started from intent
+ startedFromIntent = tryedFromIntent;
- // DatabaseUpdate.requestDBUpdateWithWork(this, false, false);
+ //period database check
DBUpdateCheckWorker.Companion.schedulePeriodicCheck(this,false);
- /*
- Watch for database update
- */
+
+ //Watch for database update
DBUpdateWorker.getWorkInfoLiveData(this)
.observe(this, workInfoList -> {
// If there are no matching work info, do nothing
if (workInfoList == null || workInfoList.isEmpty()) {
return;
}
Log.d(DEBUG_TAG, "WorkerInfo: "+workInfoList);
boolean showProgress = false;
for (WorkInfo workInfo : workInfoList) {
if (workInfo.getState() == WorkInfo.State.RUNNING) {
showProgress = true;
break;
}
}
if (showProgress) {
createDatabaseUpdateSnackbar();
} else {
if(snackbar!=null) {
snackbar.dismiss();
snackbar = null;
}
}
});
// show the main fragment
Fragment f = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame);
Log.d(DEBUG_TAG, "OnCreate the fragment is "+f);
String vl = PreferenceManager.getDefaultSharedPreferences(this).getString(SettingsFragment.PREF_KEY_STARTUP_SCREEN, "");
Log.d(DEBUG_TAG, "The default screen to open is: "+vl);
- if (showingArrivalsFromIntent){
- //do nothing but exclude a case
+ var framan = getSupportFragmentManager();
+
+ if (initialBusStopID!=null) {
+ Log.d(DEBUG_TAG, "Opening Main Fragment on arrivals, bus Stop: "+initialBusStopID);
+ createShowMainFragment(framan, MainScreenFragment.makeArgsArrivals(initialBusStopID), false);
}else if (savedInstanceState==null) {
- var framan = getSupportFragmentManager();
//we are not restarting the activity from nothing
switch (vl){
case "map" -> {requestMapFragment(false);}
case "favorites" -> checkAndShowFavoritesFragment(framan, false);
case "lines" -> showLinesFragment(framan, false, null);
case "nearby" -> createShowMainFragment(framan, MainScreenFragment.makeArgsNearby(), false);
default -> createShowMainFragment(framan, MainScreenFragment.makeArgsButtonsScreen(), false);
}
}
//boolean onCreateComplete = true;
//default values are set in the BustoApp
//checkApplyDefaultSettingsValues();
// handle the device "insets"
/*
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.rootRelativeLayout), (v, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
// Apply the insets as a margin to the view. This solution sets only the
// bottom, left, and right dimensions, but you can apply whichever insets are
// appropriate to your layout. You can also update the view padding if that's
// more appropriate.
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
mlp.leftMargin = insets.left;
mlp.bottomMargin = insets.bottom;
mlp.rightMargin = insets.right;
v.setLayoutParams(mlp);
//set for toolbar
//mlp = (ViewGroup.MarginLayoutParams) mToolbar.getLayoutParams();
//mlp.topMargin = insets.top;
//mToolbar.setLayoutParams(mlp);
mToolbar.setPadding(0, insets.top, 0, 0);
// Return CONSUMED if you don't want the window insets to keep passing
// down to descendant views.
return WindowInsetsCompat.CONSUMED;
});
//to properly handle IME
WindowInsetsControllerCompat insetsController =
WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
if (insetsController != null) {
insetsController.setSystemBarsBehavior(
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
);
}
*/
// Toolbar: solo inset superiore (status bar)
ViewCompat.setOnApplyWindowInsetsListener(mToolbar, (v, windowInsets) -> {
Insets statusBar = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
v.setPadding(0, statusBar.top, 0, 0);
return windowInsets; // NON consumare: passa gli insets ai figli
});
// Content frame: insets laterali e inferiori (navigation bar)
// I fragment figli riceveranno gli insets e potranno gestirli a loro volta
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.mainActContentFrame), (v, windowInsets) -> {
Insets systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
// Solo left/right, bottom lo gestisce ogni fragment
v.setPadding(systemBars.left, 0, systemBars.right, 0);
return windowInsets; //WindowInsetsCompat.CONSUMED; // NON consumare: passa ai fragment
});
//check if first run activity (IntroActivity) has been started once or not
final SharedPreferences theShPr = getMainSharedPreferences();
boolean hasIntroRun = theShPr.getBoolean(PreferencesHolder.PREF_INTRO_ACTIVITY_RUN,false);
if(!hasIntroRun){
startIntroductionActivity();
}
serviceAlertsViewModel.getLastTimeRunningDownload().observe(this, (timeRunning) -> {
if (timeRunning != null) {
Log.d(DEBUG_TAG, "requested alerts download at time: "+timeRunning);
}
});
serviceAlertsViewModel.launchAlertsPeriodCheck();
}
private ActionBarDrawerToggle setupDrawerToggle(Toolbar toolbar) {
// NOTE: Make sure you pass in a valid toolbar reference. ActionBarDrawToggle() does not require it
// and will not render the hamburger icon without it.
return new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.drawer_open, R.string.drawer_close);
}
/**
* Setup drawer actions
* @param navigationView the navigation view on which to set the callbacks
*/
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
menuItem -> {
int menuId = menuItem.getItemId();
if( menuActions.containsKey(menuId)){
closeDrawerIfOpen();
var runnable = menuActions.get(menuId);
if(runnable!=null) runnable.run();
return true;
} else{
return false;
}
});
}
private void closeDrawerIfOpen(){
if (mDrawer.isDrawerOpen(GravityCompat.START))
mDrawer.closeDrawer(GravityCompat.START);
}
// `onPostCreate` called when activity start-up is complete after `onStart()`
// NOTE 1: Make sure to override the method with only a single `Bundle` argument
// Note 2: Make sure you implement the correct `onPostCreate(Bundle savedInstanceState)` method.
// There are 2 signatures and only `onPostCreate(Bundle state)` shows the hamburger icon.
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
drawerToggle.syncState();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.principal_menu, menu);
MenuItem experimentsMenuItem = menu.findItem(R.id.action_experiments);
SharedPreferences shPr = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean exper_On = shPr.getBoolean(getString(R.string.pref_key_experimental), false);
experimentsMenuItem.setVisible(exper_On);
return super.onCreateOptionsMenu(menu);
}
//requesting permissions
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode==STORAGE_PERMISSION_REQ){
final String storagePerm = Manifest.permission.WRITE_EXTERNAL_STORAGE;
if (permissionDoneRunnables.containsKey(storagePerm)) {
Runnable toRun = permissionDoneRunnables.get(storagePerm);
if (toRun != null)
toRun.run();
permissionDoneRunnables.remove(storagePerm);
}
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(DEBUG_TAG, "Permissions check: " + Arrays.toString(permissions));
if (permissionDoneRunnables.containsKey(storagePerm)) {
Runnable toRun = permissionDoneRunnables.get(storagePerm);
if (toRun != null)
toRun.run();
permissionDoneRunnables.remove(storagePerm);
}
} else {
//permission denied
showToastMessage(R.string.permission_storage_maps_msg, false);
}
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
Log.d(DEBUG_TAG, "Item pressed");
if (item.getItemId() == android.R.id.home) {
mDrawer.openDrawer(GravityCompat.START);
return true;
}
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
/*@Override
public void onBackPressed() {
if (!activityCustomBackPressed())
super.onBackPressed();
}
*/
private boolean activityCustomBackPressed(){
var mainFragManager = getSupportFragmentManager();
boolean resolved = mainFragManager.getBackStackEntryCount() > 0;
Fragment shownFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame);
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
resolved = true;
}
else if (shownFrag instanceof MainScreenFragment mainFrag) { //the only case when we have to pop both stacks
mainFrag.cancelReloadArrivalsIfNeeded();
var chManager = mainFrag.getChildFragmentManager();
if(chManager.getBackStackEntryCount() > 0){
chManager.popBackStack();
Log.d(DEBUG_TAG, "Child back stack popped");
resolved = true;
} else{
Log.d(DEBUG_TAG, "No back stack on child fragment manager");
}
boolean haveToPopMainFromFragment = mainFrag.needToPopMainStackOnBack();
//mainFrag.getChildFragmentManager().popBackStack();
if(haveToPopMainFromFragment){ // pops the stack
mainFragManager.popBackStack();
}
}
else if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
mainFragManager.popBackStack();
var newFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame);
if(newFrag != null) {
int backStackCount = newFrag.getChildFragmentManager().getBackStackEntryCount();
Log.d(DEBUG_TAG, "new fragment is "+newFrag.getClass().getSimpleName() +", count of the backstack: "+backStackCount);
}
Log.d(DEBUG_TAG, "Popping main backstack");
}
else{
resolved = false;
}
return resolved;
}
/**
* Create and show the SnackBar with the message
* The fragment shown points to which view to attach the snackbar
*/
private void createDatabaseUpdateSnackbar() {
View baseView = null;
boolean showSnackbar = true;
final Fragment frag = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame);
if (frag instanceof ScreenBaseFragment){
baseView = ((ScreenBaseFragment) frag).getBaseViewForSnackBar();
showSnackbar = ((ScreenBaseFragment) frag).showSnackbarOnDBUpdate();
}
if (baseView == null) baseView = findViewById(R.id.mainActContentFrame);
//if (baseView == null) Log.e(DEBUG_TAG, "baseView null for default snackbar, probably exploding now");
if (baseView !=null && showSnackbar) {
snackbar = Snackbar.make(baseView, R.string.database_update_msg_inapp, Snackbar.LENGTH_INDEFINITE);
snackbar.setTextColor(getColor(android.R.color.white));
snackbar.setBackgroundTint(getColor(R.color.grey_800));
if (frag instanceof ScreenBaseFragment){
((ScreenBaseFragment) frag).setSnackbarPropertiesBeforeShowing(snackbar);
}
snackbar.show();
} else{
Log.e(DEBUG_TAG, "Asked to show the snackbar but the baseView is null");
}
}
/*
private void updateShowingFragmentKindInternal(@NonNull FragmentKind newKind){
if(BuildConfig.DEBUG)
Log.d(DEBUG_TAG, "Updating fragment kind, new: "+newKind+", current: "+showingFragmentKind);
boolean showingMainFragmentFromOther = false;
if(showingFragmentKind == null){
showingFragmentKind = newKind;
showingMainFragmentFromOther = false;
} else if(newKind != showingFragmentKind) {
showingMainFragmentFromOther = (
FragmentKind.getSuperKind(newKind) != FragmentKind.getSuperKind(showingFragmentKind));
showingFragmentKind = newKind;
}
}
*/
/**
* Show the actual fragment by adding it to the backstack
* @param fraMan the fragmentManager
* @param fragment the fragment
*/
private static void showMainFragment(FragmentManager fraMan, MainScreenFragment fragment, boolean addToBackStack){
FragmentTransaction ft = fraMan.beginTransaction()
.replace(R.id.mainActContentFrame, fragment, MainScreenFragment.FRAGMENT_TAG)
.setReorderingAllowed(false)
/*.setCustomAnimations(
R.anim.slide_in, // enter
R.anim.fade_out, // exit
R.anim.fade_in, // popEnter
R.anim.slide_out // popExit
)*/
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToBackStack) {
ft.addToBackStack(null);
}
ft.commit();
}
/**
* Create a new MainFragment for the arguments provided and show it in the layout
* @param fraMan the fragmentManager
* @param arguments args for the fragment
*/
private static void createShowMainFragment(FragmentManager fraMan,@Nullable Bundle arguments, boolean addToBackStack){
FragmentTransaction ft = fraMan.beginTransaction()
.replace(R.id.mainActContentFrame, MainScreenFragment.class, arguments, MainScreenFragment.FRAGMENT_TAG)
.setReorderingAllowed(false)
/*.setCustomAnimations(
R.anim.slide_in, // enter
R.anim.fade_out, // exit
R.anim.fade_in, // popEnter
R.anim.slide_out // popExit
)*/
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToBackStack) ft.addToBackStack(null);
ft.commit();
}
private void showHomeMainFragmentFromClick(){
FragmentManager fraMan = getSupportFragmentManager();
var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if(fragment instanceof MainScreenFragment mainFrag){
var visible = mainFrag.isVisible();
if(!mainFrag.isVisible()){
showMainFragment(fraMan, mainFrag, true);
}
mainFrag.showButtonsFragmentIfNotNearby(true);
} else{
createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), true);
}
}
private void requestMapFragment(final boolean allowReturn){
// starting from Android 11, we don't need to have the STORAGE permission anymore for the map cache
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.mainActContentFrame);
if(fragment instanceof MapLibreFragment){
Log.d(DEBUG_TAG, "Requested map fragment, but it is already open");
} else {
fragment = fm.findFragmentByTag(MapLibreFragment.FRAGMENT_TAG);
if(fragment != null){
Log.d(DEBUG_TAG, "Found map fragment, reopening it");
var ft = fm.beginTransaction();
ft.replace(R.id.mainActContentFrame,fragment, MapLibreFragment.FRAGMENT_TAG);
if(allowReturn) ft.addToBackStack(null);
ft.commit();
} else {
//create from scratch
//The permissions are handled in the MapLibreFragment instead
createAndShowMapFragment(null, allowReturn);
}
}
}
private void checkAndShowFavoritesFragment(FragmentManager fragmentManager, boolean addToBackStack){
if(getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame) instanceof FavoritesFragment){
Log.d(DEBUG_TAG, "Requested favorites fragment, but it is already open");
return;
}
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FAVORITES);
if(fragment!=null){
ft.replace(R.id.mainActContentFrame, fragment, TAG_FAVORITES);
}else{
//use new method
ft.replace(R.id.mainActContentFrame,FavoritesFragment.class,null,TAG_FAVORITES);
}
if (addToBackStack)
ft.addToBackStack("favorites_main");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.setReorderingAllowed(false);
ft.commit();
}
private void showLinesFragment(@NonNull FragmentManager fragmentManager, boolean addToBackStack, @Nullable Bundle fragArgs){
if(getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame) instanceof LinesGridShowingFragment){
Log.d(DEBUG_TAG, "Requested lines grid fragment, but it is already open");
return;
}
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment f = fragmentManager.findFragmentByTag(LinesGridShowingFragment.FRAGMENT_TAG);
if(f!=null){
ft.replace(R.id.mainActContentFrame, f, LinesGridShowingFragment.FRAGMENT_TAG);
}else{
//use new method
ft.replace(R.id.mainActContentFrame,LinesGridShowingFragment.class,fragArgs,
LinesGridShowingFragment.FRAGMENT_TAG);
}
if (addToBackStack)
ft.addToBackStack("linesGrid");
ft.setReorderingAllowed(true)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
@Nullable
private MainScreenFragment getMainFragmentIfVisible(){
FragmentManager fraMan = getSupportFragmentManager();
Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if (fragment!= null && fragment.isVisible()) return (MainScreenFragment) fragment;
else return null;
}
@Override
public void showFloatingActionButton(boolean yes) {
var frag = getMainFragmentIfVisible();
if(frag!=null){
frag.showFloatingActionButton(yes);
}
}
/*
public void setDrawerSelectedItem(String fragmentTag){
switch (fragmentTag){
case MainScreenFragment.FRAGMENT_TAG:
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
case MapFragment.FRAGMENT_TAG:
break;
case FavoritesFragment.FRAGMENT_TAG:
mNavView.setCheckedItem(R.id.nav_favorites_item);
break;
}
}*/
@Override
public void readyGUIfor(FragmentKind fragmentType) {
//updateShowingFragmentKindInternal(fragmentType);
if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "readyGUIfor fragment kind: " + fragmentType);
Integer titleResId = null;
switch (fragmentType){
case MAP:
mNavView.setCheckedItem(R.id.nav_map_item);
titleResId = R.string.map;
break;
case FAVORITES:
mNavView.setCheckedItem(R.id.nav_favorites_item);
titleResId = R.string.nav_favorites_text;
break;
case ARRIVALS:
titleResId = R.string.nav_arrivals_text;
mNavView.setCheckedItem(R.id.nav_home);
//TODO: Figure out way to change title
//mNavView.getCheckedItem().setTitle(R.string.nav_arrivals_text);
break;
case STOPS:
titleResId = R.string.stop_search_view_title;
mNavView.setCheckedItem(R.id.nav_home);
break;
case MAIN_SCREEN_FRAGMENT:
case HOME_BUTTONS:
titleResId=R.string.app_name_full;
mNavView.setCheckedItem(R.id.nav_home);
//mNavView.getCheckedItem().setTitle(R.string.nav_home_text);
break;
case NEARBY_STOPS:
case NEARBY_ARRIVALS:
titleResId=R.string.app_name_full;
mNavView.setCheckedItem(R.id.nav_nearby);
break;
case LINES:
titleResId=R.string.lines;
mNavView.setCheckedItem(R.id.nav_lines_item);
break;
}
if(getSupportActionBar()!=null && titleResId!=null)
getSupportActionBar().setTitle(titleResId);
MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible();
if (mainFragmentIfVisible!=null){
mainFragmentIfVisible.readyGUIfor(fragmentType);
}
}
@Override
public void requestArrivalsForStopID(String ID) {
Consumer consumer = fragment -> {
fragment.setSuppressArrivalsReload(true);
fragment.requestArrivalsForStopID(ID);
};
boolean done = getMainFragmentAndDoStuff(consumer);
if(!done){
//create the fragment
final Bundle args = MainScreenFragment.makeArgsArrivals(ID);
createShowMainFragment(getSupportFragmentManager(), args ,true);
}
}
@Override
public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class,
LinesDetailFragment.Companion.makeArgs(routeGtfsId, stopIDFrom));
tr.addToBackStack("LineFromStop-"+routeGtfsId);
tr.commit();
}
private boolean getMainFragmentAndDoStuff(Consumer consumer){
FragmentManager fraMan = getSupportFragmentManager();
var frag = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if( frag instanceof MainScreenFragment mainFrag){
if(!mainFrag.isVisible()) {
showMainFragment(fraMan, mainFrag, true);
mainFrag.setMainFragmentManagerTransition(true);
} else{
mainFrag.setMainFragmentManagerTransition(false);
}
consumer.accept(mainFrag);
return true;
} else{
return false;
}
}
@Override
public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class,
LinesDetailFragment.Companion.makeArgsPattern(routeGtfsId, optionalPatternId, args));
tr.addToBackStack("LineFromOther-"+routeGtfsId);
tr.commit();
}
@Override
public void openNearbyStopsFragment() {
boolean done = getMainFragmentAndDoStuff(MainScreenFragment::openNearbyStopsFragment);
if(!done){
createShowMainFragment(getSupportFragmentManager(), MainScreenFragment.makeArgsNearby(), true);
}
}
@Override
public void openLinesFragment() {
showLinesFragment(getSupportFragmentManager(), true, null);
}
@Override
public void openFavoritesFragment() {
checkAndShowFavoritesFragment(getSupportFragmentManager(), true);
}
@Override
public void toggleSpinner(boolean state) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
if (probableFragment!=null){
probableFragment.toggleSpinner(state);
}
}
@Override
public void enableRefreshLayout(boolean yes) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
if (probableFragment!=null){
probableFragment.enableRefreshLayout(yes);
}
}
@Override
public void showMapCenteredOnStop(@Nullable Stop stop) {
createAndShowMapFragment(stop, true);
}
//Map Fragment stuff
void createAndShowMapFragment(@Nullable Stop stop, boolean addToBackStack){
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
final MapLibreFragment fragment = MapLibreFragment.newInstance(stop);
ft.replace(R.id.mainActContentFrame, fragment, MapLibreFragment.FRAGMENT_TAG);
if (addToBackStack) ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
void startIntroductionActivity(){
Intent intent = new Intent(ActivityPrincipal.this, ActivityIntro.class);
intent.putExtra(ActivityIntro.RESTART_MAIN, false);
startActivity(intent);
}
class ToolbarItemClickListener implements Toolbar.OnMenuItemClickListener{
private final Context activityContext;
public ToolbarItemClickListener(Context activityContext) {
this.activityContext = activityContext;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
final int id = item.getItemId();
if(id == R.id.action_about){
startActivity(new Intent(ActivityPrincipal.this, ActivityAbout.class));
return true;
} else if (id == R.id.action_hack) {
openIceweasel(getString(R.string.hack_url), activityContext);
return true;
} else if (id == R.id.action_source){
openIceweasel("https://gitpull.it/source/libre-busto/", activityContext);
return true;
} else if (id == R.id.action_licence){
openIceweasel("https://www.gnu.org/licenses/gpl-3.0.html", activityContext);
return true;
} else if (id == R.id.action_experiments) {
startActivity(new Intent(ActivityPrincipal.this, ActivityExperiments.class));
return true;
} else if (id == R.id.action_tutorial) {
startIntroductionActivity();
return true;
}
return false;
}
}
@Override
protected void onPause() {
super.onPause();
// stop updating the alerts
serviceAlertsViewModel.setRunningDownloadRequests(false);
}
@Override
protected void onResume() {
super.onResume();
serviceAlertsViewModel.launchAlertsPeriodCheck();
}
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
index b4724b3..82264ac 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -1,970 +1,973 @@
/*
BusTO - Fragments components
Copyright (C) 2021 Fabio Mazza
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 .
*/
package it.reyboz.bustorino.fragments;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import it.reyboz.bustorino.BuildConfig;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.util.Permissions;
import it.reyboz.bustorino.viewmodels.IntroViewModel;
import org.jetbrains.annotations.NotNull;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS;
/**
* A simple {@link Fragment} subclass.
* Use the {@link MainScreenFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain, ParentFragmentManagerFromChild{
private static final String SAVED_FRAGMENT="saved_fragment";
private static final String DEBUG_TAG = "BusTO - MainFragment";
public static final String ARG_INITIAL_CONTENT = "initial_content";
public static final String ARG_STOP_ID = "pending_stop_id";
public static final String ARG_SEARCH_QUERY = "pending_search_query";
public final static String FRAGMENT_TAG = "MainScreenFragment";
private enum SearchMode {SEARCH_ID,SEARCH_NAME,INITIAL}
public enum InternalScreen {
HOME_BUTTONS(0),
NEARBY_STOPS(1),
ARRIVALS(2),
STOP_SEARCH(3),
NEARBY_ARRIVALS(4);
public final int code;
InternalScreen(int code) { this.code = code; }
@Nullable
public static InternalScreen fromCode(int code) {
for (InternalScreen c : values()) if (c.code == code) return c;
return null;
}
@NonNull
public static InternalScreen fromFragmentKind(@NonNull FragmentKind kind){
switch (kind){
case HOME_BUTTONS -> { return InternalScreen.HOME_BUTTONS; }
case NEARBY_STOPS -> { return InternalScreen.NEARBY_STOPS; }
case FragmentKind.ARRIVALS -> { return InternalScreen.ARRIVALS; }
case FragmentKind.STOPS -> { return InternalScreen.STOP_SEARCH; }
case FragmentKind.NEARBY_ARRIVALS -> { return InternalScreen.NEARBY_ARRIVALS; }
default -> {
throw new IllegalArgumentException("Unknown fragment kind");
}
}
}
}
private FragmentHelper fragmentHelper;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText busStopSearchByIDEditText;
private EditText busStopSearchByNameEditText;
private ProgressBar progressBar;
private FloatingActionButton floatingActionButton;
/// VIEW MODELS in BaseFragment
private boolean setupOnStart = true;
private boolean suppressArrivalsReload = false;
private boolean initialScreenShown = false;
private SearchMode searchMode = SearchMode.INITIAL;
private FragmentManager childFragMan;
/// LOCATION STUFF ///
boolean pendingIntroRun = false;
boolean pendingNearbyStopsFragmentRequest = false;
boolean pendingNearbyAddToBackStack = false;
boolean locationPermissionGranted, locationPermissionAsked = false;
//// ACTIVITY ATTACHED (LISTENER ///
private CommonFragmentListener mListener;
private String pendingStopID = null;
private String pendingSearchQuery = null;
private InternalScreen internalScreen = InternalScreen.HOME_BUTTONS;
private CoordinatorLayout coordLayout;
//this is really a hackish thing, but it works
private final LinkedBlockingQueue thingsToDoOnStart = new LinkedBlockingQueue<>();
private void refreshStop() {
if(getContext() == null){
Log.w(DEBUG_TAG,"Asked to refresh stop but context is null");
return;
}
if (childFragMan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
ArrivalsFragment fragment = (ArrivalsFragment) childFragMan.findFragmentById(R.id.resultFrame);
if (fragment == null){
//we create a new fragment, which is WRONG
Log.e("BusTO-RefreshStop", "Asking for refresh when there is no fragment");
} else{
//String stopName = fragment.getStopID();
fragment.requestArrivalsForTheFragment();
}
} else { //we create a new fragment, which is WRONG
Log.w(DEBUG_TAG, "Asked to refresh stop when there is no fragment");
}
}
private final ActivityResultLauncher requestPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<>() {
@Override
public void onActivityResult(Map result) {
if (result == null) return;
if (result.get(Manifest.permission.ACCESS_COARSE_LOCATION) == null ||
result.get(Manifest.permission.ACCESS_FINE_LOCATION) == null)
return;
Log.d(DEBUG_TAG, "Permissions for location are: " + result);
if (Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION))
|| Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))) {
locationPermissionGranted = true;
Log.w(DEBUG_TAG, "Starting position");
/*if (mListener != null && getContext() != null) {
if (locationManager == null)
locationManager = AppLocationManager.getInstance(getContext());
locationManager.addLocationRequestFor(requester);
}
*/
// show nearby fragment
//showNearbyStopsFragment();
Log.d(DEBUG_TAG, "We have location permission");
if (pendingNearbyStopsFragmentRequest) {
showNearbyFragmentIfPossible(pendingNearbyAddToBackStack);
pendingNearbyStopsFragmentRequest = false;
}
}
if (pendingNearbyStopsFragmentRequest) pendingNearbyStopsFragmentRequest = false;
}
});
public MainScreenFragment() {
// Required empty public constructor
}
public static MainScreenFragment newInstance(@NonNull InternalScreen kind,
@Nullable String stopId,
@Nullable String query) {
MainScreenFragment f = new MainScreenFragment();
f.setArguments(makeArgs(kind, stopId, query));
return f;
}
public static MainScreenFragment newInstance(@NonNull InternalScreen kind, @Nullable Bundle args){
MainScreenFragment f = new MainScreenFragment();
if (args != null) {
f.setArguments(args);
}
return f;
}
/**
* Create the bundle for the arguments of the fragment
* @param kind the kind of initial screen
* @param stopId
* @param query
* @return
*/
public static Bundle makeArgs(@NonNull InternalScreen kind, @Nullable String stopId, @Nullable String query) {
Bundle b = new Bundle();
b.putInt(ARG_INITIAL_CONTENT, kind.code);
if (stopId != null) b.putString(ARG_STOP_ID, stopId);
if (query != null) b.putString(ARG_SEARCH_QUERY, query);
return b;
}
public static Bundle makeArgsArrivals(@NonNull String stopID){
return makeArgs(InternalScreen.ARRIVALS, stopID, null);
}
public static Bundle makeArgsStops(@NonNull String query){
return makeArgs(InternalScreen.STOP_SEARCH, query, null);
}
public static Bundle makeArgsNearby(){
return makeArgs(InternalScreen.NEARBY_STOPS, null, null);
}
public static Bundle makeArgsButtonsScreen(){
return makeArgs(InternalScreen.HOME_BUTTONS, null, null);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
Log.d(DEBUG_TAG, "ARGS ARE NOT NULL: "+ args);
if (args.containsKey(ARG_INITIAL_CONTENT)) {
int code = args.getInt(ARG_INITIAL_CONTENT, InternalScreen.HOME_BUTTONS.code);
InternalScreen parsed = InternalScreen.fromCode(code);
internalScreen = (parsed != null) ? parsed : InternalScreen.HOME_BUTTONS;
}
String stopId = args.getString(ARG_STOP_ID);
- if (stopId != null) pendingStopID = stopId;
- pendingSearchQuery = args.getString(ARG_SEARCH_QUERY);
+ if (stopId != null)
+ pendingSearchQuery = stopId;
+ else
+ pendingSearchQuery = args.getString(ARG_SEARCH_QUERY);
}
fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame);
}
@Override
public boolean needToPopMainStackOnBack() {
return fragmentHelper.needToPopMainStackOnBack();
}
@Override
public void setMainFragmentManagerTransition(boolean yes) {
fragmentHelper.setMainFragmentManagerTransition(yes);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_main_screen, container, false);
/// UI ELEMENTS //
busStopSearchByIDEditText = root.findViewById(R.id.busStopSearchByIDEditText);
busStopSearchByNameEditText = root.findViewById(R.id.busStopSearchByNameEditText);
progressBar = root.findViewById(R.id.progressBar);
swipeRefreshLayout = root.findViewById(R.id.listRefreshLayout);
floatingActionButton = root.findViewById(R.id.floatingActionButton);
busStopSearchByIDEditText.setSelectAllOnFocus(true);
busStopSearchByIDEditText
.setOnEditorActionListener((v, actionId, event) -> {
// IME_ACTION_SEARCH alphabetical option
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
onSearchClick(v);
return true;
}
return false;
});
busStopSearchByNameEditText
.setOnEditorActionListener((v, actionId, event) -> {
// IME_ACTION_SEARCH alphabetical option
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
onSearchClick(v);
return true;
}
return false;
});
swipeRefreshLayout
.setOnRefreshListener(this::refreshStop);
swipeRefreshLayout.setColorSchemeResources(R.color.blue_500, R.color.orange_500);
coordLayout = root.findViewById(R.id.coord_layout);
floatingActionButton.setImageResource(R.drawable.magnifying_glass_larger);
floatingActionButton.setOnClickListener((this::onToggleKeyboardLayout));
busStopSearchByIDEditText.setOnFocusChangeListener((v, hasFocus) -> {
//Log.d(DEBUG_TAG, "stop search by ID has focus: " + hasFocus);
if(hasFocus)
setSearchModeBusStopID();
});
busStopSearchByNameEditText.setOnFocusChangeListener((v, hasFocus) -> {
//Log.d(DEBUG_TAG, "stop search by Name has focus: " + hasFocus);
if(hasFocus)
setSearchModeBusStopName();
});
AppCompatImageButton qrButton = root.findViewById(R.id.QRButton);
qrButton.setOnClickListener(this::onQRButtonClick);
AppCompatImageButton searchButton = root.findViewById(R.id.searchButton);
searchButton.setOnClickListener(this::onSearchClick);
// Fragment stuff
childFragMan = getChildFragmentManager();
childFragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED"));
/*
cr.setAccuracy(Criteria.ACCURACY_FINE);
cr.setAltitudeRequired(false);
cr.setBearingRequired(false);
cr.setCostAllowed(true);
cr.setPowerRequirement(Criteria.NO_REQUIREMENT);
*/
//locationManager = AppLocationManager.getInstance(requireContext());
IntroViewModel introViewModel = new ViewModelProvider(requireActivity()).get(IntroViewModel.class);
introViewModel.getIntroIsRunning().observe(getViewLifecycleOwner(), isRunning -> {
pendingIntroRun = isRunning;
});
// TODO: Figure out how to go back to home when pressing home in the nav side bar
/*fragShowingViewModel.getKindShowingFragment().observe(getViewLifecycleOwner(), kind -> {
Log.w(DEBUG_TAG, "showing fragment kind: " + kind);
try {
var screenType = InternalScreen.fromFragmentKind(kind);
if(screenType != internalScreen) {
showDifferentSubFragments(screenType);
internalScreen = screenType;
}
} catch (IllegalArgumentException e) {
//ignored
Log.d(DEBUG_TAG, "no update from fragment kind");
}
});
*/
Log.d(DEBUG_TAG, "OnCreateView, savedInstanceState null: "+(savedInstanceState==null));
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(DEBUG_TAG, "onViewCreated, SwipeRefreshLayout visible: "+(swipeRefreshLayout.getVisibility()==View.VISIBLE));
Log.d(DEBUG_TAG, "Saved instance state is: "+savedInstanceState);
//Restore instance state
/*if (savedInstanceState!=null){
Fragment fragment = getChildFragmentManager().getFragment(savedInstanceState, SAVED_FRAGMENT);
if (fragment!=null){
getChildFragmentManager().beginTransaction().add(R.id.resultFrame, fragment).commit();
setupOnStart = false;
}
}
*/
if (getChildFragmentManager().findFragmentById(R.id.resultFrame)!= null){
swipeRefreshLayout.setVisibility(View.VISIBLE);
// The child FragmentManager has restored its content — don't dispatch again
return;
}
if (savedInstanceState != null) return;
showDifferentSubFragments(internalScreen);
}
/**
* Installs the initial child fragment based on the arguments supplied as arguments
*/
private void showDifferentSubFragments(@NonNull InternalScreen screen) {
boolean firstTime = !initialScreenShown;
switch (screen) {
case NEARBY_STOPS:
case NEARBY_ARRIVALS: // TODO differentiate later
//add to back stack if it is not just created
showNearbyStopsFragmentChecking(!firstTime);
break;
case ARRIVALS:
// pendingStopID is consumed in onResume → requestArrivalsForStopID
+ if(pendingSearchQuery != null && isResumed()) {
+ swipeRefreshLayout.setVisibility(View.VISIBLE);
+ Log.d(DEBUG_TAG, "Searching arrivals for initial stop: "+pendingSearchQuery);
+ requestsArrivalsInternal(pendingSearchQuery, false);
+ pendingSearchQuery = null;
+ }
+
break;
case STOP_SEARCH:
if (pendingSearchQuery != null && pendingSearchQuery.length() >= 2) {
fragmentHelper.requestStopSearch(pendingSearchQuery);
} else {
showButtonsFragment(firstTime);
}
pendingSearchQuery = null;
break;
case HOME_BUTTONS:
default:
showButtonsFragment(firstTime);
}
if(!initialScreenShown){
initialScreenShown = true;
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(DEBUG_TAG, "Saving instance state");
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment!=null)
getChildFragmentManager().putFragment(outState, SAVED_FRAGMENT, fragment);
//if (fragmentHelper!=null) fragmentHelper.setBlockAllActivities(true);
}
public void setSuppressArrivalsReload(boolean value){
suppressArrivalsReload = value;
// we have to suppress the reloading of the (possible) ArrivalsFragment
/*if(value) {
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment instanceof ArrivalsFragment) {
ArrivalsFragment frag = (ArrivalsFragment) fragment;
frag.setReloadOnResume(false);
}
}
*/
}
/**
* Cancel the reload of the arrival times
* because we are going to pop the fragment
*/
public void cancelReloadArrivalsIfNeeded(){
if(getContext()==null) return; //we are not attached
//Fragment fr = getChildFragmentManager().findFragmentById(R.id.resultFrame);
fragmentHelper.stopLastRequestIfNeeded();
toggleSpinner(false);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(DEBUG_TAG, "OnAttach called, setupOnAttach: "+ setupOnStart);
if (context instanceof CommonFragmentListener) {
mListener = (CommonFragmentListener) context;
} else {
throw new RuntimeException(context
+ " must implement CommonFragmentListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
// setupOnAttached = true;
}
@Override
public void onStart() {
super.onStart();
Log.d(DEBUG_TAG, "onStart called, setupOnStart: "+setupOnStart);
try {
while (!thingsToDoOnStart.isEmpty()) {
var task = thingsToDoOnStart.take();
task.run();
}
} catch (InterruptedException e) {
Log.w(DEBUG_TAG, "Interrupted while doing task for start");
thingsToDoOnStart.clear();
}
if (setupOnStart) {
if (pendingStopID==null){
if(!pendingIntroRun){
//show the fragment
//showButtonsFragment();
}
}
else{
///TODO: if there is a stop displayed, we need to hold the update
}
setupOnStart = false;
}
}
private void showButtonsFragment(boolean addInsteadOfReplace){
swipeRefreshLayout.setVisibility(View.VISIBLE);
var ft = childFragMan.beginTransaction();
var frag = ButtonsFragment.newInstance();
if(addInsteadOfReplace)
ft.add(R.id.resultFrame,frag, ButtonsFragment.FRAGMENT_TAG);
else{
ft.replace(R.id.resultFrame, frag, ButtonsFragment.FRAGMENT_TAG);
ft.addToBackStack(null);
}
ft.commit();
}
public void showButtonsFragmentIfNotNearby(boolean addToBackStack){
if(isAdded()) {
var framan = getChildFragmentManager();
var showingFrag = framan.findFragmentById(R.id.resultFrame);
if (showingFrag == null || showingFrag instanceof NearbyStopsFragment) {
var fragHome = ButtonsFragment.newInstance();
var ft = framan.beginTransaction();
if (showingFrag == null) {
ft.add(R.id.resultFrame, fragHome, ButtonsFragment.FRAGMENT_TAG);
} else {
ft.replace(R.id.resultFrame, fragHome, ButtonsFragment.FRAGMENT_TAG);
}
if (addToBackStack) ft.addToBackStack(null);
ft.commit();
} else {
Log.d(DEBUG_TAG, "attempting to show buttons home fragment but have other types (different than nearby)");
}
} else{
Log.d(DEBUG_TAG, "Fragment is not added, putting in queue of things to do");
try {
thingsToDoOnStart.put(() -> {
showButtonsFragmentIfNotNearby(addToBackStack);
});
} catch (InterruptedException e) {
Log.e(DEBUG_TAG,"Cannot add task");
}
}
}
private void showNearbyStopsFragmentChecking(boolean addToBackStack){
if(!checkLocationPermission()){
requestLocationPermission();
pendingNearbyStopsFragmentRequest = true;
pendingNearbyAddToBackStack = addToBackStack;
Log.d(DEBUG_TAG, "requesting location permission for nearby fragment");
}
else {
Log.d(DEBUG_TAG, "Showing nearby stops fragment");
showNearbyFragmentIfPossible(addToBackStack);
}
}
@Override
public void onResume() {
super.onResume();
final Context con = requireContext();
Log.w(DEBUG_TAG, "OnResume called, setupOnStart: "+ setupOnStart);
//recheck the introduction activity has been run
if(Permissions.bothLocationPermissionsGranted(con)){
Log.d(DEBUG_TAG, "Location permission OK");
} //don't request permission
// if we have a pending stopID request, do it
Log.d(DEBUG_TAG, "Pending stop ID for arrivals: "+pendingStopID);
//this is the second time we are attaching this fragment ->
Log.d(DEBUG_TAG, "Waiting for new stop request: "+ suppressArrivalsReload);
- if(!suppressArrivalsReload && pendingStopID==null){
- //none of the following cases are true
- // check if we are showing any fragment
- /*
- //TODO: check if this is needed
- final Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
-
- if(fragment==null || swipeRefreshLayout.getVisibility() != View.VISIBLE){
- //we are not showing anything
- if(Permissions.anyLocationPermissionsGranted(getContext())){
- showNearbyFragmentIfPossible();
- }
- }
-
- */
- }
if (suppressArrivalsReload){
// we have to suppress the reloading of the (possible) ArrivalsFragment
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment instanceof ArrivalsFragment){
ArrivalsFragment frag = (ArrivalsFragment) fragment;
frag.setReloadOnResume(false);
}
//deactivate
suppressArrivalsReload = false;
}
-
- if(pendingStopID!=null){
+ // check if the fragment start query is null
+ if(pendingSearchQuery!=null) {
+ requestsArrivalsInternal(pendingSearchQuery, false);
+ pendingSearchQuery = null;
+ }
+ else if(pendingStopID!=null){
Log.d(DEBUG_TAG, "Pending request for arrivals at stop ID: "+pendingStopID);
requestArrivalsForStopID(pendingStopID);
pendingStopID = null;
}
//mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT);
//fragmentHelper.setBlockAllActivities(false);
}
@Override
public void onPause() {
//mainHandler = null;
//locationManager.removeLocationRequestFor(requester);
//fragmentHelper.setBlockAllActivities(true);
fragmentHelper.stopLastRequestIfNeeded();
super.onPause();
}
/*
GUI METHODS
*/
@Override
public void onQrScanSuccess(@NotNull String busIDToSearch) {
busStopSearchByIDEditText.setText(busIDToSearch);
requestArrivalsForStopID(busIDToSearch);
}
/**
* QR scan button clicked
*
* @param v View QRButton clicked
*/
public void onQRButtonClick(View v) {
launchBarcodeScan();
}
/**
* OK this is pure shit
*
* @param v View clicked
*/
public void onSearchClick(View v) {
//final StopsFinderByName[] stopsFinderByNames = new StopsFinderByName[]{new GTTStopsFetcher(), new FiveTStopsFetcher()};
if (searchMode == SearchMode.SEARCH_ID) {
String busStopID = busStopSearchByIDEditText.getText().toString();
fragmentHelper.stopLastRequestIfNeeded();
requestArrivalsForStopID(busStopID);
} else if (searchMode == SearchMode.SEARCH_NAME) {
// searchMode == SEARCH_BY_NAME
String query = busStopSearchByNameEditText.getText().toString();
query = query.trim();
if(getContext()!=null) {
if (query.length() < 1) {
Toast.makeText(getContext(), R.string.insert_bus_stop_name_error, Toast.LENGTH_SHORT).show();
} else if(query.length()< 2){
Toast.makeText(getContext(), R.string.query_too_short, Toast.LENGTH_SHORT).show();
}
else {
fragmentHelper.requestStopSearch(query);
}
}
}
}
public void onToggleKeyboardLayout(View v) {
switch (searchMode){
case SEARCH_ID:
setSearchModeBusStopName();
if (busStopSearchByNameEditText.requestFocus()) {
showKeyboard();
}
break;
case SEARCH_NAME:
case INITIAL:
setSearchModeBusStopID();
if (busStopSearchByIDEditText.requestFocus()) {
showKeyboard();
}
}
}
@Override
public void enableRefreshLayout(boolean yes) {
swipeRefreshLayout.setEnabled(yes);
}
////////////////////////////////////// GUI HELPERS /////////////////////////////////////////////
public void showKeyboard() {
if(getActivity() == null) return;
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
View view;
if(searchMode == SearchMode.SEARCH_ID)
view= busStopSearchByIDEditText;
else if(searchMode == SearchMode.SEARCH_NAME)
view = busStopSearchByNameEditText;
else{
Log.e(DEBUG_TAG, "Asking to show keyboard but SearchMode is "+searchMode+", ignoring");
return;
}
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
private void setSearchModeBusStopID() {
searchMode = SearchMode.SEARCH_ID;
busStopSearchByNameEditText.setVisibility(View.GONE);
busStopSearchByNameEditText.setText("");
busStopSearchByIDEditText.setVisibility(View.VISIBLE);
floatingActionButton.setImageResource(R.drawable.alphabetical);
}
private void setSearchModeBusStopName() {
searchMode = SearchMode.SEARCH_NAME;
busStopSearchByIDEditText.setVisibility(View.GONE);
busStopSearchByIDEditText.setText("");
busStopSearchByNameEditText.setVisibility(View.VISIBLE);
floatingActionButton.setImageResource(R.drawable.numeric);
}
protected boolean isNearbyFragmentShown(){
Fragment fragment = getChildFragmentManager().findFragmentByTag(NearbyStopsFragment.FRAGMENT_TAG);
return (fragment!= null && fragment.isResumed());
}
/**
* Having that cursor at the left of the edit text makes me cancer.
*
* @param busStopID bus stop ID
*/
private void setBusStopSearchByIDEditText(String busStopID) {
busStopSearchByIDEditText.setText(busStopID);
busStopSearchByIDEditText.setSelection(busStopID.length());
}
@Nullable
@Override
public View getBaseViewForSnackBar() {
return coordLayout;
}
@Override
public void toggleSpinner(boolean enable) {
if (enable) {
//already set by the RefreshListener when needed
//swipeRefreshLayout.setRefreshing(true);
progressBar.setVisibility(View.VISIBLE);
} else {
swipeRefreshLayout.setRefreshing(false);
progressBar.setVisibility(View.GONE);
}
}
private void prepareGUIForArrivals() {
swipeRefreshLayout.setEnabled(true);
swipeRefreshLayout.setVisibility(View.VISIBLE);
//actionHelpMenuItem.setVisible(true);
}
private void prepareGUIForBusStops() {
swipeRefreshLayout.setEnabled(false);
swipeRefreshLayout.setVisibility(View.VISIBLE);
//actionHelpMenuItem.setVisible(false);
}
@Override
public void showFloatingActionButton(boolean yes) {
//mListener.showFloatingActionButton(yes);
if(yes)
floatingActionButton.setVisibility(View.VISIBLE);
else
floatingActionButton.setVisibility(View.GONE);
}
/**
* This provides a temporary fix to make the transition
* to a single asynctask go smoother
*
* @param fragmentType the type of fragment created
*/
@Override
public void readyGUIfor(FragmentKind fragmentType) {
if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "Readying main fragment for type "+fragmentType);
//if we are getting results, already, stop waiting for nearbyStops
if (fragmentType == FragmentKind.ARRIVALS || fragmentType == FragmentKind.STOPS) {
hideKeyboard();
if (pendingNearbyStopsFragmentRequest) {
//locationManager.removeLocationRequestFor(requester);
pendingNearbyStopsFragmentRequest = false;
}
}
if (fragmentType == null) Log.e("ActivityMain", "Problem with fragmentType");
else
switch (fragmentType) {
case ARRIVALS:
prepareGUIForArrivals();
break;
case STOPS:
prepareGUIForBusStops();
break;
default:
//Log.d(DEBUG_TAG, "Fragment type is unknown");
return;
}
// Shows hints
}
@Override
public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom) {
//pass to activity
if(mListener!=null) mListener.openLineFromStop(routeGtfsId, stopIDFrom);
}
@Override
public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
if(mListener!=null) mListener.openLineFromVehicle(routeGtfsId, optionalPatternId, args);
}
@Override
public void openNearbyStopsFragment() {
if(isAdded())
showNearbyStopsFragmentChecking(true);
else
try{
thingsToDoOnStart.put(() -> showNearbyStopsFragmentChecking(true));
} catch (InterruptedException e) {
Log.e(DEBUG_TAG, "trying to put open nearby in task but was interrupted");
}
}
@Override
public void openLinesFragment() {
if(mListener!=null) mListener.openLinesFragment();
}
@Override
public void openFavoritesFragment() {
if(mListener!=null) mListener.openFavoritesFragment();
}
@Override
public void showMapCenteredOnStop(Stop stop) {
if(mListener!=null) mListener.showMapCenteredOnStop(stop);
}
-
- /**
- * Main method for stops requests
- * @param ID the Stop ID
- */
- @Override
- public void requestArrivalsForStopID(String ID) {
+ private void requestsArrivalsInternal(String stopID, boolean addToBackStack) {
if (!isResumed()){
- //defer request
- pendingStopID = ID;
- Log.d(DEBUG_TAG, "Deferring update for stop "+ID+ " saved: "+pendingStopID);
+ //defer request to onResume - it will be added to the backstack
+ pendingStopID = stopID;
+ Log.d(DEBUG_TAG, "Deferring update for stop "+stopID+ " saved: "+pendingStopID);
return;
}
final boolean delayedRequest = !(pendingStopID==null);
final FragmentManager framan = getChildFragmentManager();
if (getContext()==null){
Log.e(DEBUG_TAG, "Asked for arrivals with null context");
return;
}
- if (ID == null || ID.isEmpty()) {
+ if (stopID == null || stopID.isEmpty()) {
// we're still in UI thread, no need to mess with Progress
showToastMessage(R.string.insert_bus_stop_number_error, true);
toggleSpinner(false);
} else{
- var palinaTrial = new Palina(ID);
+ // ensure that the new sub-fragment is gonna be visible
+ swipeRefreshLayout.setVisibility(View.VISIBLE);
+
+ var palinaTrial = new Palina(stopID);
if (framan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment fragment) {
if (fragment.isFragmentForTheSameStop(palinaTrial)){
// Run with previous fetchers
//fragment.getCurrentFetchers().toArray()
fragment.requestArrivalsForTheFragment();
} else{
// The rest of the case is handled by the fragment Helper
- fragmentHelper.showArrivalsFragmentForStop(palinaTrial, true);
+ fragmentHelper.showArrivalsFragmentForStop(palinaTrial, addToBackStack);
}
}
else {
// this is not needed any more
//prepareGUIForArrivals();
- fragmentHelper.showArrivalsFragmentForStop(palinaTrial, true);
+ fragmentHelper.showArrivalsFragmentForStop(palinaTrial, addToBackStack);
}
}
}
+ /**
+ * Main method for stops requests
+ * @param ID the Stop ID
+ */
+ @Override
+ public void requestArrivalsForStopID(String ID) {
+ requestsArrivalsInternal(ID, true);
+ }
+
private boolean checkLocationPermission(){
final Context context = getContext();
if(context==null) return false;
final boolean noPermission = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED;
return !noPermission;
}
private void requestLocationPermission(){
if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
makeToast(R.string.enable_position_message_nearby);
}
requestPermissionLauncher.launch(LOCATION_PERMISSIONS);
}
private void showNearbyFragmentIfPossible(boolean addToBackStack) {
if (isNearbyFragmentShown()) {
//nothing to do
Log.d(DEBUG_TAG, "Asked to show nearby fragment but we already are showing it");
return;
}
if (getContext() == null) {
Log.e(DEBUG_TAG, "Wanting to show nearby fragment but context is null");
return;
}
if (!childFragMan.isDestroyed()) {
//Go ahead with the request
swipeRefreshLayout.setVisibility(View.VISIBLE);
final Fragment existingFrag = childFragMan.findFragmentById(R.id.resultFrame);
// fragment;
if (!(existingFrag instanceof NearbyStopsFragment)){
Log.d(DEBUG_TAG, "actually showing Nearby Stops Fragment");
//there is no fragment showing
var nearbyFrag = (NearbyStopsFragment) childFragMan.findFragmentByTag(NearbyStopsFragment.FRAGMENT_TAG);
if(nearbyFrag==null){
nearbyFrag = NearbyStopsFragment.newInstance(NearbyStopsFragment.FragType.STOPS);
}
FragmentTransaction ft = childFragMan.beginTransaction();
ft.replace(R.id.resultFrame, nearbyFrag, NearbyStopsFragment.FRAGMENT_TAG);
if(addToBackStack) ft.addToBackStack(null);
if (getActivity()!=null && !getActivity().isFinishing())
ft.commit();
else Log.e(DEBUG_TAG, "Not showing nearby fragment because activity null or is finishing");
}
pendingNearbyStopsFragmentRequest = false;
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt
index 458f800..523fc45 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt
@@ -1,858 +1,861 @@
/*
BusTO - Fragments components
Copyright (C) 2018 Fabio Mazza
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 .
*/
package it.reyboz.bustorino.fragments
import android.content.Context
import android.content.res.ColorStateList
import android.location.Location
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.widget.AppCompatButton
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkInfo
import com.google.android.material.button.MaterialButton
import it.reyboz.bustorino.BuildConfig
import it.reyboz.bustorino.R
import it.reyboz.bustorino.adapters.ArrivalsStopAdapter
import it.reyboz.bustorino.adapters.SquareStopAdapter
import it.reyboz.bustorino.backend.*
import it.reyboz.bustorino.data.DatabaseUpdate
import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager
import it.reyboz.bustorino.middleware.FusedNativeLocationProvider
import it.reyboz.bustorino.middleware.FusedNativeLocationProvider.LocationUpdateListener
import it.reyboz.bustorino.util.Permissions
import it.reyboz.bustorino.util.Permissions.Companion.bothLocationPermissionsGranted
import it.reyboz.bustorino.util.StopSorterByDistance
import it.reyboz.bustorino.util.ViewUtils
import it.reyboz.bustorino.viewmodels.NearbyStopsViewModel
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
class NearbyStopsFragment : ScreenBaseFragment() {
override fun getBaseViewForSnackBar(): View? {
return null
}
enum class FragType(val num: Int) {
STOPS(1), ARRIVALS(2);
companion object {
@JvmStatic
fun fromNum(i: Int): FragType {
when (i) {
1 -> return STOPS
2 -> return ARRIVALS
else -> throw IllegalArgumentException("type not recognized")
}
}
}
}
private enum class LocationShowingStatus {
SEARCHING, LOCATION_FOUND, DISABLED, NO_PERMISSION //NO_STOPS_NEARBY
}
private var mListener: FragmentListenerMain? = null
private var fragmentType = FragType.STOPS
private lateinit var gridRecyclerView: RecyclerView
private var dataAdapter: SquareStopAdapter? = null
private var gridLayoutManager: AutoFitGridLayoutManager? = null
private var lastPosition: GPSPoint? = null
private var circlingProgressBar: ProgressBar? = null
private lateinit var flatProgressBar: ProgressBar
//protected SharedPreferences globalSharedPref;
//private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
private var messageTextView: TextView? = null
private lateinit var enableLocationButton : MaterialButton
private var titleTextView: TextView? = null
private var loadingTextView: TextView? = null
private var scrollListener: CommonScrollListener? = null
private var switchButton: AppCompatButton? = null
private var firstLocForStops = true
private var firstLocForArrivals = true
private var stopsMaxDistance = -3
private var stopsMinNumber = -1
//These are useful for the case of nearby arrivals
private var arrivalsStopAdapter: ArrivalsStopAdapter? = null
private var currentNearbyStops: ArrayList? = null
private var showingStatus = LocationShowingStatus.NO_PERMISSION
private var isLocationEnabled = false
private var dataShownInAdapter = AtomicBoolean(false)
private var noDataMessageId = R.string.no_stops_nearby
private var loadingDataMessageId = R.string.position_searching_message
private val locationUpdateListener: LocationUpdateListener = object : LocationUpdateListener {
override fun onLocationUpdate(location: Location) {
if (location.getAccuracy() < MIN_ACCURACY) {
lastPosition = GPSPoint(location.getLatitude(), location.getLongitude())
viewModel.setLastLocation(location)
} else{
Log.d(DEBUG_TAG, "Refusing location ${location.latitude},${location.longitude} because accuracy: ${location.accuracy} > MIN_ACCURACY=$MIN_ACCURACY")
}
}
override fun onFusedStatusChanged(isEnabled: Boolean) {
Log.d(DEBUG_TAG, "Location provider is enabled: " + isEnabled)
isLocationEnabled = isEnabled
if(!dataShownInAdapter.get())
if (isEnabled) {
setShowingStatus(LocationShowingStatus.SEARCHING)
} else {
setShowingStatus(LocationShowingStatus.DISABLED)
}
}
}
private val locationPermissionLauncher = getPositionRequestLauncher(){ granted ->
startLocationUpdatesByType()
setShowingStatus(LocationShowingStatus.SEARCHING)
}
// Two different settings for the location provider
private val locationOptionsArrivals = FusedNativeLocationProvider.Options(5 * 1000L, 25f)
private val locationOptionsStops = FusedNativeLocationProvider.Options(1000L, 5f)
private var locationProvider: FusedNativeLocationProvider? = null
/*private val arrivalsListener: ArrivalsListener = object : ArrivalsListener {
override fun setProgress(completedRequests: Int, pendingRequests: Int) {
if (pendingRequests == 0) {
flatProgressBar.setIndeterminate(true)
flatProgressBar.setVisibility(View.GONE)
} else {
flatProgressBar.setIndeterminate(false)
flatProgressBar.progress = completedRequests
}
}
/*override fun onAllRequestsCancelled() {
if (flatProgressBar != null) flatProgressBar!!.setVisibility(View.GONE)
}
*/
override fun showCompletedArrivals(completedPalinas: ArrayList) {
showArrivalsInRecycler(completedPalinas)
}
}
*/
//ViewModel
private val viewModel : NearbyStopsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let{
setFragmentType(FragType.fromNum(it.getInt(FRAGMENT_TYPE_KEY)))
}
//locManager = (LocationManager) requireContext().getSystemService(Context.LOCATION_SERVICE);
//fragmentLocationListener = new FragmentLocationListener();
if (getContext() != null) {
//globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE);
//globalSharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
}
//NearbyArrivalsDownloader nearbyArrivalsDownloader = new NearbyArrivalsDownloader(getContext().getApplicationContext(), arrivalsListener);
locationProvider = FusedNativeLocationProvider(requireContext())
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
val context = requireContext()
val root = inflater.inflate(R.layout.fragment_nearby_stops, container, false)
gridRecyclerView = root.findViewById(R.id.stopGridRecyclerView)
gridLayoutManager = AutoFitGridLayoutManager(
context.getApplicationContext(),
utils.convertDipToPixels(context, COLUMN_WIDTH_DP.toFloat()).toInt()
)
gridRecyclerView.setLayoutManager(gridLayoutManager)
gridRecyclerView.setHasFixedSize(false)
circlingProgressBar = root.findViewById(R.id.circularProgressBar)
flatProgressBar = root.findViewById(R.id.horizontalProgressBar)
messageTextView = root.findViewById(R.id.messageTextView)
enableLocationButton = root.findViewById(R.id.grantLocationButton)
titleTextView = root.findViewById(R.id.titleTextView)
loadingTextView = root.findViewById(R.id.positionLoadingTextView)
switchButton = root.findViewById(R.id.switchButton)
scrollListener = CommonScrollListener(mListener, false)
switchButton!!.setOnClickListener { v: View? -> switchFragmentType() }
if (BuildConfig.DEBUG) Log.d(DEBUG_TAG, "onCreateView")
val appContext = requireContext().applicationContext
DatabaseUpdate.watchUpdateWorkStatus(context, this){ workInfos ->
if (workInfos.isEmpty()) {
viewModel.setDBUpdateRunning(false)
return@watchUpdateWorkStatus
}
val wi = workInfos.get(0)
if (wi.state == WorkInfo.State.RUNNING && locationProvider!!.isRunning()) {
locationProvider!!.stopUpdates()
viewModel.setDBUpdateRunning(true)
} else {
//start the request
checkPermissionLocationStart()
viewModel.setDBUpdateRunning(false)
//actually restart request
}
}
//add location listener
locationProvider!!.addListener(locationUpdateListener)
enableLocationButton.setOnClickListener {
locationPermissionLauncher.launch(Permissions.LOCATION_PERMISSIONS)
}
return root
}
private fun checkPermissionLocationStart(){
Log.d(DEBUG_TAG, "Check permission and start location updates")
if (bothLocationPermissionsGranted(requireContext())) {
if (!locationProvider!!.isRunning()) {
startLocationUpdatesByType()
setShowingStatus(LocationShowingStatus.SEARCHING)
} else{
Log.w(DEBUG_TAG, "Asked to check and start location updates, but provider is already running")
}
} else {
setShowingStatus(LocationShowingStatus.NO_PERMISSION)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
gridRecyclerView.setVisibility(View.INVISIBLE)
gridRecyclerView.addOnScrollListener(scrollListener!!)
//mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS)
//observe the livedata
viewModel.stopsAtDistance.observe(getViewLifecycleOwner()) {stops ->
Log.d(DEBUG_TAG, "Received " + stops.size + " stops nearby")
var distance = viewModel.distanceMtLiveData.getValue()
if (distance == null) {
distance = 40
}
if ((stops.size < stopsMinNumber && distance <= stopsMaxDistance)) {
viewModel.setDistance(distance + 50)
//viewModel.requestStopsAtDistance(distance, true);
//Log.d(DEBUG_TAG, "Doubling distance now!");
return@observe // THIS WORKS AS AN `else`
}
displayStopsOrRequestArrivals(stops)
}
viewModel.locationLiveData.observe(getViewLifecycleOwner()) {loc ->
if(loc!=null){
setShowingStatus(LocationShowingStatus.LOCATION_FOUND)
}
//the stops are updated manually
}
viewModel.downloadingArrivals.observe(viewLifecycleOwner){ running ->
flatProgressBar.isIndeterminate = true
if(!running) flatProgressBar.visibility = View.GONE
else flatProgressBar.visibility = View.VISIBLE
}
/*viewModel.progressPerc.observe(viewLifecycleOwner){ progress ->
flatProgressBar.isIndeterminate = false
flatProgressBar.progress = progress
flatProgressBar.max = 100
if (progress<100){
flatProgressBar.visibility = View.VISIBLE
}
}
*/
viewModel.arrivalsDecoupled.observe(viewLifecycleOwner){ stoprouteList ->
if (getContext() == null) {
Log.e(DEBUG_TAG, "Trying to show arrivals in Recycler but we're not attached")
return@observe
}
showArrivals(stoprouteList)
//arrivalsStopAdapter.notifyDataSetChanged();
//showRecyclerHidingLoadMessage()
//if (mListener != null) mListener!!.readyGUIfor(FragmentKind.NEARBY_ARRIVALS)
}
//added
//checkPermissionLocationStart()
}
private fun displayStopsOrRequestArrivals(stops: ArrayList){
if (!stops.isEmpty()) {
currentNearbyStops = stops
//displayStopsOrLaunchArrivalsRequest(stops, lastPosition)
viewModel.locationLiveData.value?.let{loc ->
when(fragmentType){
FragType.STOPS->{
lastPosition?.let{loc ->
Collections.sort(stops, StopSorterByDistance(loc))
showStopsInRecycler(stops,loc)
}
}
FragType.ARRIVALS -> {
+ lastPosition?.let{
+ Collections.sort(stops, StopSorterByDistance(loc))
+ }
viewModel.requestArrivalsForStops(stops)
}
}
}
} else{
showNoStopsMessage()
viewModel.cancelAllArrivalsRequests()
}
}
private fun showArrivals(stoprouteList: ArrayList){
val context = requireContext()
if (firstLocForArrivals) {
mListener?.let{
lastPosition?.let{ pos ->
arrivalsStopAdapter = ArrivalsStopAdapter(stoprouteList, it, context, pos)
gridRecyclerView.setAdapter(arrivalsStopAdapter)
firstLocForArrivals = false
}
}
} else {
lastPosition?.let{ pos ->
arrivalsStopAdapter?.setRoutesPairListAndPosition(stoprouteList, pos)
}
}
dataShownInAdapter.set(true)
}
/**
* Internal bit used to start location updates
*/
private fun startLocationUpdatesByType() {
when (fragmentType) {
FragType.STOPS -> locationProvider!!.startUpdates(locationOptionsStops)
FragType.ARRIVALS -> locationProvider!!.startUpdates(locationOptionsArrivals)
}
}
/**
* Use this method to set the fragment type
* @param type the type, TYPE_ARRIVALS or TYPE_STOPS
*/
private fun setFragmentType(type: FragType) {
val isChanged = fragmentType != type
this.fragmentType = type
if (isChanged) {
startLocationUpdatesByType()
}
}
/**
* Set the location in the view model if it is good
* @param location new location
*/
/*
private fun updateLocationViewModel(location: Location, accuracy: Float = 150f) {
}
*/
private fun setShowingStatus(newStatus: LocationShowingStatus) {
var newStatus = newStatus
if (newStatus == showingStatus) {
return
}
if (BuildConfig.DEBUG) Log.d(DEBUG_TAG, "Changing showing status from $showingStatus to $newStatus")
if (!isLocationEnabled && newStatus != LocationShowingStatus.NO_PERMISSION) {
Log.d(DEBUG_TAG, "asked to show status: $newStatus but the position is disabled")
newStatus = LocationShowingStatus.DISABLED
}
when (newStatus) {
LocationShowingStatus.LOCATION_FOUND -> {
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
gridRecyclerView.setVisibility(View.VISIBLE)
messageTextView!!.setVisibility(View.GONE)
enableLocationButton.setVisibility(View.GONE)
}
LocationShowingStatus.NO_PERMISSION -> {
circlingProgressBar?.setVisibility(View.GONE)
flatProgressBar.setVisibility(View.GONE)
loadingTextView?.setVisibility(View.GONE)
messageTextView?.setText(R.string.enable_position_message_nearby)
messageTextView?.setVisibility(View.VISIBLE)
enableLocationButton.setVisibility(View.VISIBLE)
}
LocationShowingStatus.DISABLED -> {
//if (showingStatus== LocationShowingStatus.SEARCHING){
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
flatProgressBar.setVisibility(View.GONE)
//}
messageTextView!!.setText(R.string.enable_location_message)
messageTextView!!.setVisibility(View.VISIBLE)
enableLocationButton.setVisibility(View.GONE)
}
LocationShowingStatus.SEARCHING -> {
circlingProgressBar!!.setVisibility(View.VISIBLE)
flatProgressBar.setVisibility(View.GONE)
gridRecyclerView.setVisibility(View.GONE)
messageTextView!!.setVisibility(View.GONE)
enableLocationButton.setVisibility(View.GONE)
loadingTextView?.apply {
setText(loadingDataMessageId)
visibility = View.VISIBLE
}
}
}
showingStatus = newStatus
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is FragmentListenerMain) {
mListener = context as FragmentListenerMain
} else {
throw RuntimeException(
context
.toString() + " must implement OnFragmentInteractionListener"
)
}
Log.d(DEBUG_TAG, "OnAttach called")
//viewModel = ViewModelProvider(this).get(NearbyStopsViewModel::class.java)
}
override fun onPause() {
super.onPause()
//gridRecyclerView.setAdapter(null)
Log.d(DEBUG_TAG, "On paused called")
locationProvider!!.stopUpdates()
}
override fun onResume() {
super.onResume()
//fix view if we were showing the stops or the arrivals
loadPreferencesStops()
setGuiForFragmentType(fragmentType)
//if(lastPosition == null){
viewModel.locationLiveData.value?.let{loc -> lastPosition = loc }
//}
if(bothLocationPermissionsGranted(requireContext())){
locationProvider?.apply {
if(isLocationEnabled()){
//location is enabled, start updates
startLocationUpdatesByType()
if(lastPosition == null){
setShowingStatus(LocationShowingStatus.SEARCHING)
}
} else{
setShowingStatus(LocationShowingStatus.DISABLED)
}
}
} else{
setShowingStatus(LocationShowingStatus.NO_PERMISSION)
}
//setupDataAndLayoutByFragmentType()
mListener!!.enableRefreshLayout(false)
if(fragmentType == FragType.ARRIVALS){
viewModel.arrivalsDecoupled.value?.let{
//re-do the adapter
firstLocForArrivals = true
showArrivals(it)
}
} else if(fragmentType == FragType.STOPS) {
viewModel.stopsAtDistance.value?.let {
//remake the adapter
firstLocForStops = true
displayStopsOrRequestArrivals(it)
}
}
/*
when (fragmentType) {
FragType.STOPS -> if (dataAdapter != null) {
//gridRecyclerView.setAdapter(dataAdapter);
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
}
FragType.ARRIVALS -> if (arrivalsStopAdapter != null) {
//gridRecyclerView.setAdapter(arrivalsStopAdapter);
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
}
}
*/
Log.d(DEBUG_TAG, "OnResume called")
if (getContext() == null) {
Log.e(DEBUG_TAG, "NULL CONTEXT, everything is going to crash now")
stopsMinNumber = 5
stopsMaxDistance = 600
}
//Re-read preferences
}
private fun loadPreferencesStops(){
val shpr = PreferenceManager.getDefaultSharedPreferences(requireContext().getApplicationContext())
//For some reason, they are all saved as strings
stopsMaxDistance = shpr.getInt(getString(R.string.pref_key_radius_recents), 600)
var isMinStopInt = true
try {
stopsMinNumber = shpr.getInt(getString(R.string.pref_key_num_recents), 5)
} catch (ex: ClassCastException) {
isMinStopInt = false
}
if (!isMinStopInt) try {
stopsMinNumber = shpr.getString(getString(R.string.pref_key_num_recents), "5")!!.toInt()
} catch (ex: NumberFormatException) {
stopsMinNumber = 5
}
if (BuildConfig.DEBUG) Log.d(
DEBUG_TAG,
"Max distance for stops: $stopsMaxDistance, Min number of stops: $stopsMinNumber"
)
}
override fun onDetach() {
super.onDetach()
mListener = null
//if (arrivalsManager != null) arrivalsManager!!.cancelAllRequests()
}
override fun onStart() {
super.onStart()
if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "onStart called")
//checkPermissionLocationStart()
}
/**
* Display the stops, or run new set of requests for arrivals
*/
/*private fun displayStopsOrLaunchArrivalsRequest(stops: ArrayList, location: GPSPoint) {
if (stops.isEmpty()) {
setNoStopsLayout()
return
}
//quick trial to hopefully always get the stops in the correct order
when (fragment_type) {
FragType.STOPS -> {
setShowingStatus(LocationShowingStatus.STOPS_FOUND)
showStopsInRecycler(stops, location)
}
FragType.ARRIVALS -> {
viewModel.requestArrivalsForStops(stops)
//setShowingStatus(LocationShowingStatus.SEARCHING)
viewModel.arrivalsDecoupled.value?.let{
}
}
}
}
*/
/**
* Call when you need to switch the type of fragment
*/
private fun switchFragmentType() {
when (fragmentType) {
FragType.ARRIVALS -> {
viewModel.cancelAllArrivalsRequests()
setFragmentType(FragType.STOPS)
//when switching from arrivals
firstLocForStops = true
}
FragType.STOPS -> {
setFragmentType(FragType.ARRIVALS)
firstLocForArrivals = true
}
}
//now it's switched
setGuiForFragmentType(fragmentType)
viewModel.stopsAtDistance.value?.let{
//re-issue update, triggering chain
viewModel.stopsAtDistance.value = it
}
/*if(fragmentType == FragType.ARRIVALS) {
viewModel.stopsAtDistance.value?.let{
viewModel.requestArrivalsForStops(it)
}
}
*/
//
//setupDataAndLayoutByFragmentType()
}
private fun setGuiForFragmentType(fragmentType: FragType) {
when (fragmentType) {
FragType.STOPS ->{
switchButton!!.text = getString(R.string.show_arrivals)
titleTextView!!.text = getString(R.string.nearby_stops_message)
noDataMessageId = R.string.no_stops_nearby
loadingDataMessageId = R.string.position_searching_message
//switchButton?.backgroundTintList = ColorStateList.valueOf(
// ViewUtils.getColorFromTheme(requireContext(), R.attr.colorPrimaryDark))
}
FragType.ARRIVALS ->{
titleTextView!!.text = getString(R.string.nearby_arrivals_message)
switchButton!!.text = getString(R.string.show_stops)
noDataMessageId = R.string.no_stops_nearby_arrivals
loadingDataMessageId = R.string.searching_arrivals_indefinite
//switchButton?.backgroundTintList = ColorStateList.valueOf(
// ResourcesCompat.getColor(resources, R.color.light_blue_900, requireActivity().theme))
}
}
}
/**
* Prepare the views for the set fragment type
*/
/*private fun setupDataAndLayoutByFragmentType() {
var dataAvailable = false
if (fragmentType == FragType.STOPS) {
switchButton!!.text = getString(R.string.show_arrivals)
titleTextView!!.text = getString(R.string.nearby_stops_message)
viewModel.stopsAtDistance.value?.let { stops->
// if data adapter is not null set stops, otherwise
dataAdapter?.setStops(stops) ?: lastPosition?.let{ pos ->
dataAdapter = SquareStopAdapter(stops, mListener, pos)
}
Log.d(DEBUG_TAG, "Found ${stops.size} stops")
}
dataAdapter?.let{
gridRecyclerView.adapter = it
dataAvailable = true
}
mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS)
} else if (fragmentType == FragType.ARRIVALS) {
titleTextView!!.text = getString(R.string.nearby_arrivals_message)
switchButton!!.text = getString(R.string.show_stops)
val arrivalsSorted = viewModel.arrivalsDecoupled.value
arrivalsSorted?.let{
arrivalsStopAdapter?.setRoutesPairListAndPosition(
it, lastPosition) ?: lastPosition?.let{pos ->
arrivalsStopAdapter = ArrivalsStopAdapter(it, mListener!!, requireContext(), pos)
}
}
arrivalsStopAdapter?.let{
gridRecyclerView.setAdapter(it)
}
mListener?.readyGUIfor(FragmentKind.NEARBY_ARRIVALS)
}
if(gridRecyclerView.adapter == null){
flatProgressBar.isIndeterminate = true
flatProgressBar.visibility = View.VISIBLE
} else{
flatProgressBar.visibility = View.GONE
flatProgressBar.isIndeterminate = false
}
}
*/
//useful methods
/**//// GUI METHODS //////// */
private fun showStopsInRecycler(stops: MutableList, location: GPSPoint) {
// hide the progress bar
flatProgressBar.visibility = View.GONE
Collections.sort(stops, StopSorterByDistance(location))
if (dataAdapter == null) {
dataAdapter = SquareStopAdapter(stops, mListener, lastPosition)
firstLocForStops = false
} else {
dataAdapter!!.setUserPosition(lastPosition)
dataAdapter!!.setStops(stops)
}
gridRecyclerView.setAdapter(dataAdapter)
if(gridRecyclerView.visibility != View.VISIBLE){
if(showingStatus == LocationShowingStatus.LOCATION_FOUND)
Log.e(DEBUG_TAG, "Visualization error: the recyclerView is not visible but location status is $showingStatus")
else{
Log.w(DEBUG_TAG, "Grid recyclerView should be visible for the stops, setting status ${LocationShowingStatus.LOCATION_FOUND}")
setShowingStatus(LocationShowingStatus.LOCATION_FOUND)
}
}
dataShownInAdapter.set(true)
//showRecyclerHidingLoadMessage();
/*if (gridRecyclerView!!.getVisibility() != View.VISIBLE) {
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
gridRecyclerView!!.setVisibility(View.VISIBLE)
}
messageTextView!!.setVisibility(View.GONE)
*/
}
/**
* Does exactly what is says on the tin
*/
/*private fun showRecyclerHidingLoadMessage() {
if (gridRecyclerView.getVisibility() != View.VISIBLE) {
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
gridRecyclerView.setVisibility(View.VISIBLE)
}
messageTextView!!.setVisibility(View.GONE)
}
*/
private fun showNoStopsMessage(){
messageTextView!!.setVisibility(View.VISIBLE)
messageTextView!!.setText(noDataMessageId)
flatProgressBar.visibility = View.GONE
circlingProgressBar!!.setVisibility(View.GONE)
loadingTextView!!.setVisibility(View.GONE)
enableLocationButton.setVisibility(View.GONE)
}
/*
* Local locationListener, to use for the GPS
*/
/*
class FragmentLocationListener implements LocationListenerCompat {
private long lastUpdateTime = -1;
public boolean isRegistered = false;
@Override
public void onLocationChanged(@NonNull Location location) {
if(viewModel==null){
return;
}
if(location.getAccuracy()<200) {
lastPosition = new GPSPoint(location.getLatitude(), location.getLongitude());
//viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true);
viewModel.setLastLocation(location);
}
lastUpdateTime = System.currentTimeMillis();
//Log.d("BusTO:NearPositListen","can start request for stops: "+ !dbUpdateRunning);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
Log.d(DEBUG_TAG, "Location provider "+provider+" enabled");
if(provider.equals(LocationManager.GPS_PROVIDER)){
setShowingStatus(LocationShowingStatus.SEARCHING);
}
}
@Override
public void onProviderDisabled(@NonNull String provider) {
Log.d(DEBUG_TAG, "Location provider "+provider+" disabled");
if(provider.equals(LocationManager.GPS_PROVIDER)) {
setShowingStatus(LocationShowingStatus.DISABLED);
}
}
@Override
public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) {
LocationListenerCompat.super.onStatusChanged(provider, status, extras);
}
}
*/
companion object {
private const val DEBUG_TAG = "NearbyStopsFragment"
private const val FRAGMENT_TYPE_KEY = "FragmentType"
const val FRAGMENT_TAG: String = "NearbyStopsFrag"
const val COLUMN_WIDTH_DP: Int = 250
const val MIN_ACCURACY = 200.0
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
* @return A new instance of fragment NearbyStopsFragment.
*/
@JvmStatic
fun newInstance(type: FragType): NearbyStopsFragment {
//if(fragmentType != TYPE_STOPS && fragmentType != TYPE_ARRIVALS )
// throw new IllegalArgumentException("WRONG KIND OF FRAGMENT USED");
val fragment = NearbyStopsFragment()
val args = Bundle(1)
args.putInt(FRAGMENT_TYPE_KEY, type.num)
fragment.setArguments(args)
return fragment
}
}
}
diff --git a/app/src/main/res/layout/activity_principal.xml b/app/src/main/res/layout/activity_principal.xml
index ea5afc4..a42b3c6 100644
--- a/app/src/main/res/layout/activity_principal.xml
+++ b/app/src/main/res/layout/activity_principal.xml
@@ -1,55 +1,55 @@
\ No newline at end of file
diff --git a/app/src/main/res/values-night-v27/themes.xml b/app/src/main/res/values-night-v27/themes.xml
new file mode 100644
index 0000000..7ebf668
--- /dev/null
+++ b/app/src/main/res/values-night-v27/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 9bb257f..606ca61 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -1,11 +1,16 @@
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v19/styles.xml b/app/src/main/res/values-v19/styles.xml
deleted file mode 100644
index 5f8f81e..0000000
--- a/app/src/main/res/values-v19/styles.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v27/styles.xml
new file mode 100644
index 0000000..f521326
--- /dev/null
+++ b/app/src/main/res/values-v27/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v35/styles.xml b/app/src/main/res/values-v35/styles.xml
deleted file mode 100644
index 1646d1b..0000000
--- a/app/src/main/res/values-v35/styles.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 83324ae..3c9bf34 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,101 +1,102 @@
#ff9800
#E77D13
#F57C00
#f57200
#e66b00
#cc6600
#994d00
+ #b35900
#2196F3
#0b6fc1
#2a65e8
#2060dd
#8A4247
#FFF3E0
#fff5e5
#e08700
#d16900
#2378e8
#0079f5
#2a968b
#0067ff
#2F59CC
#CC5E43
#548017
#228b22
#0ABA34
#009688
#00a38d
#009480
#00a887
#4DB6AC
#80cbc4
#008175
#F5F5F5
#dddddd
#f8f8f8
#bababa
#acacac
#757575
#444
#353535
#303030
#f2f2f2
#DE0908
#b30000
#dd441f
#b30d0d
#f15000
#f2621a
#f47333
#2060DD
#FFFFFF
#000000
#1c1c1c
#3f3f3f
@color/blue_mid_2
@color/red_dark
@color/blue_extra
#3089e8
#FF039BE5
#FF01579B
#FF40C4FF
#FF00B0FF
#66000000
#555
#cccccc
@color/grey_500
#00000000
@color/orange_700
@color/grey_200
@color/grey_050
@color/grey_200
@color/orange_500
@color/blue_extraurbano
@color/metro_red
@color/orange_icons_10light
@color/grey_400
@color/orange_750_l45
@color/grey_700
@color/blue_700
@color/light_blue_900
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 43597e3..3c0d23d 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,106 +1,109 @@
+
+
\ No newline at end of file