com.juick.android.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.juick.android.MainActivity.java

Source

/*
 * Juick
 * Copyright (C) 2008-2012, Ugnich Anton
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.juick.android;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.*;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentTransaction;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.*;
import com.actionbarsherlock.app.ActionBar;
import com.crashlytics.android.Crashlytics;
import com.juick.android.api.ChildrenMessageSource;
import com.juick.android.bnw.BNWMicroBlog;
import com.juick.android.facebook.Facebook;
import com.juick.android.ja.GlavSuMicroblog;
import com.juick.android.juick.*;
import com.juick.android.point.PointMicroBlog;
import com.juickadvanced.R;
import com.juickadvanced.RESTResponse;
import com.juickadvanced.data.MessageID;
import com.juickadvanced.data.bnw.BnwMessageID;
import com.juickadvanced.data.facebook.FacebookMessageID;
import com.juickadvanced.data.juick.JuickMessage;
import com.juickadvanced.data.juick.JuickMessageID;
/* http://code.google.com/p/android-file-dialog/ */
import com.juickadvanced.data.point.PointMessageID;
import com.juickadvanced.protocol.JuickHttpAPI;
import com.juickadvanced.xmpp.commands.AccountProof;
import com.lamerman.FileDialog;
import com.lamerman.SelectionMode;
import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView;
import io.fabric.sdk.android.Fabric;
import io.fabric.sdk.android.services.common.Crash;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.util.*;

import static android.os.Build.*;

