mobisocial.musubi.BootstrapActivity.java Source code

Java tutorial

Introduction

Here is the source code for mobisocial.musubi.BootstrapActivity.java

Source

/*
 * Copyright 2012 The Stanford MobiSocial Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mobisocial.musubi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import mobisocial.metrics.UsageMetrics;
import mobisocial.musubi.model.helpers.DatabaseFile;
import mobisocial.musubi.model.helpers.EncodedMessageManager;
import mobisocial.musubi.service.AddressBookUpdateHandler;
import mobisocial.musubi.service.MusubiService;
import mobisocial.musubi.ui.SettingsActivity;
import mobisocial.musubi.ui.fragments.EulaFragment;
import mobisocial.musubi.util.CertifiedHttpClient;
import mobisocial.socialkit.musubi.Musubi;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.widget.TextView;

public class BootstrapActivity extends FragmentActivity {
    static final String TAG = BootstrapActivity.class.getSimpleName();
    public static final String EXTRA_ORIGINAL_INTENT = "original-intent";
    public static final String PREF_INSTALLED_VERSION = "installed_version";
    public static final String PREF_LAST_UPDATE_CHECK = "version_checked";
    static final long ONE_DAY = 86400000;

    static boolean sBootstrapped = false;
    boolean mNeedsEULA;
    boolean mNeedsIntro;
    boolean mNeedsFriends;

    EulaFragment.Callback mEulaCallback = new EulaFragment.Callback() {
        @Override
        public void eulaResult(boolean accepted) {
            if (accepted) {
                EulaFragment.acceptedEULA(BootstrapActivity.this);
                mNeedsEULA = false;
            } else {
                setResult(RESULT_CANCELED);
                finish();
            }
        }
    };

    /**
     * finishes the caller if bootstrapping is necessary
     * @param activity
     * @return true if the bootstrap activity will be started
     */
    public static boolean bootstrapIfNecessary(Activity activity) {
        if (sBootstrapped) {
            return false;
        }
        Intent intent = new Intent(activity, BootstrapActivity.class);
        intent.putExtra(EXTRA_ORIGINAL_INTENT, (Parcelable) activity.getIntent());
        intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        activity.startActivity(intent);
        activity.finish();
        return true;
    }

    public static boolean isBootstrapped() {
        return sBootstrapped;
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new InitializeMusubiTask().execute();
    }

    //TODO: do these settings really belong here? maybe...
    //TODO: add these settings to the database instead?
    private void ensureRingtone() {
        SharedPreferences settings = getSharedPreferences(SettingsActivity.PREFS_NAME, 0);
        if (settings.getString("ringtone", null) != null) {
            return;
        }

        RingtoneManager ringtoneManager = new RingtoneManager(this);
        ringtoneManager.setType(RingtoneManager.TYPE_NOTIFICATION);
        String ringtoneUri = null;
        String backupUri = null;
        Cursor cursor = ringtoneManager.getCursor();
        try {
            cursor.moveToFirst();
            while (!cursor.isAfterLast()) {
                String ringtone = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
                if (ringtone.equalsIgnoreCase("dDeneb")) {
                    ringtoneUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX) + "/"
                            + cursor.getString(RingtoneManager.ID_COLUMN_INDEX);

                    break;
                }
                if (backupUri == null) {
                    backupUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX) + "/"
                            + cursor.getString(RingtoneManager.ID_COLUMN_INDEX);
                }
                cursor.moveToNext();
            }
        } finally {
            cursor.deactivate();
        }

        SharedPreferences.Editor editor = settings.edit();
        if (ringtoneUri != null) {
            editor.putString("ringtone", ringtoneUri);
        } else {
            if (backupUri != null) {
                editor.putString("ringtone", backupUri.toString());
            } else {
                editor.putString("ringtone", "none");
            }
        }
        editor.commit();
    }

    Integer getRequiredVersion() {
        if (UsageMetrics.CHIRP_VERSIONING_ENDPOINT == null) {
            return null;
        }

        HttpClient http = new CertifiedHttpClient(this);
        HttpParams params = http.getParams();
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpConnectionParams.setConnectionTimeout(params, 6000);
        HttpConnectionParams.setSoTimeout(params, 6000);

        HttpGet get = new HttpGet(UsageMetrics.CHIRP_VERSIONING_ENDPOINT);
        try {
            HttpResponse response = http.execute(get);
            BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line;
            while ((line = in.readLine()) != null) {
                sb.append(line);
            }
            in.close();
            JSONObject json = new JSONObject(sb.toString());
            return json.getInt("required");
        } catch (IOException e) {
            Log.e(TAG, "Error getting versioning info", e);
            return null;
        } catch (JSONException e) {
            Log.e(TAG, "Bad versioning format", e);
            return null;
        }
    }

    /**
      * Attempts to make a database connection in order to trigger a
      * database upgrade. Before doing so, we display a DialogFragment
      * alerting the user of an upgrade, and afterwards we dismiss the dialog.
      * The dialog is not displayed if the connection is done quickly.
      */
    class InitializeMusubiTask extends AsyncTask<Void, String, Integer> {
        final Integer RESULT_INITIALIZED_OK = 0;
        final Integer RESULT_NEEDS_UPGRADE = 1;

        SharedPreferences mPreferences;
        int mInstalledVersion;
        int mPreviousVersion;

        boolean mNeedsVersionCheck;
        boolean mVersionChanged;
        boolean mQuickBootsrap = false;

        @Override
        protected void onPreExecute() {
            mPreferences = getSharedPreferences(SettingsActivity.PREFS_NAME, 0);
            mPreviousVersion = mPreferences.getInt(PREF_INSTALLED_VERSION, 0);
            try {
                PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
                mInstalledVersion = info.versionCode;
            } catch (NameNotFoundException e) {
                throw new RuntimeException("Couldn't find my own package", e);
            }

            long time = System.currentTimeMillis();
            long versionChecked = mPreferences.getLong(PREF_LAST_UPDATE_CHECK, 0);
            mNeedsVersionCheck = (time - versionChecked > ONE_DAY);
            mVersionChanged = (mPreviousVersion != mInstalledVersion);
            mNeedsEULA = EulaFragment.needsEULA(BootstrapActivity.this);

            mQuickBootsrap = !(mNeedsVersionCheck || mVersionChanged || mNeedsEULA);
            if (!mQuickBootsrap) {
                setContentView(R.layout.splash);
            }
        }

        @Override
        protected Integer doInBackground(Void... params) {
            SQLiteOpenHelper databaseSource = App.getDatabaseSource(BootstrapActivity.this);
            if (databaseSource instanceof DatabaseFile) {
                DatabaseFile dbf = (DatabaseFile) databaseSource;
                dbf.setActivityForEmergencyUI(BootstrapActivity.this);
            }

            if (mQuickBootsrap) {
                return RESULT_INITIALIZED_OK;
            }

            publishProgress("Loading...");
            if (mNeedsVersionCheck) {
                publishProgress("Checking for updates...");
                // Make sure this version is okay to use
                Integer requiredVersion = getRequiredVersion();
                if (requiredVersion != null && mInstalledVersion < requiredVersion) {
                    // Update required.
                    return RESULT_NEEDS_UPGRADE;
                }
                if (requiredVersion != null) {
                    // verified latest version.
                    long time = System.currentTimeMillis();
                    mPreferences.edit().putLong(PREF_LAST_UPDATE_CHECK, time).commit();
                }
            }

            if (!mVersionChanged) {
                // No need to check for further bootstrapping.
                // The goal is to entirely skip the splash screen even if the system
                // has killed our application.
                return RESULT_INITIALIZED_OK;
            }

            // Handle basic initial settings
            ensureRingtone();

            // Force database upgrade
            publishProgress("Preparing database...");
            databaseSource.getReadableDatabase();

            // Require EULA
            boolean neededEula = mNeedsEULA;
            if (mNeedsEULA) {
                FragmentManager fm = getSupportFragmentManager();
                EulaFragment eula = (EulaFragment) fm.findFragmentByTag("eula");
                if (eula == null) {
                    eula = new EulaFragment(true);
                    eula.show(fm, "eula");
                }
                eula.addCallback(mEulaCallback);
            }

            while (mNeedsEULA) {
                publishProgress("Laweyering up...");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            }

            if (neededEula) {
                // wait for first message to become available
                mNeedsIntro = true;
                publishProgress("Preparing greeting...");

                Uri firstPost = MusubiService.WIZARD_READY;
                ContentObserver intro = new ContentObserver(new Handler(getMainLooper())) {
                    @Override
                    public void onChange(boolean selfChange) {
                        mNeedsIntro = false;
                        getContentResolver().unregisterContentObserver(this);
                    }
                };
                getContentResolver().registerContentObserver(firstPost, false, intro);
                startService(new Intent(BootstrapActivity.this, MusubiService.class));
                while (mNeedsIntro) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                    }
                }
            }

            return RESULT_INITIALIZED_OK;
        }

        @Override
        protected void onPostExecute(Integer result) {
            if (result == RESULT_INITIALIZED_OK) {
                if (mPreviousVersion != mInstalledVersion) {
                    mPreferences.edit().putInt(PREF_INSTALLED_VERSION, mInstalledVersion).commit();
                }
                sBootstrapped = true;
                new CleanUpMusubiTask().execute();

                Intent original = (Intent) getIntent().getParcelableExtra(EXTRA_ORIGINAL_INTENT);
                original.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                startActivity(original);
                finish();
            } else if (result == RESULT_NEEDS_UPGRADE) {
                FragmentManager fm = getSupportFragmentManager();
                UpgradeDialog upgrade = (UpgradeDialog) fm.findFragmentByTag("upgrade");
                if (upgrade == null) {
                    upgrade = UpgradeDialog.newInstance();
                    upgrade.show(fm, "upgrade");
                }
            }
        }

        @Override
        protected void onProgressUpdate(String... values) {
            TextView loading_text_view = (TextView) findViewById(mobisocial.musubi.R.id.loading_text);
            loading_text_view.setText(values[0]);
        }
    }

    class CleanUpMusubiTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            long startTime = System.currentTimeMillis();
            SQLiteOpenHelper db = App.getDatabaseSource(getApplicationContext());
            new EncodedMessageManager(db).deleteProcessedOldItems(7);
            long totalTime = System.currentTimeMillis() - startTime;
            Log.d(TAG, String.format("++++ Object truncation took %d ms.", totalTime));
            return null;
        }
    }

    static class UpgradeDialog extends DialogFragment {
        public static UpgradeDialog newInstance() {
            Bundle b = new Bundle();
            UpgradeDialog d = new UpgradeDialog();
            d.setArguments(b);
            return d;
        }

        public UpgradeDialog() {
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return new AlertDialog.Builder(getActivity()).setTitle("Upgrade Required")
                    .setMessage("An important update is required to use Musubi. Upgrade now?")
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent market = Musubi.getMarketIntent();
                            getActivity().startActivity(market);
                            getActivity().finish();
                        }
                    }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            getActivity().finish();
                        }
                    }).create();
        }
    }
}