mirror of
https://github.com/MatomoCamp/matomocamp-companion-android.git
synced 2024-09-19 16:13:46 +02:00
Move bookmark status into its own ViewModel controlled by the activity and integrate a bottom app bar with a floating action button to manage bookmark status.
Auto-hide the top toolbar on scroll in event details screens Remove legacy UnderlinePageIndicator. Swiping is still possible.
This commit is contained in:
parent
2e17feb3ef
commit
700ad50eb9
22 changed files with 401 additions and 749 deletions
|
@ -53,11 +53,13 @@
|
|||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
<activity
|
||||
android:name=".activities.TrackScheduleEventActivity"
|
||||
android:label="@string/event_details"/>
|
||||
android:label="@string/event_details"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
<activity
|
||||
android:name=".activities.EventDetailsActivity"
|
||||
android:label="@string/event_details"
|
||||
android:parentActivityName=".activities.TrackScheduleActivity">
|
||||
android:parentActivityName=".activities.TrackScheduleActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
package be.digitalia.fosdem.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
@ -17,10 +21,15 @@ import androidx.lifecycle.ViewModelProviders;
|
|||
import be.digitalia.fosdem.R;
|
||||
import be.digitalia.fosdem.fragments.EventDetailsFragment;
|
||||
import be.digitalia.fosdem.model.Event;
|
||||
import be.digitalia.fosdem.model.Track;
|
||||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
import be.digitalia.fosdem.utils.ThemeUtils;
|
||||
import be.digitalia.fosdem.viewmodels.BookmarkStatusViewModel;
|
||||
import be.digitalia.fosdem.viewmodels.EventViewModel;
|
||||
import be.digitalia.fosdem.widgets.BookmarkStatusAdapter;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
|
||||
/**
|
||||
* Displays a single event passed either as a complete Parcelable object in extras or as an id in data.
|
||||
|
@ -31,16 +40,25 @@ public class EventDetailsActivity extends AppCompatActivity implements Observer<
|
|||
|
||||
public static final String EXTRA_EVENT = "event";
|
||||
|
||||
private AppBarLayout appBarLayout;
|
||||
private Toolbar toolbar;
|
||||
private BottomAppBar bottomAppBar;
|
||||
|
||||
private BookmarkStatusViewModel bookmarkStatusViewModel;
|
||||
private Event event;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.content);
|
||||
setContentView(R.layout.single_event);
|
||||
appBarLayout = findViewById(R.id.appbar);
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
bottomAppBar = findViewById(R.id.bottom_appbar);
|
||||
setSupportActionBar(bottomAppBar);
|
||||
|
||||
ActionBar bar = getSupportActionBar();
|
||||
bar.setDisplayHomeAsUpEnabled(false);
|
||||
bar.setDisplayShowTitleEnabled(false);
|
||||
ImageButton floatingActionButton = findViewById(R.id.fab);
|
||||
bookmarkStatusViewModel = ViewModelProviders.of(this).get(BookmarkStatusViewModel.class);
|
||||
BookmarkStatusAdapter.setupWithImageButton(bookmarkStatusViewModel, this, floatingActionButton);
|
||||
|
||||
Event event = getIntent().getParcelableExtra(EXTRA_EVENT);
|
||||
|
||||
|
@ -94,14 +112,28 @@ public class EventDetailsActivity extends AppCompatActivity implements Observer<
|
|||
private void initEvent(@NonNull Event event) {
|
||||
this.event = event;
|
||||
// Enable up navigation only after getting the event details
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
ThemeUtils.setActionBarTrackColor(this, event.getTrack().getType());
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material);
|
||||
toolbar.setNavigationContentDescription(R.string.abc_action_bar_up_description);
|
||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
navigateUp();
|
||||
}
|
||||
});
|
||||
|
||||
final Track.Type trackType = event.getTrack().getType();
|
||||
ThemeUtils.setStatusBarTrackColor(this, trackType);
|
||||
final ColorStateList trackColor = ContextCompat.getColorStateList(this, trackType.getColorResId());
|
||||
appBarLayout.setBackgroundColor(trackColor.getDefaultColor());
|
||||
bottomAppBar.setBackgroundTint(trackColor);
|
||||
|
||||
bookmarkStatusViewModel.setEvent(event);
|
||||
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
void navigateUp() {
|
||||
// Navigate up to the track associated with this event
|
||||
Intent upIntent = new Intent(this, TrackScheduleActivity.class);
|
||||
upIntent.putExtra(TrackScheduleActivity.EXTRA_DAY, event.getDay());
|
||||
|
@ -121,7 +153,6 @@ public class EventDetailsActivity extends AppCompatActivity implements Observer<
|
|||
startActivity(upIntent);
|
||||
finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// CreateNfcAppDataCallback
|
||||
|
|
|
@ -3,13 +3,15 @@ package be.digitalia.fosdem.activities;
|
|||
import android.content.Intent;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageButton;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import be.digitalia.fosdem.R;
|
||||
import be.digitalia.fosdem.fragments.EventDetailsFragment;
|
||||
import be.digitalia.fosdem.fragments.RoomImageDialogFragment;
|
||||
|
@ -20,6 +22,8 @@ import be.digitalia.fosdem.model.Track;
|
|||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
import be.digitalia.fosdem.utils.ThemeUtils;
|
||||
import be.digitalia.fosdem.viewmodels.BookmarkStatusViewModel;
|
||||
import be.digitalia.fosdem.widgets.BookmarkStatusAdapter;
|
||||
|
||||
/**
|
||||
* Track Schedule container, works in both single pane and dual pane modes.
|
||||
|
@ -27,9 +31,7 @@ import be.digitalia.fosdem.utils.ThemeUtils;
|
|||
* @author Christophe Beyls
|
||||
*/
|
||||
public class TrackScheduleActivity extends AppCompatActivity
|
||||
implements TrackScheduleListFragment.Callbacks,
|
||||
EventDetailsFragment.FloatingActionButtonProvider,
|
||||
CreateNfcAppDataCallback {
|
||||
implements TrackScheduleListFragment.Callbacks, CreateNfcAppDataCallback {
|
||||
|
||||
public static final String EXTRA_DAY = "day";
|
||||
public static final String EXTRA_TRACK = "track";
|
||||
|
@ -41,15 +43,14 @@ public class TrackScheduleActivity extends AppCompatActivity
|
|||
private boolean isTabletLandscape;
|
||||
private Event lastSelectedEvent;
|
||||
|
||||
private ImageView floatingActionButton;
|
||||
private BookmarkStatusViewModel bookmarkStatusViewModel = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.track_schedule);
|
||||
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
|
||||
|
||||
floatingActionButton = findViewById(R.id.fab);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
day = extras.getParcelable(EXTRA_DAY);
|
||||
|
@ -60,7 +61,9 @@ public class TrackScheduleActivity extends AppCompatActivity
|
|||
bar.setTitle(track.toString());
|
||||
bar.setSubtitle(day.toString());
|
||||
setTitle(String.format("%1$s, %2$s", track.toString(), day.toString()));
|
||||
ThemeUtils.setActionBarTrackColor(this, track.getType());
|
||||
ThemeUtils.setStatusBarTrackColor(this, track.getType());
|
||||
final int trackColor = ContextCompat.getColor(this, track.getType().getColorResId());
|
||||
toolbar.setBackgroundColor(trackColor);
|
||||
|
||||
isTabletLandscape = getResources().getBoolean(R.bool.tablet_landscape);
|
||||
|
||||
|
@ -100,6 +103,12 @@ public class TrackScheduleActivity extends AppCompatActivity
|
|||
}
|
||||
|
||||
if (isTabletLandscape) {
|
||||
ImageButton floatingActionButton = findViewById(R.id.fab);
|
||||
if (floatingActionButton != null) {
|
||||
bookmarkStatusViewModel = ViewModelProviders.of(this).get(BookmarkStatusViewModel.class);
|
||||
BookmarkStatusAdapter.setupWithImageButton(bookmarkStatusViewModel, this, floatingActionButton);
|
||||
}
|
||||
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
}
|
||||
|
@ -128,6 +137,10 @@ public class TrackScheduleActivity extends AppCompatActivity
|
|||
fm.beginTransaction().remove(currentFragment).commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
if (bookmarkStatusViewModel != null) {
|
||||
bookmarkStatusViewModel.setEvent(event);
|
||||
}
|
||||
} else {
|
||||
// Classic mode: Show event details in a new activity
|
||||
Intent intent = new Intent(this, TrackScheduleEventActivity.class);
|
||||
|
@ -138,13 +151,6 @@ public class TrackScheduleActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
// EventDetailsFragment.FloatingActionButtonProvider
|
||||
|
||||
@Override
|
||||
public ImageView getActionButton() {
|
||||
return floatingActionButton;
|
||||
}
|
||||
|
||||
// CreateNfcAppDataCallback
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package be.digitalia.fosdem.activities;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.widget.ImageButton;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
@ -21,9 +23,12 @@ import be.digitalia.fosdem.model.Track;
|
|||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
import be.digitalia.fosdem.utils.ThemeUtils;
|
||||
import be.digitalia.fosdem.viewmodels.BookmarkStatusViewModel;
|
||||
import be.digitalia.fosdem.viewmodels.TrackScheduleViewModel;
|
||||
import be.digitalia.fosdem.widgets.BookmarkStatusAdapter;
|
||||
import be.digitalia.fosdem.widgets.ContentLoadingProgressBar;
|
||||
import com.viewpagerindicator.UnderlinePageIndicator;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -41,14 +46,18 @@ public class TrackScheduleEventActivity extends AppCompatActivity implements Obs
|
|||
private int initialPosition = -1;
|
||||
private ContentLoadingProgressBar progress;
|
||||
private ViewPager pager;
|
||||
private UnderlinePageIndicator pageIndicator;
|
||||
private TrackScheduleEventAdapter adapter;
|
||||
TrackScheduleEventAdapter adapter;
|
||||
|
||||
BookmarkStatusViewModel bookmarkStatusViewModel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.track_schedule_event);
|
||||
AppBarLayout appBarLayout = findViewById(R.id.appbar);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
BottomAppBar bottomAppBar = findViewById(R.id.bottom_appbar);
|
||||
setSupportActionBar(bottomAppBar);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
final Day day = extras.getParcelable(EXTRA_DAY);
|
||||
|
@ -57,26 +66,45 @@ public class TrackScheduleEventActivity extends AppCompatActivity implements Obs
|
|||
progress = findViewById(R.id.progress);
|
||||
pager = findViewById(R.id.pager);
|
||||
adapter = new TrackScheduleEventAdapter(getSupportFragmentManager());
|
||||
pageIndicator = findViewById(R.id.indicator);
|
||||
pageIndicator.setSelectedColor(ContextCompat.getColor(this, track.getType().getColorResId()));
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
initialPosition = extras.getInt(EXTRA_POSITION, -1);
|
||||
}
|
||||
|
||||
ActionBar bar = getSupportActionBar();
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
bar.setTitle(track.toString());
|
||||
bar.setSubtitle(day.toString());
|
||||
ThemeUtils.setActionBarTrackColor(this, track.getType());
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material);
|
||||
toolbar.setNavigationContentDescription(R.string.abc_action_bar_up_description);
|
||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
toolbar.setTitle(track.toString());
|
||||
toolbar.setSubtitle(day.toString());
|
||||
setTitle(String.format("%1$s, %2$s", track.toString(), day.toString()));
|
||||
ThemeUtils.setStatusBarTrackColor(this, track.getType());
|
||||
final ColorStateList trackColor = ContextCompat.getColorStateList(this, track.getType().getColorResId());
|
||||
appBarLayout.setBackgroundColor(trackColor.getDefaultColor());
|
||||
bottomAppBar.setBackgroundTint(trackColor);
|
||||
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
// Monitor the currently displayed event to update the bookmark status in FAB
|
||||
ImageButton floatingActionButton = findViewById(R.id.fab);
|
||||
bookmarkStatusViewModel = ViewModelProviders.of(this).get(BookmarkStatusViewModel.class);
|
||||
BookmarkStatusAdapter.setupWithImageButton(bookmarkStatusViewModel, this, floatingActionButton);
|
||||
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
bookmarkStatusViewModel.setEvent(adapter.getEvent(position));
|
||||
}
|
||||
});
|
||||
|
||||
setCustomProgressVisibility(true);
|
||||
final TrackScheduleViewModel viewModel = ViewModelProviders.of(this).get(TrackScheduleViewModel.class);
|
||||
viewModel.setTrack(day, track);
|
||||
viewModel.getSchedule().observe(this, this);
|
||||
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
}
|
||||
|
||||
private void setCustomProgressVisibility(boolean isVisible) {
|
||||
|
@ -99,12 +127,6 @@ public class TrackScheduleEventActivity extends AppCompatActivity implements Obs
|
|||
return NfcUtils.createEventAppData(this, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(List<StatusEvent> schedule) {
|
||||
setCustomProgressVisibility(false);
|
||||
|
@ -117,21 +139,25 @@ public class TrackScheduleEventActivity extends AppCompatActivity implements Obs
|
|||
// to ensure the current position is restored properly
|
||||
if (pager.getAdapter() == null) {
|
||||
pager.setAdapter(adapter);
|
||||
pageIndicator.setViewPager(pager);
|
||||
}
|
||||
|
||||
if (initialPosition != -1) {
|
||||
pager.setCurrentItem(initialPosition, false);
|
||||
initialPosition = -1;
|
||||
}
|
||||
|
||||
final int currentPosition = pager.getCurrentItem();
|
||||
if (currentPosition >= 0) {
|
||||
bookmarkStatusViewModel.setEvent(adapter.getEvent(currentPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TrackScheduleEventAdapter extends FragmentStatePagerAdapter {
|
||||
private static class TrackScheduleEventAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private List<StatusEvent> events = null;
|
||||
|
||||
public TrackScheduleEventAdapter(FragmentManager fm) {
|
||||
TrackScheduleEventAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package be.digitalia.fosdem.fragments;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
|
@ -13,10 +10,8 @@ import android.text.*;
|
|||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.*;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.core.app.ShareCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
@ -40,14 +35,6 @@ import java.util.Map;
|
|||
|
||||
public class EventDetailsFragment extends Fragment {
|
||||
|
||||
/**
|
||||
* Interface implemented by container activities
|
||||
*/
|
||||
public interface FloatingActionButtonProvider {
|
||||
// May return null
|
||||
ImageView getActionButton();
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
LayoutInflater inflater;
|
||||
TextView personsTextView;
|
||||
|
@ -62,9 +49,6 @@ public class EventDetailsFragment extends Fragment {
|
|||
ViewHolder holder;
|
||||
EventDetailsViewModel viewModel;
|
||||
|
||||
private MenuItem bookmarkMenuItem;
|
||||
private ImageView actionButton;
|
||||
|
||||
public static EventDetailsFragment newInstance(Event event) {
|
||||
EventDetailsFragment f = new EventDetailsFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
@ -79,6 +63,7 @@ public class EventDetailsFragment extends Fragment {
|
|||
event = getArguments().getParcelable(ARG_EVENT);
|
||||
viewModel = ViewModelProviders.of(this).get(EventDetailsViewModel.class);
|
||||
viewModel.setEvent(event);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
public Event getEvent() {
|
||||
|
@ -181,23 +166,6 @@ public class EventDetailsFragment extends Fragment {
|
|||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof FloatingActionButtonProvider) {
|
||||
actionButton = ((FloatingActionButtonProvider) activity).getActionButton();
|
||||
if (actionButton != null) {
|
||||
actionButton.setOnClickListener(actionButtonClickListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the actionButton is initialized before creating the options menu
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
viewModel.getBookmarkStatus().observe(getViewLifecycleOwner(), new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable Boolean isBookmarked) {
|
||||
updateBookmarkMenuItem(isBookmarked, true);
|
||||
}
|
||||
});
|
||||
viewModel.getEventDetails().observe(getViewLifecycleOwner(), new Observer<EventDetails>() {
|
||||
@Override
|
||||
public void onChanged(EventDetails eventDetails) {
|
||||
|
@ -222,34 +190,16 @@ public class EventDetailsFragment extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private final View.OnClickListener actionButtonClickListener = new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
viewModel.toggleBookmarkStatus();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
holder = null;
|
||||
if (actionButton != null) {
|
||||
// Clear the reference to this fragment
|
||||
actionButton.setOnClickListener(null);
|
||||
actionButton = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.event, menu);
|
||||
menu.findItem(R.id.share).setIntent(getShareChooserIntent());
|
||||
bookmarkMenuItem = menu.findItem(R.id.bookmark);
|
||||
if (actionButton != null) {
|
||||
bookmarkMenuItem.setEnabled(false).setVisible(false);
|
||||
}
|
||||
updateBookmarkMenuItem(viewModel.getBookmarkStatus().getValue(), false);
|
||||
}
|
||||
|
||||
private Intent getShareChooserIntent() {
|
||||
|
@ -261,67 +211,9 @@ public class EventDetailsFragment extends Fragment {
|
|||
.createChooserIntent();
|
||||
}
|
||||
|
||||
void updateBookmarkMenuItem(Boolean isBookmarked, boolean animate) {
|
||||
if (actionButton != null) {
|
||||
// Action Button is used as bookmark button
|
||||
|
||||
if (isBookmarked == null) {
|
||||
actionButton.setEnabled(false);
|
||||
} else {
|
||||
// Only animate if the button was showing a previous value
|
||||
animate = animate && actionButton.isEnabled();
|
||||
actionButton.setEnabled(true);
|
||||
|
||||
if (isBookmarked) {
|
||||
actionButton.setContentDescription(getString(R.string.remove_bookmark));
|
||||
actionButton.setImageResource(animate ? R.drawable.avd_bookmark_add_24dp : R.drawable.ic_bookmark_white_24dp);
|
||||
} else {
|
||||
actionButton.setContentDescription(getString(R.string.add_bookmark));
|
||||
actionButton.setImageResource(animate ? R.drawable.avd_bookmark_remove_24dp : R.drawable.ic_bookmark_outline_white_24dp);
|
||||
}
|
||||
if (animate) {
|
||||
((Animatable) actionButton.getDrawable()).start();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Standard menu item is used as bookmark button
|
||||
|
||||
if (bookmarkMenuItem != null) {
|
||||
if (isBookmarked == null) {
|
||||
bookmarkMenuItem.setEnabled(false);
|
||||
} else {
|
||||
// Only animate if the menu item was showing a previous value
|
||||
animate = animate && bookmarkMenuItem.isEnabled();
|
||||
bookmarkMenuItem.setEnabled(true);
|
||||
|
||||
if (isBookmarked) {
|
||||
bookmarkMenuItem.setTitle(R.string.remove_bookmark);
|
||||
bookmarkMenuItem.setIcon(animate ? R.drawable.avd_bookmark_add_24dp : R.drawable.ic_bookmark_white_24dp);
|
||||
} else {
|
||||
bookmarkMenuItem.setTitle(R.string.add_bookmark);
|
||||
bookmarkMenuItem.setIcon(animate ? R.drawable.avd_bookmark_remove_24dp : R.drawable.ic_bookmark_outline_white_24dp);
|
||||
}
|
||||
if (animate) {
|
||||
((Animatable) bookmarkMenuItem.getIcon()).stop();
|
||||
((Animatable) bookmarkMenuItem.getIcon()).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyOptionsMenu() {
|
||||
super.onDestroyOptionsMenu();
|
||||
bookmarkMenuItem = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.bookmark:
|
||||
viewModel.toggleBookmarkStatus();
|
||||
return true;
|
||||
case R.id.add_to_agenda:
|
||||
addToAgenda();
|
||||
return true;
|
||||
|
@ -329,7 +221,6 @@ public class EventDetailsFragment extends Fragment {
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private void addToAgenda() {
|
||||
Intent intent = new Intent(Intent.ACTION_EDIT);
|
||||
intent.setType("vnd.android.cursor.item/event");
|
||||
|
@ -344,8 +235,7 @@ public class EventDetailsFragment extends Fragment {
|
|||
EventDetails details = viewModel.getEventDetails().getValue();
|
||||
final int personsCount = (details == null) ? 0 : details.getPersons().size();
|
||||
if (personsCount > 0) {
|
||||
description = String.format("%1$s: %2$s\n\n%3$s", getResources().getQuantityString(R.plurals.speakers, personsCount), event.getPersonsSummary(),
|
||||
description);
|
||||
description = String.format("%1$s: %2$s\n\n%3$s", getResources().getQuantityString(R.plurals.speakers, personsCount), event.getPersonsSummary(), description);
|
||||
}
|
||||
intent.putExtra(CalendarContract.Events.DESCRIPTION, description);
|
||||
Date time = event.getStartTime();
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package be.digitalia.fosdem.model;
|
||||
|
||||
public class BookmarkStatus {
|
||||
|
||||
private final boolean isBookmarked;
|
||||
private final boolean isUpdate;
|
||||
|
||||
public BookmarkStatus(boolean isBookmarked, boolean isUpdate) {
|
||||
this.isBookmarked = isBookmarked;
|
||||
this.isUpdate = isUpdate;
|
||||
}
|
||||
|
||||
public boolean isBookmarked() {
|
||||
return isBookmarked;
|
||||
}
|
||||
|
||||
public boolean isUpdate() {
|
||||
return isUpdate;
|
||||
}
|
||||
}
|
|
@ -1,24 +1,17 @@
|
|||
package be.digitalia.fosdem.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import be.digitalia.fosdem.model.Track;
|
||||
|
||||
public class ThemeUtils {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void setActionBarTrackColor(@NonNull AppCompatActivity activity, @NonNull Track.Type trackType) {
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
final int color = ContextCompat.getColor(activity, trackType.getColorResId());
|
||||
actionBar.setBackgroundDrawable(new ColorDrawable(color));
|
||||
|
||||
public static void setStatusBarTrackColor(@NonNull Activity activity, @NonNull Track.Type trackType) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
final int color = ContextCompat.getColor(activity, trackType.getColorResId());
|
||||
final int darkColor = ContextCompat.getColor(activity, trackType.getDarkColorResId());
|
||||
activity.getWindow().setStatusBarColor(darkColor);
|
||||
final ActivityManager.TaskDescription taskDescription;
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package be.digitalia.fosdem.viewmodels;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.core.util.ObjectsCompat;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import be.digitalia.fosdem.db.AppDatabase;
|
||||
import be.digitalia.fosdem.livedata.ExtraTransformations;
|
||||
import be.digitalia.fosdem.model.BookmarkStatus;
|
||||
import be.digitalia.fosdem.model.Event;
|
||||
|
||||
public class BookmarkStatusViewModel extends AndroidViewModel {
|
||||
|
||||
private final AppDatabase appDatabase = AppDatabase.getInstance(getApplication());
|
||||
private final MutableLiveData<Event> event = new MutableLiveData<>();
|
||||
private final LiveData<BookmarkStatus> bookmarkStatus = Transformations.switchMap(event,
|
||||
new Function<Event, LiveData<BookmarkStatus>>() {
|
||||
@Override
|
||||
public LiveData<BookmarkStatus> apply(Event event) {
|
||||
if (event == null) {
|
||||
MutableLiveData<BookmarkStatus> singleNullResult = new MutableLiveData<>();
|
||||
singleNullResult.setValue(null);
|
||||
return singleNullResult;
|
||||
}
|
||||
|
||||
return Transformations.map(
|
||||
// Prevent updating the UI when a bookmark is added back or removed back
|
||||
ExtraTransformations.distinctUntilChanged(
|
||||
appDatabase.getBookmarksDao().getBookmarkStatus(event)
|
||||
), new Function<Boolean, BookmarkStatus>() {
|
||||
@Override
|
||||
public BookmarkStatus apply(Boolean isBookmarked) {
|
||||
if (isBookmarked == null) {
|
||||
return null;
|
||||
}
|
||||
final boolean isUpdate = firstResultReceived;
|
||||
firstResultReceived = true;
|
||||
return new BookmarkStatus(isBookmarked, isUpdate);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
private boolean firstResultReceived = false;
|
||||
|
||||
public BookmarkStatusViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
public void setEvent(Event event) {
|
||||
if (!ObjectsCompat.equals(event, this.event.getValue())) {
|
||||
firstResultReceived = false;
|
||||
this.event.setValue(event);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<BookmarkStatus> getBookmarkStatus() {
|
||||
return bookmarkStatus;
|
||||
}
|
||||
|
||||
public void toggleBookmarkStatus() {
|
||||
final Event event = this.event.getValue();
|
||||
final BookmarkStatus currentStatus = bookmarkStatus.getValue();
|
||||
// Ignore the action if the status for the current event hasn't been received yet
|
||||
if (event != null && currentStatus != null && firstResultReceived) {
|
||||
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (currentStatus.isBookmarked()) {
|
||||
appDatabase.getBookmarksDao().removeBookmark(event);
|
||||
} else {
|
||||
appDatabase.getBookmarksDao().addBookmark(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package be.digitalia.fosdem.viewmodels;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
|
@ -9,7 +8,6 @@ import androidx.lifecycle.LiveData;
|
|||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import be.digitalia.fosdem.db.AppDatabase;
|
||||
import be.digitalia.fosdem.livedata.ExtraTransformations;
|
||||
import be.digitalia.fosdem.model.Event;
|
||||
import be.digitalia.fosdem.model.EventDetails;
|
||||
|
||||
|
@ -17,16 +15,6 @@ public class EventDetailsViewModel extends AndroidViewModel {
|
|||
|
||||
private final AppDatabase appDatabase = AppDatabase.getInstance(getApplication());
|
||||
private final MutableLiveData<Event> event = new MutableLiveData<>();
|
||||
private final LiveData<Boolean> bookmarkStatus = Transformations.switchMap(event,
|
||||
new Function<Event, LiveData<Boolean>>() {
|
||||
@Override
|
||||
public LiveData<Boolean> apply(Event event) {
|
||||
// Prevent animating the UI when a bookmark is added back or removed back
|
||||
return ExtraTransformations.distinctUntilChanged(
|
||||
appDatabase.getBookmarksDao().getBookmarkStatus(event)
|
||||
);
|
||||
}
|
||||
});
|
||||
private final LiveData<EventDetails> eventDetails = Transformations.switchMap(event,
|
||||
new Function<Event, LiveData<EventDetails>>() {
|
||||
@Override
|
||||
|
@ -45,27 +33,6 @@ public class EventDetailsViewModel extends AndroidViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getBookmarkStatus() {
|
||||
return bookmarkStatus;
|
||||
}
|
||||
|
||||
public void toggleBookmarkStatus() {
|
||||
final Event event = this.event.getValue();
|
||||
final Boolean isBookmarked = bookmarkStatus.getValue();
|
||||
if (event != null && isBookmarked != null) {
|
||||
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isBookmarked) {
|
||||
appDatabase.getBookmarksDao().removeBookmark(event);
|
||||
} else {
|
||||
appDatabase.getBookmarksDao().addBookmark(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<EventDetails> getEventDetails() {
|
||||
return eventDetails;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package be.digitalia.fosdem.widgets;
|
||||
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.Observer;
|
||||
import be.digitalia.fosdem.R;
|
||||
import be.digitalia.fosdem.model.BookmarkStatus;
|
||||
import be.digitalia.fosdem.viewmodels.BookmarkStatusViewModel;
|
||||
|
||||
public class BookmarkStatusAdapter {
|
||||
|
||||
private BookmarkStatusAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect an ImageButton to a BookmarkStatusViewModel
|
||||
* to update its icon according to the current status and trigger a bookmark toggle on click.
|
||||
*/
|
||||
public static void setupWithImageButton(@NonNull final BookmarkStatusViewModel viewModel, @NonNull LifecycleOwner owner,
|
||||
@NonNull final ImageButton imageButton) {
|
||||
imageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
viewModel.toggleBookmarkStatus();
|
||||
}
|
||||
});
|
||||
viewModel.getBookmarkStatus().observe(owner, new Observer<BookmarkStatus>() {
|
||||
@Override
|
||||
public void onChanged(BookmarkStatus bookmarkStatus) {
|
||||
if (bookmarkStatus == null) {
|
||||
imageButton.setEnabled(false);
|
||||
imageButton.setImageResource(R.drawable.ic_bookmark_outline_white_24dp);
|
||||
} else {
|
||||
// Only animate updates, when the button was already enabled
|
||||
final boolean animate = bookmarkStatus.isUpdate() && imageButton.isEnabled();
|
||||
imageButton.setEnabled(true);
|
||||
if (bookmarkStatus.isBookmarked()) {
|
||||
imageButton.setContentDescription(imageButton.getContext().getString(R.string.remove_bookmark));
|
||||
imageButton.setImageResource(animate ? R.drawable.avd_bookmark_add_24dp : R.drawable.ic_bookmark_white_24dp);
|
||||
} else {
|
||||
imageButton.setContentDescription(imageButton.getContext().getString(R.string.add_bookmark));
|
||||
imageButton.setImageResource(animate ? R.drawable.avd_bookmark_remove_24dp : R.drawable.ic_bookmark_outline_white_24dp);
|
||||
}
|
||||
final Drawable drawable = imageButton.getDrawable();
|
||||
if (drawable instanceof Animatable) {
|
||||
((Animatable) drawable).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Patrik Akerfeldt
|
||||
* Copyright (C) 2011 Jake Wharton
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.viewpagerindicator;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
/**
|
||||
* A PageIndicator is responsible to show an visual indicator on the total views
|
||||
* number and the current visible view.
|
||||
*/
|
||||
public interface PageIndicator extends ViewPager.OnPageChangeListener {
|
||||
/**
|
||||
* Bind the indicator to a ViewPager.
|
||||
*
|
||||
* @param view
|
||||
*/
|
||||
void setViewPager(ViewPager view);
|
||||
|
||||
/**
|
||||
* Bind the indicator to a ViewPager.
|
||||
*
|
||||
* @param view
|
||||
* @param initialPosition
|
||||
*/
|
||||
void setViewPager(ViewPager view, int initialPosition);
|
||||
|
||||
/**
|
||||
* <p>Set the current page of both the ViewPager and indicator.</p>
|
||||
*
|
||||
* <p>This <strong>must</strong> be used if you need to set the page before
|
||||
* the views are drawn on screen (e.g., default start page).</p>
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
void setCurrentItem(int item);
|
||||
|
||||
/**
|
||||
* Notify the indicator that the fragment list has changed.
|
||||
*/
|
||||
void notifyDataSetChanged();
|
||||
}
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Jake Wharton
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.viewpagerindicator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import be.digitalia.fosdem.R;
|
||||
|
||||
/**
|
||||
* Draws a line for each page. The current page line is colored differently
|
||||
* than the unselected page lines.
|
||||
*/
|
||||
public class UnderlinePageIndicator extends View implements PageIndicator {
|
||||
private static final int INVALID_POINTER = -1;
|
||||
static final int FADE_FRAME_MS = 30;
|
||||
|
||||
final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
boolean mFades;
|
||||
private int mFadeDelay;
|
||||
private int mFadeLength;
|
||||
int mFadeBy;
|
||||
|
||||
private ViewPager mViewPager;
|
||||
private int mScrollState;
|
||||
private int mCurrentPage;
|
||||
private float mPositionOffset;
|
||||
|
||||
private int mTouchSlop;
|
||||
private float mLastMotionX = -1;
|
||||
private int mActivePointerId = INVALID_POINTER;
|
||||
private boolean mIsDragging;
|
||||
|
||||
final Runnable mFadeRunnable = new Runnable() {
|
||||
@Override public void run() {
|
||||
if (!mFades) return;
|
||||
|
||||
final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0);
|
||||
mPaint.setAlpha(alpha);
|
||||
invalidate();
|
||||
if (alpha > 0) {
|
||||
postDelayed(this, FADE_FRAME_MS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public UnderlinePageIndicator(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public UnderlinePageIndicator(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.vpiUnderlinePageIndicatorStyle);
|
||||
}
|
||||
|
||||
public UnderlinePageIndicator(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
if (isInEditMode()) return;
|
||||
|
||||
//Retrieve styles attributes
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePageIndicator, defStyle, R.style.UnderlinePageIndicator);
|
||||
|
||||
setFades(a.getBoolean(R.styleable.UnderlinePageIndicator_fades, false));
|
||||
setSelectedColor(a.getColor(R.styleable.UnderlinePageIndicator_selectedColor, 0));
|
||||
setFadeDelay(a.getInteger(R.styleable.UnderlinePageIndicator_fadeDelay, 0));
|
||||
setFadeLength(a.getInteger(R.styleable.UnderlinePageIndicator_fadeLength, 0));
|
||||
|
||||
Drawable background = a.getDrawable(R.styleable.UnderlinePageIndicator_android_background);
|
||||
if (background != null) {
|
||||
ViewCompat.setBackground(this, background);
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
|
||||
}
|
||||
|
||||
public boolean getFades() {
|
||||
return mFades;
|
||||
}
|
||||
|
||||
public void setFades(boolean fades) {
|
||||
if (fades != mFades) {
|
||||
mFades = fades;
|
||||
if (fades) {
|
||||
post(mFadeRunnable);
|
||||
} else {
|
||||
removeCallbacks(mFadeRunnable);
|
||||
mPaint.setAlpha(0xFF);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getFadeDelay() {
|
||||
return mFadeDelay;
|
||||
}
|
||||
|
||||
public void setFadeDelay(int fadeDelay) {
|
||||
mFadeDelay = fadeDelay;
|
||||
}
|
||||
|
||||
public int getFadeLength() {
|
||||
return mFadeLength;
|
||||
}
|
||||
|
||||
public void setFadeLength(int fadeLength) {
|
||||
mFadeLength = fadeLength;
|
||||
mFadeBy = 0xFF / (mFadeLength / FADE_FRAME_MS);
|
||||
}
|
||||
|
||||
public int getSelectedColor() {
|
||||
return mPaint.getColor();
|
||||
}
|
||||
|
||||
public void setSelectedColor(int selectedColor) {
|
||||
mPaint.setColor(selectedColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (mViewPager == null) {
|
||||
return;
|
||||
}
|
||||
final int count = mViewPager.getAdapter().getCount();
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentPage >= count) {
|
||||
setCurrentItem(count - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
final int paddingLeft = getPaddingLeft();
|
||||
final float pageWidth = (getWidth() - paddingLeft - getPaddingRight()) / (1f * count);
|
||||
final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset);
|
||||
final float right = left + pageWidth;
|
||||
final float top = getPaddingTop();
|
||||
final float bottom = getHeight() - getPaddingBottom();
|
||||
canvas.drawRect(left, top, right, bottom, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent ev) {
|
||||
if (super.onTouchEvent(ev)) {
|
||||
return true;
|
||||
}
|
||||
if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mLastMotionX = ev.getX();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
final float x = ev.getX(activePointerIndex);
|
||||
final float deltaX = x - mLastMotionX;
|
||||
|
||||
if (!mIsDragging) {
|
||||
if (Math.abs(deltaX) > mTouchSlop) {
|
||||
mIsDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mIsDragging) {
|
||||
mLastMotionX = x;
|
||||
if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
|
||||
mViewPager.fakeDragBy(deltaX);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (!mIsDragging) {
|
||||
final int count = mViewPager.getAdapter().getCount();
|
||||
final int width = getWidth();
|
||||
final float halfWidth = width / 2f;
|
||||
final float sixthWidth = width / 6f;
|
||||
|
||||
if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
|
||||
if (action != MotionEvent.ACTION_CANCEL) {
|
||||
mViewPager.setCurrentItem(mCurrentPage - 1);
|
||||
}
|
||||
return true;
|
||||
} else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
|
||||
if (action != MotionEvent.ACTION_CANCEL) {
|
||||
mViewPager.setCurrentItem(mCurrentPage + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mIsDragging = false;
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
final int index = ev.getActionIndex();
|
||||
mLastMotionX = ev.getX(index);
|
||||
mActivePointerId = ev.getPointerId(index);
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
final int pointerIndex = ev.getActionIndex();
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
}
|
||||
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewPager(ViewPager viewPager) {
|
||||
if (mViewPager == viewPager) {
|
||||
return;
|
||||
}
|
||||
if (mViewPager != null) {
|
||||
//Clear us from the old pager.
|
||||
mViewPager.removeOnPageChangeListener(this);
|
||||
}
|
||||
if (viewPager.getAdapter() == null) {
|
||||
throw new IllegalStateException("ViewPager does not have adapter instance.");
|
||||
}
|
||||
mViewPager = viewPager;
|
||||
mViewPager.addOnPageChangeListener(this);
|
||||
invalidate();
|
||||
post(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (mFades) {
|
||||
post(mFadeRunnable);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewPager(ViewPager view, int initialPosition) {
|
||||
setViewPager(view);
|
||||
setCurrentItem(initialPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentItem(int item) {
|
||||
if (mViewPager == null) {
|
||||
throw new IllegalStateException("ViewPager has not been bound.");
|
||||
}
|
||||
mViewPager.setCurrentItem(item);
|
||||
mCurrentPage = item;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
mScrollState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
mCurrentPage = position;
|
||||
mPositionOffset = positionOffset;
|
||||
if (mFades) {
|
||||
if (positionOffsetPixels > 0) {
|
||||
removeCallbacks(mFadeRunnable);
|
||||
mPaint.setAlpha(0xFF);
|
||||
} else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) {
|
||||
postDelayed(mFadeRunnable, mFadeDelay);
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||
mCurrentPage = position;
|
||||
mPositionOffset = 0;
|
||||
invalidate();
|
||||
mFadeRunnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState savedState = (SavedState)state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
mCurrentPage = savedState.currentPage;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState savedState = new SavedState(superState);
|
||||
savedState.currentPage = mCurrentPage;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
int currentPage;
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
SavedState(Parcel in) {
|
||||
super(in);
|
||||
currentPage = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(currentPage);
|
||||
}
|
||||
|
||||
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
style="@style/Toolbar.Fosdem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:elevation="4dp"
|
||||
android:elevation="@dimen/toolbar_elevation"
|
||||
android:gravity="top" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
|
@ -36,15 +36,12 @@
|
|||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
style="@style/FloatingActionButton.BookmarkStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/detail_fab_margin_end"
|
||||
android:layout_marginRight="@dimen/detail_fab_margin_end"
|
||||
android:contentDescription="@string/add_bookmark"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
||||
app:fabSize="normal"
|
||||
app:layout_anchor="@+id/toolbar"
|
||||
app:layout_anchorGravity="bottom|end"
|
||||
app:srcCompat="@drawable/ic_bookmark_outline_white_24dp" />
|
||||
app:layout_anchorGravity="bottom|end" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -1,15 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
tools:context=".activities.EventDetailsActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/detail_bottom_padding">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -144,4 +146,4 @@
|
|||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</androidx.core.widget.NestedScrollView>
|
46
app/src/main/res/layout/single_event.xml
Normal file
46
app/src/main/res/layout/single_event.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="?attr/actionBarSize"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottom_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="bottom"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:fabAlignmentMode="end" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
style="@style/FloatingActionButton.BookmarkStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_anchor="@id/bottom_appbar" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -1,8 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<be.digitalia.fosdem.widgets.ContentLoadingProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
|
@ -15,12 +32,24 @@
|
|||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
android:layout_marginBottom="?attr/actionBarSize"
|
||||
android:visibility="gone"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.viewpagerindicator.UnderlinePageIndicator
|
||||
android:id="@+id/indicator"
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottom_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:layout_gravity="bottom"/>
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="bottom"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:fabAlignmentMode="end" />
|
||||
|
||||
</merge>
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
style="@style/FloatingActionButton.BookmarkStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_anchor="@id/bottom_appbar" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -2,11 +2,6 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/bookmark"
|
||||
android:icon="@drawable/ic_bookmark_outline_white_24dp"
|
||||
android:title="@string/add_bookmark"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/share"
|
||||
android:icon="@drawable/ic_share_white_24dp"
|
||||
|
|
6
app/src/main/res/values-sw600dp-land/dimens.xml
Normal file
6
app/src/main/res/values-sw600dp-land/dimens.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<dimen name="detail_bottom_padding">0dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -6,8 +6,9 @@
|
|||
<dimen name="main_menu_footer_height">32dp</dimen>
|
||||
<dimen name="list_item_padding">8dp</dimen>
|
||||
<dimen name="content_margin">16dp</dimen>
|
||||
<dimen name="toolbar_elevation">8dp</dimen>
|
||||
<dimen name="toolbar_elevation">4dp</dimen>
|
||||
<dimen name="schedule_column_width">360dp</dimen>
|
||||
<dimen name="detail_bottom_padding">12dp</dimen>
|
||||
<dimen name="detail_card_view_elevation">6dp</dimen>
|
||||
<!-- Enlarge the card view by 6dp left & right and 9dp top & bottom
|
||||
to compensate for the compatibility padding of the elevation -->
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
<item name="tabIndicatorColor">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="FloatingActionButton.BookmarkStatus" parent="Widget.Design.FloatingActionButton">
|
||||
<item name="android:contentDescription">@string/add_bookmark</item>
|
||||
<item name="android:enabled">false</item>
|
||||
<item name="android:theme">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||
<item name="fabSize">normal</item>
|
||||
<item name="srcCompat">@drawable/ic_bookmark_outline_white_24dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SeparatorLine">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">1dp</item>
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2012 Jake Wharton
|
||||
Copyright (C) 2011 Patrik Åkerfeldt
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="ViewPagerIndicator">
|
||||
|
||||
<!-- Style of the underline indicator. -->
|
||||
<attr name="vpiUnderlinePageIndicatorStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="centered" format="boolean" />
|
||||
<attr name="selectedColor" format="color" />
|
||||
<attr name="strokeWidth" format="dimension" />
|
||||
<attr name="unselectedColor" format="color" />
|
||||
|
||||
<declare-styleable name="UnderlinePageIndicator">
|
||||
|
||||
<!-- Whether or not the selected indicator fades. -->
|
||||
<attr name="fades" format="boolean" />
|
||||
<!-- Length of the delay to fade the indicator. -->
|
||||
<attr name="fadeDelay" format="integer" />
|
||||
<!-- Length of the indicator fade to transparent. -->
|
||||
<attr name="fadeLength" format="integer" />
|
||||
<!-- Color of the selected line that represents the current page. -->
|
||||
<attr name="selectedColor" />
|
||||
<!-- View background -->
|
||||
<attr name="android:background" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2012 Jake Wharton
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="UnderlinePageIndicator" parent="android:Widget">
|
||||
<item name="fades">true</item>
|
||||
<item name="fadeDelay">300</item>
|
||||
<item name="fadeLength">400</item>
|
||||
<item name="selectedColor">?attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue