Android 11 appears to have "optimized" application rotation by
regenerating fragments automatically. This means the tab fragments
aren't getting created by TabsAdapter.getItem, so that code didn't
know about them, which caused it to not know which tab was active so
all of the application state wasn't getting updated in the tabs after
rotation.
Fix this by telling TabsAdapter about fragments that are already
created -- altosdroid hears about them in the registerTab hook.
Signed-off-by: Keith Packard <keithp@keithp.com>
Log.e(TAG, String.format(format, arguments));
}
Log.e(TAG, String.format(format, arguments));
}
+ static void trace(String format, Object ... arguments) {
+ error(format, arguments);
+ for (StackTraceElement el : Thread.currentThread().getStackTrace())
+ Log.e(TAG, "\t" + el.toString() + "\n");
+ }
+
static void check_ui(String format, Object ... arguments) {
static void check_ui(String format, Object ... arguments) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.e(TAG, String.format("ON UI THREAD " + format, arguments));
- for (StackTraceElement el : Thread.currentThread().getStackTrace())
- Log.e(TAG, "\t" + el.toString() + "\n");
- }
+ if (Looper.myLooper() == Looper.getMainLooper())
+ trace("ON UI THREAD " + format, arguments);
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
+ AltosDebug.debug("onServiceConnected\n");
mService = new Messenger(service);
try {
Message msg = Message.obtain(null, TelemetryService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
mService = new Messenger(service);
try {
Message msg = Message.obtain(null, TelemetryService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
+ AltosDebug.debug("attempt to register telemetry service client failed\n");
// In this case the service has crashed before we could even do anything with it
}
if (pending_usb_device != null) {
// In this case the service has crashed before we could even do anything with it
}
if (pending_usb_device != null) {
}
public void onServiceDisconnected(ComponentName className) {
}
public void onServiceDisconnected(ComponentName className) {
+ AltosDebug.debug("onServiceDisconnected\n");
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
}
};
void doBindService() {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
}
};
void doBindService() {
+ AltosDebug.debug("doBindService\n");
bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
+ AltosDebug.debug("doUnbindService\n");
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
+ public AltosDroidTab findTab(String name) {
+ for (AltosDroidTab mTab : mTabs)
+ if (name.equals(mTab.tab_name()))
+ return mTab;
+ return null;
+ }
+
public void registerTab(AltosDroidTab mTab) {
mTabs.add(mTab);
}
public void registerTab(AltosDroidTab mTab) {
mTabs.add(mTab);
}
void update_ui(TelemetryState telem_state, AltosState state, boolean quiet) {
void update_ui(TelemetryState telem_state, AltosState state, boolean quiet) {
+ AltosDebug.debug("update_ui telem %b state %b quiet %b saved_state %b\n",
+ telem_state != null,
+ state != null,
+ quiet,
+ saved_state != null);
+
this.state = state;
int prev_state = AltosLib.ao_flight_invalid;
this.state = state;
int prev_state = AltosLib.ao_flight_invalid;
saved_state = new SavedState(state);
}
saved_state = new SavedState(state);
}
- for (AltosDroidTab mTab : mTabs)
+ for (AltosDroidTab mTab : mTabs) {
+ AltosDebug.debug("mTab %s current %s\n",
+ mTab, mTabsAdapter.currentItem());
mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
if (mAltosVoice != null && mTabsAdapter.currentItem() != null)
mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem(), quiet);
if (mAltosVoice != null && mTabsAdapter.currentItem() != null)
mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem(), quiet);
@Override
public void onCreate(Bundle savedInstanceState) {
@Override
public void onCreate(Bundle savedInstanceState) {
+ AltosDebug.init(this);
+ AltosDebug.debug("+++ ON CREATE +++");
+
// Initialise preferences
AltosDroidPreferences.init(this);
setTheme(themes[AltosDroidPreferences.font_size()]);
super.onCreate(savedInstanceState);
// Initialise preferences
AltosDroidPreferences.init(this);
setTheme(themes[AltosDroidPreferences.font_size()]);
super.onCreate(savedInstanceState);
- AltosDebug.init(this);
- AltosDebug.debug("+++ ON CREATE +++");
-
fm = getSupportFragmentManager();
fm = getSupportFragmentManager();
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
- mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null);
- mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null);
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null, findTab(tab_pad_name));
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null, findTab(tab_flight_name));
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null, findTab(tab_recover_name));
+ mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null, findTab(tab_map_name));
// Display the Version
mVersion = (TextView) findViewById(R.id.version);
// Display the Version
mVersion = (TextView) findViewById(R.id.version);
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- AltosDebug.debug("onNewIntent");
+ AltosDebug.debug("+ ON NEW INTENT +");
- private void enable_location_updates() {
+ private void enable_location_updates(boolean do_update) {
// Listen for GPS and Network position updates
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Listen for GPS and Network position updates
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
AltosDebug.debug("Failed to get GPS updates\n");
}
AltosDebug.debug("Failed to get GPS updates\n");
}
- update_ui(telemetry_state, state, true);
+ if (do_update)
+ update_ui(telemetry_state, state, true);
}
static final int MY_PERMISSION_REQUEST = 1001;
}
static final int MY_PERMISSION_REQUEST = 1001;
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
if (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
have_location_permission = true;
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
if (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
have_location_permission = true;
- enable_location_updates();
+ enable_location_updates(true);
if (map_online != null)
map_online.position_permission();
}
if (map_online != null)
map_online.position_permission();
}
@Override
public void onResume() {
@Override
public void onResume() {
AltosDebug.debug("+ ON RESUME +");
AltosDebug.debug("+ ON RESUME +");
if (!asked_permission) {
asked_permission = true;
if (ActivityCompat.checkSelfPermission(this,
if (!asked_permission) {
asked_permission = true;
if (ActivityCompat.checkSelfPermission(this,
}
}
if (have_location_permission)
}
}
if (have_location_permission)
- enable_location_updates();
+ enable_location_updates(false);
}
@Override
public void onPause() {
}
@Override
public void onPause() {
AltosDebug.debug("- ON PAUSE -");
AltosDebug.debug("- ON PAUSE -");
// Stop listening for location updates
if (have_location_permission)
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
// Stop listening for location updates
if (have_location_permission)
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
@Override
public void onStop() {
@Override
public void onStop() {
AltosDebug.debug("-- ON STOP --");
AltosDebug.debug("-- ON STOP --");
}
@Override
public void onDestroy() {
}
@Override
public void onDestroy() {
AltosDebug.debug("--- ON DESTROY ---");
AltosDebug.debug("--- ON DESTROY ---");
+ super.onDestroy();
+
+ saved_state = null;
+
doUnbindService();
if (mAltosVoice != null) {
mAltosVoice.stop();
doUnbindService();
if (mAltosVoice != null) {
mAltosVoice.stop();
@Override
public void onAttach(Context context) {
@Override
public void onAttach(Context context) {
+ AltosDebug.debug("tab onAttach %s %s\n", tab_name(), this);
super.onAttach(context);
altos_droid = (AltosDroid) context;
altos_droid.registerTab(this);
super.onAttach(context);
altos_droid = (AltosDroid) context;
altos_droid.registerTab(this);
@Override
public void onDetach() {
@Override
public void onDetach() {
+ AltosDebug.debug("tab onDetach %s %s\n", tab_name(), this);
super.onDetach();
altos_droid.unregisterTab(this);
altos_droid = null;
super.onDetach();
altos_droid.unregisterTab(this);
altos_droid = null;
@Override
public void onResume() {
super.onResume();
@Override
public void onResume() {
super.onResume();
- AltosDebug.debug("onResume tab %s\n", tab_name());
+ AltosDebug.debug("onResume tab %s %s\n", tab_name(), this);
set_visible(true);
}
public void update_ui(TelemetryState telem_state, AltosState state,
AltosGreatCircle from_receiver, Location receiver, boolean is_current)
{
set_visible(true);
}
public void update_ui(TelemetryState telem_state, AltosState state,
AltosGreatCircle from_receiver, Location receiver, boolean is_current)
{
+ AltosDebug.debug("update_ui %s is_current %b\n", tab_name(), is_current);
last_telem_state = telem_state;
last_state = state;
last_from_receiver = from_receiver;
last_telem_state = telem_state;
last_state = state;
last_from_receiver = from_receiver;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ AltosDebug.debug("TabPad onCreateView\n");
View v = inflater.inflate(R.layout.tab_pad, container, false);
battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
View v = inflater.inflate(R.layout.tab_pad, container, false);
battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
+
+ AltosDebug.debug("TabPad onCreateView done battery_voltage_view %s\n", battery_voltage_view);
+ return v;
}
public String tab_name() { return AltosDroid.tab_pad_name; }
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
}
public String tab_name() { return AltosDroid.tab_pad_name; }
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ AltosDebug.debug("pad show state %b bvv %s\n", state != null, battery_voltage_view);
if (state != null) {
battery_voltage_view.setText(AltosDroid.number("%1.2f V", state.battery_voltage));
battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
if (state != null) {
battery_voltage_view.setText(AltosDroid.number("%1.2f V", state.battery_voltage));
battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
double altitude = AltosLib.MISSING;
if (receiver.hasAltitude())
altitude = receiver.getAltitude();
double altitude = AltosLib.MISSING;
if (receiver.hasAltitude())
altitude = receiver.getAltitude();
- receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
- receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+ String lat_text = AltosDroid.pos(receiver.getLatitude(), "N", "S");
+ String lon_text = AltosDroid.pos(receiver.getLongitude(), "E", "W");
+ AltosDebug.debug("lat %s lon %s\n", lat_text, lon_text);
+ receiver_latitude_view.setText(lat_text);
+ receiver_longitude_view.setText(lon_text);
set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
}
}
set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
}
}
private final Bundle args;
private Fragment fragment;
private final Bundle args;
private Fragment fragment;
- TabInfo(String _tag, Class<?> _class, Bundle _args) {
+ TabInfo(String _tag, Class<?> _class, Bundle _args, Fragment _fragment) {
tag = _tag;
clss = _class;
args = _args;
tag = _tag;
clss = _class;
args = _args;
mViewPager.addOnPageChangeListener(this);
}
mViewPager.addOnPageChangeListener(this);
}
- public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
+ public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args, Fragment fragment) {
tabSpec.setContent(new DummyTabFactory(mContext));
String tag = tabSpec.getTag();
tabSpec.setContent(new DummyTabFactory(mContext));
String tag = tabSpec.getTag();
- TabInfo info = new TabInfo(tag, clss, args);
+ TabInfo info = new TabInfo(tag, clss, args, fragment);
mTabs.add(info);
mTabHost.addTab(tabSpec);
notifyDataSetChanged();
mTabs.add(info);
mTabHost.addTab(tabSpec);
notifyDataSetChanged();
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
AltosDebug.debug("TabsAdapter.getItem(%d)", position);
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
AltosDebug.debug("TabsAdapter.getItem(%d)", position);
- info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ if (info.fragment == null)
+ info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
AltosDroidTab cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
AltosDroidTab cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
+ AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d cur %s prev %s", tabId, position, cur_frag, prev_frag);
+
if (prev_frag != cur_frag) {
if (prev_frag != null) {
prev_frag.set_visible(false);
}
}
if (prev_frag != cur_frag) {
if (prev_frag != null) {
prev_frag.set_visible(false);
}
}
- if (cur_frag != null) {
+
+ /* This happens when the tab is selected before any of them
+ * have been created, like during rotation
+ */
+ if (cur_frag != null)
cur_frag.set_visible(true);
cur_frag.set_visible(true);
- }
- AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position);
mViewPager.setCurrentItem(position);
}
mViewPager.setCurrentItem(position);
}