roomorama / Caldroid

A better calendar for Android
Other
1.42k stars 532 forks source link

Caldroid dates disappear on rotation #429

Open abuicke opened 7 years ago

abuicke commented 7 years ago

I don't think this issue is a duplicate of #185 unless I'm missing something, because the same issue occurs on my phone as well as my tablet. I'm using the CaldroidFragment with a TabLayout like this:

Here is the layout XML for my main activity

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/activity_main_root_view"
             xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:app="http://schemas.android.com/apk/res-auto"
             android:layout_width="match_parent"
             android:layout_height="match_parent">
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabGravity="fill"
                app:tabMode="fixed"/>
        </android.support.design.widget.AppBarLayout>

        <android.support.v4.view.ViewPager
            android:id="@+id/fragment_view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    </android.support.design.widget.CoordinatorLayout>
</FrameLayout>

Here is my MainActivity which sets up the CaldroidFragment with the tabs


public class MainActivity extends AppCompatActivity {

  private ListFragment mListFragment;
  private MapFragment mMapFragment;
  private CalendarFragment mCalendarFragment;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mListFragment = ListFragment.newInstance();
    mMapFragment = MapFragment.newInstance();
    mCalendarFragment = CalendarFragment.newInstance();

    setupViewPagerWithTabs();
  }

  private void setupViewPagerWithTabs() {
    final ViewPager viewPager = (ViewPager) findViewById(R.id.fragment_view_pager);
    final ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(mListFragment, null);
    adapter.addFrag(mMapFragment, null);
    adapter.addFrag(mCalendarFragment, null);

    viewPager.setAdapter(adapter);

    final TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);

    final int[] tabIcons = {
        android.R.drawable.ic_menu_info_details,
        android.R.drawable.ic_dialog_map,
        android.R.drawable.ic_menu_my_calendar
    };

    tabLayout.getTabAt(0).setIcon(tabIcons[0]);
    tabLayout.getTabAt(1).setIcon(tabIcons[1]);
    tabLayout.getTabAt(2).setIcon(tabIcons[2]);
  }

}

Here is my custom FragmentPagerAdapter

public class ViewPagerAdapter extends FragmentPagerAdapter {

  private static final String TAG = ViewPagerAdapter.class.getSimpleName();

  private final List<Fragment> mFragmentList = new ArrayList<>();
  private final List<String> mFragmentTitleList = new ArrayList<>();

  public ViewPagerAdapter(final FragmentManager manager) {
    super(manager);
  }

  @Override
  public Fragment getItem(final int position) {
    return mFragmentList.get(position);
  }

  @Override
  public int getCount() {
    return mFragmentList.size();
  }

  public void addFrag(final Fragment fragment, final String title) {
    mFragmentList.add(fragment);
    mFragmentTitleList.add(title);
  }

  @Override
  public CharSequence getPageTitle(final int position) {
    if(position < mFragmentTitleList.size()) {
      return mFragmentTitleList.get(position);
    }else {
      return null;
    }
  }

}

And this is a subclass of the CaldroidFragment I'm using (some methods removed for simplicity), although the problem occurs even with the normal CaldroidFragement.

public class CalendarFragment extends CaldroidFragment {

  private SimpleTask[] mSimpleTasks;

  public static CalendarFragment newInstance() {
    return new CalendarFragment();
  }

  @Override
  public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, final Bundle savedInstanceState) {
    final View layout = super.onCreateView(inflater, container, savedInstanceState);

    final SimpleTask[] savedSimpleTasks = savedInstanceState != null ?
        (SimpleTask[]) savedInstanceState.getParcelableArray(SIMPLE_TASKS_SAVE_KEY) : null;

    if (mSimpleTasks != null) {
      loadTasksIntoView(mSimpleTasks);
    } else if (savedSimpleTasks != null) {
      mSimpleTasks = savedSimpleTasks;
      loadTasksIntoView(savedSimpleTasks);
    } else {
      final String errMsg = "no simple tasks set for CalendarFragment";
      Log.w(TAG, errMsg);
      if (TaskManagerApplication.ASSERTIONS_ON) throw new AssertionError(errMsg);
    }

    return layout;
  }

}
abuicke commented 7 years ago

