Java tutorial
/** * The MIT License (MIT) * * Copyright (c) 2015 <a href="https://bitbucket.org/malachid/fetlife-oss/src/default/Contributors.md?at=default">FetLife-OSS Contributors</a> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.goldenspiral.fetlifeoss.ui.fragments; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.databinding.DataBindingUtil; import android.databinding.ViewDataBinding; import android.net.Uri; import android.os.Bundle; import android.support.annotation.MenuRes; import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; import android.support.v7.view.ContextThemeWrapper; import android.support.v7.widget.ShareActionProvider; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.google.gson.Gson; import net.goldenspiral.fetlifeoss.R; import net.goldenspiral.fetlifeoss.ui.MainActivity; import net.goldenspiral.fetlifeoss.ui.components.FetLifeDatabinder; import net.goldenspiral.fetlifeoss.util.gson.GsonUtil; /** * Created by malachi on 8/8/15. */ public abstract class FetLifeFragment<BINDING extends ViewDataBinding> extends Fragment implements View.OnClickListener { private ShareActionProvider shareActionProvider; private Intent lastShareIntent = null; private View rootView; private Gson gson; /** * Default constructor. <strong>Every</strong> fragment must have an * empty constructor, so it can be instantiated when restoring its * activity's state. It is strongly recommended that subclasses do not * have other constructors with parameters, since these constructors * will not be called when the fragment is re-instantiated; instead, * arguments can be supplied by the caller with {@link #setArguments} * and later retrieved by the Fragment with {@link #getArguments}. * <p/> * <p>Applications should generally not implement a constructor. The * first place application code an run where the fragment is ready to * be used is in {@link #onAttach(Activity)}, the point where the fragment * is actually associated with its activity. Some applications may also * want to implement {@link #onInflate} to retrieve attributes from a * layout resource, though should take care here because this happens for * the fragment is attached to its activity. */ public FetLifeFragment() { super(); } protected Gson gson() { if (gson != null) return gson; gson = GsonUtil.instance.getBuilder().create(); return gson; } protected <DATA> Bundle addToBundle(final Bundle bundle, @StringRes final int keyId, final DATA data) { return addToBundle(bundle, getResources().getString(keyId), data); } protected <DATA> Bundle addToBundle(final Bundle bundle, final String key, final DATA data) { bundle.putString(key, gson().toJson(data)); return bundle; } protected <DATA> DATA getFromBundle(final Bundle bundle, @StringRes final int keyId, final Class<DATA> dataClass) { return getFromBundle(bundle, getResources().getString(keyId), dataClass); } protected <DATA> DATA getFromBundle(final Bundle bundle, final String key, final Class<DATA> dataClass) { if (bundle == null) return null; if (!bundle.containsKey(key)) return null; return gson().fromJson(bundle.getString(key), dataClass); } /** * Title resource id. * * @return title resource identifier */ public int getTitle() { return -1; } /** * Initialize the contents of the Activity's standard options menu. You * should place your menu items in to <var>menu</var>. For this method * to be called, you must have first called {@link #setHasOptionsMenu}. See * {@link android.app.Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} * for more information. * * @param menu The options menu in which you place your items. * @param inflater * @see #setHasOptionsMenu * @see #onPrepareOptionsMenu * @see #onOptionsItemSelected */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); int resId = getOptionMenuResId(); if (resId == 0) return; inflater.inflate(resId, menu); MenuItem item = menu.findItem(R.id.menu_share); if (item != null) { shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item); if (lastShareIntent != null) shareActionProvider.setShareIntent(lastShareIntent); } } protected @MenuRes int getOptionMenuResId() { return 0; } /** * Report that this fragment would like to participate in populating * the options menu by receiving a call to {@link #onCreateOptionsMenu} * and related methods. * * @param hasMenu If true, the fragment has menu items to contribute. */ @Override public void setHasOptionsMenu(boolean hasMenu) { super.setHasOptionsMenu(hasMenu); final MainActivity activity = getMainActivity(); if (activity != null) { final ActionBar bar = activity.getSupportActionBar(); if (bar != null) bar.invalidateOptionsMenu(); } } private BINDING dataBinding; public void setRootBinding(BINDING binding) { this.dataBinding = binding; setRootView(dataBinding.getRoot()); } public BINDING getRootBinding() { return dataBinding; } /** * Set the root view of this fragment so other classes (like Adapters) can get ahold of it * * @param rootView as returned by onCreateView */ public void setRootView(View rootView) { this.rootView = rootView; } public View getRootView() { return rootView; } public MainActivity getMainActivity() { return (MainActivity) getActivity(); } /** * * @param inflater The LayoutInflater object that can be used to inflate any views in the fragment, * @param container If non-null, this is the parent view that the fragment's * UI should be attached to. The fragment should not add the view itself, * but this can be used to generate the LayoutParams of the view. * @param theme to use for the new UI * @param layout to inflate * * @return Return the View for the fragment's UI, or null. */ protected View inflate(final LayoutInflater inflater, final ViewGroup container, int theme, int layout) { // create ContextThemeWrapper from the original Activity Context with the custom theme final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), theme); // clone the inflater using the ContextThemeWrapper LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper); // inflate the layout using the cloned inflater, not default inflater return localInflater.inflate(layout, container, false); } /** * Convenience wrapper that assumes AppTheme * * @param inflater The LayoutInflater object that can be used to inflate any views in the fragment, * @param container If non-null, this is the parent view that the fragment's * UI should be attached to. The fragment should not add the view itself, * but this can be used to generate the LayoutParams of the view. * @param layout to inflate * * @return Return the View for the fragment's UI, or null. */ protected View inflate(final LayoutInflater inflater, final ViewGroup container, int layout) { return inflate(inflater, container, R.style.AppTheme, layout); } protected BINDING bind(final LayoutInflater inflater, final ViewGroup container, int theme, int layout) { // create ContextThemeWrapper from the original Activity Context with the custom theme final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), theme); // clone the inflater using the ContextThemeWrapper LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper); // inflate the layout using the cloned inflater, not default inflater // @TODO AndroidStudio shows an error for FetLifeDatabinder - see it for details return DataBindingUtil.inflate(localInflater, layout, container, false, FetLifeDatabinder.INSTANCE); } protected BINDING bind(final LayoutInflater inflater, final ViewGroup container, int layout) { return bind(inflater, container, R.style.AppTheme, layout); } /** * Set this fragment to be the click listener for the given views. * Must call setRootView first. * * @param viewIds to bind * @see #setRootView */ protected void setOnClickListeners(int... viewIds) { for (int viewId : viewIds) rootView.findViewById(viewId).setOnClickListener(this); } /** * Called when a view has been clicked. * * @param view The view that was clicked. */ @Override public void onClick(View view) { } public void onBackPressed() { Activity activity = getActivity(); if (activity != null) activity.finish(); } /** * (Un)Lock the navigation drawer * * @param locked if it should be locked closed */ public void setDrawerLocked(boolean locked) { MainActivity activity = getMainActivity(); if (activity != null) activity.setDrawerLocked(locked); } /** * Convenience function since we are likely to do this a lot */ public void snack(String fmt, Object... args) { Snackbar.make(getRootView(), String.format(fmt, args), Snackbar.LENGTH_LONG).show(); } protected void setShareIntent(Intent intent) { lastShareIntent = intent; if (shareActionProvider != null) shareActionProvider.setShareIntent(intent); } protected void setShareIntent(String message) { final Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TITLE, getResources().getString(R.string.share_title)); sendIntent.putExtra(Intent.EXTRA_TEXT, message); sendIntent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(getResources().getString(R.string.share_referrer))); sendIntent.setType("text/plain"); setShareIntent(sendIntent); } protected void setShareIntent(@StringRes int resId, Object... args) { setShareIntent(format(resId, args)); } protected String format(@StringRes int resId, Object... args) { return String.format(getResources().getString(resId), args); } }