public class AuthenticationManager {
    public static AuthenticationManager authenticationManager;
    private Context applicationContext;
    private RestApi restAPI;

    private Gson gson;

    private static String preferencesName = "AuthenticationPreference";
    private static String apiKeyPreferenceName = "AutheticationApiKeyPreference";
    private static String usernamePreferenceName = "AutheticationUsernamePreference";

    private AuthenticationManager() {
        // Init
    }

    public static AuthenticationManager getAuthenticationManager() {
        if (authenticationManager == null) {
            authenticationManager = new AuthenticationManager();
        }
        return authenticationManager;
    }

    public void setApplicationContext(Context context) {
        applicationContext = context;
        authenticationManager.setUpRestApi();
    }

    public Boolean isLoggedIn(Context context){
        if(apiKey(context) == null || username(context) == null){
            return false;
        }

        return true;
    }

    public String apiKey(Context context){
        SharedPreferences preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
        String apiKey = preferences.getString(apiKeyPreferenceName, null);
        return apiKey;
    }

    public String username(Context context){
        SharedPreferences preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
        String username = preferences.getString(usernamePreferenceName, null);
        return username;
    }

    public void login(String username, String password, Context context, final AuthenticationDelegate delegate){
        if(Online()){

            //Get the language currently set, defaults to Azerbaijari ("az")
            Locale locale = Language.getLanguage(context);

            String language = context.getString(R.string.default_language);
            if(locale != null) {
                language = locale.getLanguage();
            }


            HashMap requestParametners = new HashMap();
            requestParametners.put("installation_uuid", AnalyticsManager.installationUUID(context).toString());
            requestParametners.put("language", language);
            requestParametners.put("username", username);
            requestParametners.put("password", password);

            restAPI.login(requestParametners, new Callback<LoginRequest>() {
                @Override
                public void success(LoginRequest loginRequest, Response response){
                    Integer code = loginRequest.getCode();

                    if(code == 0) {
                        delegate.didReceiveErrorOnLogin();
                        return;
                    }

//                    String bodyText = response.getBody().toString();

//                    JsonElement body = gson().fromJson(bodyText, String);
//                    gson()
                    String apiKey = loginRequest.getApiKey();
                    String username = loginRequest.getUsername();

                    SharedPreferences preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putString(apiKeyPreferenceName, apiKey);
                    editor.putString(usernamePreferenceName, username);
                    editor.commit();

                    delegate.didLoginSuccessfully();
                }

                @Override
                public void failure(RetrofitError error) {
                    delegate.didReceiveErrorOnLogin();
                }
            });
        }
    }

    public void logout(Context context) {
        SharedPreferences preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString(apiKeyPreferenceName, null);
        editor.putString(usernamePreferenceName, null);
        editor.commit();
    }

    private void setUpRestApi() {
        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void intercept(RequestInterceptor.RequestFacade request) {
                request.addHeader("Accept", "application/json; charset=utf-8");
            }
        };