refreshView() also doesn't seem to work. I'm not sue if this is related.

abuicke commented 7 years ago

I eventually got it working by using nested Fargments instead of extending CaldroidFragment and by using Dates instead of DateTimes. I have seen another issue where someone reported setBackgroundDrawableForDateTime not working. This is a fairly significant issue. I will investigate this problem and hopefully be able to create a pull request when I have time. However, given your lack of responsiveness to my issue is there any point? Will you accept a fix if I provide one?

Here is the code I used to get it working:

public class CalendarFragment extends Fragment {

  private CaldroidFragment mCaldroidFragment;

  public static CalendarFragment newInstance() {
    return new CalendarFragment();
  }

  @Override
  public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, final Bundle savedInstanceState) {
    final View layout = inflater.inflate(R.layout.calendar_fragment_layout, container, false);

    mCaldroidFragment = new CaldroidFragment();
    final Bundle args = new Bundle();
    Calendar cal = Calendar.getInstance();
    args.putInt(CaldroidFragment.MONTH, cal.get(Calendar.MONTH) + 1);
    args.putInt(CaldroidFragment.YEAR, cal.get(Calendar.YEAR));
    args.putBoolean(CaldroidFragment.ENABLE_SWIPE, true);
    args.putBoolean(CaldroidFragment.SIX_WEEKS_IN_CALENDAR, true);
    mCaldroidFragment.setArguments(args);
    final CaldroidListener listener = new DateListener();
    mCaldroidFragment.setCaldroidListener(listener);
    final FragmentTransaction t = getActivity().getSupportFragmentManager().beginTransaction();
    t.replace(R.id.caldroid_layout, mCaldroidFragment);
    t.commit();

    return layout;
  }
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/caldroid_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

Here is code related to the Date/DateTime background drawable issue. If it's not clear enough for anyone please let me know and I can explain more. The main thing I did was create a helper method in a util class called toJavaDate which you can see below:

private void loadTasksIntoView(@NonNull final Task[] tasks) {
    assertNotNull(tasks);
    if (tasks.length == 0) {
      Log.w(TAG, "tasks are empty");
      if (TaskManagerApplication.ASSERTIONS_ON) throw new AssertionError("tasks are empty");
    }

    final Resources resources = getResources();
    // noinspection deprecation
    final int colorAccent = resources.getColor(R.color.colorAccent);
    final Drawable startDateBgDrawable = new ColorDrawable(colorAccent);
    final Drawable endDateBgDrawable = new ColorDrawable(Color.RED);

    for (final Task task: tasks) {
      final DateTime startDateTime = task.getStartDate();
      final DateTime endDateTime = task.getEndDate();
      final Date startDate = toJavaDate(startDateTime);
      final Date endDate = toJavaDate(endDateTime);
      mCaldroidFragment.setTextColorForDate(R.color.caldroid_white, startDate);
      mCaldroidFragment.setTextColorForDate(R.color.caldroid_white, endDate);
      mCaldroidFragment.setBackgroundDrawableForDate(startDateBgDrawable, startDate);
      Log.v(TAG, "added start date " + startDate + " to calendar fragment");
      mCaldroidFragment.setBackgroundDrawableForDate(endDateBgDrawable, endDate);
      Log.v(TAG, "added end date " + endDate + " to calendar fragment");
    }

    mCaldroidFragment.refreshView();
  }
public final class DateTimeUtils {

  public static Date toJavaDate(final DateTime jodaDateTime) {
    final TimeZone timeZone = TimeZone.getDefault();
    return toJavaDate(jodaDateTime, timeZone);
  }

  public static Date toJavaDate(final DateTime jodaDateTime, final TimeZone timeZone) {
    final long jodaMillis = jodaDateTime.getMilliseconds(timeZone);
    return new Date(jodaMillis);
  }

}