diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 697e514a..0953972d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -1,21 +1,44 @@ package org.joinmastodon.android.fragments; +import android.app.Fragment; +import android.graphics.Outline; +import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.LinearLayout; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.ui.views.TabBar; +import org.parceler.Parcels; +import androidx.annotation.IdRes; import androidx.annotation.Nullable; import me.grishka.appkit.fragments.AppKitFragment; +import me.grishka.appkit.fragments.LoaderFragment; +import me.grishka.appkit.imageloader.ViewImageLoader; +import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; +import me.grishka.appkit.utils.V; import me.grishka.appkit.views.FragmentRootLinearLayout; public class HomeFragment extends AppKitFragment{ private FragmentRootLinearLayout content; private HomeTimelineFragment homeTimelineFragment; + private NotificationsFragment notificationsFragment; + private SearchFragment searchFragment; + private ProfileFragment profileFragment; + private TabBar tabBar; + private View tabBarWrap; + private ImageView tabBarAvatar; + @IdRes + private int currentTab=R.id.tab_home; private String accountID; @@ -35,11 +58,42 @@ public class HomeFragment extends AppKitFragment{ fragmentContainer.setId(R.id.fragment_wrap); content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); + inflater.inflate(R.layout.tab_bar, content); + tabBar=content.findViewById(R.id.tabbar); + tabBar.setListener(this::onTabSelected); + tabBarWrap=content.findViewById(R.id.tabbar_wrap); + + tabBarAvatar=tabBar.findViewById(R.id.tab_profile_ava); + tabBarAvatar.setOutlineProvider(new ViewOutlineProvider(){ + @Override + public void getOutline(View view, Outline outline){ + outline.setOval(0, 0, view.getWidth(), view.getHeight()); + } + }); + tabBarAvatar.setClipToOutline(true); + Account self=AccountSessionManager.getInstance().getAccount(accountID).self; + ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28))); + Bundle args=new Bundle(); args.putString("account", accountID); homeTimelineFragment=new HomeTimelineFragment(); homeTimelineFragment.setArguments(args); - getChildFragmentManager().beginTransaction().add(R.id.fragment_wrap, homeTimelineFragment).commit(); + searchFragment=new SearchFragment(); + searchFragment.setArguments(args); + notificationsFragment=new NotificationsFragment(); + notificationsFragment.setArguments(args); + args=new Bundle(args); + args.putParcelable("profileAccount", Parcels.wrap(AccountSessionManager.getInstance().getAccount(accountID).self)); + args.putBoolean("noAutoLoad", true); + profileFragment=new ProfileFragment(); + profileFragment.setArguments(args); + + getChildFragmentManager().beginTransaction() + .add(R.id.fragment_wrap, homeTimelineFragment) + .add(R.id.fragment_wrap, searchFragment).hide(searchFragment) + .add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment) + .add(R.id.fragment_wrap, profileFragment).hide(profileFragment) + .commit(); return content; } @@ -47,11 +101,51 @@ public class HomeFragment extends AppKitFragment{ @Override public void onHiddenChanged(boolean hidden){ super.onHiddenChanged(hidden); - homeTimelineFragment.onHiddenChanged(hidden); + fragmentForTab(currentTab).onHiddenChanged(hidden); } @Override public boolean wantsLightStatusBar(){ return true; } + + @Override + public boolean wantsLightNavigationBar(){ + return true; + } + + @Override + public void onApplyWindowInsets(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=27){ + int inset=insets.getSystemWindowInsetBottom(); + tabBarWrap.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0); + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0)); + }else{ + super.onApplyWindowInsets(insets); + } + } + + private Fragment fragmentForTab(@IdRes int tab){ + if(tab==R.id.tab_home){ + return homeTimelineFragment; + }else if(tab==R.id.tab_search){ + return searchFragment; + }else if(tab==R.id.tab_notifications){ + return notificationsFragment; + }else if(tab==R.id.tab_profile){ + return profileFragment; + } + throw new IllegalArgumentException(); + } + + private void onTabSelected(@IdRes int tab){ + Fragment newFragment=fragmentForTab(tab); + getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit(); + if(newFragment instanceof LoaderFragment){ + LoaderFragment lf=(LoaderFragment) newFragment; + if(!lf.loaded) + lf.loadData(); + } + currentTab=tab; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java index aa8ef1f4..6c8e9d1e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java @@ -1,12 +1,17 @@ package org.joinmastodon.android.fragments; import android.app.Activity; +import android.content.res.Configuration; import android.os.Bundle; +import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.Toolbar; import com.squareup.otto.Subscribe; @@ -34,7 +39,6 @@ public class HomeTimelineFragment extends StatusListFragment{ @Override public void onAttach(Activity activity){ super.onAttach(activity); - setTitle(R.string.app_name); setHasOptionsMenu(true); loadData(); } @@ -56,6 +60,7 @@ public class HomeTimelineFragment extends StatusListFragment{ super.onViewCreated(view, savedInstanceState); fab=view.findViewById(R.id.fab); fab.setOnClickListener(this::onFabClick); + updateToolbarLogo(); } @Override @@ -65,20 +70,26 @@ public class HomeTimelineFragment extends StatusListFragment{ @Override public boolean onOptionsItemSelected(MenuItem item){ - Bundle args=new Bundle(); - args.putString("account", accountID); - int id=item.getItemId(); - if(id==R.id.new_toot){ - Nav.go(getActivity(), ComposeFragment.class, args); - }else if(id==R.id.notifications){ - Nav.go(getActivity(), NotificationsFragment.class, args); - }else if(id==R.id.my_profile){ - args.putParcelable("profileAccount", Parcels.wrap(AccountSessionManager.getInstance().getAccount(accountID).self)); - Nav.go(getActivity(), ProfileFragment.class, args); - } +// Bundle args=new Bundle(); +// args.putString("account", accountID); +// int id=item.getItemId(); +// if(id==R.id.new_toot){ +// Nav.go(getActivity(), ComposeFragment.class, args); +// }else if(id==R.id.notifications){ +// Nav.go(getActivity(), NotificationsFragment.class, args); +// }else if(id==R.id.my_profile){ +// args.putParcelable("profileAccount", Parcels.wrap(AccountSessionManager.getInstance().getAccount(accountID).self)); +// Nav.go(getActivity(), ProfileFragment.class, args); +// } return true; } + @Override + public void onConfigurationChanged(Configuration newConfig){ + super.onConfigurationChanged(newConfig); + updateToolbarLogo(); + } + @Subscribe public void onStatusCreated(StatusCreatedEvent ev){ prependItems(Collections.singletonList(ev.status)); @@ -89,4 +100,12 @@ public class HomeTimelineFragment extends StatusListFragment{ args.putString("account", accountID); Nav.go(getActivity(), ComposeFragment.class, args); } + + private void updateToolbarLogo(){ + ImageView logo=new ImageView(getActivity()); + logo.setScaleType(ImageView.ScaleType.CENTER); + logo.setImageResource(R.drawable.logo); + Toolbar toolbar=getToolbar(); + toolbar.addView(logo, new Toolbar.LayoutParams(Gravity.CENTER)); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsFragment.java index 79e8b535..8971c445 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsFragment.java @@ -19,7 +19,6 @@ public class NotificationsFragment extends BaseStatusListFragment{ public void onAttach(Activity activity){ super.onAttach(activity); setTitle(R.string.notifications); - loadData(); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 8a60b7f1..f2bb6fef 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -19,7 +19,8 @@ public class ProfileFragment extends StatusListFragment{ super.onAttach(activity); user=Parcels.unwrap(getArguments().getParcelable("profileAccount")); setTitle("@"+user.acct); - loadData(); + if(!getArguments().getBoolean("noAutoLoad")) + loadData(); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SearchFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SearchFragment.java new file mode 100644 index 00000000..cde339c4 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SearchFragment.java @@ -0,0 +1,15 @@ +package org.joinmastodon.android.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import me.grishka.appkit.fragments.ToolbarFragment; + +public class SearchFragment extends ToolbarFragment{ + @Override + public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + return new View(getActivity()); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/TabBar.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/TabBar.java new file mode 100644 index 00000000..001224fa --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/TabBar.java @@ -0,0 +1,55 @@ +package org.joinmastodon.android.ui.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import org.joinmastodon.android.R; + +import java.util.function.IntConsumer; + +import androidx.annotation.IdRes; + +public class TabBar extends LinearLayout{ + @IdRes + private int selectedTabID; + private IntConsumer listener; + + public TabBar(Context context){ + this(context, null); + } + + public TabBar(Context context, AttributeSet attrs){ + this(context, attrs, 0); + } + + public TabBar(Context context, AttributeSet attrs, int defStyle){ + super(context, attrs, defStyle); + } + + @Override + public void onViewAdded(View child){ + super.onViewAdded(child); + if(child.getId()!=0){ + if(selectedTabID==0){ + selectedTabID=child.getId(); + child.setSelected(true); + } + child.setOnClickListener(this::onChildClick); + } + } + + private void onChildClick(View v){ + if(v.getId()==selectedTabID) + return; + findViewById(selectedTabID).setSelected(false); + v.setSelected(true); + selectedTabID=v.getId(); + listener.accept(selectedTabID); + } + + public void setListener(IntConsumer listener){ + this.listener=listener; + } +} diff --git a/mastodon/src/main/res/drawable/bg_tab_profile.xml b/mastodon/src/main/res/drawable/bg_tab_profile.xml new file mode 100644 index 00000000..4c026d0d --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_tab_profile.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_alert_28_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_alert_28_filled.xml new file mode 100644 index 00000000..ad64bd6c --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_alert_28_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular.xml new file mode 100644 index 00000000..792dffc3 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_alert_28_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector.xml new file mode 100644 index 00000000..aa7761f8 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_alert_28_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_home_28_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_home_28_filled.xml new file mode 100644 index 00000000..35a44897 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_home_28_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_home_28_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_home_28_regular.xml new file mode 100644 index 00000000..acccca52 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_home_28_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_home_28_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_home_28_selector.xml new file mode 100644 index 00000000..5a705d1f --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_home_28_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_search_28_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_search_28_filled.xml new file mode 100644 index 00000000..c74ce0f3 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_search_28_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_search_28_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_search_28_regular.xml new file mode 100644 index 00000000..e3800534 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_search_28_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_search_28_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_search_28_selector.xml new file mode 100644 index 00000000..2895c1a0 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_search_28_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_settings_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_settings_24_regular.xml new file mode 100644 index 00000000..91c24e02 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_settings_24_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/logo.xml b/mastodon/src/main/res/drawable/logo.xml new file mode 100644 index 00000000..42ef5a36 --- /dev/null +++ b/mastodon/src/main/res/drawable/logo.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index 77d520ea..d50da55b 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -25,7 +25,7 @@ tools:text="123"/> - @@ -48,7 +48,7 @@ tools:text="123"/> - @@ -71,7 +71,7 @@ tools:text="123"/> - diff --git a/mastodon/src/main/res/layout/tab_bar.xml b/mastodon/src/main/res/layout/tab_bar.xml new file mode 100644 index 00000000..e0ed2757 --- /dev/null +++ b/mastodon/src/main/res/layout/tab_bar.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/menu/home.xml b/mastodon/src/main/res/menu/home.xml index 92299623..5d42d671 100644 --- a/mastodon/src/main/res/menu/home.xml +++ b/mastodon/src/main/res/menu/home.xml @@ -1,6 +1,8 @@ - - - + \ No newline at end of file diff --git a/mastodon/src/main/res/values-v27/colors.xml b/mastodon/src/main/res/values-v27/colors.xml new file mode 100644 index 00000000..072083f4 --- /dev/null +++ b/mastodon/src/main/res/values-v27/colors.xml @@ -0,0 +1,4 @@ + + + @color/actionbar_bg + \ No newline at end of file diff --git a/mastodon/src/main/res/values/colors.xml b/mastodon/src/main/res/values/colors.xml index 49d380bf..18061f26 100644 --- a/mastodon/src/main/res/values/colors.xml +++ b/mastodon/src/main/res/values/colors.xml @@ -21,6 +21,8 @@ #E9EDF2 #282C37 #80667085 + #FAFBFC + #000 #FF9F0A #79BD9A diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index 30988b32..8369f76c 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -27,4 +27,5 @@ %dd Share toot + Settings \ No newline at end of file diff --git a/mastodon/src/main/res/values/styles.xml b/mastodon/src/main/res/values/styles.xml index b248104e..f8d6e096 100644 --- a/mastodon/src/main/res/values/styles.xml +++ b/mastodon/src/main/res/values/styles.xml @@ -5,7 +5,19 @@ false false true + true @color/white + @color/actionbar_bg + @color/navigation_bar_bg + @color/gray_800 + @color/gray_800 + @style/Theme.Mastodon.Toolbar + + +