package org.jorge.cmp.ui.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.crashlytics.android.Crashlytics;
import com.squareup.picasso.Callback;
import org.jorge.cmp.LoLin1Application;
import org.jorge.cmp.R;
import org.jorge.cmp.datamodel.LoLin1Account;
import org.jorge.cmp.io.net.NetworkOperations;
import org.jorge.cmp.io.prefs.PreferenceAssistant;
import org.jorge.cmp.ui.activity.SettingsActivity;
import org.jorge.cmp.ui.adapter.NavigationDrawerAdapter;
import org.jorge.cmp.ui.adapter.NavigationDrawerAdapter.NavigationItem;
import org.jorge.cmp.util.PicassoUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
/**
* @author poliveira
* 24/10/2014
*/
public class NavigationDrawerFragment extends Fragment implements NavigationDrawerAdapter
.NavigationDrawerCallbacks {
private final String TAG = NavigationDrawerFragment.class.getName();
private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
private NavigationDrawerAdapter.NavigationDrawerCallbacks mCallbacks;
private RecyclerView mDrawerList;
private View mFragmentContainerView;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mActionBarDrawerToggle;
private boolean mUserLearnedDrawer;
private boolean mFromSavedInstanceState;
private int mCurrentSelectedPosition;
private ActionBarActivity mActivity;
private Context mContext;
private ImageView mUserImageView;
private final Queue<Runnable> mWhenClosedTasks = new LinkedList<>();
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return mActionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected
(item);
}
@Override
public void onDestroy() {
super.onDestroy();
PicassoUtils.cancel(mContext, TAG, mUserImageView);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_navigation_drawer, container, Boolean.FALSE);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Required to not to transfer the click to the view behind.
}
});
mDrawerList = (RecyclerView) view.findViewById(R.id.drawerList);
LinearLayoutManager layoutManager = new LinearLayoutManager(mActivity);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mDrawerList.setLayoutManager(layoutManager);
mDrawerList.setHasFixedSize(Boolean.TRUE);
mDrawerList.setOverScrollMode(View.OVER_SCROLL_NEVER);
View helpAction = view.findViewById(R.id.action_help), settingsAction = view.findViewById
(R.id.action_settings);
settingsAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
closeDrawer(new Runnable() {
@Override
public void run() {
startActivity(
new Intent(mContext, SettingsActivity.class));
mActivity.overridePendingTransition(R.anim.move_in_from_bottom,
R.anim.move_out_to_bottom);
}
});
}
});
helpAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
closeDrawer(new Runnable() {
@Override
public void run() {
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse(mContext.getString(R.string.help_url)));
startActivity(browserIntent);
}
});
}
});
final List<NavigationItem> navigationItems = readMenuItems();
NavigationDrawerAdapter adapter = new NavigationDrawerAdapter(mContext, navigationItems);
adapter.setNavigationDrawerCallbacks(this);
mDrawerList.setAdapter(adapter);
selectItem(mCurrentSelectedPosition);
return view;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUserLearnedDrawer = PreferenceAssistant.readSharedBoolean(mContext,
PreferenceAssistant.PREF_USER_LEARNED_DRAWER, Boolean.FALSE);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = Boolean.TRUE;
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallbacks = (NavigationDrawerAdapter.NavigationDrawerCallbacks) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
}
mActivity = (ActionBarActivity) activity;
mContext = LoLin1Application.getInstance().getContext();
}
public void setup(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar,
LoLin1Account acc) {
mFragmentContainerView = mActivity.findViewById(fragmentId);
mDrawerLayout = drawerLayout;
mActionBarDrawerToggle = new ActionBarDrawerToggle(mActivity, mDrawerLayout, toolbar,
R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if (!isAdded()) return;
mActivity.invalidateOptionsMenu();
if (!PreferenceAssistant.readSharedBoolean(mContext,
PreferenceAssistant.PREF_PULL_TO_REFRESH_LEARNED, Boolean.FALSE)) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, R.string.pull_to_refresh,
Toast.LENGTH_SHORT).show();
}
});
PreferenceAssistant.writeSharedBoolean(mContext,
PreferenceAssistant.PREF_PULL_TO_REFRESH_LEARNED, Boolean.TRUE);
}
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
if (!isAdded()) return;
if (!mUserLearnedDrawer) {
mUserLearnedDrawer = Boolean.TRUE;
PreferenceAssistant.writeSharedBoolean(mContext,
PreferenceAssistant.PREF_USER_LEARNED_DRAWER,
Boolean.TRUE);
}
mActivity.invalidateOptionsMenu();
}
};
if (!mUserLearnedDrawer && !mFromSavedInstanceState)
mDrawerLayout.openDrawer(mFragmentContainerView);
mDrawerLayout.post(new Runnable() {
@Override
public void run() {
mActionBarDrawerToggle.syncState();
}
});
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
mUserImageView = (ImageView) mDrawerLayout.findViewById(R.id.user_photo);
((TextView) mDrawerLayout.findViewById(R.id.user_name)).setText(acc.getUsername());
((TextView) mDrawerLayout.findViewById(R.id.realm_name)).setText(acc.getRealmEnum().name
().toUpperCase(Locale.ENGLISH));
}
public void asyncLoadUserImage(LoLin1Account acc) {
new AsyncTask<Object, Void, String>() {
private ImageView iv;
private String realm
,
username;
private final String PROFILE_ICON_URL_PATTERN = NavigationDrawerFragment.this
.mContext.getString(R.string.profile_icon_url_pattern)
,
REALM_INFO_URL_PATTERN = NavigationDrawerFragment.this
.mContext.getString(R.string.realm_info_url_pattern)
,
USER_INFO_URL_PATTERN = NavigationDrawerFragment.this
.mContext.getString(R.string.profile_icon_id_url_pattern);
final String KEY_CONTENTS = "CONTENTS";
final Bundle mVersionBundle = new Bundle()
,
mIdBundle = new Bundle();
@Override
protected String doInBackground(Object... params) {
iv = (ImageView) params[0];
realm = (String) params[1];
username = (String) params[2];
final CountDownLatch urlBuilderLatch = new CountDownLatch(2);
findProfileIconVersion(urlBuilderLatch);
findProfileIconId(urlBuilderLatch);
try {
urlBuilderLatch.await();
} catch (InterruptedException e) {
//Should never happen
Crashlytics.logException(e);
}
PreferenceAssistant.writeSharedString(mContext,
PreferenceAssistant.PREF_LAST_PROFILE_ICON_VERSION,
mVersionBundle.getString(KEY_CONTENTS));
return String.format(Locale.ENGLISH, PROFILE_ICON_URL_PATTERN,
mVersionBundle.getString(KEY_CONTENTS), mIdBundle.getString(KEY_CONTENTS));
}
private void findProfileIconVersion(CountDownLatch latch) {
new AsyncTask<Object, Void, String>() {
private CountDownLatch countDownLatch;
private String realm;
private Bundle retBag;
@Override
protected String doInBackground(Object... params) {
countDownLatch = (CountDownLatch) params[0];
realm = (String) params[1];
retBag = (Bundle) params[2];
try {
return new JSONObject(NetworkOperations.performGETRequest(new URL(String
.format(Locale.ENGLISH,
REALM_INFO_URL_PATTERN, realm))).body().string())
.getJSONObject("n")
.getString("profileicon");
//If the body is malformed Picasso will find an error and hide the
// ImageView,
// so it's fine
} catch (IOException | JSONException e) {
return "null";
}
}
@Override
protected void onPostExecute(String s) {
retBag.putString(KEY_CONTENTS, s);
countDownLatch.countDown();
}
}.executeOnExecutor(Executors.newSingleThreadExecutor(), latch, realm,
mVersionBundle);
}
private void findProfileIconId(CountDownLatch latch) {
new AsyncTask<Object, Void, String>() {
private CountDownLatch countDownLatch;
private String realm;
private Bundle retBag;
private String username;
@Override
protected String doInBackground(Object... params) {
countDownLatch = (CountDownLatch) params[0];
username = (String) params[1];
realm = (String) params[2];
retBag = (Bundle) params[3];
try {
return new JSONObject(NetworkOperations.performGETRequest(new URL(String
.format(Locale.ENGLISH, USER_INFO_URL_PATTERN, username,
realm))
).body().string()).getJSONObject(username.toLowerCase(Locale.ENGLISH)
).getString
("profileIconId");
} catch (JSONException | IOException e) {
return "null";
}
}
@Override
protected void onPostExecute(String s) {
retBag.putString(KEY_CONTENTS, s);
countDownLatch.countDown();
}
}.executeOnExecutor(Executors.newSingleThreadExecutor(), latch, username, realm,
mIdBundle);
}
@Override
protected void onPostExecute(String imageUrl) {
PicassoUtils.loadInto(mContext, imageUrl, new Callback() {
@Override
public void onSuccess() {
iv.setVisibility(View.VISIBLE);
}
@Override
public void onError() {
if (iv.isShown())
iv.setVisibility(View.GONE);
}
}, iv, TAG);
}
}.executeOnExecutor(Executors.newSingleThreadExecutor(), mUserImageView,
acc.getRealmEnum().name().toUpperCase(Locale.ENGLISH), acc.getUsername());
}
public void closeDrawer(Runnable... runnables) {
mDrawerLayout.closeDrawer(mFragmentContainerView);
Collections.addAll(mWhenClosedTasks, runnables);
while (!mWhenClosedTasks.isEmpty()) {
mActivity.runOnUiThread(mWhenClosedTasks.poll());
}
}
@Override
public void onDetach() {
super.onDetach();
}
public List<NavigationItem> readMenuItems() {
List<NavigationItem> items = new ArrayList<>();
Resources resources = mContext.getResources();
final String NAVIGATION_TITLE_STANDARD_DRAWABLE_PATTERN = mContext.getString(R.string
.navigation_title_standard_resource_pattern),
NAVIGATION_TITLE_SELECTED_DRAWABLE_PATTERN = mContext.getString(R.string
.navigation_title_selected_resource_pattern);
final String[] itemNames = resources.getStringArray(R.array.navigation_drawer_items);
final List<Drawable> standardItemIcons = new ArrayList<>(),
selectedItemIcons = new ArrayList<>();
for (int i = 0; i < itemNames.length; i++) {
final String standardDrawableResourceName = String.format
(Locale.ENGLISH, NAVIGATION_TITLE_STANDARD_DRAWABLE_PATTERN, i),
selectedDrawableResourceName = String.format
(Locale.ENGLISH, NAVIGATION_TITLE_SELECTED_DRAWABLE_PATTERN, i);
try {
final Field standardDrawableResourceField = R.drawable.class.getDeclaredField
(standardDrawableResourceName),
selectedDrawableResourceField = R.drawable.class.getDeclaredField
(selectedDrawableResourceName);
standardItemIcons.add(resources.getDrawable(standardDrawableResourceField.getInt
(standardDrawableResourceField)));
selectedItemIcons.add(resources.getDrawable(selectedDrawableResourceField.getInt
(selectedDrawableResourceField)));
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException("Expected drawable resource " +
standardDrawableResourceName + " not found.");
}
}
if (standardItemIcons.size() < itemNames.length) {
throw new IllegalStateException("Not enough icons for this many navigation choices");
}
for (int i = 0; i < itemNames.length; i++) {
items.add(new NavigationItem(itemNames[i], standardItemIcons.get(i),
selectedItemIcons.get(i)));
}
return items;
}
public void selectItem(int position) {
mCurrentSelectedPosition = position;
if (mDrawerLayout != null) {
closeDrawer();
}
if (mCallbacks != null) {
mCallbacks.onNavigationDrawerItemSelected(position);
}
((NavigationDrawerAdapter) mDrawerList.getAdapter()).selectPosition(position);
}
public boolean isDrawerOpen() {
return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mActionBarDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
}
@Override
public void onNavigationDrawerItemSelected(int position) {
selectItem(position);
}
public int getPosition() {
return ((NavigationDrawerAdapter) mDrawerList.getAdapter()).getSelectedPosition();
}
public void lockDrawerClosed() {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
public void unlockDrawer() {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
}