public class MainActivity extends JuickFragmentActivity implements ActionBar.OnNavigationListener, IRunningActivity,
        SharedPreferences.OnSharedPreferenceChangeListener {

    public static final int ACTIVITY_SIGNIN = 2;
    public static final int ACTIVITY_PREFERENCES = 3;
    public static final int COLORS_THEME_STORAGE_ACTION_SAVE = 100;
    public static final int COLORS_THEME_STORAGE_ACTION_LOAD = 101;
    public static final int PENDINGINTENT_CONSTANT = 713242183;

    public static int displayWidth;
    public static int displayHeight;

    //
    NavigationItem lastNavigationItem = null;
    Object restoreData;
    public SharedPreferences sp;
    public Handler handler;
    public boolean resumed;
    private boolean reloadOnResume;

    public static Map<String, MicroBlog> microBlogs = new HashMap<String, MicroBlog>();

    static {
        JuickMicroBlog juickMicroBlog = new JuickMicroBlog();
        microBlogs.put(juickMicroBlog.getCode(), juickMicroBlog);
        BNWMicroBlog bnwMicroBlog = new BNWMicroBlog();
        microBlogs.put(bnwMicroBlog.getCode(), bnwMicroBlog);
        PointMicroBlog pointMicroBlog = new PointMicroBlog();
        microBlogs.put(pointMicroBlog.getCode(), pointMicroBlog);
        GlavSuMicroblog glavsu = new GlavSuMicroblog();
        microBlogs.put(glavsu.getCode(), glavsu);
        Facebook fb = new Facebook();
        microBlogs.put(fb.getCode(), fb);

        for (MicroBlog microBlog : microBlogs.values()) {
            microBlog.initialize();
        }
        Log.i("JuickAdvanced", "static init");
    }

    public DragSortListView navigationList;

    public static MicroBlog getMicroBlog(String key) {
        return microBlogs.get(key);
    }

    public static MicroBlog getMicroBlog(MessageID mid) {
        return microBlogs.get(mid.getMicroBlogCode());
    }

    public static MicroBlog getMicroBlog(JuickMessage msg) {
        return microBlogs.get(msg.getMID().getMicroBlogCode());
    }

    public void logoutService(String code) {
        for (Utils.URLAuth authorizer : Utils.authorizers) {
            if (authorizer.isForBlog(code)) {
                authorizer.reset(this, handler);
            }
        }
        ((BaseAdapter) navigationList.getAdapter()).notifyDataSetChanged();

    }

    public static void handleException(Throwable ne) {
        Crashlytics.logException(ne);
    }

    public static class NavigationItem {
        public int labelId;
        public int imageId;
        public int id;
        public int itemOrder;
        public String sharedPrefsKey;

        public NavigationItem(int id, int labelId, int imageId, String sharedPrefsKey) {
            this.labelId = labelId;
            this.imageId = imageId;
            this.id = id;
            this.sharedPrefsKey = sharedPrefsKey;
        }

        public void action() {
        }

        public void restoreReadMarker() {

        }

        public ArrayList<String> getMenuItems() {
            return null;
        }

        public void handleMenuAction(int which, String value) {

        }
    }

    boolean navigationMenuShown = false;

    public boolean isNavigationMenuShown() {
        return navigationMenuShown;
    }

    public void openNavigationMenu(boolean animate) {
        if (isNavigationMenuShown())
            return;
        navigationMenuShown = true;
        final ViewGroup navigationPanel = (ViewGroup) findViewById(R.id.navigation_panel);
        navigationPanel.setVisibility(View.VISIBLE);
        final View frag = (ViewGroup) findViewById(R.id.messagesfragment);
        if (animate) {
            AnimationSet set = new AnimationSet(true);
            TranslateAnimation translate = new TranslateAnimation(0, navigationPanel.getWidth(), 0, 0);
            translate.setFillAfter(false);
            translate.setFillEnabled(true);
            translate.setDuration(400);
            set.addAnimation(translate);
            set.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    //To change body of implemented methods use File | Settings | File Templates.
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    frag.clearAnimation();
                    layoutNavigationPane();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                    //To change body of implemented methods use File | Settings | File Templates.
                }
            });
            frag.startAnimation(set);
        } else {
            layoutNavigationPane();
        }
    }

    public void closeNavigationMenu(boolean animate, boolean immediate) {
        if (!isNavigationMenuShown())
            return;
        final ViewGroup navigationPanel = (ViewGroup) findViewById(R.id.navigation_panel);
        final View frag = findViewById(R.id.messagesfragment);
        final DragSortListView navigationList = (DragSortListView) findViewById(R.id.navigation_list);
        if (navigationList.isDragEnabled()) {
            navigationList.setDragEnabled(false);
            updateNavigation();
        }
        if (animate) {
            final AnimationSet set = new AnimationSet(true);
            TranslateAnimation translate = new TranslateAnimation(0, -navigationPanel.getWidth(), 0, 0);
            translate.setFillAfter(false);
            translate.setFillEnabled(true);
            translate.setDuration(400);
            set.addAnimation(translate);
            set.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    //To change body of implemented methods use File | Settings | File Templates.
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    navigationMenuShown = false;
                    frag.clearAnimation();
                    layoutNavigationPane();
                    //navigationPanel.setVisibility(View.INVISIBLE);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                    //To change body of implemented methods use File | Settings | File Templates.
                }
            });
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    frag.startAnimation(set);
                    getActivity().findViewById(R.id.layout_container).invalidate();
                }
            }, immediate ? 1 : 200); // to smooth the slide

        } else {
            navigationMenuShown = false;
            layoutNavigationPane();
        }
    }

    public ArrayList<NavigationItem> navigationItems = new ArrayList<NavigationItem>();
    public ArrayList<NavigationItem> allNavigationItems = new ArrayList<NavigationItem>();

    public void runDefaultFragmentWithBundle(Bundle args, NavigationItem ni) {
        mf = new MessagesFragment();
        mf.init(restoreData, this);
        setFragmentNeededMetrics();
        restoreData = null;
        lastNavigationItem = ni;
        final SharedPreferences spn = getSharedPreferences("saved_last_navigation_type", MODE_PRIVATE);
        spn.edit().putInt("last_navigation", ni.labelId).commit();
        replaceFragment(mf, args);
    }

    private void setFragmentNeededMetrics() {
        if (true /* sp.getBoolean("googlePlusNavigation", false) */) {
            final ViewGroup navigationPanel = (ViewGroup) findViewById(R.id.navigation_panel);
            if (mf != null && navigationPanel.getWidth() != 0) {
                mf.setRightScrollBound(navigationPanel.getWidth());
            }
        }
    }

    public static int nActiveMainActivities = 0;

    @Override
    protected void onNewIntent(Intent intent) {
        if (navigationItems.size() == 0) {
            initStage2();
        }
        super.onNewIntent(intent);
        maybeLaunchIntent(intent, false);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        /*
                System.getProperties().put("http.proxyPort", "58080");
                System.getProperties().put("http.proxyHost", "localhost");
                System.getProperties().put("https.proxyPort", "58080");
                System.getProperties().put("https.proxyHost", "localhost");
        */
        nActiveMainActivities++;
        sp = PreferenceManager.getDefaultSharedPreferences(this);
        JuickAdvancedApplication.maybeEnableAcceleration(this);
        JuickAdvancedApplication.setupTheme(this);
        XMPPService.log("MainActivity.create()");

        handler = new Handler();
        super.onCreate(savedInstanceState);
        displayWidth = getWindow().getWindowManager().getDefaultDisplay().getWidth();
        displayHeight = getWindow().getWindowManager().getDefaultDisplay().getHeight();

        if (maybeLaunchIntent(getIntent(), true))
            return;

        initStage2();

    }

    private void initStage2() {
        sp.registerOnSharedPreferenceChangeListener(this);

        JuickHttpAPI.setHttpsEnabled(sp.getBoolean("useHttpsForJuickHttpAPI", false));

        String juickAccountName = JuickAPIAuthorizer.getJuickAccountName(this);
        if (juickAccountName != null) {
            Crashlytics.setString("juick_user", juickAccountName);
        }
        if (sp.getBoolean("fullScreenMessages", false)) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        }
        if (sp.getBoolean("hardware_accelerated", false)) {
            JuickAdvancedApplication.maybeEnableAcceleration(this);
        }
        toggleXMPP(this);
        toggleJAMessaging();
        startService(new Intent(this, DatabaseService.class));

        clearObsoleteImagesInCache();

        maybeSendUsageReport();
        maybeWarnXMPP();

        updateActionBarMode();

        //        if (getIntent().hasExtra("lastNavigationPosition")) {
        //            int lastNavigationPosition1 = getIntent().getExtras().getInt("lastNavigationPosition");
        //            bar.selectTab(bar.getTabAt(lastNavigationPosition1));
        //        }

        MessageListBackingData mlbd = JuickAdvancedApplication.instance.getSavedList(getActivity());
        if (mlbd != null) {
            for (int i = 0; i < navigationItems.size(); i++) {
                NavigationItem navigationItem = navigationItems.get(i);
                if (navigationItem.labelId == mlbd.navigationItemLabelId) {
                    setSelectedNavigationItem(i);
                    break;
                }
            }
        }

        initCensor();

        setContentView(R.layout.main);
        final MyRelativeLayout mll = (MyRelativeLayout) findViewById(R.id.layout_container);
        mll.listener = new MyRelativeLayout.Listener() {
            @Override
            public void onLayout() {
                layoutNavigationPane();
                setFragmentNeededMetrics();
            }
        };
        restoreData = getLastCustomNonConfigurationInstance();
        new WhatsNew(this).runAll();
        MainActivity.restyleChildrenOrWidget(getWindow().getDecorView());

        WhatsNew.checkForUpdates(this, null, false);
        WhatsNew.checkForNews(this, new Utils.Function<Void, ArrayList<WhatsNew.News>>() {
            @Override
            public Void apply(final ArrayList<WhatsNew.News> newses) {
                if (newses == null)
                    return null;
                Utils.ServiceGetter<XMPPService> xmppServiceGetter = new Utils.ServiceGetter<XMPPService>(
                        MainActivity.this, XMPPService.class);
                xmppServiceGetter.getService(new Utils.ServiceGetter.Receiver<XMPPService>() {
                    @Override
                    public void withService(XMPPService service) {
                        if (service != null) {
                            final SharedPreferences rdnews = getSharedPreferences("read_news", MODE_PRIVATE);
                            for (int i = 0; i < newses.size(); i++) {
                                final WhatsNew.News news = newses.get(i);
                                if (news != null && news.code != null && news.code.equals("critical")) {
                                    if ("SannySanoff"
                                            .equals(JuickAPIAuthorizer.getJuickAccountName(MainActivity.this))) {
                                        service.handleTextMessage("critical@ja_server", news.body);
                                    }
                                    continue;
                                }
                                if (!rdnews.getBoolean("reported_" + news.code, false)) {
                                    rdnews.edit().putBoolean("reported_" + news.code, true).commit();
                                    service.handleTextMessage("news@ja_server", news.body);
                                }
                            }
                        }
                    }
                });
                return null; //To change body of implemented methods use File | Settings | File Templates.
            }
        });
        updateNavigation();

    }

    int selectedNavigationItem = -1;

    private int getSelectedNavigationIndex() {
        return selectedNavigationItem;
    }

    private void setSelectedNavigationItem(int index) {
        boolean googlePlus = true; /* sp.getBoolean("googlePlusNavigation", false); */
        selectedNavigationItem = index;
        if (!googlePlus) {
            getSupportActionBar().setSelectedNavigationItem(index);
        } else {
            onNavigationItemSelected(index, 0);
        }
        updateNavigationBarTitle();
    }

    private void updateActionBarMode() {
        boolean googlePlus = true; /* sp.getBoolean("googlePlusNavigation", false); */
        ActionBar bar = getSupportActionBar();
        bar.setDisplayShowTitleEnabled(false);
        bar.setDisplayShowHomeEnabled(false);
        bar.setDisplayHomeAsUpEnabled(false);
        bar.setNavigationMode(googlePlus ? ActionBar.NAVIGATION_MODE_STANDARD : ActionBar.NAVIGATION_MODE_LIST);
        bar.setDisplayShowCustomEnabled(googlePlus);

        if (googlePlus) {
            View inflate = getLayoutInflater().inflate(R.layout.navbar, null);
            bar.setCustomView(inflate);
            View.OnClickListener listener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (isNavigationMenuShown()) {
                        closeNavigationMenu(true, true);
                    } else {
                        openNavigationMenu(true);
                    }
                }
            };
            inflate.findViewById(R.id.button).setOnClickListener(listener);
            inflate.findViewById(R.id.text).setOnClickListener(listener);
            restyleChildrenOrWidget(inflate, false);
        }
        updateNavigationBarTitle();
    }

    private void updateNavigationBarTitle() {
        if (getSelectedNavigationIndex() >= 0 && getSelectedNavigationIndex() < navigationItems.size()) {
            ActionBar bar = getSupportActionBar();
            String title = getString(navigationItems.get(getSelectedNavigationIndex()).labelId);
            bar.setTitle(title);
            if (bar.getCustomView() != null) {
                ((TextView) bar.getCustomView().findViewById(R.id.text)).setText(title);
            }
        }
    }

    @Override
    public void onFragmentCreated() {
        super.onFragmentCreated(); //To change body of overridden methods use File | Settings | File Templates.
        // ((MyListView)mf.getListView()).mindLeftFling = true;
    }

    private void layoutNavigationPane() {
        final MyRelativeLayout mll = (MyRelativeLayout) findViewById(R.id.layout_container);
        final View navigationPanel = mll.findViewById(R.id.navigation_panel);
        boolean oldBlockLayoutRequests = mll.blockLayoutRequests;
        mll.blockLayoutRequests = true;
        int pix = navigationPanel.getWidth();
        final View frag = mll.findViewById(R.id.messagesfragment);
        if (isNavigationMenuShown()) {
            frag.layout(pix, 0, pix + mll.getWidth(), mll.getHeight());
        } else {
            frag.layout(0, 0, mll.getWidth(), mll.getHeight());
        }
        frag.clearAnimation();
        mll.blockLayoutRequests = oldBlockLayoutRequests;
        visitViewHierarchy(getWindow().getDecorView(), new ViewHierarchyVisitor() {
            @Override
            public void visitView(View v) {
                if (v.getClass().getName().contains("OverflowMenuButton") && v instanceof ImageButton) {
                    ImageButton ib = (ImageButton) v;
                    ib.setImageResource(R.drawable.ic_menu_moreoverflow);
                }
                //super.visitView(v);    //To change body of overridden methods use File | Settings | File Templates.
            }
        });
    }

    public static float spToPixels(Context context, Float sp) {
        float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
        return sp * scaledDensity;
    }

    private void maybeWarnXMPP() {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
        boolean shouldWarn = sp.getBoolean("xmpp_privacy_should_warn", false);
        if (shouldWarn) {
            JuickPreferencesActivity.launchXMPPPrivacyDialog(this, true);
        }
        //To change body of created methods use File | Settings | File Templates.
    }

    public static String updateAvailable;

    private boolean maybeLaunchIntent(Intent intent, boolean shouldFinish) {
        if (intent != null) {
            Uri uri = intent.getData();
            if (uri != null && uri.getPathSegments().size() > 0 && parseUri(uri, shouldFinish)) {
                return true;
            }
        }
        return false;
    }

    public static Thread usageReportThread;

    private void maybeSendUsageReport() {
        if (usageReportThread != null)
            return;
        String sendStats = sp.getString("usage_statistics", "no");
        if (!sendStats.equals("send") && !sendStats.equals("send_wifi"))
            return;
        long last_usage_sent = sp.getLong("last_usage_sent", 0);
        if (last_usage_sent == 0) {
            sp.edit().putLong("last_usage_sent", System.currentTimeMillis()).commit();
            return;
        }
        if (System.currentTimeMillis() - last_usage_sent > WhatsNew.REPORT_SEND_PERIOD) {
            ConnectivityManager connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
            NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
            if (sendStats.equals("send_wifi") && !wifi.isConnected())
                return;
            usageReportThread = new Thread() {
                @Override
                public void run() {
                    new WhatsNew(MainActivity.this).reportUsage();
                }
            };
            usageReportThread.start();
        }
    }

    public static final int NAVITEM_UNREAD = 1001;
    public static final int NAVITEM_SAVED = 1002;
    public static final int NAVITEM_RECENT_READ = 1003;
    public static final int NAVITEM_RECENT_WRITE = 1004;
    public static final int NAVITEM_ALL_COMBINED = 1005;
    public static final int NAVITEM_SUBS_COMBINED = 1006;
    public static final int NAVITEM_GLAVSU = 1007;

    public void updateNavigation() {
        navigationItems = new ArrayList<NavigationItem>();

        List<MicroBlog> blogs = new ArrayList<MicroBlog>(microBlogs.values());
        Collections.<MicroBlog>sort(blogs, new Comparator<MicroBlog>() {
            @Override
            public int compare(MicroBlog microBlog, MicroBlog microBlog2) {
                return microBlog.getPiority() - microBlog2.getPiority();
            }
        });
        for (MicroBlog blog : blogs) {
            blog.addNavigationSources(navigationItems, this);
        }
        navigationItems.add(new NavigationItem(NAVITEM_UNREAD, R.string.navigationUnread,
                R.drawable.navicon_juickadvanced, "msrcUnread") {
            @Override
            public void action() {
                final NavigationItem thisNi = this;
                final ProgressDialog pd = new ProgressDialog(MainActivity.this);
                pd.setIndeterminate(true);
                pd.setTitle(R.string.navigationUnread);
                pd.setCancelable(true);
                pd.show();
                UnreadSegmentsView.loadPeriods(MainActivity.this,
                        new Utils.Function<Void, ArrayList<DatabaseService.Period>>() {
                            @Override
                            public Void apply(ArrayList<DatabaseService.Period> periods) {
                                if (pd.isShowing()) {
                                    pd.cancel();
                                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                                    final AlertDialog alerDialog;
                                    if (periods.size() == 0) {
                                        alerDialog = builder.setTitle(getString(R.string.UnreadSegments))
                                                .setMessage(getString(R.string.YouHaveNotEnabledForUnreadSegments))
                                                .setCancelable(true)
                                                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                                                    @Override
                                                    public void onClick(DialogInterface dialogInterface, int i) {
                                                        dialogInterface.dismiss();
                                                        restoreLastNavigationPosition();
                                                    }
                                                }).create();
                                    } else {
                                        UnreadSegmentsView unreadSegmentsView = new UnreadSegmentsView(
                                                MainActivity.this, periods);
                                        final int myIndex = navigationItems.indexOf(thisNi);
                                        alerDialog = builder.setTitle(getString(R.string.ChooseUnreadSegment))
                                                .setView(unreadSegmentsView).setCancelable(true)
                                                .setNegativeButton(android.R.string.cancel,
                                                        new DialogInterface.OnClickListener() {
                                                            @Override
                                                            public void onClick(DialogInterface dialogInterface,
                                                                    int i) {
                                                                dialogInterface.dismiss();
                                                                restoreLastNavigationPosition();
                                                            }
                                                        })
                                                .create();
                                        unreadSegmentsView.setListener(new UnreadSegmentsView.PeriodListener() {
                                            @Override
                                            public void onPeriodClicked(DatabaseService.Period period) {
                                                alerDialog.dismiss();
                                                int beforeMid = period.beforeMid;
                                                Bundle args = new Bundle();
                                                args.putSerializable("messagesSource",
                                                        new UnreadSegmentMessagesSource(
                                                                getString(R.string.navigationUnread),
                                                                MainActivity.this, period));
                                                if (getSelectedNavigationIndex() != myIndex) {
                                                    setSelectedNavigationItem(myIndex);
                                                }
                                                runDefaultFragmentWithBundle(args, thisNi);
                                            }
                                        });
                                    }
                                    alerDialog.show();
                                    restyleChildrenOrWidget(alerDialog.getWindow().getDecorView());
                                }
                                return null;
                            }
                        });
                return;
            }
        });
        navigationItems.add(new NavigationItem(NAVITEM_SAVED, R.string.navigationSaved,
                R.drawable.navicon_juickadvanced, "msrcSaved") {
            @Override
            public void action() {
                final Bundle args = new Bundle();
                args.putSerializable("messagesSource", new SavedMessagesSource(MainActivity.this));
                runDefaultFragmentWithBundle(args, this);
            }
        });
        navigationItems.add(new NavigationItem(NAVITEM_RECENT_READ, R.string.navigationRecentlyOpened,
                R.drawable.navicon_juickadvanced, "msrcRecentOpen") {
            @Override
            public void action() {
                final Bundle args = new Bundle();
                args.putSerializable("messagesSource", new RecentlyOpenedMessagesSource(MainActivity.this));
                runDefaultFragmentWithBundle(args, this);
            }
        });
        navigationItems.add(new NavigationItem(NAVITEM_RECENT_WRITE, R.string.navigationRecentlyCommented,
                R.drawable.navicon_juickadvanced, "msrcRecentComment") {
            @Override
            public void action() {
                final Bundle args = new Bundle();
                args.putSerializable("messagesSource", new RecentlyCommentedMessagesSource(MainActivity.this));
                runDefaultFragmentWithBundle(args, this);
            }
        });
        navigationItems.add(new NavigationItem(NAVITEM_ALL_COMBINED, R.string.navigationAllCombined,
                R.drawable.navicon_juickadvanced, "msrcAllCombined") {
            @Override
            public void action() {
                final Bundle args = new Bundle();
                args.putSerializable("messagesSource",
                        new CombinedAllMessagesSource(MainActivity.this, "combined_all"));
                runDefaultFragmentWithBundle(args, this);
            }

            @Override
            public ArrayList<String> getMenuItems() {
                String s = getString(R.string.SelectSources);
                ArrayList<String> strings = new ArrayList<String>();
                strings.add(s);
                return strings;
            }

            @Override
            public void handleMenuAction(int which, String value) {
                switch (which) {
                case 0:
                    selectSourcesForAllCombined();
                    break;
                }
            }
        });
        navigationItems.add(new NavigationItem(NAVITEM_SUBS_COMBINED, R.string.navigationSubsCombined,
                R.drawable.navicon_juickadvanced, "msrcSubsCombined") {
            @Override
            public void action() {
                final NavigationItem thiz = this;
                new Thread("MessageSource Initializer") {
                    @Override
                    public void run() {
                        final Bundle args = new Bundle();
                        args.putSerializable("messagesSource",
                                new CombinedSubscriptionMessagesSource(MainActivity.this));
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                runDefaultFragmentWithBundle(args, thiz);
                            }

                        });
                    }
                }.start();
                final Bundle args = new Bundle();
                runDefaultFragmentWithBundle(args, this);
            }

            @Override
            public ArrayList<String> getMenuItems() {
                String s = getString(R.string.SelectSources);
                ArrayList<String> strings = new ArrayList<String>();
                strings.add(s);
                return strings;
            }

            @Override
            public void handleMenuAction(int which, String value) {
                switch (which) {
                case 0:
                    selectSourcesForAllSubs();
                    break;
                }
            }
        });
        int index = 10000;
        final SharedPreferences sp_order = getSharedPreferences("messages_source_ordering", MODE_PRIVATE);
        for (NavigationItem navigationItem : navigationItems) {
            navigationItem.itemOrder = sp_order.getInt("order_" + navigationItem.id, -1);
            if (navigationItem.itemOrder == -1) {
                navigationItem.itemOrder = index;
                sp_order.edit().putInt("order_" + navigationItem.id, navigationItem.itemOrder).commit();
            }
            index++;
        }
        Collections.sort(navigationItems, new Comparator<NavigationItem>() {
            @Override
            public int compare(NavigationItem lhs, NavigationItem rhs) {
                return lhs.itemOrder - rhs.itemOrder; // increasing
            }
        });
        allNavigationItems = new ArrayList<NavigationItem>(navigationItems);
        final Iterator<NavigationItem> iterator = navigationItems.iterator();
        while (iterator.hasNext()) {
            NavigationItem next = iterator.next();
            if (next.sharedPrefsKey != null) {
                if (!sp.getBoolean(next.sharedPrefsKey, defaultValues(next.sharedPrefsKey))) {
                    iterator.remove();
                }
            }
        }
        sp_order.edit().commit(); // save

        final boolean compressedMenu = sp.getBoolean("compressedMenu", false);
        float menuFontScale = 1;
        try {
            menuFontScale = Float.parseFloat(sp.getString("menuFontScale", "1.0"));
        } catch (Exception e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }
        final float finalMenuFontScale = menuFontScale;
        navigationList = (DragSortListView) findViewById(R.id.navigation_list);

        // adapter for old-style navigation
        final BaseAdapter navigationAdapter = new BaseAdapter() {
            @Override
            public int getCount() {
                return navigationItems.size();
            }

            @Override
            public Object getItem(int position) {
                return navigationItems.get(position);
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                if (position == -1) {
                    // NOOK is funny
                    return convertView;
                }
                final int screenHeight = getWindow().getWindowManager().getDefaultDisplay().getHeight();
                final int layoutId = R.layout.simple_list_item_1_mine;
                final PressableLinearLayout retval = convertView != null ? (PressableLinearLayout) convertView
                        : (PressableLinearLayout) getLayoutInflater().inflate(layoutId, null);
                TextView tv = (TextView) retval.findViewById(android.R.id.text1);
                if (parent instanceof Spinner) {
                    tv.setTextSize(18 * finalMenuFontScale);
                    tv.setEllipsize(TextUtils.TruncateAt.MARQUEE);
                } else {
                    tv.setTextSize(22 * finalMenuFontScale);
                }
                tv.setText(getString(navigationItems.get(position).labelId));
                if (compressedMenu) {
                    int minHeight = (int) ((screenHeight * 0.7) / getCount());
                    tv.setMinHeight(minHeight);
                    tv.setMinimumHeight(minHeight);
                }
                retval.setPressedListener(new PressableLinearLayout.PressedListener() {
                    @Override
                    public void onPressStateChanged(boolean selected) {
                        MainActivity.restyleChildrenOrWidget(retval, false);
                    }

                    @Override
                    public void onSelectStateChanged(boolean selected) {
                        MainActivity.restyleChildrenOrWidget(retval, false);
                    }
                });
                MainActivity.restyleChildrenOrWidget(retval, false);
                return retval;
            }
        };
        // adapter for new-style navigation
        final BaseAdapter navigationListAdapter = new BaseAdapter() {
            @Override
            public int getCount() {
                return getItems().size();
            }

            private ArrayList<NavigationItem> getItems() {
                if (navigationList.isDragEnabled()) {
                    return allNavigationItems;
                } else {
                    return navigationItems;
                }
            }

            @Override
            public Object getItem(int position) {
                return getItems().get(position);
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            float textSize = -1;

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                if (position == -1) {
                    // NOOK is funny
                    return convertView;
                }
                if (textSize < 0) {
                    View inflate = getLayoutInflater().inflate(android.R.layout.simple_list_item_1, null);
                    TextView text = (TextView) inflate.findViewById(android.R.id.text1);
                    textSize = text.getTextSize();
                }
                final int layoutId = R.layout.navigation_list_item;
                final PressableLinearLayout retval = convertView != null ? (PressableLinearLayout) convertView
                        : (PressableLinearLayout) getLayoutInflater().inflate(layoutId, null);
                TextView tv = (TextView) retval.findViewById(android.R.id.text1);
                final ArrayList<NavigationItem> items = getItems();
                final NavigationItem theItem = items.get(position);
                tv.setText(getString(theItem.labelId));
                ImageButton menuButton = (ImageButton) retval.findViewById(R.id.menu_button);
                menuButton.setFocusable(false);
                ArrayList<String> menuItems = theItem.getMenuItems();
                menuButton.setVisibility(menuItems != null && menuItems.size() > 0 ? View.VISIBLE : View.GONE);
                menuButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        doNavigationItemMenu(theItem);
                    }
                });
                ImageView iv = (ImageView) retval.findViewById(android.R.id.icon);
                iv.setImageResource(theItem.imageId);
                retval.findViewById(R.id.draggable)
                        .setVisibility(items != allNavigationItems ? View.GONE : View.VISIBLE);
                retval.findViewById(R.id.checkbox).setVisibility(
                        items != allNavigationItems || theItem.sharedPrefsKey == null ? View.GONE : View.VISIBLE);
                CheckBox cb = (CheckBox) retval.findViewById(R.id.checkbox);
                cb.setOnCheckedChangeListener(null);
                if (theItem.sharedPrefsKey != null) {
                    cb.setChecked(sp.getBoolean(theItem.sharedPrefsKey, defaultValues(theItem.sharedPrefsKey)));
                }
                cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        sp.edit().putBoolean(theItem.sharedPrefsKey, isChecked).commit();
                    }
                });
                int spacing = sp.getInt("navigation_spacing", 0);
                tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize + spacing);

                retval.setPadding(0, spacing, 0, spacing);
                return retval;
            }
        };
        ActionBar bar = getSupportActionBar();
        updateActionBarMode();
        navigationList.setDragEnabled(false);
        ((DragSortController) navigationList.getFloatViewManager()).setDragHandleId(R.id.draggable);
        navigationList.setAdapter(navigationListAdapter);
        navigationList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
                setSelectedNavigationItem(position);
                closeNavigationMenu(true, false);
            }
        });
        navigationList.setDropListener(new DragSortListView.DropListener() {
            @Override
            public void drop(int from, int to) {
                final NavigationItem item = allNavigationItems.remove(from);
                allNavigationItems.add(to, item);

                int index = 0;
                for (NavigationItem allNavigationItem : allNavigationItems) {
                    if (allNavigationItem.itemOrder != index) {
                        allNavigationItem.itemOrder = index;
                        sp_order.edit().putInt("order_" + allNavigationItem.id, allNavigationItem.itemOrder)
                                .commit();
                    }
                    index++;
                }
                sp_order.edit().commit(); // save
                navigationListAdapter.notifyDataSetChanged();
                //To change body of implemented methods use File | Settings | File Templates.
            }
        });
        bar.setListNavigationCallbacks(navigationAdapter, this);
        final View navigationMenuButton = (View) findViewById(R.id.navigation_menu_button);
        navigationMenuButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AlertDialog.Builder(MainActivity.this)
                        .setItems(navigationList.isDragEnabled() ? R.array.navigation_menu_end
                                : R.array.navigation_menu_start, new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        int spacing = sp.getInt("navigation_spacing", 0);
                                        switch (which) {
                                        case 0:
                                            if (navigationList.isDragEnabled()) {
                                                navigationList.setDragEnabled(false);
                                                updateNavigation();
                                            } else {
                                                navigationList.setDragEnabled(true);
                                            }
                                            break;
                                        case 1:
                                            final Map<String, ?> all = sp_order.getAll();
                                            for (String s : all.keySet()) {
                                                sp_order.edit().remove(s).commit();
                                            }
                                            sp_order.edit().commit();
                                            updateNavigation();
                                            break;
                                        case 2: // wider
                                            sp.edit().putInt("navigation_spacing", spacing + 1).commit();
                                            ((BaseAdapter) navigationList.getAdapter()).notifyDataSetInvalidated();
                                            break;
                                        case 3: // narrower
                                            if (spacing > 0) {
                                                sp.edit().putInt("navigation_spacing", spacing - 1).commit();
                                                ((BaseAdapter) navigationList.getAdapter())
                                                        .notifyDataSetInvalidated();
                                            }
                                            break;
                                        case 4: // logout from..
                                            logoutFromSomeServices();
                                            break;

                                        }
                                        navigationListAdapter.notifyDataSetChanged();
                                    }
                                })
                        .setCancelable(true).create().show();
            }
        });

        final SharedPreferences spn = getSharedPreferences("saved_last_navigation_type", MODE_PRIVATE);
        int restoredLastNavItem = spn.getInt("last_navigation", 0);
        if (restoredLastNavItem != 0) {
            NavigationItem allSources = null;
            for (int i = 0; i < navigationItems.size(); i++) {
                NavigationItem navigationItem = navigationItems.get(i);
                if (navigationItem.labelId == restoredLastNavItem) {
                    lastNavigationItem = navigationItem;
                }
                if (navigationItem.labelId == R.string.navigationAll) {
                    allSources = navigationItem;
                }
            }
            if (lastNavigationItem == null) {
                lastNavigationItem = allSources; // could be null if not configured
            }
            if (lastNavigationItem == null && navigationItems.size() > 0) {
                lastNavigationItem = navigationItems.get(0); // last default
            }
            if (lastNavigationItem != null) {
                restoreLastNavigationPosition();
                lastNavigationItem.action();
            }
        }

    }

    private void selectSourcesForAllSubs() {
        selectSourcesForCombined(CombinedSubscriptionMessagesSource.COMBINED_SUBSCRIPTION_MESSAGES_SOURCE,
                new String[] { JuickMessageID.CODE, PointMessageID.CODE, BnwMessageID.CODE,
                        FacebookMessageID.CODE });
    }

    @TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
    private void selectSourcesForCombined(final String prefix, final String[] codes) {
        final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
        ScrollView v = new ScrollView(this);
        v.setLayoutParams(new ScrollView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
        final LinearLayout ll = new LinearLayout(this);
        ll.setLayoutParams(new ScrollView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
        ll.setOrientation(LinearLayout.VERTICAL);
        v.addView(ll);
        for (int i = 0; i < codes.length; i++) {
            final CompoundButton sw = VERSION.SDK_INT >= 14 ? new Switch(this) : new CheckBox(this);
            sw.setPadding(10, 10, 10, 10);
            sw.setLayoutParams(new ScrollView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            sw.setText(codes[i]);
            sw.setChecked(sp.getBoolean(prefix + codes[i], true));
            ll.addView(sw);
        }

        new AlertDialog.Builder(MainActivity.this).setView(v).setCancelable(true)
                .setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        SharedPreferences.Editor e = sp.edit();
                        for (int i = 0; i < ll.getChildCount(); i++) {
                            CompoundButton cb = (CompoundButton) ll.getChildAt(i);
                            assert cb != null;
                            e.putBoolean(prefix + codes[i], cb.isChecked());
                        }
                        e.commit();
                    }
                }).create().show();

    }

    private void selectSourcesForAllCombined() {
        selectSourcesForCombined(CombinedAllMessagesSource.COMBINED_ALL_MESSAGE_SOURCE,
                new String[] { JuickMessageID.CODE, PointMessageID.CODE, BnwMessageID.CODE });
    }

    private void doNavigationItemMenu(final NavigationItem theItem) {
        ArrayList<String> menuItems = theItem.getMenuItems();
        final String[] menuItemsA = menuItems.toArray(new String[menuItems.size()]);
        new AlertDialog.Builder(MainActivity.this).setItems(menuItemsA, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                theItem.handleMenuAction(which, menuItemsA[which]);
            }
        }).setCancelable(true).create().show();

    }

    private void logoutFromSomeServices() {
        new Thread("Get logged in accounts") {
            @Override
            public void run() {
                final HashSet<AccountProof> accountProofs = JAXMPPClient.getAccountProofs(MainActivity.this, false);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (accountProofs.size() == 0) {
                            Toast.makeText(MainActivity.this, getString(R.string.NoAuth), Toast.LENGTH_LONG).show();
                            return;
                        }
                        final CharSequence[] arr = new CharSequence[accountProofs.size()];
                        int ix = 0;
                        for (AccountProof accountProof : accountProofs) {
                            arr[ix++] = accountProof.getProofAccountType();
                        }
                        new AlertDialog.Builder(MainActivity.this)
                                .setItems(arr, new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        String what = arr[which].toString();
                                        ArrayList<Utils.URLAuth> authorizers = Utils.authorizers;
                                        for (Utils.URLAuth authorizer : authorizers) {
                                            if (authorizer.isForBlog(what)) {
                                                authorizer.reset(MainActivity.this, handler);
                                            }
                                        }
                                        Toast.makeText(MainActivity.this,
                                                getString(R.string.AuthHasBeenClearedFor) + " " + what,
                                                Toast.LENGTH_LONG).show();
                                    }
                                }).setCancelable(true).create().show();
                    }
                });
            }
        }.start();

    }

    private boolean defaultValues(String sharedPrefsKey) {
        if (sharedPrefsKey.equals("msrcTopMessages"))
            return true;
        if (sharedPrefsKey.equals("msrcWithPhotos"))
            return true;
        return false;
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        try {
            super.onRestoreInstanceState(savedInstanceState);
        } catch (Exception e) {
            /*
            fix for 4.0:
            Caused+by:+java.lang.NullPointerException
                    at+android.view.View.dispatchRestoreInstanceState(View.java:10064)
                    at+android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2421)
                    at+android.view.View.restoreHierarchyState(View.java:10047)
                    at+com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1630)
                    at+android.app.Activity.onRestoreInstanceState(Activity.java:906)
                
             */
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    private void clearObsoleteImagesInCache() {
        final File cacheDir = new File(getCacheDir(), "image_cache");
        int daysToKeep = 10;
        try {
            daysToKeep = Integer.parseInt(sp.getString("image.daystokeep", "10"));
        } catch (NumberFormatException e) {
            Toast.makeText(this, "Invalid 'days to keep cache' value - number format", Toast.LENGTH_SHORT).show();
        }
        final int finalDaysToKeep = daysToKeep;
        new Thread("clearObsoleteImagesInCache") {
            @Override
            public void run() {
                String[] list = cacheDir.list();
                if (list != null) {
                    for (String fname : list) {
                        File file = new File(cacheDir, fname);
                        if (file.lastModified() < System.currentTimeMillis()
                                - finalDaysToKeep * 24 * 60 * 60 * 1000) {
                            file.delete();
                        }
                    }
                }
            }

        }.start();
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        if (mf != null) {
            return mf.saveState();
        }
        return super.onRetainCustomNonConfigurationInstance();
    }

    @Override
    protected void onDestroy() {
        XMPPService.log("MainActivity.destroy()");
        nActiveMainActivities--;
        saveState(true);
        super.onDestroy();
        if (sp != null)
            sp.unregisterOnSharedPreferenceChangeListener(this);
    }

    private void saveState(boolean urgent) {
        if (mf != null) {
            if (lastNavigationItem != null) {
                MessageListBackingData messageListBackingData = mf.getMessageListBackingData();
                if (messageListBackingData != null) {
                    messageListBackingData.navigationItemLabelId = lastNavigationItem.labelId;
                    JuickAdvancedApplication.instance.setSavedList(messageListBackingData, urgent);
                }
            }
        }
    }

    private void toggleJuickGCM(MainActivity activity) {
        JuickAdvancedApplication.instance.maybeStartJuickGCMClient(activity);
        new Thread("toggleJuickGCM") {
            @Override
            public void run() {
                try {
                    Thread.sleep(500); // another start may run
                } catch (InterruptedException e) {
                    e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
                }
                boolean useJuickGCM = sp.getBoolean("juick_gcm", false);
                if (JuickAdvancedApplication.instance.juickGCMClient != null) {
                    if (useJuickGCM) {
                        JuickAdvancedApplication.instance.juickGCMClient.start();
                    } else {
                        JuickAdvancedApplication.instance.juickGCMClient.stop();
                    }
                }
            }
        }.start();
    }

    public static void toggleXMPP(Context ctx) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
        boolean useXMPP = sp.getBoolean("useXMPP", false);
        if (useXMPP) {
            ctx.startService(new Intent(ctx, XMPPService.class));
        } else {
            if (isXMPPServiceRunning(ctx)) {
                Intent service = new Intent(ctx, XMPPService.class);
                service.putExtra("terminate", true);
                ctx.startService(service);
            }
        }
    }

    private void toggleJAMessaging() {
        boolean useJAM = sp.getBoolean("enableJAMessaging", false);
        toggleJAMessaging(this, useJAM);
    }

    private void initCensor() {
        Censor.setCensorshipLevel(Integer.parseInt(sp.getString("censor", "0")));
    }

    public static boolean commandJAMService(Context ctx, String command) {
        if (isJAMServiceRunning(ctx)) {
            Intent service = new Intent(ctx, JAMService.class);
            String[] split = command.split(":");
            if (split.length == 1) {
                service.putExtra(command, true);
            } else {
                service.putExtra(split[0], split[1]);
            }
            ctx.startService(service);
            return true;
        } else {
            return false;
        }
    }

    public static void toggleJAMessaging(Context ctx, boolean useJAM) {
        if (isJAMServiceRunning(ctx) && useJAM) {
            return; // already
        }
        XMPPService.log("MainActivity.toggleJAMessaging(" + useJAM + ")");
        if (useJAM) {
            JuickAdvancedApplication.showXMPPToast("toggleJAMessaging: " + useJAM);
            ctx.startService(new Intent(ctx, JAMService.class));
        } else {
            if (isJAMServiceRunning(ctx)) {
                JuickAdvancedApplication.showXMPPToast("toggleJAMessaging: " + useJAM);
                Intent service = new Intent(ctx, JAMService.class);
                service.putExtra("terminate", true);
                ctx.startService(service);
            }
        }
    }

    static boolean isXMPPServiceRunning(Context ctx) {
        ActivityManager manager = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
        String className = XMPPService.class.getName();
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (className.equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public static boolean isJAMServiceRunning(Context ctx) {
        ActivityManager manager = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
        String className = JAMService.class.getName();
        List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);
        for (ActivityManager.RunningServiceInfo service : runningServices) {
            if (className.equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    public boolean onNavigationItemSelected(final int itemPosition, long z) {
        restyle();
        NavigationItem thisItem = navigationItems.get(itemPosition);
        if (lastNavigationItem == thisItem)
            return false; // happens during screen rotate
        MessageListBackingData savedList = JuickAdvancedApplication.instance.getSavedList(this);
        if (savedList != null) {
            if (thisItem.labelId != savedList.navigationItemLabelId) {
                JuickAdvancedApplication.instance.setSavedList(null, false);
            }
        }
        thisItem.action();
        return true;
    }

    public boolean restoreLastNavigationPosition() {
        for (int i = 0; i < navigationItems.size(); i++) {
            NavigationItem navigationItem = navigationItems.get(i);
            if (navigationItem == lastNavigationItem) {
                if (getSelectedNavigationIndex() != i) {
                    setSelectedNavigationItem(i);
                    updateNavigationBarTitle();
                    return true;
                }
            }
        }
        return false;
    }

    private void replaceFragment(MessagesFragment mf, Bundle args) {
        try {
            final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            mf.setArguments(args);
            ft.replace(R.id.messagesfragment, mf);
            ft.commit();
            getSupportFragmentManager().executePendingTransactions();
        } catch (Exception e) {
            // tanunax
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == ACTIVITY_SIGNIN) {
            if (resultCode == RESULT_OK) {
                initStage2();
            } else {
                finish();
            }
        } else if (requestCode == ACTIVITY_PREFERENCES) {
            if (resultCode == RESULT_OK) {
                //                Intent intent = getIntent();
                //                finish();
                //                startActivity(intent);
                //
            }
        } else if (requestCode == COLORS_THEME_STORAGE_ACTION_SAVE) {
            if (resultCode == RESULT_OK) {
                final String filePath = data.getStringExtra(FileDialog.RESULT_PATH);
                ColorsTheme.saveColorsTheme(getApplicationContext(), filePath);
            }
        } else if (requestCode == COLORS_THEME_STORAGE_ACTION_LOAD) {
            if (resultCode == RESULT_OK) {
                final String filePath = data.getStringExtra(FileDialog.RESULT_PATH);
                ColorsTheme.loadColorsTheme(getApplicationContext(), filePath);
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        com.actionbarsherlock.view.MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        boolean savedMessagesSource = mf != null && mf.messagesSource != null
                && mf.messagesSource instanceof SavedMessagesSource;
        menu.findItem(R.id.menuitem_new_saved_sharing_key).setVisible(savedMessagesSource);
        menu.findItem(R.id.menuitem_existing_saved_sharing_key).setVisible(savedMessagesSource);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(com.actionbarsherlock.view.MenuItem item) {
        if (item == null)
            return true;
        switch (item.getItemId()) {
        case R.id.menuitem_preferences:
            Intent prefsIntent = new Intent(this, NewJuickPreferenceActivity.class);
            prefsIntent.putExtra("menu", NewJuickPreferenceActivity.Menu.TOP_LEVEL.name());
            startActivityForResult(prefsIntent, ACTIVITY_PREFERENCES);
            return true;
        case R.id.menuitem_newmessage:
            if (mf != null) {
                Intent intent1 = new Intent(this, NewMessageActivity.class);
                if (mf.messagesSource.getKind().startsWith("combined")) {
                    //Toast.makeText(this, getString(R.string.GoToParticularToPost), Toast.LENGTH_LONG).show();
                } else {
                    intent1.putExtra("microblog", mf.messagesSource.getMicroBlog().getCode());
                }
                startActivity(intent1);
                return true;
            }
        case R.id.menuitem_search:
            if (mf != null) {
                if (mf.messagesSource.getKind().equals("combined")) {
                    Toast.makeText(this, getString(R.string.GoToParticularToSearch), Toast.LENGTH_LONG).show();
                } else {
                    Intent intent = new Intent(this, ExploreActivity.class);
                    intent.putExtra("messagesSource", mf.messagesSource);
                    startActivity(intent);
                }
            }
            return true;
        case R.id.reload:
            doReload();
            return true;
        case R.id.menuitem_new_saved_sharing_key:
            new AlertDialog.Builder(this).setTitle("New Access URL generation")
                    .setMessage("Old URL will become onvalid. Continue?")
                    .setPositiveButton("Continue", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            obtainSavedMessagesURL(true);
                        }
                    }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //To change body of implemented methods use File | Settings | File Templates.
                        }
                    }).setCancelable(true).show();
            return true;
        case R.id.menuitem_existing_saved_sharing_key:
            obtainSavedMessagesURL(false);
            return true;
        case R.id.menuitem_load_colors_theme:
            startColorsThemeStorageAction(COLORS_THEME_STORAGE_ACTION_LOAD);
            return true;
        case R.id.menuitem_save_colors_theme:
            startColorsThemeStorageAction(COLORS_THEME_STORAGE_ACTION_SAVE);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void startColorsThemeStorageAction(int storageAction) {
        Intent intent = new Intent(this, FileDialog.class);
        File storageDir = ColorsTheme.getStorageDir(getApplicationContext());
        String storageDirAbsolutePath = storageDir.getAbsolutePath();
        intent.putExtra(FileDialog.START_PATH, storageDirAbsolutePath);
        //can user select directories or not
        intent.putExtra(FileDialog.CAN_SELECT_DIR, false);
        //alternatively you can set file filter
        intent.putExtra(FileDialog.FORMAT_FILTER, ColorsTheme.FILE_DIALOG_FILE_EXTENSIONS);
        intent.putExtra(FileDialog.SELECTION_MODE,
                (storageAction == COLORS_THEME_STORAGE_ACTION_SAVE) ? SelectionMode.MODE_CREATE
                        : SelectionMode.MODE_OPEN);
        startActivityForResult(intent, storageAction);
    }

    private void obtainSavedMessagesURL(final boolean reset) {
        final ProgressDialog pd = new ProgressDialog(MainActivity.this);
        pd.setIndeterminate(true);
        pd.setTitle("Saved Messages");
        pd.setMessage("Waiting for server key");
        pd.setCancelable(true);
        pd.show();
        new Thread("obtainSavedMessagesURL") {
            @Override
            public void run() {

                final RESTResponse restResponse = DatabaseService.obtainSharingURL(MainActivity.this, reset);

                if (restResponse.getErrorText() == null)
                    try {
                        new JSONObject(restResponse.getResult());
                    } catch (JSONException e) {
                        restResponse.errorText = e.toString();
                    }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        pd.cancel();
                        if (restResponse.getErrorText() != null) {
                            Toast.makeText(MainActivity.this, restResponse.getErrorText(), Toast.LENGTH_LONG)
                                    .show();
                        } else {
                            try {
                                final String url = (String) new JSONObject(restResponse.getResult()).get("url");
                                if (url.length() == 0) {
                                    Toast.makeText(MainActivity.this,
                                            "You don't have key yet. Choose another option.", Toast.LENGTH_LONG)
                                            .show();
                                } else {
                                    new AlertDialog.Builder(MainActivity.this).setTitle("You got key!")
                                            .setMessage(url)
                                            .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                                                @Override
                                                public void onClick(DialogInterface dialog, int which) {
                                                }
                                            }).setNeutralButton("Share with..",
                                                    new DialogInterface.OnClickListener() {
                                                        @Override
                                                        public void onClick(DialogInterface dialog, int which) {
                                                            Intent share = new Intent(
                                                                    android.content.Intent.ACTION_SEND);
                                                            share.setType("text/plain");
                                                            share.addFlags(
                                                                    Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);

                                                            share.putExtra(Intent.EXTRA_SUBJECT,
                                                                    "My Saved messages on Juick Advanced");
                                                            share.putExtra(Intent.EXTRA_TEXT, url);

                                                            startActivity(
                                                                    Intent.createChooser(share, "Share link"));
                                                        }
                                                    })
                                            .setCancelable(true).show();
                                }
                            } catch (JSONException e) {
                                Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
                            }
                        }
                    }
                });
            }
        }.start();

    }

    long lastReload = System.currentTimeMillis();

    public void doReload() {
        if (System.currentTimeMillis() - lastReload < 1000)
            return;
        if (resumed) {
            if (lastNavigationItem != null) {
                JuickAdvancedApplication.instance.setSavedList(null, false);
                NavigationItem oldItem = lastNavigationItem;
                lastNavigationItem.restoreReadMarker();
                lastNavigationItem = null;
                oldItem.action();
                lastReload = System.currentTimeMillis();
            }
        } else {
            reloadOnResume = true;
        }
    }

    private boolean parseUri(Uri uri, boolean shouldFinish) {
        List<String> segs = uri.getPathSegments();
        if (uri == null || uri.getHost() == null)
            return true;
        if (uri.getHost().contains("juick.com")) {
            if ((segs.size() == 1 && segs.get(0).matches("\\A[0-9]+\\z"))
                    || (segs.size() == 2 && segs.get(1).matches("\\A[0-9]+\\z") && !segs.get(0).equals("places"))) {
                int mid = Integer.parseInt(segs.get(segs.size() - 1));
                if (mid > 0) {
                    if (shouldFinish)
                        finish();
                    Intent intent = new Intent(this, ThreadActivity.class);
                    intent.setData(null);
                    JuickMessageID jmid = new JuickMessageID(mid);
                    intent.putExtra("mid", jmid);
                    intent.putExtra("messagesSource", ChildrenMessageSource.forMID(this, jmid));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    startActivity(intent);
                    return true;
                }
            } else if (segs.size() == 1 && segs.get(0).matches("\\A[a-zA-Z0-9\\-]+\\z")) {
                //TODO show user
            }
        }
        if (uri.getHost().contains("bnw.im")) {
            String[] hostPart = uri.getHost().split("\\.");
            if (hostPart.length == 2 && segs.size() == 2 && segs.get(0).equals("p")) { // http://bnw.im/p/KLMNOP
                // open thread
                if (shouldFinish)
                    finish();
                Intent intent = new Intent(this, ThreadActivity.class);
                intent.setData(null);
                BnwMessageID mid = new BnwMessageID(segs.get(1));
                intent.putExtra("mid", mid);
                intent.putExtra("messagesSource", ChildrenMessageSource.forMID(this, mid));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
            }

        }
        if (uri.getHost().contains("point.im")) {
            String s = uri.toString();
            if (s.endsWith("/"))
                return false;
            s = s.substring(s.indexOf("point.im") + 9);
            if (s.contains("#")) {
                s = s.substring(0, s.indexOf("#"));
            }
            if (s.contains("/") || s.length() > 6)
                return false;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) != Character.toLowerCase(s.charAt(i)))
                    return false;
            }
            PointMessageID mid = new PointMessageID("", s, -1);
            Intent intent = new Intent(this, ThreadActivity.class);
            intent.setData(null);
            intent.putExtra("mid", mid);
            intent.putExtra("messagesSource", ChildrenMessageSource.forMID(this, mid));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);

        }
        return false;
    }

    public static Runnable installerOnResume;

    @Override
    protected void onResume() {
        restyle();
        resumed = true;
        super.onResume();
        JuickAdvancedApplication.instance.maybeStartJuickGCMClient(this);
        if (reloadOnResume) {
            reloadOnResume = false;
            doReload();
        }
        if (installerOnResume != null) {
            try {
                installerOnResume.run();
            } finally {
                installerOnResume = null;
            }
        }
    }

    @Override
    protected void onPause() {
        resumed = false;
        super.onPause();
    }

    private void restyle() {
        final ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
        restyleChildrenOrWidget(decorView);
    }

    public static void restyleChildrenOrWidget(View view) {
        restyleChildrenOrWidget(view, false);
    }

    public static class ViewHierarchyVisitor {
        public void visitView(View v) {

        }
    }

    public static void visitViewHierarchy(View view, ViewHierarchyVisitor visitor) {
        visitor.visitView(view);
        if (view instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) view;
            int childCount = group.getChildCount();
            for (int i = 0; i < childCount; i++) {
                visitViewHierarchy(group.getChildAt(i), visitor);
            }
        }
    }

    public static void restyleChildrenOrWidget(View view, boolean dontBackground) {
        if (view == null)
            return;
        ColorsTheme.ColorTheme colorTheme = JuickMessagesAdapter.getColorTheme(view.getContext());
        boolean pressed = view.isPressed();
        boolean selected = view.isSelected();
        String name = view.getClass().getName();
        if (name.contains("$HomeView"))
            return;
        if (view instanceof AbsListView) {
            ((AbsListView) view).setCacheColorHint(colorTheme.getBackground(pressed));
        }
        if (view instanceof EditText) {
            EditText et = (EditText) view;
            et.setTextColor(colorTheme.getForeground(pressed));
            et.setBackgroundColor(colorTheme.getBackground(pressed));
        } else if (view instanceof RadioButton) {
            RadioButton btn = (RadioButton) view;
            btn.setTextColor(colorTheme.getForeground(pressed));
            btn.setBackgroundColor(colorTheme.getBackground());
        } else if (view instanceof Spinner) {
            View scan = view;
            boolean shouldRecolor = false;
            while (scan != null) {
                if (scan.getClass().getName().toLowerCase().contains("action")) {
                    shouldRecolor = true;
                    break;
                }
                if ("shouldRecolor".equals(scan.getTag())) {
                    shouldRecolor = true;
                    break;
                }
                try {
                    scan = (View) scan.getParent();
                } catch (Exception e) {
                    scan = null;
                }
            }
            if (shouldRecolor)
                restyleViewGroup((Spinner) view, colorTheme, pressed, selected, dontBackground);
        } else if (view instanceof Button) {
            //            Button btn = (Button) view;
            //            btn.setTextColor(colorTheme.getForeground(pressed));
            //            btn.setBackgroundColor(colorTheme.getButtonBackground());
        } else if (view instanceof TextView) {
            TextView text = (TextView) view;
            final int id = text.getId();
            if (id != R.id.old_title /* && id != R.id.gotoMain */) // keep it authentic
                text.setTextColor(colorTheme.getForeground(pressed));
        } else if (view instanceof ViewGroup) {
            restyleViewGroup((ViewGroup) view, colorTheme, pressed, selected, dontBackground);
        }
    }

    private static void restyleViewGroup(ViewGroup view, ColorsTheme.ColorTheme colorTheme, boolean pressed,
            boolean selected, boolean dontBackground) {
        ViewGroup parent = view;
        if (parent.getId() == R.id.navigation_panel)
            return;
        int childCount = parent.getChildCount();
        int background = colorTheme.getBackground(pressed);
        int foreground = colorTheme.getForeground(pressed);
        if (selected) {
            background = calculatePressedBackground(background, foreground);
        }
        if (!dontBackground) {
            boolean skipDraw = false;
            if ((view instanceof LinearLayout || view instanceof FrameLayout || view instanceof RelativeLayout)) {
                Context context = view.getContext();
                if (context instanceof MainActivity || context instanceof MessagesActivity
                        || context instanceof ThreadActivity) {
                    // no unneeded background in given scrolling activities
                    skipDraw = true;
                    if (view.getBackground() != null) {
                        // but enable for layouts that with color
                        skipDraw = false;
                    }
                    if (!skipDraw && view.getClass().getName().toLowerCase().contains("decorview")) {
                        // given activities manage themselves
                        skipDraw = true;
                    }
                }
            }
            if (!skipDraw)
                parent.setBackgroundColor(background);
        }
        if (view instanceof ListView)
            dontBackground = true;
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            restyleChildrenOrWidget(child, dontBackground);
        }
    }

    private static int calculatePressedBackground(int background, int foreground) {
        int r1 = (background & 0x00FF0000) >> 16;
        int g1 = (background & 0x0000FF00) >> 8;
        int b1 = (background & 0x000000FF) >> 0;
        int r2 = (foreground & 0x00FF0000) >> 16;
        int g2 = (foreground & 0x0000FF00) >> 8;
        int b2 = (foreground & 0x000000FF) >> 0;
        final double K = r1 > r2 ? 0.1 : 0.2;
        int r = r1 + (int) ((r2 - r1) * K);
        int g = g1 + (int) ((g2 - g1) * K);
        int b = b1 + (int) ((b2 - b1) * K);
        int newColor = 0xFF000000 + (r << 16) + (g << 8) + b;
        return newColor;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
        if (s == null)
            return;

        if (s.equals("useHttpsForJuickHttpAPI")) {
            JuickHttpAPI.setHttpsEnabled(sp.getBoolean("useHttpsForJuickHttpAPI", false));
        }

        if (s.equals("useXMPP")) {
            toggleXMPP(this);
        }
        if (s.equals("juick_gcm")) {
            toggleJuickGCM(this);
        }
        if (s.equals("enableJAMessaging")) {
            toggleJAMessaging();
        }
        if (s.equals("googlePlusNavigation")) {
            updateActionBarMode();
        }
        if (s.equals("enableBrowserComponent")) {
            ComponentName cn = new ComponentName("com.juickadvanced", "com.juick.android.SimpleBrowser");
            boolean skipDontKillApp = sp.getBoolean("skip_dont_kill_app", false);
            getPackageManager().setComponentEnabledSetting(cn,
                    sp.getBoolean(s, false) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    skipDontKillApp ? 0 : PackageManager.DONT_KILL_APP);
        }
        boolean censorLevelChanged = false;
        if (s.equals("censor")) {
            censorLevelChanged = true;
            Censor.setCensorshipLevel(Integer.parseInt(sp.getString("censor", "0")));
        }
        boolean dontWatchPreferences = sp.getBoolean("dontWatchPreferences", false);
        if (dontWatchPreferences)
            return;
        if (s.startsWith("msrc")) {
            final DragSortListView navigationList = (DragSortListView) findViewById(R.id.navigation_list);
            if (navigationList.isDragEnabled())
                return; // editing is being done
            final int REFRESHNAV = 0xFE0D;
            if (!handler.hasMessages(REFRESHNAV)) {
                Message msg = Message.obtain(handler, new Runnable() {
                    @Override
                    public void run() {
                        updateNavigation();
                    }
                });
                msg.what = REFRESHNAV;
                handler.sendMessageDelayed(msg, 100);
            }
        }
        boolean invalidateRendering = false;
        if (censorLevelChanged) {
            invalidateRendering = true;
        }
        if (s.startsWith("Colors.")) {
            invalidateRendering = true;
        }
        String[] refreshCauses = new String[] { "messagesFontScale", "showNumbers", "wrapUserpics",
                "showUserpics", };
        for (String refreshCause : refreshCauses) {
            if (refreshCause.equals(s)) {
                if (s.equals("messagesFontScale") && sp.getBoolean("enableScaleByGesture", true))
                    continue; // should have been pinch zoom
                invalidateRendering = true;
                break;
            }
        }
        if (invalidateRendering) {
            if (mf != null && mf.listAdapter != null)
                mf.listAdapter.notifyDataSetInvalidated();
        }
        if (s.equals("enableLoginNameWithCrashReport")) {
            if (sp.getBoolean("enableLoginNameWithCrashReport", false)) {
                String juickAccountName = JuickAPIAuthorizer.getJuickAccountName(this);
                if (juickAccountName != null)
                    Crashlytics.setString("juick_user", juickAccountName);
            } else {
                Crashlytics.setString("juick_user", "");
            }

        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        saveState(false);
        super.onSaveInstanceState(outState);
    }

    @Override
    public Activity getActivity() {
        return this;
    }

    @Override
    public Handler getHandler() {
        return handler;
    }

    @Override
    public void onBackPressed() {
        if (isNavigationMenuShown()) {
            closeNavigationMenu(true, true);
            return;
        }
        if (mf != null && mf.listAdapter != null && mf.listAdapter.imagePreviewHelper != null
                && mf.listAdapter.imagePreviewHelper.handleBack())
            return;
        super.onBackPressed();
    }

    long lastNPE = 0;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        try {
            return super.dispatchTouchEvent(ev);
        } catch (Exception e) {
            if (System.currentTimeMillis() - lastNPE > 5 * 60 * 1000L) {
                lastNPE = System.currentTimeMillis();
                MainActivity.handleException(new RuntimeException("Handled NPE in alcatel", e));
            }
            return true;
        }
    }

    @Override
    public boolean isRunning() {
        return resumed;
    }

}