        //create an adapter for retrofit with base url
        RestAdapter restAdapter = new RestAdapter.Builder().setLogLevel(RestAdapter.LogLevel.FULL)
                .setRequestInterceptor(requestInterceptor)
                .setEndpoint(applicationContext.getResources().getString(R.string.server_url)).build();
        //creating a service for adapter with our GET class
        restAPI = restAdapter.create(RestApi.class);
    }

    private boolean Online() {
        ConnectivityManager manager = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        // check that there is an active network
        if (info == null) {
            return false;
        }

        return info.isConnected();
    }

    private Gson gson(){
        if(this.gson == null){
            this.gson = new Gson();
        }

        return this.gson;
    }

} public class PostFragmentAdapter extends FragmentPagerAdapter  {
    public static ArrayList<Article> postItems;

    private int mCount;
    private Context mContext;

    public PostFragmentAdapter(Context mContext,FragmentManager fm) {
        super(fm);

        this.mContext = mContext;

        if(postItems != null){
           mCount = postItems.size();
        }

    }

    @Override
    public Fragment getItem(int position) {
        //First let's get what category we're going to be in. For the moment we're going to assume there are always ten stories
        return DetailPost.newInstance(mContext, postItems.get(position));
    }

    @Override
    public int getCount() {
        return mCount;
    }

    @Override
    public CharSequence getPageTitle(int position) {
      return PostFragmentAdapter.postItems.get(position).getHeadline();
    }


    public void setCount(int count) {
        if (count > 0 && count <= 10) {
            mCount = count;
            notifyDataSetChanged();
        }
    }
} public class PostListAdapter extends ArrayAdapter<Article> implements ImageCacheDelegate {
    // Android's "findItemByID request is really really expensive apparently.
    // This is the standard "ViewHolder" pattern which handles the requests at the time
    // The object is created.
    // All references to setting the fields should go through here
    private static class ViewHolderItem {
        ImageView imageView;
        TextView headline;
        String tag;
        TextView dateView;
    }
    private List<Article> itemsList;
    private HashMap<String, ArrayList<Article>> itemsHash;
    private List<String> categories;
    private Integer articlesPerCategory;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    private LayoutInflater inflater;
    private TypefaceManager fontManager;

    static private String mainViewTag = "mainViewTag";
    static private String subViewTag = "subViewTag";
    static private String headerViewTag = "headerViewTag";

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Let's figure out what type of view we'll need, so we don't have to recreate the view object
        // If not necessary. Saves a lot of CPU cycles.

        View cell = new View(getContext());
        Article article;
        ViewHolderItem viewHolder;

        String tag = null;
        if(cell != null){
            ViewHolderItem viewHolderItem = (ViewHolderItem)cell.getTag();
            if(viewHolderItem != null) {
                tag = viewHolderItem.tag;
            } else {
                viewHolderItem = new ViewHolderItem();
                cell.setTag(viewHolderItem);
            }
        }

        // If there are no categories we just go by normal ordering
        if(itemsHash == null) {

            // If it's the top view, initialize a first_item_view
            if(position == 0){
                if(convertView == null || tag != mainViewTag){
                    cell = inflater.inflate(R.layout.first_item_view, null);
                    cell.setTag(mainViewTag);
                } else {
                    cell = convertView;
                }
            } else {
                // Otherwise, stick with a normal list
                if(convertView == null || tag != subViewTag) {
                    cell = inflater.inflate(R.layout.list_news_item, null);
                    cell.setTag(subViewTag);
                } else {
                    cell = convertView;
                }
            }

            try {
                article = itemsList.get(position);
            } catch (Exception e) {
                Gson gson = new Gson();
                article = gson.fromJson(gson.toJsonTree(itemsList.get(position)), Article.class);
            }
        } else {
            // We're dealing with categories here
            // First we need to figure out if we're in a header
            List items = this.items();
            Object item = items.get(position);
            if(item.getClass() == String.class) {
                if(convertView == null || tag != headerViewTag){
                    cell = new TextView(getContext());
                    cell.setPadding(30,30,10,20);
                    cell.setTag(headerViewTag);
                } else {
                    cell = convertView;
                }

                TextView textView = (TextView)cell;
                String category_name = (String)item;
                category_name = category_name.replace("_", " ");
                category_name = category_name.substring(0,1).toUpperCase() + category_name.substring(1);

                textView.setText(category_name);
                textView.setTypeface(fontManager.getAmericanTypewriter());

                return textView;
            } else {
                article = (Article)item;

                // We check if the previous element is a string
                Object previousElement = this.items().get(position - 1);
                if(previousElement.getClass() == String.class){
                    if(convertView == null || tag != mainViewTag){
                        cell = inflater.inflate(R.layout.first_item_view, null);
                        cell.setTag(mainViewTag);
                    } else {
                        cell = convertView;
                    }
                } else {
                    // Otherwise, stick with a normal list
                    if(convertView == null || tag != subViewTag) {
                        cell = inflater.inflate(R.layout.list_news_item, null);
                        cell.setTag(subViewTag);
                    } else {
                        cell = convertView;
                    }
                }

            }
        }

        if(cell.getTag().getClass() != ViewHolderItem.class) {
            viewHolder = new ViewHolderItem();
            viewHolder.imageView = (ImageView) cell.findViewById(R.id.post_Image);
            viewHolder.tag = (String) cell.getTag();
            viewHolder.headline = (TextView) cell.findViewById(R.id.post_Name);
            viewHolder.dateView = (TextView) cell.findViewById(R.id.post_Date);

            cell.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolderItem)cell.getTag();
        }

        // Set images
        ViewHolderItem viewHolderItem = (ViewHolderItem)cell.getTag();
        ImageView imageView = viewHolderItem.imageView;

        try{
            imageView.setVisibility(View.INVISIBLE);
            if(article.getHeaderImage() != null && article.getHeaderImage().size() > 0){
                GlideApp.with(cell).load(article.getHeaderImage())
                        .placeholder(new ColorDrawable(Color.BLACK))
                        .listener(new RequestListener<Drawable>() {
                            @Override
                            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                                imageView.setVisibility(View.GONE);

                                return false;
                            }

                            @Override
                            public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                                imageView.setVisibility(View.VISIBLE);
                                return false;
                            }

                        })
                        .into(imageView);
            } else if(article.getImages().size() > 0) {
                GlideApp.with(cell).load(article.getImages().get(0).get("url"))
                        .placeholder(new ColorDrawable(Color.BLACK))
                        .listener(new RequestListener<Drawable>() {
                            @Override
                            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                                imageView.setVisibility(View.GONE);
                                return false;
                            }

                            @Override
                            public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                                imageView.setVisibility(View.VISIBLE);
                                return false;
                            }

                        })
                        .into(imageView);
            } else {
                imageView.setVisibility(View.GONE);
            }
        } catch(Exception e){
            Log.w("error", "Exception");

        }

        if(viewHolderItem.tag.equals(mainViewTag)){
            TextView description = (TextView)cell.findViewById(R.id.postDescription);
            if(article.getDescription() != null) {
                description.setText(article.getDescription());
            }
        }

        TextView titleView = viewHolderItem.headline;

        try{
            titleView.setTypeface(fontManager.getRobotoMedium());
            titleView.setText(article.getHeadline());
            TextView dateView = viewHolderItem.dateView;
            Date date = sdf.parse(String.valueOf(article.getPublishDate()));
            if (article.getAuthor() != null && article.getAuthor().length() > 0 && SettingsManager.shouldShowAuthor(getContext())) {
                String separator = Language.bylineSeperator(this.getContext());
                String text = DateUtil.setTime(getContext(), date.getTime(), true) + separator + article.getAuthor();
                dateView.setText(text);
            } else {
                dateView.setText(DateUtil.setTime(getContext(), date.getTime(), true));
            }
        } catch(Exception e) {
            return new View(getContext());
        }

        return cell;
    }

    public PostListAdapter(HomeActivity activity, int layout, ArrayList<Article> articles){
        super(activity, layout, articles);
        this.setInitialVariables(getContext(), layout, null, 0);

        this.itemsList = articles;
    }

    public PostListAdapter(Context context, int layout) {
        super(context, layout);
        this.setInitialVariables(context, layout, categories, 0);
    }

    public PostListAdapter(Context context, int layout, ArrayList<Article> items) {
        super(context, layout);
        this.setInitialVariables(context, layout, null, 0);

        this.itemsList = items;
    }

    public PostListAdapter(Context context, int layout, HashMap<String, ArrayList<Article>> items, List categories, Integer articlesPerCategory) {
        super(context, layout);
        this.setInitialVariables(context, layout, categories, articlesPerCategory);

        this.itemsHash = items;
    }

    // If articlesPerCategory is less than 1, just show them all.
    private void setInitialVariables(Context context, int layout, List categories, Integer articlesPerCategory){
        this.categories = categories;
        if(articlesPerCategory < 1){
            articlesPerCategory = Integer.MAX_VALUE;
        }

        this.articlesPerCategory = articlesPerCategory;
        inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // init Typeface
        fontManager = new TypefaceManager(context.getAssets());
    }


    private Integer totalItems() {
        Integer count = 0;

        if(this.itemsHash == null){
            if(this.itemsList != null){
                count = this.itemsList.size();
            }
        } else {
            count = this.categories.size();
            for (String key : this.categories) {
                Integer size = this.itemsHash.get(key).size();
                if (size > articlesPerCategory) {
                    count += articlesPerCategory;
                } else {
                    count += size;
                }
            }
        }

        return count;
    }

    public int getCount() {
        return this.totalItems();
    }

    // This returns a List of all the elements needed to show
    // If it's not categorized it just returns the list
    // If it's categorized, the list is flattened with headers in between
    private List items() {
        if(this.itemsHash == null){
            return this.itemsList;
        }

        ArrayList items = new ArrayList();

        for(String key: this.categories){
            items.add(key);

            if(this.itemsHash.get(key).size() > articlesPerCategory){
                items.addAll(this.itemsHash.get(key).subList(0, 5));
            } else {
                items.addAll(this.itemsHash.get(key));
            }
        }

        return items;
    }

    public ArrayList<Article> items(Boolean cleanCategories) {
        if(itemsHash == null){
            return (ArrayList<Article>)itemsList;
        }

        List items = items();
        List itemsCopy = items();
        for(Object item: items){
            if(item.getClass() == String.class){
                itemsCopy.remove(item);
            }
        }

        return (ArrayList<Article>) itemsCopy;
    }

    public Object getItemAtPosition(int position) {
        return items().get(position);
    }

    public ArrayList<Article> getArticlesForCategory(String category){
        if(itemsHash != null){
            return itemsHash.get(category);
        }
        return null;
    }

    // ImageCacheDelegate

    public void didLoadImage(Bitmap bitmap, ImageView imageView, String url) {
        imageView.setImageBitmap(bitmap);
        imageView.setVisibility(View.VISIBLE);
    }

    public void errorLoadingImage(ImageView imageView, String url) {
        imageView.setImageResource(R.drawable.fallback);
        imageView.setVisibility(View.VISIBLE);
    }


} public class CategoryFragment extends Fragment implements ObservableScrollViewCallbacks {

    private PostListAdapter mListAdapter;
    public ArrayList<Article> articles;

    private View mRootView;
    private ListView mListView;

    public CategoryFragment(){

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        mRootView = inflater.inflate(R.layout.fragment_category, container, false);
        mListView = (ListView)mRootView.findViewById(R.id.category_lists);

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                PostFragmentAdapter.postItems = articles;

                Article article = articles.get(position);
                Intent i = new Intent(getActivity().getApplicationContext(), DetailPostActivity.class);

                i.putExtra("postPosition", articles.indexOf(article));
                i.putExtra("postTitle", article.getHeadline());
                i.putExtra("description", article.getDescription());


                startActivity(i);
                AnalyticsManager.logContentView(article.getHeadline(),
                        "Article Opened", article.getId());
            }
        });

        Intent i = getActivity().getIntent();

        articles = (ArrayList<Article>) i.getExtras().get("articles");
        setArticles(articles);

        return mRootView;
    }

    public void setArticles(ArrayList<Article> articles) {
        this.articles = articles;

        mListAdapter = new PostListAdapter(getContext(), R.layout.list_news_item, articles);
        mListView.setAdapter(mListAdapter);
    }

    @Override
    public void onScrollChanged(int scrollY, boolean firstScroll,
                                boolean dragging) {
    }

    @Override
    public void onDownMotionEvent() {
    }

    @Override
    public void onUpOrCancelMotionEvent(ScrollState scrollState) {
    }

} public class DonatePage extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    AQuery aq;

    private OnFragmentInteractionListener mListener;

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment DonatePage.
     */
    // TODO: Rename and change types and number of parameters
    public static DonatePage newInstance(String param1, String param2) {
        DonatePage fragment = new DonatePage();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    public DonatePage() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_donate_page, container, false);
        aq = new AQuery(view);

        aq.id(R.id.btn_donate).clicked(new View.OnClickListener() {
            @Override
            public void onClick(View view){
                String amount = aq.id(R.id.txt_amount).getText().toString().trim();
                if(amount.length() == 0){
                    Toast.makeText(getActivity(), getActivity().getString(R.string.enter_valid_amount), Toast.LENGTH_LONG).show();
                    return;
                }
                InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(view.getWindowToken(), 0);

                DonateConfirmationDialog.newInstance(amount).show(getFragmentManager(), "confirm_donation");
            }
        });
        return view;
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public static class DonateConfirmationDialog extends DialogFragment{
        public static DonateConfirmationDialog newInstance(String a){
            DonateConfirmationDialog fragment = new DonateConfirmationDialog();
            Bundle b = new Bundle();
            b.putString("amount", a);
            fragment.setArguments(b);

            return fragment;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            String amount = getArguments().getString("amount");
            amount = new DecimalFormat("USD ###,###.##").format(Double.parseDouble(amount));
            //builder.setTitle("Confirm Donation");
            builder.setMessage(getActivity().getString(R.string.donate_confirmation_prompt) + " " + amount + "?");
            builder.setPositiveButton("Donate", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    showDonationSummary();
                }
            });

            builder.setNegativeButton("Nope!", null);
            Dialog dialog = builder.create();

            return dialog;
        }

        private void showDonationSummary(){
            FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.container_body, new DonationSummary());
            transaction.commit();

            // set the toolbar title
            try {
                getActivity().getActionBar().setTitle("Thank you!");
            }catch (NullPointerException e){
                e.printStackTrace();
            }
        }
    }
} public class LanguageSelectDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        builder.setTitle(R.string.choose_language)
                .setNegativeButton(R.string.cancel, null);

        // Sort the languages in alphabetic order
        final Set languages = Language.getAppLanguages(getActivity());

        // The joys of Java casting, why are there fourteen types of lists?
        // This literally just massages everything into being comprehensible collections and lists
        ArrayList<String> languageArray = new ArrayList(Arrays.asList(languages.toArray()));

        //Also, it sorts them
        Collections.sort(languageArray);
        final CharSequence[] languageChars = languageArray.toArray(new CharSequence[languages.size()]);

        int index = -1;
        Locale currentLocale = Language.getLanguage(getActivity());

        ArrayList<String> fullLanguageArrayList = new ArrayList<>();

        // This loop gets the index to be selected and also get the correct full language name for display
        for(int i = 0; i < languageChars.length; i++) {
            CharSequence languageSequence = languageChars[i];

            if(currentLocale.getLanguage().equals(languageSequence.toString())){
                index = i;
            }

            Locale locale = Language.languageForTag(languageSequence.toString());
            //Serbian has both Latin and Cyrillic, since this is rare, Android of course only
            //support Cyrillic, the same one that no one really uses
            String displayLanguage;
            if(!locale.getLanguage().equals("sr")) {
                displayLanguage = locale.getDisplayLanguage(locale);
            } else {
                displayLanguage = "Srpski";
            }

            fullLanguageArrayList.add(displayLanguage);
        }

        // More massaging
        CharSequence[] fullLanguageCharSequence = fullLanguageArrayList.toArray(new CharSequence[fullLanguageArrayList.size()]);

        // Finally set the choices
        builder.setSingleChoiceItems(fullLanguageCharSequence, index, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                String locale = languageChars[which].toString();
                Locale l = Language.languageForTag(locale);
                Language.setLanguage(getActivity(), l);

                dismiss();
            }
        });

        // Create the AlertDialog object and return it
        return builder.create();
    }

} public interface ImageCacheDelegate
{
    void didLoadImage(Bitmap bitmap, ImageView imageView, String url);
    void errorLoadingImage(ImageView imageView, String url);
} public interface OnFragmentInteractionListener {
    void onFragmentInteraction(Uri id);
    void onFragmentInteraction(String id);
} public interface RestApi {

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, Callback<ArticlePost> response, @Query("categories")boolean categories);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("language")String language, Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("language")String language, @Query("categories")boolean categories, Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size, Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("categories")boolean categories, @Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size, Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size, @Query("language")String language ,Callback<ArticlePost> response);

    @GET("/articles.json")
    void getArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("categories")boolean categories,@Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size, @Query("language")String language ,Callback<ArticlePost> response);

    @GET("/search.json")
    void searchArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("q")String query,@Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size,Callback<ArticlePost> response);

    @GET("/search.json")
    void searchArticles(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("q")String query,@Query("start_date")long start_date,@Query("end_date")long end_date,@Query("pages")int pages,@Query("page_size")int page_size,@Query("language")String language,Callback<ArticlePost> response);

    @GET("/article.json")
    void getArticle(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("id")String id, Callback<ArticlePost> response);

    @GET("/article.json")
    void getArticle(@Query("installation_uuid")String installation_uuid, @Query("api_key")String api_key, @Query("id")String id,@Query("language")String language, Callback<ArticlePost> response);

    @POST("/authenticate")
    void login(@Body()HashMap body, Callback<LoginRequest> response);
    //void login(@Query("installation_uuid")String installation_uuid, @Query("language")String language, @Query("username")String username, @Query("password")String password, Callback<LoginRequest> response);
} public interface ArticleDelegate {
    void didRetrieveArticle(Article article);
} public class Article implements Serializable{

    @Expose
    private String headline;
    @Expose
    private String description;
    @Expose
    private String body;
    @SerializedName("publish_date")
    @Expose
    private Integer publishDate;
    @Expose
    private String author;
    @Expose
    private String organization;
    @Expose
    private String language;
    @Expose
    @SerializedName("header_image")
    private HashMap<String, String> header_image = new HashMap<String, String>();
    @Expose
    @SerializedName("images")
    private List<HashMap<String, String>> images = new ArrayList<HashMap<String, String>>();
    @Expose
    @SerializedName("videos")
    private List<HashMap<String, String>> videos = new ArrayList<HashMap<String, String>>();
    @Expose
    private String id;
    @SerializedName("image_urls")
    @Expose
    private List<String> imageUrls = new ArrayList<String>();
    @SerializedName("captions")
    @Expose
    private List<String> photoBylines = new ArrayList<String>();
    @SerializedName("photoBylines")
    @Expose
    private List<String> captions = new ArrayList<String>();
    @SerializedName("url")
    @Expose
    private String url;

    /**
     *
     * @return
     * The headline
     */
    public String getHeadline() {
        return headline;
    }

    /**
     *
     * @param headline
     * The headline
     */
    public void setHeadline(String headline) {
        this.headline = headline;
    }

    /**
     *
     * @return
     * The description
     */
    public String getDescription() {
        return description;
    }

    /**
     *
     * @param description
     * The description
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     *
     * @return
     * The body
     */
    public String getBody() {
        return body;
    }

    /**
     *
     * @param body
     * The body
     */
    public void setBody(String body) {
        this.body = body;
    }

    /**
     *
     * @return
     * The publishDate
     */
    public Integer getPublishDate() {
        return publishDate;
    }

    /**
     *
     * @param publishDate
     * The publish_date
     */
    public void setPublishDate(Integer publishDate) {
        this.publishDate = publishDate;
    }

    /**
     *
     * @return
     * The author
     */
    public String getAuthor() {
        return author;
    }

    /**
     *
     * @param author
     * The author
     */
    public void setAuthor(String author) {
        this.author = author;
    }

    /**
     *
     * @return
     * The organization
     */
    public String getOrganization() {
        return organization;
    }

    /**
     *
     * @param organization
     * The organization
     */
    public void setOrganization(String organization) {
        this.organization = organization;
    }

    /**
     *
     * @return
     * The language
     */
    public String getLanguage() {
        return language;
    }

    /**
     *
     * @param language
     * The language
     */
    public void setLanguage(String language) {
        this.language = language;
    }

    /**
     *
     * @return
     * The id
     */
    public String getId() {
        return id;
    }

    /**
     *
     * @param id
     * The id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     *
     * @param headerImage
     * The header_image
     */
    public void setHeaderImage(HashMap<String, String> headerImage) {
        this.header_image = headerImage;
        List<HashMap<String, String>> images = this.getImages();
        if(images.size() > 0 && headerImage != null && images.get(0).get("url") == headerImage.get("url")){
            images.remove(0);
            this.setImages(images);
        }
    }

    /**
     *
     * @return
     * The images
     */
    public HashMap<String, String> getHeaderImage() {
        return header_image;
    }

    /**
     *
     * @param images
     * The images
     */
    public void setImages(List<HashMap<String, String>> images) {

        if(images.size() > 0 && this.getHeaderImage() != null && images.get(0).get("url") == this.getHeaderImage().get("url")){
            images.remove(0);
        }

        this.images = images;
    }

    /**
     *
     * @return
     * The images
     */
    public List<HashMap<String, String>> getImages() {
        return images;
    }

    /**
     *
     * @param videos
     * The videos
     */
    public void setVideos(List<HashMap<String, String>> videos) {
        this.videos = videos;
    }

    /**
     *
     * @return
     * The videos
     */
    public List<HashMap<String, String>> getVideos() {
        return videos;
    }


    /**
     *
     * @return
     * The imageUrls
     */
    public List<String> getImageUrls() {
        return imageUrls;
    }

    /**
     *
     * @param imageUrls
     * The image_urls
     */
    public void setImageUrls(List<String> imageUrls) {
        this.imageUrls = imageUrls;
    }


    /**
     *
     * @return
     * The image captions
     */
    public List<String> getCaptions() {
        return captions;
    }

    /**
     *
     * @param captions
     * The image captions
     */
    public void setCaptions(List<String> captions) {
        this.captions = captions;
    }

    /**
     *
     * @return
     * The image bylines
     */
    public List<String> getPhotoBylines() {
        return photoBylines;
    }

    /**
     *
     * @param photoBylines
     * The image captions
     */
    public void setPhotoBylines(List<String> photoBylines) {
        this.photoBylines = photoBylines;
    }

    /**
     *
     * @return
     * The url
     */
    public String getUrl() {
        return url;
    }

    /**
     *
     * @param url
     * The url
     */
    public void setUrl(String url) {
        this.url = url;
    }

} public class ArticlePost implements Serializable {

        @SerializedName("start_date")
        @Expose
        private Integer startDate;
        @SerializedName("end_date")
        @Expose
        private Integer endDate;
        @SerializedName("total_results")
        @Expose
        private Integer totalResults = 0;
        @SerializedName("total_pages")
        @Expose
        private Integer totalPages;
        @Expose
        private Integer page;
        @Expose
        private Object results = new Object();

        @Expose
        private ArrayList<String> categories;

        /**
         *
         * @return
         * The startDate
         */
        public Integer getStartDate() {
            return startDate;
        }

        /**
         *
         * @param startDate
         * The start_date
         */
        public void setStartDate(Integer startDate) {
            this.startDate = startDate;
        }

        /**
         *
         * @return
         * The endDate
         */
        public Integer getEndDate() {
            return endDate;
        }

        /**
         *
         * @param endDate
         * The end_date
         */
        public void setEndDate(Integer endDate) {
            this.endDate = endDate;
        }

        /**
         *
         * @return
         * The totalResults
         */
        public Integer getTotalResults() {
            return totalResults;
        }

        /**
         *
         * @param totalResults
         * The total_results
         */
        public void setTotalResults(Integer totalResults) {
            this.totalResults = totalResults;
        }

        /**
         *
         * @return
         * The totalPages
         */
        public Integer getTotalPages() {
            return totalPages;
        }

        /**
         *
         * @param totalPages
         * The total_pages
         */
        public void setTotalPages(Integer totalPages) {
            this.totalPages = totalPages;
        }

        /**
         *
         * @return
         * The page
         */
        public Integer getPage() {
            return page;
        }

        /**
         *
         * @param page
         * The page
         */
        public void setPage(Integer page) {
            this.page = page;
        }

        /**
         *
         * @return
         * The results
         */
        public Object getResults(){
            return results;
        }

        /**
         *
         * @param results
         * The results
         */
        public void setResults(Object results) {
            this.results = results;
        }

        /**
         *
         * @return
         * The categories
         */
        public ArrayList<String> getCategories(){
            return categories;
        }

        /**
         *
         * @param categories
         * The results
         */
        public void setCategories(ArrayList<String> categories) {
            this.categories = categories;
        }

} public class ImageQueueSingleton {
    private static ImageQueueSingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;

    private ImageQueueSingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(mRequestQueue,
                new ImageLoader.ImageCache() {
                    private final LruCache<String, Bitmap>
                            cache = new LruCache<String, Bitmap>(20);

                    @Override
                    public Bitmap getBitmap(String url) {
                        return cache.get(url);
                    }

                    @Override
                    public void putBitmap(String url, Bitmap bitmap) {
                        cache.put(url, bitmap);
                    }
                });
    }

    public static synchronized ImageQueueSingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new ImageQueueSingleton(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
                    && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
                HttpStack stack = null;
                try {
                    SSLContext sslcontext = SSLContext.getInstance("TLSv1");

                    sslcontext.init(null,
                            null,
                            null);
                    SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

                    stack = new HurlStack(null, NoSSLv3Factory);
                } catch (KeyManagementException e) {
                    e.printStackTrace();
                    Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
                    stack = new HurlStack();
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
                    stack = new HurlStack();
                }
                mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), stack);
            } else {
                mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
            }
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
} public class AnalyticsManager {

    public enum AnalyticType {
        FABRIC, GOOGLE_ANALYTICS
    }

    private static final String PREFS_NAME = "UUID_Identifier_Preference_Name";


    private AnalyticType analyticType;
    private static HashMap<String, TimerEvent> timeTrackers;

    public static AnalyticsManager analyticsManager;

    private AnalyticsManager() {
        // Init
    }

    public static AnalyticsManager getAnalyticsManager() {
        if (analyticsManager == null) {
            analyticsManager = new AnalyticsManager();
        }
        return analyticsManager;
    }

    public void setAnalyticsManager(AnalyticType analyticType, Context context){
        this.analyticType = analyticType;
        switch (analyticType){
            case FABRIC:
                Fabric.with(context, new Crashlytics());
                break;
            case GOOGLE_ANALYTICS:
                break;
        }

        this.setLanguage(Language.getLanguage(context).getLanguage());
    }

    public AnalyticType getAnalyticType(){
        return this.analyticType;
    }

    public void setLanguage(String language){
        AnalyticType analyticType = getAnalyticType();
        if(analyticType == null){
            return;
        }

        switch (analyticType) {
            case FABRIC:
                Crashlytics.setString("language", language);
                break;
            case GOOGLE_ANALYTICS:
                break;
        }
    }

    public static void logContentView(String contentName){
        logContentView(contentName, null, null);
    }

    public static void logContentView(String contentName, String contentType){
        logContentView(contentName, contentType);
    }

    public static void logContentView(String contentName, String contentType, String contentId) {
        if(!analyticsCorrectlySetUp()){
            return;
        }
        switch (getAnalyticsManager().analyticType) {
            case FABRIC:
                ContentViewEvent viewEvent = new ContentViewEvent();
                if(contentName != null){
                    viewEvent.putContentName(contentName);
                }

                if(contentType != null){
                    viewEvent.putContentType(contentType);
                }

                if(contentId != null){
                    viewEvent.putContentId(contentId);
                }

                Answers.getInstance().logContentView(viewEvent);
                break;
            case GOOGLE_ANALYTICS:
                //Not implemented
                break;
            default:
                //Do nothing
        }

    }

    public static void logSearch(String query) {
        if(!analyticsCorrectlySetUp()){
            return;
        }
        switch (getAnalyticsManager().analyticType) {
            case FABRIC:
                Answers.getInstance().logSearch(new SearchEvent().putQuery(query));
                break;
            case GOOGLE_ANALYTICS:
                //Not implemented
                break;
            default:
                //Do nothing
        }

    }

    public static void logShare(String method, String contentName, String contentType, String contentId) {
        if(!analyticsCorrectlySetUp()){
            return;
        }
        switch (getAnalyticsManager().analyticType) {
            case FABRIC:
                Answers.getInstance().logShare(new ShareEvent()
                        .putMethod(method)
                        .putContentName(contentName)
                        .putContentType(contentType)
                        .putContentId(contentId));
                break;
            case GOOGLE_ANALYTICS:
                //Not implemented
                break;
            default:
                //Do nothing
        }

    }

    public static void startTimerForContentView(Object object, String name){
        if(!analyticsCorrectlySetUp()){
            return;
        }
        TimerEvent timerEvent = new TimerEvent(name);
        String identifier = identitfier(object, name);

        if(timeTrackers == null) {
            timeTrackers = new HashMap<>();
        }

        if(timeTrackers.get(identifier) != null){
            return;
        }

        timeTrackers.put(identifier, timerEvent);
    }

    public static void endTimerForContentView(Object object, String name){
        if(!analyticsCorrectlySetUp() || timeTrackers == null){
            return;
        }
        String identifier = identitfier(object,name);
        TimerEvent timerEvent = timeTrackers.get(identifier);
        if(timerEvent != null) {
            timerEvent.endTimer();

            switch (getAnalyticsManager().analyticType) {
                case FABRIC:
                    Answers.getInstance().logCustom(timerEvent);
                    break;
                case GOOGLE_ANALYTICS:
                    //Not implemented
                    break;
                default:
                    //Do nothing
            }
            timeTrackers.remove(identifier);
        }
    }

    public static void logError(String errorMessage) {
        if(!analyticsCorrectlySetUp()){
            return;
        }
        switch (getAnalyticsManager().analyticType) {
            case FABRIC:
                String errorMessageSubstring;
                if(errorMessage.length() > 60) {
                    errorMessageSubstring = errorMessage.substring(0, 59);
                } else {
                    errorMessageSubstring = errorMessage;
                }
                Answers.getInstance().logCustom(new CustomEvent(errorMessage));
                break;
            case GOOGLE_ANALYTICS:
                //Not implemented
                break;
            default:
                //Do nothing
        }
    }

    private static String identitfier(Object object, String name) {
        String identifier = Integer.toString(object.hashCode()) + ":" + name;
        return identifier;
    }

    private static Boolean analyticsCorrectlySetUp() {

        Boolean valid = true;

        if(getAnalyticsManager().analyticType == null){
            valid = false;
        }

        return valid;
    }

    public static UUID installationUUID(Context context) {
        SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
        String uuidString = settings.getString("INSTALLATION_UUID", null);
        UUID uuid;
        if(uuidString != null) {
            uuid = UUID.fromString(uuidString);
        } else{
            uuid = UUID.randomUUID();
            SharedPreferences.Editor editor = settings.edit();
            editor.putString("INSTALLATION_UUID", uuid.toString());
            editor.commit();
        }

        return uuid;
    }
} public class CacheManager implements ComponentCallbacks2 {
    private LruCache<String, Bitmap> imageMemoryCache;
    private static Context mContext;
    private static CacheManager mInstance;

    private DiskLruCache mDiskLruCache;
    private final Object mDiskCacheLock = new Object();
    private boolean mDiskCacheStarting = true;
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
    private static final String DISK_CACHE_SUBDIR = "thumbnails";


    private CacheManager(Context context) {
        mContext = context;

        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        // Use 1/8th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 8;

        imageMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };

        // Initialize disk cache on background thread
        File cacheDir = getDiskCacheDir(context, DISK_CACHE_SUBDIR);
        new InitDiskCacheTask().execute(cacheDir);

    }

    public static synchronized CacheManager getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new CacheManager(context);
        }
        return mInstance;
    }


    // Creates a unique subdirectory of the designated app cache directory. Tries to use external
    // but if not mounted, falls back on internal storage.
    public static File getDiskCacheDir(Context context, String uniqueName) {
        // Check if media is mounted or storage is built-in, if so, try and use external cache dir
        // otherwise use internal cache dir
        final String cachePath =
                Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                        !Environment.isExternalStorageRemovable() ? context.getExternalCacheDir().getPath() :
                        context.getCacheDir().getPath();

        return new File(cachePath + File.separator + uniqueName);
    }


    class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
        @Override
        protected Void doInBackground(File... params) {
//            synchronized (mDiskCacheLock) {
//                File cacheDir = params[0];
//                try {
//                    //mDiskLruCache = DiskLruCache.open(cacheDir, 1, 1, DISK_CACHE_SIZE);
//                } catch (IOException e) {}
//                mDiskCacheStarting = false; // Finished initialization
//                mDiskCacheLock.notifyAll(); // Wake any waiting threads
//            }
            return null;
        }
    }

    public void onConfigurationChanged(Configuration configuration){

    }

    public void onLowMemory() {
        try {
            mDiskLruCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void onTrimMemory (int level) {
        try {
            mDiskLruCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


} public class DateUtil {

    /**
     * Date format for the posts in the main page.
     */
    private static final String POST_DATE_MAIN_LIST_ITEM = "LLLL dd, yyyy";


    /**
     * Date format for the post in the single post page.
     */
    private static final String POST_DATE_TITLE_FORMAT = "dd MMMMM yyyy";

    /**
     * Date format for the published posts.
     */
    private static final String POSTS_DATE_PUBLISHED_FORMAT = "yyyyMMdd";

    /**
     *<code>SimpleDateFormat</code> for the posts in the main page.
     */
    private static SimpleDateFormat postDateMainListItemFormat(Context context) {
        return new SimpleDateFormat(
                POST_DATE_MAIN_LIST_ITEM, Language.getLanguage(context));
    }

    /**
     * <code>SimpleDateFormat</code> for the post in the single post page.
     */
    public static final SimpleDateFormat postDateTitleFormat = new SimpleDateFormat(
            POST_DATE_TITLE_FORMAT);

    /**
     * <code>SimpleDateFormat</code> for the published posts.
     */
    public static final SimpleDateFormat postsDatePublishedFormatter = new SimpleDateFormat(
            POSTS_DATE_PUBLISHED_FORMAT);

    //Yes, you have to pass in context here, so we can get the strings file. Android is dumb
   public static String setTime(Context context, long time, boolean colloquial){
        String status = "";

        if(time != 0){
            //set the text to show date and time //find out time since last load in milliseconds
            long difference = System.currentTimeMillis() - (time); //the time since the last load
            if(colloquial && Language.dateShouldBeColloquial(context)) {
                if (difference < (1000 * 60 * 60) && difference > 0) {
                    //if within 1 hour, display minutes
                    int minutesAgo = (int) Math.floor((difference / 1000) / 60);
                    status += (minutesAgo == 0) ? context.getResources().getString(R.string.just_now) : ((minutesAgo == 1) ? minutesAgo + " " +  context.getResources().getString(R.string.minutes_ago) : minutesAgo + " " + context.getResources().getString(R.string.minutes_ago));
                } else {
                    //if we are within 24 hours, display hours
                    if (difference < (1000 * 60 * 60 * 24) && difference > 0) {
                        int hoursAgo = (int) Math.floor(((difference / 1000) / 60) / 60);
                        status += (hoursAgo == 1) ? hoursAgo + " " + context.getResources().getString(R.string.hours_ago) : hoursAgo + " " + context.getResources().getString(R.string.hours_ago);
                    } else if (difference > 0) {//if we are within 2 days, display yesterday
                        int hold = 1000 * 60 * 60;
                        status += difference < (hold * 48) ? context.getResources().getString(R.string.yesterday) : difference < (hold * 72) ? context.getResources().getString(R.string.three_day_ago) :
                                DateUtil.postDateMainListItemFormat(context).format(time);
                    }
                }
            } else {
                status = DateUtil.postDateMainListItemFormat(context).format(time);
            }

            status = Language.replaceForLanguages(context, status);
        }else return "";
        return status;
    }
} public class Language {

    private static String preferenceName = "LanguagePreference";
    private static String languageKey = "defaultLanguage";

    // For Serbian, in API < 21 it's annoyingly complex to get the Latin variant of the Serbian Language
    // So, instead of running a very annoying FOR loop every time, we just run it once per run and cache it here.
    // I can't imagine a system provided static locale changing ever...
    private static Locale serbianLatinLocale = null;

    private static ArrayList<LanguageListener> listeners;

    public static void addListener(LanguageListener listener) {
        if(listeners == null) {
            listeners = new ArrayList<>();
        }

        if(listeners.contains(listener)){
            return;
        }

        listeners.add(listener);
    }

    // Adopted from http://stackoverflow.com/a/33145518
    public static Set<String> getAppLanguages(Context ctx) {
        int id = R.string.menu_search;
        DisplayMetrics dm = ctx.getResources().getDisplayMetrics();
        Configuration conf = ctx.getResources().getConfiguration();
        Locale originalLocale = conf.locale;
        conf.locale = Locale.ENGLISH;
        final String reference = new Resources( ctx.getAssets(), dm, conf ).getString( id );

        Set<String> result = new HashSet<>();
        result.add( Locale.ENGLISH.getLanguage() );

        for( String loc : ctx.getAssets().getLocales() ){
            if( loc.isEmpty() ) continue;
            Locale l = languageForTag(loc);
            conf.locale = l;
            Resources resources = new Resources(ctx.getAssets(), dm, conf);

            if( !reference.equals(resources.getString(id))) {
                result.add(l.getLanguage());
            }
        }

        conf.locale = originalLocale;

        // Here we go through the languages that are set in the config
        String setLanguagesString = ctx.getResources().getString(R.string.available_languages);
        String[] setLanguages = setLanguagesString.replaceAll("\\s+","").split(",");
        Set<String> comparedResults = new HashSet<>();

        for(String language : result){
            // ew
            for(String compareLanguage : setLanguages){
                if(language.equals(compareLanguage)){
                    comparedResults.add(compareLanguage);
                }
            }
        }
        return comparedResults;
    }

    public static Locale languageForTag(String tag) {
        Locale l;
        if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH){
            l = new Locale(tag.substring(0, 2));
        } else {
            if(tag == null){
                //stuff
                String something = "";
            }
            l = Locale.forLanguageTag(tag);
        }

        return l;
    }

    public static Locale getLanguage(Context context){
        SharedPreferences preferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE);

        String language = preferences.getString(languageKey, null);

        Locale l;
        if(language == null){
            l = Locale.getDefault();
        } else {
            if(language.equals("sr")){
                l = serbianLatinLocale();
            } else {
                l = languageForTag(language);
            }
        }

        return l;
    }

    public static void setLanguage(Context context, Locale locale){
        //Check if the language can be set to this

        if(!languageAvailable(context, locale)){
            return;
        }

        if(locale.getLanguage().equals("sr")) {
            locale = serbianLatinLocale();
        }

        SharedPreferences preferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString(languageKey, locale.getLanguage());

        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            updateResources(context, locale);
        }

        updateResourcesLegacy(context, locale);


        editor.commit();

        //Sets the language to report if there's a crash
        AnalyticsManager.getAnalyticsManager().setLanguage(locale.getDisplayLanguage());

        callListeners();
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, Locale locale) {

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, Locale locale) {

        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }

        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }

    public static Context setDeviceToSavedLanguage(Context context) {
        Locale locale = getLanguage(context);

        //Check if the saved language is available, if not, get the first one
        if(languageAvailable(context, locale)) {
            setLanguage(context, locale);
        } else {
            Set<String> languages = getAppLanguages(context);
            String language = null;
            for(String tempLanguage : languages){
                language = tempLanguage;
                break;
            }
            setLanguage(context, languageForTag(language));
        }

        return context;
    }

    public static String bylineSeperator(Context context) {
        String language = getLanguageString(context);

        String separator;
        if(language.equals("en")){
            separator = " by ";
        } else if(language.equals("az")) {
            separator = ", ";
        } else if(language.equals("ru")) {
            separator = ", ";
        } else if(language.equals("rom")) {
            separator = ", ";
        } else if(language.equals("sr")) {
            separator = ", ";
        } else {
            separator = " ";
        }

        return separator;
    }

    public static Boolean dateShouldBeColloquial(Context context) {
        Locale locale = getLanguage(context);
        String language = locale.getLanguage();

        if(language.equals("az") || language.equals("rom") || language.equals("sr")){
            return false;
        }

        return true;
    }

    // Mostly for Serbian, but this does any language/script specific replacements that Android messes up
    public static String replaceForLanguages(Context context, String string){

        // Serbian comes in both Latin and Cyrillic scrips, but we're going to prefer Latin
        if(Language.getLanguage(context).getLanguage().equals("sr")){
            HashMap<String, String> languageReplacements = serbianDateReplacements();
            for (String key: languageReplacements.keySet()) {
                string = string.replaceAll(key, languageReplacements.get(key));
            }
        }

        return string;
    }

    // Builds up a HashMap for string replacements used in replaceForLanguages
    private static HashMap<String, String> serbianDateReplacements() {
        HashMap<String, String> languageReplacements = new HashMap<>();
        languageReplacements.put("january_cyrillic",   "january_latin");
        languageReplacements.put("february_cyrillic",  "february_latin");
        languageReplacements.put("march_cyrillic",     "march_latin");
        languageReplacements.put("april_cyrillic",     "april_latin");
        languageReplacements.put("may_cyrillic",       "may_latin");
        languageReplacements.put("june_cyrillic",      "june_latin");
        languageReplacements.put("july_cyrillic",      "july_latin");
        languageReplacements.put("august_cyrillic",    "august_latin");
        languageReplacements.put("september_cyrillic", "september_latin");
        languageReplacements.put("october_cyrillic",   "october_latin");
        languageReplacements.put("november_cyrillic",  "november_latin");
        languageReplacements.put("december_cyrillic",  "december_latin");
        return languageReplacements;
    }

    private static String getLanguageString(Context context){
        Locale locale = getLanguage(context);
        String langauge = locale.getLanguage();
        return langauge;
    }
    private static void callListeners() {

        if(listeners == null){
            return;
        }

        for(LanguageListener listener : listeners){
            listener.languageChanged();
        }
    }

    private static Boolean languageAvailable(Context context, Locale locale){
        Set<String> languages = getAppLanguages(context);
        Boolean languageAvailable = false;
        for(String language : languages){
            if(language.equals(locale.getLanguage())){
                languageAvailable = true;
            }
        }

        return languageAvailable;
    }

    private static Locale serbianLatinLocale(){

        if(serbianLatinLocale != null){
            return serbianLatinLocale;
        }

        Locale locale = null;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            for (Locale checkLocale : Locale.getAvailableLocales()) {
                if (checkLocale.getISO3Language().equals("srp") && checkLocale.getCountry().equals("LATN") && checkLocale.getVariant().equals("")) {
                    locale = checkLocale;
                }
            }
        } else {
            locale = new Locale.Builder().setLanguage("sr").setRegion("RS").setScript("Latn").build();
        }

        serbianLatinLocale = locale;

        return locale;
    }
} public interface LanguageListener {
    void languageChanged();
} public class PromotionsManager {

    public static PromotionsManager promotionsManager;

    private PromotionsManager() {
        // Init
    }

    public static PromotionsManager getPromotionsManager() {
        if (promotionsManager == null) {
            promotionsManager = new PromotionsManager();
        }
        return promotionsManager;
    }

    public ArrayList promotions(Context context) {

        Map promotion = loadPromotions(context);
        ArrayList arrayList = new ArrayList();

        if(promotion == null){
            return null;
        }

        String startDateString = (String)promotion.get("start-date");
        String endDateString = (String)promotion.get("end-date");

        Calendar startDate = new GregorianCalendar();;
        Calendar endDate = new GregorianCalendar();;

        SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

        try {
            startDate.setTime(format.parse(startDateString));
            endDate.setTime(format.parse(endDateString));
        } catch (ParseException e) {
            e.printStackTrace();
        }

        // We want it to do one extra day.
        endDate.add(Calendar.DAY_OF_MONTH, 1);

        if (Calendar.getInstance().after(startDate) && Calendar.getInstance().before(endDate)) {
            arrayList.add(promotion);
        }

        return arrayList;
    }

    private Map loadPromotions(Context context) {

        String filePath = "promotions.json";

        StringBuilder buf=new StringBuilder();

        InputStream htmlStream = null;
        try {

            htmlStream = context.getAssets().open(filePath);

            BufferedReader in = new BufferedReader(new InputStreamReader(htmlStream, "UTF-8"));
            String str;

            while ((str=in.readLine()) != null) {
                buf.append(str+"\n");
            }

            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        Gson gson = new Gson();
        Map map = (Map) gson.fromJson(buf.toString(), Map.class);
//        Yaml yaml = new Yaml();
//        Map map = (Map) yaml.load(buf.toString());

        return map;
    }


} public class SettingsManager {
    public static boolean shouldShowAuthor(Context context) {
        String showAuthorString = context.getResources().getString(R.string.show_author).toLowerCase();
        if(showAuthorString.equalsIgnoreCase("false")) {
            return false;
        }

        return true;
    }
} public class SyncManager {
    public static SyncManager syncManager;
    private Context applicationContext;
    private RestApi restAPI;

    private Gson gson;

    private SyncManager() {
        // Init
    }

    public static SyncManager getSyncManager() {
        if (syncManager == null) {
            syncManager = new SyncManager();
        }
        return syncManager;
    }

    public void setApplicationContext(Context context) {
        applicationContext = context;
        syncManager.setUpRestApi();
    }

    public void articles(Context context, final ArticlesDelegate delegate){
        if(Online()){
            //Get the language currently set, defaults to Azerbaijari ("az")
            Locale locale = Language.getLanguage(context);

            String language = context.getString(R.string.default_language);
            if(locale != null) {
                language = locale.getLanguage();
            }

            String apiKey = AuthenticationManager.getAuthenticationManager().apiKey(this.applicationContext);
            restAPI.getArticles(AnalyticsManager.installationUUID(context).toString(), apiKey, language, true, new Callback<ArticlePost>() {
                @Override
                public void success(ArticlePost articlePost, Response response) {
                    //There's a bunch of type juggling here because of the nested nature of it all
                    Gson gson = gson();

                    HashMap<String, ArrayList<Article>> articles = new HashMap<String, ArrayList<Article>>();

                    // If categories are not enabled
                    if(articlePost.getResults().getClass() == ArrayList.class){
                        ArrayList<Article> tempArticles = new ArrayList<Article>();
                        for (LinkedTreeMap jsonArticle : (ArrayList<LinkedTreeMap>)articlePost.getResults()) {
                            tempArticles.add(reconstructArticleFromJSON(jsonArticle));
                        }
                        delegate.didRetrieveArticles(tempArticles, null, null);
                    } else {
                        HashMap<String, ArrayList<LinkedTreeMap>> categories = gson.fromJson(gson.toJsonTree(articlePost.getResults()), HashMap.class);

                        for (String tempArrayListKey : categories.keySet()) {
                            //Now we need to cycle through each hash key and add it back to the list

                            ArrayList<LinkedTreeMap> categoryList = (ArrayList<LinkedTreeMap>) categories.get(tempArrayListKey);


                            ArrayList<Article> tempArticles = reconstructArticleArrayFromJSON(categoryList);
                            articles.put(tempArrayListKey, tempArticles);
                        }
                        delegate.didRetrieveArticles(articles, articlePost.getCategories(), null);
                    }
                }

                @Override
                public void failure(RetrofitError error) {
                    delegate.didRetrieveArticles(null, null, error);
                }
            });
        }
    }

    public void articleForId(String article_id, Context context, final ArticleDelegate delegate){
        if(Online()){
            String apiKey = AuthenticationManager.getAuthenticationManager().apiKey(this.applicationContext);
            restAPI.getArticle(AnalyticsManager.installationUUID(context).toString(), apiKey, article_id, Language.getLanguage(context).getLanguage(), new Callback<ArticlePost>() {
                @Override
                public void success(ArticlePost articlePost, Response response) {
                    //Rearrange the posts so that the first article has an image
                    //We want this to be the same order everywhere so we do it here
                    //If there's none, then we hide the image box

                    ArrayList<Article> articles = reconstructArticleArrayFromJSON((ArrayList<LinkedTreeMap>)articlePost.getResults());

                    if(articles.size() > 0){
                        delegate.didRetrieveArticle(articles.get(0));
                    }
                }

                @Override
                public void failure(RetrofitError error) {
                    AnalyticsManager.logError(error.getLocalizedMessage());
                    Log.e("ERROR", "Failed to parse JSON ", error);
                }
            });
        }
    }



    private void setUpRestApi() {
        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void intercept(RequestInterceptor.RequestFacade request) {
                request.addHeader("Accept", "application/json; charset=utf-8");
            }
        };

        //create an adapter for retrofit with base url
        RestAdapter restAdapter = new RestAdapter.Builder().setLogLevel(RestAdapter.LogLevel.FULL)
                .setRequestInterceptor(requestInterceptor)
                .setEndpoint(applicationContext.getResources().getString(R.string.server_url)).build();
        //creating a service for adapter with our GET class
        restAPI = restAdapter.create(RestApi.class);
    }

    private boolean Online() {
        ConnectivityManager manager = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        // check that there is an active network
        if (info == null) {
            return false;
        }

        return info.isConnected();
    }

    private ArrayList<Article> reconstructArticleArrayFromJSON(ArrayList<LinkedTreeMap> jsonArray) {
        Gson gson = gson();

        //Now we need to cycle through each hash key and add it back to the list
        ArrayList<Article> tempArticles = new ArrayList<Article>();

        for (LinkedTreeMap linkedTreeMapArticle : (ArrayList<LinkedTreeMap>) gson.fromJson(gson.toJsonTree(jsonArray), ArrayList.class)) {
            Article article = reconstructArticleFromJSON(linkedTreeMapArticle);
            tempArticles.add(article);
        }
        return tempArticles;
    }

    private Article reconstructArticleFromJSON(LinkedTreeMap json) {
        Gson gson = gson();

        Article article = gson.fromJson(gson.toJsonTree(json), Article.class);
        return article;
    }

    private Gson gson(){
        if(this.gson == null){
            this.gson = new Gson();
        }

        return this.gson;
    }

} public interface ArticlesDelegate
{
    void didRetrieveArticles(Object articles, ArrayList<String> categories, RetrofitError error);
}
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.androidquery.AQuery;
import com.pushapp.press.util.AuthenticationManager;
import com.github.ksoichiro.android.observablescrollview.ObservableListView;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.pushapp.press.adapter.PostFragmentAdapter;
import com.pushapp.press.adapter.PostListAdapter;
import com.pushapp.press.fragment.CategoryFragment;
import com.pushapp.press.fragment.DonatePage;
import com.pushapp.press.fragment.LanguageSelectDialogFragment;
import com.pushapp.press.interfaces.CacheManager.ImageCacheDelegate;
import com.pushapp.press.interfaces.OnFragmentInteractionListener;
import com.pushapp.press.interfaces.RestApi;
import com.pushapp.press.interfaces.SyncManager.ArticleDelegate;
import com.pushapp.press.model.Article;
import com.pushapp.press.model.ArticlePost;
import com.pushapp.press.model.ImageQueueSingleton;
import com.pushapp.press.util.AnalyticsManager;
import com.pushapp.press.util.CacheManager;
import com.pushapp.press.util.DateUtil;
import com.pushapp.press.util.Language;
import com.pushapp.press.util.LanguageListener;
import com.pushapp.press.util.PromotionsManager;
import com.pushapp.press.util.SettingsManager;
import com.pushapp.press.util.SyncManager;
import com.pushapp.press.interfaces.SyncManager.ArticlesDelegate;
import java.lang.reflect.Array;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TimeZone;
import retrofit.Callback;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.client.Response;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.CrashManagerListener;
                    } else {
                        Article article = (Article) selectedItem;
                        Intent i = new Intent(HomeActivity.this, DetailPostActivity.class);

                        i.putExtra("postPosition", mListAdapter.items(false).indexOf(selectedItem));
                        i.putExtra("postTitle", article.getHeadline());
                        i.putExtra("description", article.getDescription());

                        startActivity(i);
                        AnalyticsManager.logContentView(article.getHeadline(),
                                "Article Opened", article.getId());
                    }
                }
            }

        });
    }

    // ImageCacheDelegate
    public void didLoadImage(Bitmap bitmap, ImageView imageView, String url) {
        imageView.setImageBitmap(bitmap);
    }

    public void errorLoadingImage(ImageView imageView, String url) {
        imageView.setImageResource(R.drawable.fallback);
    }


    /**
     * Check if we have a network connection
     * @return boolean
     */
    boolean Online() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        // check that there is an active network
        if (info != null) {
            return info.isConnected();
        }else {
            return false;
        }
    }

    private void displayView(int position) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        Fragment fragment = null;
        switch (position) {
            case 0:
                this.mHomeLayout.setVisibility(View.VISIBLE);
                this.mSearchView.setVisibility(View.GONE);

                Fragment frag = fragmentManager.findFragmentByTag("Fragment");
                if(frag != null)
                    fragmentTransaction.remove(frag).commit();
                break;
            case 1:
                fragment = new DonatePage();
                break;
            case 2:
                Intent i = new Intent(HomeActivity.this, AboutActivity.class);
                startActivity(i);
                break;
            default:
                break;
        }

        if (fragment != null) {
            fragmentTransaction.replace(com.pushapp.press.R.id.container_body, fragment, "Fragment");
            fragmentTransaction.commit();
            this.mSearchView.setVisibility(View.GONE);
            this.mHomeLayout.setVisibility(View.GONE);
        }

    }

    @Override
    public void onBackPressed() {


        Fragment frag = getSupportFragmentManager().findFragmentByTag("Fragment");
        if(frag != null) {
            getSupportFragmentManager().beginTransaction().remove(frag).commit();
            this.mHomeLayout.setVisibility(View.VISIBLE);
        }
        else if(isSearchShown) {
            updateViews(false);
        }

        else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(com.pushapp.press.R.menu.menu_main, menu);

        // Hide the language button if there's less than 2 languages
        if(Language.getAppLanguages(this).size() < 2) {
            menu.getItem(2).setVisible(false);
        }

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        if(id== R.id.action_search) {
            handleMenuSearch();
        }else if(id== R.id.action_about){
            Intent i = new Intent(HomeActivity.this, AboutActivity.class);
            startActivity(i);
        }else if(id== R.id.action_language){
            LanguageSelectDialogFragment newLanguageSelectDialogFragment = new LanguageSelectDialogFragment();