switch(intensity) {
                case ACTIVE:
                    viewHolder.tv0.setText("");
                    break;
                case COOLDOWN:
                case RESTING:
                case RECOVERY:
                case WARMUP:
                case REPEAT:
                    viewHolder.tv0.setText(String.format(Locale.getDefault(), "(%s)", getResources().getString(intensity.getTextId())));
                default:
                    break;
            }

package org.runnerup.view;

import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import org.runnerup.BuildConfig;
import org.runnerup.R;
import org.runnerup.common.util.Constants;
import org.runnerup.content.ActivityProvider;
import org.runnerup.db.ActivityCleaner;
import org.runnerup.db.DBHelper;
import org.runnerup.db.PathSimplifier;
import org.runnerup.export.SyncManager;
import org.runnerup.export.Synchronizer;
import org.runnerup.export.Synchronizer.Feature;
import org.runnerup.util.Bitfield;
import org.runnerup.util.FileNameHelper;
import org.runnerup.util.Formatter;
import org.runnerup.util.GraphWrapper;
import org.runnerup.util.MapWrapper;
import org.runnerup.widget.TitleSpinner;
import org.runnerup.widget.WidgetUtil;
import org.runnerup.workout.Intensity;
import org.runnerup.workout.Sport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import static org.runnerup.content.ActivityProvider.GPX_MIME;
import static org.runnerup.content.ActivityProvider.TCX_MIME;

public class DetailActivity extends AppCompatActivity implements Constants {

    private long mID = 0;

    private SQLiteDatabase mDB = null;

    private final HashSet<String> pendingSynchronizers = new HashSet<>();

    private final HashSet<String> alreadySynched = new HashSet<>();

    private final Map<String, String> synchedExternalId = new HashMap<>();

    private boolean lapHrPresent = false;

    private ContentValues[] laps = null;

    private final ArrayList<ContentValues> reports = new ArrayList<>();

    private final ArrayList<BaseAdapter> adapters = new ArrayList<>(2);

    private int mode;

    private final static int MODE_SAVE = 0;

    private final static int MODE_DETAILS = 1;

    private boolean edit = false;

    private boolean uploading = false;

    private Button saveButton = null;

    private Button uploadButton = null;

    private Button resumeButton = null;

    private TextView activityTime = null;

    private TextView activityPace = null;

    private View activityPaceSeparator = null;

    private TextView activityDistance = null;

    private TitleSpinner sport = null;

    private EditText notes = null;

    private MapWrapper mapWrapper = null;

    private SyncManager syncManager = null;

    private Formatter formatter = null;

    // activity start time in unix timestamp
    private long mStartTime = 0;

    /**
     * Called when the activity is first created.
     */
    @SuppressLint("ObsoleteSdkInt")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (BuildConfig.MAPBOX_ENABLED > 0) {
            MapWrapper.start(this);
            setContentView(R.layout.detail);
        } else {
            // No MapBox key, load without mapview, do not set mapWrapper
            setContentView(R.layout.detail_nomap);
        }
        Toolbar toolbar = findViewById(R.id.actionbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        WidgetUtil.addLegacyOverflowButton(getWindow());
        Intent intent = getIntent();
        mID = intent.getLongExtra("ID", -1);
        String mode = intent.getStringExtra("mode");
        mDB = DBHelper.getReadableDatabase(this);
        syncManager = new SyncManager(this);
        formatter = new Formatter(this);
        if (mode.contentEquals("save")) {
            this.mode = MODE_SAVE;
        } else if (mode.contentEquals("details")) {
            this.mode = MODE_DETAILS;
        } else {
            if (BuildConfig.DEBUG) {
                throw new AssertionError();
            }
        }
        saveButton = findViewById(R.id.save_button);
        Button discardButton = findViewById(R.id.discard_button);
        resumeButton = findViewById(R.id.resume_button);
        uploadButton = findViewById(R.id.upload_button);
        activityTime = findViewById(R.id.activity_time);
        activityDistance = findViewById(R.id.activity_distance);
        activityPace = findViewById(R.id.activity_pace);
        activityPaceSeparator = findViewById(R.id.activity_pace_separator);
        sport = findViewById(R.id.summary_sport);
        notes = findViewById(R.id.notes_text);
        if (BuildConfig.MAPBOX_ENABLED > 0) {
            Object mapView = findViewById(R.id.mapview);
            mapWrapper = new MapWrapper(this, mDB, mID, formatter, mapView);
            mapWrapper.onCreate(savedInstanceState);
        }
        saveButton.setOnClickListener(saveButtonClick);
        uploadButton.setOnClickListener(uploadButtonClick);
        if (this.mode == MODE_SAVE) {
            resumeButton.setOnClickListener(resumeButtonClick);
            discardButton.setOnClickListener(discardButtonClick);
            setEdit(true);
        } else if (this.mode == MODE_DETAILS) {
            resumeButton.setVisibility(View.GONE);
            discardButton.setVisibility(View.GONE);
            setEdit(false);
        }
        fillHeaderData();
        requery();
        uploadButton.setVisibility(View.GONE);
        TabHost th = findViewById(R.id.tabhost);
        th.setup();
        TabSpec tabSpec = th.newTabSpec("notes");
        tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Notes)));
        tabSpec.setContent(R.id.tab_main);
        th.addTab(tabSpec);
        tabSpec = th.newTabSpec("laps");
        tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Laps)));
        tabSpec.setContent(R.id.tab_lap);
        th.addTab(tabSpec);
        if (BuildConfig.MAPBOX_ENABLED > 0) {
            tabSpec = th.newTabSpec("map");
            tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Map)));
            tabSpec.setContent(R.id.tab_map);
            th.addTab(tabSpec);
        }
        tabSpec = th.newTabSpec("graph");
        tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Graph)));
        tabSpec.setContent(R.id.tab_graph);
        th.addTab(tabSpec);
        LinearLayout graphTab = findViewById(R.id.tab_graph);
        LinearLayout hrzonesBarLayout = findViewById(R.id.hrzonesBarLayout);
        GraphWrapper graphWrapper = new GraphWrapper(this, graphTab, hrzonesBarLayout, formatter, mDB, mID);
        tabSpec = th.newTabSpec("share");
        tabSpec.setIndicator(WidgetUtil.createHoloTabIndicator(this, getString(R.string.Upload)));
        tabSpec.setContent(R.id.tab_upload);
        th.addTab(tabSpec);
        {
            ListView lv = findViewById(R.id.laplist);
            LapListAdapter adapter = new LapListAdapter();
            adapters.add(adapter);
            lv.setAdapter(adapter);
        }
        {
            ListView lv = findViewById(R.id.report_list);
            ReportListAdapter adapter = new ReportListAdapter();
            adapters.add(adapter);
            lv.setAdapter(adapter);
        }
    }

    private void setEdit(boolean value) {
        edit = value;
        if (value)
            saveButton.setVisibility(View.VISIBLE);
        else
            saveButton.setVisibility(View.GONE);
        WidgetUtil.setEditable(notes, value);
        sport.setEnabled(value);
    }

    private void setUploadVisibility() {
        boolean enabled = !pendingSynchronizers.isEmpty();
        if (enabled) {
            uploadButton.setVisibility(View.VISIBLE);
        } else {
            uploadButton.setVisibility(View.GONE);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.detail_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            return super.onOptionsItemSelected(item);
        } else if (id == R.id.menu_delete_activity) {
            deleteButtonClick.onClick(null);
        } else if (id == R.id.menu_edit_activity) {
            if (!edit) {
                setEdit(true);
                notes.requestFocus();
                requery();
            }
        } else if (id == R.id.menu_recompute_activity) {
            new AlertDialog.Builder(this).setTitle(R.string.Recompute_activity).setMessage(R.string.Are_you_sure).setPositiveButton(R.string.Yes, (dialog, which) -> {
                dialog.dismiss();
                new ActivityCleaner().recompute(mDB, mID);
                requery();
                fillHeaderData();
                finish();
            }).setNegativeButton(R.string.No, (dialog, which) -> dialog.dismiss()).show();
        } else if (id == R.id.menu_simplify_path) {
            new AlertDialog.Builder(this).setTitle(R.string.path_simplification_menu).setMessage(R.string.Are_you_sure).setPositiveButton(R.string.Yes, (dialog, which) -> {
                dialog.dismiss();
                PathSimplifier simplifier = new PathSimplifier(this);
                ArrayList<String> ids = simplifier.getNoisyLocationIDsAsStrings(mDB, mID);
                ActivityCleaner.deleteLocations(mDB, ids);
                new ActivityCleaner().recompute(mDB, mID);
                requery();
                fillHeaderData();
                finish();
            }).setNegativeButton(R.string.No, (dialog, which) -> dialog.dismiss()).show();
        } else if (id == R.id.menu_share_activity) {
            shareActivity();
        }
        return true;
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mapWrapper != null) {
            mapWrapper.onResume();
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        if (mapWrapper != null) {
            mapWrapper.onStart();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mapWrapper != null) {
            mapWrapper.onStop();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mapWrapper != null) {
            mapWrapper.onPause();
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mapWrapper != null) {
            mapWrapper.onSaveInstanceState(outState);
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        if (mapWrapper != null) {
            mapWrapper.onLowMemory();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        DBHelper.closeDB(mDB);
        syncManager.close();
        if (mapWrapper != null) {
            mapWrapper.onDestroy();
        }
    }

    private void requery() {
        {
            String[] from = new String[] { "_id", DB.LAP.LAP, DB.LAP.INTENSITY, DB.LAP.TIME, DB.LAP.DISTANCE, DB.LAP.PLANNED_TIME, DB.LAP.PLANNED_DISTANCE, DB.LAP.PLANNED_PACE, DB.LAP.AVG_HR };
            Cursor c = mDB.query(DB.LAP.TABLE, from, DB.LAP.ACTIVITY + " == " + mID, null, null, null, "_id", null);
            laps = DBHelper.toArray(c);
            c.close();
            lapHrPresent = false;
            for (ContentValues v : laps) {
                if (v.containsKey(DB.LAP.AVG_HR) && v.getAsInteger(DB.LAP.AVG_HR) > 0) {
                    lapHrPresent = true;
                    break;
                }
            }
        }
        {
            String sql = "SELECT DISTINCT " + "  acc._id, " + ("  acc." + DB.ACCOUNT.NAME + ", ") + ("  acc." + DB.ACCOUNT.FLAGS + ", ") + ("  acc." + DB.ACCOUNT.AUTH_CONFIG + ", ") + ("  acc." + DB.ACCOUNT.FORMAT + ", ") + ("  rep._id as repid, ") + ("  rep." + DB.EXPORT.ACCOUNT + ", ") + ("  rep." + DB.EXPORT.ACTIVITY + ", ") + ("  rep." + DB.EXPORT.EXTERNAL_ID + ", ") + ("  rep." + DB.EXPORT.STATUS) + (" FROM " + DB.ACCOUNT.TABLE + " acc ") + (" LEFT OUTER JOIN " + DB.EXPORT.TABLE + " rep ") + (" ON ( acc._id = rep." + DB.EXPORT.ACCOUNT) + ("     AND rep." + DB.EXPORT.ACTIVITY + " = " + mID + " )");
            Cursor c = mDB.rawQuery(sql, null);
            alreadySynched.clear();
            synchedExternalId.clear();
            pendingSynchronizers.clear();
            reports.clear();
            if (c.moveToFirst()) {
                do {
                    ContentValues tmp = DBHelper.get(c);
                    Synchronizer synchronizer = syncManager.add(tmp);
                    // Note: Show all configured accounts (also those are not currently enabled)
                    // Uploaded but removed accounts are not displayed
                    if (synchronizer == null || !synchronizer.checkSupport(Feature.UPLOAD) || !synchronizer.isConfigured()) {
                        continue;
                    }
                    String name = tmp.getAsString(DB.ACCOUNT.NAME);
                    reports.add(tmp);
                    if (tmp.containsKey("repid")) {
                        alreadySynched.add(name);
                        if (tmp.containsKey(DB.EXPORT.STATUS) && tmp.getAsInteger(DB.EXPORT.STATUS) == Synchronizer.ExternalIdStatus.getInt(Synchronizer.ExternalIdStatus.OK)) {
                            String url = syncManager.getSynchronizerByName(name).getActivityUrl(synchedExternalId.get(name));
                            if (url != null) {
                                synchedExternalId.put(name, tmp.getAsString(DB.EXPORT.EXTERNAL_ID));
                            }
                        }
                    } else if (tmp.containsKey(DB.ACCOUNT.FLAGS) && Bitfield.test(tmp.getAsLong(DB.ACCOUNT.FLAGS), DB.ACCOUNT.FLAG_UPLOAD)) {
                        pendingSynchronizers.add(name);
                    }
                } while (c.moveToNext());
            }
            c.close();
        }
        if (mode == MODE_DETAILS) {
            setUploadVisibility();
        }
        for (BaseAdapter a : adapters) {
            a.notifyDataSetChanged();
        }
    }

    private void fillHeaderData() {
        // Fields from the database (projection)
        // Must include the _id column for the adapter to work
        String[] from = new String[] { DB.ACTIVITY.START_TIME, DB.ACTIVITY.DISTANCE, DB.ACTIVITY.TIME, DB.ACTIVITY.COMMENT, DB.ACTIVITY.SPORT };
        Cursor c = mDB.query(DB.ACTIVITY.TABLE, from, "_id == " + mID, null, null, null, null, null);
        c.moveToFirst();
        ContentValues tmp = DBHelper.get(c);
        c.close();
        if (tmp.containsKey(DB.ACTIVITY.START_TIME)) {
            long st = tmp.getAsLong(DB.ACTIVITY.START_TIME);
            mStartTime = st;
            setTitle(formatter.formatDateTime(st));
        }
        double d = 0;
        if (tmp.containsKey(DB.ACTIVITY.DISTANCE)) {
            d = tmp.getAsDouble(DB.ACTIVITY.DISTANCE);
            activityDistance.setText(formatter.formatDistance(Formatter.Format.TXT_SHORT, (long) d));
        } else {
            activityDistance.setText("");
        }
        long t = 0;
        if (tmp.containsKey(DB.ACTIVITY.TIME)) {
            t = tmp.getAsInteger(DB.ACTIVITY.TIME);
            activityTime.setText(formatter.formatElapsedTime(Formatter.Format.TXT_SHORT, t));
        } else {
            activityTime.setText("");
        }
        if (t != 0) {
            activityPace.setVisibility(View.VISIBLE);
            activityPaceSeparator.setVisibility(View.VISIBLE);
            activityPace.setText(formatter.formatVelocityByPreferredUnit(Formatter.Format.TXT_LONG, d / t));
        } else {
            activityPace.setVisibility(View.GONE);
            activityPaceSeparator.setVisibility(View.GONE);
        }
        if (tmp.containsKey(DB.ACTIVITY.COMMENT)) {
            notes.setText(tmp.getAsString(DB.ACTIVITY.COMMENT));
        }
        if (tmp.containsKey(DB.ACTIVITY.SPORT)) {
            sport.setValue(tmp.getAsInteger(DB.ACTIVITY.SPORT));
        }
    }

    private class ViewHolderLapList {

        private TextView tv0;

        private TextView tv1;

        private TextView tv2;

        private TextView tv3;

        private TextView tv4;

        private TextView tvHr;
    }

    private class LapListAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return laps.length;
        }

        @Override
        public Object getItem(int position) {
            return laps[position];
        }

        @Override
        public long getItemId(int position) {
            return laps[position].getAsLong("_id");
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = convertView;
            ViewHolderLapList viewHolder;
            if (view == null) {
                viewHolder = new ViewHolderLapList();
                LayoutInflater inflater = LayoutInflater.from(DetailActivity.this);
                view = inflater.inflate(R.layout.laplist_row, parent, false);
                viewHolder.tv0 = view.findViewById(R.id.lap_list_type);
                viewHolder.tv1 = view.findViewById(R.id.lap_list_id);
                viewHolder.tv2 = view.findViewById(R.id.lap_list_distance);
                viewHolder.tv3 = view.findViewById(R.id.lap_list_time);
                viewHolder.tv4 = view.findViewById(R.id.lap_list_pace);
                viewHolder.tvHr = view.findViewById(R.id.lap_list_hr);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolderLapList) view.getTag();
            }
            int i = laps[position].getAsInteger(DB.LAP.INTENSITY);
            Intensity intensity = Intensity.values()[i];

            viewHolder.tv1.setText(laps[position].getAsString("_id"));
            double d = laps[position].containsKey(DB.LAP.DISTANCE) ? laps[position].getAsDouble(DB.LAP.DISTANCE) : 0;
            viewHolder.tv2.setText(formatter.formatDistance(Formatter.Format.TXT_LONG, (long) d));
            long t = laps[position].containsKey(DB.LAP.TIME) ? laps[position].getAsLong(DB.LAP.TIME) : 0;
            viewHolder.tv3.setText(formatter.formatElapsedTime(Formatter.Format.TXT_SHORT, t));
            if (t != 0) {
                viewHolder.tv4.setText(formatter.formatVelocityByPreferredUnit(Formatter.Format.TXT_LONG, d / t));
            } else {
                viewHolder.tv4.setText("");
            }
            int hr = laps[position].containsKey(DB.LAP.AVG_HR) ? laps[position].getAsInteger(DB.LAP.AVG_HR) : 0;
            if (hr > 0) {
                viewHolder.tvHr.setVisibility(View.VISIBLE);
                // Use CUE_LONG instead of TXT_LONG to include unit
                viewHolder.tvHr.setText(formatter.formatHeartRate(Formatter.Format.CUE_LONG, hr));
            } else if (lapHrPresent) {
                viewHolder.tvHr.setVisibility(View.INVISIBLE);
            } else {
                viewHolder.tvHr.setVisibility(View.GONE);
            }
            return view;
        }
    }

    private class ReportListAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return reports.size() + 1;
        }

        @Override
        public Object getItem(int position) {
            if (position < reports.size())
                return reports.get(position);
            return this;
        }

        @Override
        public long getItemId(int position) {
            if (position < reports.size())
                return reports.get(position).getAsLong("_id");
            return 0;
        }

        private class ViewHolderDetailActivity {

            private TextView tv0;

            private CheckBox cb;

            private TextView tv1;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (position == reports.size()) {
                Button b = new Button(DetailActivity.this);
                b.setText(R.string.Configure_accounts);
                b.setBackgroundResource(R.drawable.btn_blue);
                b.setTextColor(AppCompatResources.getColorStateList(DetailActivity.this, R.color.btn_text_color));
                b.setOnClickListener(v -> {
                    Intent i = new Intent(DetailActivity.this, AccountListActivity.class);
                    DetailActivity.this.startActivityForResult(i, SyncManager.EDIT_ACCOUNT_REQUEST);
                });
                return b;
            }
            View view = convertView;
            ViewHolderDetailActivity viewHolder;
            // Note: Special ViewHolder support as the Configure button is not in the view
            if (view == null || view.getTag() == null) {
                viewHolder = new ViewHolderDetailActivity();
                LayoutInflater inflater = LayoutInflater.from(DetailActivity.this);
                view = inflater.inflate(R.layout.reportlist_row, parent, false);
                viewHolder.tv0 = view.findViewById(R.id.reportlist_account_id);
                viewHolder.cb = view.findViewById(R.id.reportlist_sent);
                viewHolder.tv1 = view.findViewById(R.id.reportlist_account_name);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolderDetailActivity) view.getTag();
            }
            ContentValues tmp = reports.get(position);
            String name = tmp.getAsString(DB.ACCOUNT.NAME);
            viewHolder.cb.setOnCheckedChangeListener(null);
            viewHolder.cb.setChecked(false);
            viewHolder.cb.setEnabled(false);
            viewHolder.cb.setTag(name);
            viewHolder.tv1.setTag(name);
            viewHolder.tv1.setTextColor(viewHolder.cb.getTextColors());
            if (alreadySynched.contains(name)) {
                viewHolder.cb.setChecked(true);
                if (synchedExternalId.containsKey(name)) {
                    // Indicate Clickable label
                    viewHolder.tv1.setTextColor(Color.BLUE);
                }
                viewHolder.cb.setText(R.string.Uploaded);
                viewHolder.cb.setOnLongClickListener(clearUploadClick);
            } else {
                if (pendingSynchronizers.contains(name)) {
                    viewHolder.cb.setChecked(true);
                } else {
                    viewHolder.cb.setChecked(false);
                }
                viewHolder.cb.setText(R.string.upload);
                viewHolder.cb.setOnLongClickListener(null);
            }
            if (mode == MODE_DETAILS) {
                viewHolder.cb.setEnabled(true);
            } else if (mode == MODE_SAVE) {
                viewHolder.cb.setEnabled(true);
            }
            viewHolder.cb.setOnCheckedChangeListener(onSendChecked);
            viewHolder.tv0.setText(tmp.getAsString("_id"));
            viewHolder.tv1.setText(name);
            return view;
        }
    }

    private void saveActivity() {
        ContentValues tmp = new ContentValues();
        tmp.put(DB.ACTIVITY.COMMENT, notes.getText().toString());
        tmp.put(DB.ACTIVITY.SPORT, sport.getValueInt());
        String[] whereArgs = { Long.toString(mID) };
        mDB.update(DB.ACTIVITY.TABLE, tmp, "_id = ?", whereArgs);
        // path simplification (reduce resolution of location entries in database)
        try {
            PathSimplifier simplifier = PathSimplifier.getPathSimplifierForSave(this);
            if (simplifier != null) {
                ArrayList<String> ids = simplifier.getNoisyLocationIDsAsStrings(mDB, mID);
                ActivityCleaner.deleteLocations(mDB, ids);
                (new ActivityCleaner()).recompute(mDB, mID);
            }
        } catch (Exception e) {
            Log.e(getClass().getName(), "Failed to simplify path: " + e.getMessage());
        }
    }

    private final OnLongClickListener clearUploadClick = arg0 -> {
        final String name = (String) arg0.getTag();
        new AlertDialog.Builder(DetailActivity.this).setTitle("Clear upload for " + name).setMessage(R.string.Are_you_sure).setPositiveButton(R.string.Yes, (dialog, which) -> {
            dialog.dismiss();
            syncManager.clearUpload(name, mID);
            requery();
        }).setNegativeButton(R.string.No, // Do nothing but close the dialog
        (dialog, which) -> dialog.dismiss()).show();
        return false;
    };

    // Note: onClick set in reportlist_row.xml
    public void onClickAccountName(View arg0) {
        final String name = (String) arg0.getTag();
        if (synchedExternalId.containsKey(name) && !TextUtils.isEmpty(synchedExternalId.get(name))) {
            String url = syncManager.getSynchronizerByName(name).getActivityUrl(synchedExternalId.get(name));
            if (url != null) {
                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                startActivity(browserIntent);
            }
        }
    }

    private final OnClickListener saveButtonClick = new OnClickListener() {

        public void onClick(View v) {
            saveActivity();
            if (mode == MODE_DETAILS) {
                setEdit(false);
                requery();
                return;
            }
            uploading = true;
            syncManager.startUploading((synchronizerName, status) -> {
                uploading = false;
                DetailActivity.this.setResult(RESULT_OK);
                DetailActivity.this.finish();
            }, pendingSynchronizers, mID);
        }
    };

    private final OnClickListener discardButtonClick = v -> {
        new AlertDialog.Builder(DetailActivity.this).setTitle(R.string.Discard).setMessage(R.string.Are_you_sure).setPositiveButton(R.string.Yes, (dialog, which) -> {
            dialog.dismiss();
            DetailActivity.this.setResult(RESULT_CANCELED);
            DetailActivity.this.finish();
        }).setNegativeButton(R.string.No, // Do nothing but close the dialog
        (dialog, which) -> dialog.dismiss()).show();
    };

    @Override
    public void onBackPressed() {
        if (uploading) {
            return;
        }
        if (mode == MODE_SAVE) {
            resumeButtonClick.onClick(resumeButton);
        } else {
            super.onBackPressed();
        }
    }

    private final OnClickListener resumeButtonClick = v -> {
        DetailActivity.this.setResult(RESULT_FIRST_USER);
        DetailActivity.this.finish();
    };

    private final OnClickListener uploadButtonClick = v -> {
        uploading = true;
        syncManager.startUploading((synchronizerName, status) -> {
            uploading = false;
            requery();
        }, pendingSynchronizers, mID);
    };

    private final OnCheckedChangeListener onSendChecked = new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
            final String name = (String) arg0.getTag();
            if (alreadySynched.contains(name)) {
                // Only accept long clicks
                arg0.setChecked(true);
            } else {
                if (arg1) {
                    pendingSynchronizers.add((String) arg0.getTag());
                } else {
                    // noinspection SuspiciousMethodCalls
                    pendingSynchronizers.remove(arg0.getTag());
                }
                if (mode == MODE_DETAILS) {
                    setUploadVisibility();
                }
            }
        }
    };

    private final OnClickListener deleteButtonClick = v -> {
        new AlertDialog.Builder(DetailActivity.this).setTitle(R.string.Delete_activity).setMessage(R.string.Are_you_sure).setPositiveButton(R.string.Yes, (dialog, which) -> {
            DBHelper.deleteActivity(mDB, mID);
            dialog.dismiss();
            DetailActivity.this.setResult(RESULT_OK);
            DetailActivity.this.finish();
        }).setNegativeButton(R.string.No, // Do nothing but close the dialog
        (dialog, which) -> dialog.dismiss()).show();
    };

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == SyncManager.CONFIGURE_REQUEST) {
            syncManager.onActivityResult(requestCode, resultCode, data);
        }
        requery();
    }

    private void shareActivity() {
        final int[] which = { // TODO preselect tcx - choice should be remembered
        1 };
        final CharSequence[] items = { "gpx", "tcx" };
        new AlertDialog.Builder(this).setTitle(getString(R.string.Share_activity)).setPositiveButton(R.string.OK, (dialog, w) -> {
            if (which[0] == -1) {
                dialog.dismiss();
                return;
            }
            final Context context = DetailActivity.this;
            final CharSequence fmt = items[which[0]];
            final Intent intent = new Intent(Intent.ACTION_SEND);
            if (fmt.equals("tcx")) {
                intent.setType(TCX_MIME);
            } else {
                intent.setType(GPX_MIME);
            }
            // Use of content:// (or STREAM?) instead of file:// is not supported in ES and other apps
            // Solid Explorer File Manager works though
            String actType = Sport.textOf(sport.getValueInt());
            Uri uri = Uri.parse("content://" + ActivityProvider.AUTHORITY + "/" + fmt + "/" + mID + "/" + FileNameHelper.getExportFileName(mStartTime, actType) + fmt);
            intent.putExtra(Intent.EXTRA_STREAM, uri);
            context.startActivity(Intent.createChooser(intent, getString(R.string.Share_activity)));
        }).setNegativeButton(R.string.Cancel, (dialog, which1) -> {
            // Do nothing but close the dialog
            dialog.dismiss();
        }).setSingleChoiceItems(items, which[0], (dialog, w) -> which[0] = w).show();
    }
}


switch(requestCode) {
            case REQUEST_FILE:
                addAttachment(data.getData());
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && data.getClipData() != null) {
                    ClipData clipData = data.getClipData();
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        addAttachment(clipData.getItemAt(i).getUri());
                    }
                }
                break;
        }

package i2p.bote.android;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.tokenautocomplete.FilteredArrayAdapter;
import net.i2p.data.DataFormatException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import i2p.bote.I2PBote;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.android.util.Person;
import i2p.bote.android.widget.ContactsCompletionView;
import i2p.bote.email.Attachment;
import i2p.bote.email.Email;
import i2p.bote.email.EmailIdentity;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.packet.dht.Contact;

public class NewEmailFragment extends Fragment {

    private Callbacks mCallbacks = sDummyCallbacks;

    public interface Callbacks {

        public void onTaskFinished();

        public void onBackPressAllowed();
    }

    private static Callbacks sDummyCallbacks = new Callbacks() {

        public void onTaskFinished() {
        }

        public void onBackPressAllowed() {
        }
    };

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof Callbacks))
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = sDummyCallbacks;
    }

    public static final String QUOTE_MSG_FOLDER = "sender";

    public static final String QUOTE_MSG_ID = "recipient";

    public static enum QuoteMsgType {

        REPLY, REPLY_ALL, FORWARD
    }

    public static final String QUOTE_MSG_TYPE = "type";

    private static final long MAX_RECOMMENDED_ATTACHMENT_SIZE = 1048576;

    private static final int REQUEST_FILE = 1;

    private String mSenderKey;

    Spinner mSpinner;

    int mDefaultPos;

    ArrayAdapter<Person> mAdapter;

    ImageView mMore;

    ContactsCompletionView mTo;

    ContactsCompletionView mCc;

    ContactsCompletionView mBcc;

    EditText mSubject;

    EditText mContent;

    LinearLayout mAttachments;

    private long mTotalAttachmentSize;

    private View mAttachmentSizeWarning;

    boolean mMoreVisible;

    boolean mDirty;

    public static NewEmailFragment newInstance(String quoteMsgFolder, String quoteMsgId, QuoteMsgType quoteMsgType) {
        NewEmailFragment f = new NewEmailFragment();
        Bundle args = new Bundle();
        args.putString(QUOTE_MSG_FOLDER, quoteMsgFolder);
        args.putString(QUOTE_MSG_ID, quoteMsgId);
        args.putSerializable(QUOTE_MSG_TYPE, quoteMsgType);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_new_email, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mSpinner = (Spinner) view.findViewById(R.id.sender_spinner);
        mMore = (ImageView) view.findViewById(R.id.more);
        mTo = (ContactsCompletionView) view.findViewById(R.id.to);
        mCc = (ContactsCompletionView) view.findViewById(R.id.cc);
        mBcc = (ContactsCompletionView) view.findViewById(R.id.bcc);
        mSubject = (EditText) view.findViewById(R.id.subject);
        mContent = (EditText) view.findViewById(R.id.message);
        mAttachments = (LinearLayout) view.findViewById(R.id.attachments);
        String quoteMsgFolder = getArguments().getString(QUOTE_MSG_FOLDER);
        String quoteMsgId = getArguments().getString(QUOTE_MSG_ID);
        QuoteMsgType quoteMsgType = (QuoteMsgType) getArguments().getSerializable(QUOTE_MSG_TYPE);
        boolean hide = I2PBote.getInstance().getConfiguration().getHideLocale();
        List<Person> toRecipients = new ArrayList<Person>();
        List<Person> ccRecipients = new ArrayList<Person>();
        String origSubject = null;
        String origContent = null;
        String origFrom = null;
        try {
            Email origEmail = BoteHelper.getEmail(quoteMsgFolder, quoteMsgId);
            if (origEmail != null) {
                mSenderKey = BoteHelper.extractEmailDestination(BoteHelper.getOneLocalRecipient(origEmail).toString());
                if (quoteMsgType == QuoteMsgType.REPLY) {
                    String recipient = BoteHelper.getNameAndDestination(origEmail.getReplyAddress(I2PBote.getInstance().getIdentities()));
                    toRecipients.add(extractPerson(recipient));
                } else if (quoteMsgType == QuoteMsgType.REPLY_ALL) {
                    // TODO split between To and Cc
                    // TODO don't include our address
                    // What happens if an email is received by multiple local identities?
                    for (Address address : origEmail.getAllAddresses(true)) {
                        Person person = extractPerson(address.toString());
                        if (person != null)
                            toRecipients.add(person);
                    }
                }
                origSubject = origEmail.getSubject();
                origContent = origEmail.getText();
                origFrom = BoteHelper.getShortSenderName(origEmail.getOneFromAddress(), 50);
            }
        } catch (PasswordException e) {
            // Should not happen, we cannot get to this page without authenticating
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (MessagingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // Set up identities spinner
        IdentityAdapter identities = new IdentityAdapter(getActivity());
        mSpinner.setAdapter(identities);
        mSpinner.setSelection(mDefaultPos);
        // Set up Cc/Bcc button
        mMore.setImageDrawable(new IconicsDrawable(getActivity(), GoogleMaterial.Icon.gmd_unfold_more).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(3));
        mMore.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                mCc.setVisibility(mMoreVisible ? View.GONE : View.VISIBLE);
                mBcc.setVisibility(mMoreVisible ? View.GONE : View.VISIBLE);
                mMore.setImageDrawable(new IconicsDrawable(getActivity(), mMoreVisible ? GoogleMaterial.Icon.gmd_unfold_more : GoogleMaterial.Icon.gmd_unfold_less).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(mMoreVisible ? 3 : 4));
                mMoreVisible = !mMoreVisible;
            }
        });
        // Set up contacts auto-complete
        List<Person> contacts = new ArrayList<Person>();
        try {
            for (Contact contact : I2PBote.getInstance().getAddressBook().getAll()) {
                contacts.add(new Person(contact.getName(), contact.getBase64Dest(), BoteHelper.decodePicture(contact.getPictureBase64())));
            }
        } catch (PasswordException e) {
            // TODO handle
            e.printStackTrace();
        }
        mAdapter = new FilteredArrayAdapter<Person>(getActivity(), android.R.layout.simple_list_item_1, contacts) {

            @Override
            protected boolean keepObject(Person obj, String mask) {
                mask = mask.toLowerCase(Locale.US);
                return obj.getName().toLowerCase(Locale.US).startsWith(mask) || obj.getAddress().toLowerCase(Locale.US).startsWith(mask);
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View v;
                if (convertView == null)
                    v = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.listitem_contact, parent, false);
                else
                    v = convertView;
                setViewContent(v, position);
                return v;
            }

            private void setViewContent(View v, int position) {
                Person person = getItem(position);
                ((TextView) v.findViewById(R.id.contact_name)).setText(person.getName());
                ImageView picView = (ImageView) v.findViewById(R.id.contact_picture);
                Bitmap picture = person.getPicture();
                if (picture == null) {
                    ViewGroup.LayoutParams lp = picView.getLayoutParams();
                    picture = BoteHelper.getIdenticonForAddress(person.getAddress(), lp.width, lp.height);
                }
                picView.setImageBitmap(picture);
            }
        };
        mTo.setAdapter(mAdapter);
        mCc.setAdapter(mAdapter);
        mBcc.setAdapter(mAdapter);
        for (Person recipient : toRecipients) {
            mTo.addObject(recipient);
        }
        for (Person recipient : ccRecipients) {
            mCc.addObject(recipient);
        }
        if (origSubject != null) {
            String subjectPrefix;
            if (quoteMsgType == QuoteMsgType.FORWARD) {
                subjectPrefix = getResources().getString(hide ? R.string.subject_prefix_fwd_hide : R.string.subject_prefix_fwd);
            } else {
                subjectPrefix = getResources().getString(hide ? R.string.response_prefix_re_hide : R.string.response_prefix_re);
            }
            if (!origSubject.startsWith(subjectPrefix))
                origSubject = subjectPrefix + " " + origSubject;
            mSubject.setText(origSubject);
        }
        if (origContent != null) {
            StringBuilder quotation = new StringBuilder();
            quotation.append("\n\n");
            quotation.append(getResources().getString(hide ? R.string.response_quote_wrote_hide : R.string.response_quote_wrote, origFrom));
            String[] lines = origContent.split("\r?\n|\r");
            for (String line : lines) quotation = quotation.append("\n> ").append(line);
            mContent.setText(quotation);
        }
        if (savedInstanceState == null) {
            mTo.setPrefix(getResources().getString(R.string.email_to) + " ");
            mCc.setPrefix(getResources().getString(R.string.email_cc) + " ");
            mBcc.setPrefix(getResources().getString(R.string.email_bcc) + " ");
        }
        TextWatcher dirtyWatcher = new TextWatcher() {

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mDirty = true;
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        };
        mSubject.addTextChangedListener(dirtyWatcher);
        mContent.addTextChangedListener(dirtyWatcher);
    }

    private Person extractPerson(String recipient) {
        if (recipient.equals("Anonymous"))
            return null;
        String recipientName = BoteHelper.extractName(recipient);
        try {
            recipientName = BoteHelper.getName(recipient);
        } catch (PasswordException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        String recipientAddr = BoteHelper.extractEmailDestination(recipient);
        if (recipientAddr == null) {
            // Assume external address
            recipientAddr = recipient;
            if (recipientName.isEmpty())
                recipientName = recipientAddr;
            return new Person(recipientName, recipientAddr, null, true);
        } else {
            if (// Dest with no name
            recipientName.isEmpty())
                recipientName = recipientAddr.substring(0, 5);
            Bitmap recipientPic = null;
            try {
                recipientPic = BoteHelper.getPictureForDestination(recipientAddr);
            } catch (PasswordException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
            return new Person(recipientName, recipientAddr, recipientPic);
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.new_email, menu);
        menu.findItem(R.id.action_attach_file).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_attach_file));
        menu.findItem(R.id.action_send_email).setIcon(BoteHelper.getMenuIcon(getActivity(), GoogleMaterial.Icon.gmd_send));
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch(item.getItemId()) {
            case R.id.action_attach_file:
                requestFile();
                return true;
            case R.id.action_send_email:
                if (sendEmail())
                    mCallbacks.onTaskFinished();
                return true;
            case android.R.id.home:
                if (mDirty) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                    builder.setTitle(R.string.stop_composing_email).setMessage(R.string.all_changes_will_be_discarded).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
                                mCallbacks.onBackPressAllowed();
                            else
                                getActivity().onNavigateUp();
                        }
                    }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.cancel();
                        }
                    }).show();
                    return true;
                } else
                    return super.onOptionsItemSelected(item);
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void requestFile() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.setType("*/*");
        i.addCategory(Intent.CATEGORY_OPENABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
            i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        startActivityForResult(Intent.createChooser(i, getResources().getString(R.string.select_attachment)), REQUEST_FILE);
    }

    @SuppressLint("NewApi")
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            if (resultCode == Activity.RESULT_CANCELED) {
                System.out.println("Cancelled");
            }
            return;
        }

    }

    private void addAttachment(Uri uri) {
        // Try to create a ContentAttachment using the provided Uri.
        try {
            final ContentAttachment attachment = new ContentAttachment(getActivity(), uri);
            final View v = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, mAttachments, false);
            v.setTag(attachment);
            ((TextView) v.findViewById(R.id.filename)).setText(attachment.getFileName());
            ((TextView) v.findViewById(R.id.size)).setText(attachment.getHumanReadableSize());
            ImageView attachmentAction = (ImageView) v.findViewById(R.id.attachment_action);
            attachmentAction.setImageDrawable(new IconicsDrawable(getActivity(), GoogleMaterial.Icon.gmd_clear).colorRes(R.color.md_grey_600).sizeDp(24).paddingDp(5));
            attachmentAction.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View view) {
                    updateAttachmentSizeCount(attachment.getSize(), false);
                    attachment.clean();
                    mAttachments.removeView(v);
                }
            });
            mAttachments.addView(v);
            updateAttachmentSizeCount(attachment.getSize(), true);
        } catch (IllegalArgumentException iae) {
            Log.e(Constants.ANDROID_LOG_TAG, "Failed to get attachment", iae);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e(Constants.ANDROID_LOG_TAG, "File not found: " + uri);
        }
    }

    private void updateAttachmentSizeCount(long size, boolean increase) {
        if (increase) {
            mTotalAttachmentSize += size;
            if (mTotalAttachmentSize > MAX_RECOMMENDED_ATTACHMENT_SIZE && mAttachmentSizeWarning == null) {
                mAttachmentSizeWarning = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment_warning, mAttachments, false);
                TextView warning = (TextView) mAttachmentSizeWarning.findViewById(R.id.attachment_warning_text);
                warning.setText(getString(R.string.attachment_size_warning, BoteHelper.getHumanReadableSize(getActivity(), MAX_RECOMMENDED_ATTACHMENT_SIZE)));
                mAttachments.addView(mAttachmentSizeWarning, 0);
            }
        } else {
            mTotalAttachmentSize -= size;
            if (mTotalAttachmentSize <= MAX_RECOMMENDED_ATTACHMENT_SIZE && mAttachmentSizeWarning != null) {
                mAttachments.removeView(mAttachmentSizeWarning);
                mAttachmentSizeWarning = null;
            }
        }
    }

    private boolean sendEmail() {
        Email email = new Email(I2PBote.getInstance().getConfiguration().getIncludeSentTime());
        try {
            // Set sender
            EmailIdentity sender = (EmailIdentity) mSpinner.getSelectedItem();
            InternetAddress ia = new InternetAddress(sender == null ? "Anonymous" : BoteHelper.getNameAndDestination(sender.getKey()));
            email.setFrom(ia);
            // We must continue to set "Sender:" even with only one mailbox
            // in "From:", which is against RFC 2822 but required for older
            // Bote versions to see a sender (and validate the signature).
            email.setSender(ia);
            for (Object obj : mTo.getObjects()) {
                Person person = (Person) obj;
                email.addRecipient(Message.RecipientType.TO, new InternetAddress(person.getAddress(), person.getName()));
            }
            if (mMoreVisible) {
                for (Object obj : mCc.getObjects()) {
                    Person person = (Person) obj;
                    email.addRecipient(Message.RecipientType.CC, new InternetAddress(person.getAddress(), person.getName()));
                }
                for (Object obj : mBcc.getObjects()) {
                    Person person = (Person) obj;
                    email.addRecipient(Message.RecipientType.BCC, new InternetAddress(person.getAddress(), person.getName()));
                }
            }
            // Check that we have someone to send to
            Address[] rcpts = email.getAllRecipients();
            if (rcpts == null || rcpts.length == 0) {
                // No recipients
                mTo.setError(getActivity().getString(R.string.add_one_recipient));
                mTo.requestFocus();
                return false;
            } else {
                mTo.setError(null);
            }
            email.setSubject(mSubject.getText().toString(), "UTF-8");
            // Extract the attachments
            List<Attachment> attachments = new ArrayList<Attachment>();
            for (int i = 0; i < mAttachments.getChildCount(); i++) {
                View v = mAttachments.getChildAt(i);
                // Warning views don't have tags set
                if (v.getTag() != null)
                    attachments.add((Attachment) v.getTag());
            }
            // Set the text and add attachments
            email.setContent(mContent.getText().toString(), attachments);
            // Cache the fact that we sent this email
            BoteHelper.setEmailSent(email, true);
            // Send the email
            I2PBote.getInstance().sendEmail(email);
            // Clean up attachments
            for (Attachment attachment : attachments) {
                if (!attachment.clean())
                    Log.e(Constants.ANDROID_LOG_TAG, "Can't clean up attachment: <" + attachment + ">");
            }
            return true;
        } catch (PasswordException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (AddressException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (MessagingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    private class IdentityAdapter extends ArrayAdapter<EmailIdentity> {

        private LayoutInflater mInflater;

        public IdentityAdapter(Context context) {
            super(context, android.R.layout.simple_spinner_item);
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            try {
                Collection<EmailIdentity> identities = I2PBote.getInstance().getIdentities().getAll();
                mDefaultPos = 0;
                String selectedIdentity = getActivity().getSharedPreferences(Constants.SHARED_PREFS, 0).getString(Constants.PREF_SELECTED_IDENTITY, null);
                for (EmailIdentity identity : identities) {
                    add(identity);
                    boolean isDefaultIdentity = selectedIdentity == null ? identity.isDefaultIdentity() : identity.getKey().equals(selectedIdentity);
                    boolean selectByDefault = mSenderKey == null ? isDefaultIdentity : identity.getKey().equals(mSenderKey);
                    if (selectByDefault)
                        mDefaultPos = getPosition(identity);
                }
            } catch (PasswordException e) {
                // TODO Handle
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Handle
                e.printStackTrace();
            } catch (GeneralSecurityException e) {
                // TODO Handle
                e.printStackTrace();
            }
        }

        @Override
        public EmailIdentity getItem(int position) {
            if (position > 0)
                return super.getItem(position - 1);
            else
                return null;
        }

        @Override
        public int getPosition(EmailIdentity item) {
            if (item != null)
                return super.getPosition(item) + 1;
            else
                return 0;
        }

        @Override
        public int getCount() {
            return super.getCount() + 1;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v;
            if (convertView == null)
                v = mInflater.inflate(android.R.layout.simple_spinner_item, parent, false);
            else
                v = convertView;
            setViewText(v, position);
            return v;
        }

        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            View v;
            if (convertView == null)
                v = mInflater.inflate(android.R.layout.simple_spinner_dropdown_item, parent, false);
            else
                v = convertView;
            setViewText(v, position);
            return v;
        }

        private void setViewText(View v, int position) {
            TextView text = (TextView) v.findViewById(android.R.id.text1);
            EmailIdentity identity = getItem(position);
            if (identity == null)
                text.setText("Anonymous");
            else
                text.setText(identity.getPublicName());
        }
    }

    public void onBackPressed() {
        if (mDirty) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(R.string.stop_composing_email).setMessage(R.string.all_changes_will_be_discarded).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    dialogInterface.dismiss();
                    mCallbacks.onBackPressAllowed();
                }
            }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    dialogInterface.cancel();
                }
            }).show();
        } else
            mCallbacks.onBackPressAllowed();
    }
}


switch(getter.getType()) {
            case COLLECTION:
                try {
                    return getter.getCollectionValue(graph).get((Integer) indexesKeys.pop());
                } catch (IndexOutOfBoundsException e) {
                    return null;
                }
            case MAP:
                return getter.getMapValue(graph).get(indexesKeys.pop());
            case REFERENCE:
            default:
                return getter.getReferenceValue(graph);
        }

package org.phoenixctms.ctsms.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import org.phoenixctms.ctsms.util.Accessor.AccessorType;

public abstract class GraphEnumerator {

    protected static void appendProperties(Class clazz, ArrayList properties, Class graph, ArrayList<Accessor> getterChain, Object passThrough, int depth, MethodTransfilter transfilter, Comparator compareCollectionValues, boolean omitFields, HashMap<Class, HashSet<String>> excludedFieldMap, boolean enumerateReferences, boolean enumerateCollections, boolean enumerateMaps, String associationPathSeparator) throws Exception {
        if (graph != null && properties != null) {
            Method[] methods = graph.getMethods();
            if (methods != null) {
                ArrayList<Class> deferredReturnTypes = new ArrayList<Class>();
                ArrayList<ArrayList<Accessor>> deferredGetterChains = new ArrayList<ArrayList<Accessor>>();
                for (int i = 0; i < methods.length; i++) {
                    if (transfilter.include(methods[i]) && !transfilter.exclude(methods[i])) {
                        Class[] parameterTypes = methods[i].getParameterTypes();
                        if (parameterTypes != null && parameterTypes.length == 0) {
                            GraphEnumerator property = getInstance(clazz, new Accessor(methods[i], AccessorType.REFERENCE), getterChain, transfilter, associationPathSeparator);
                            if (!property.isFieldExcluded(graph, property.fieldName, excludedFieldMap) && (!omitFields || !property.isFieldOmitted(graph, property.fieldName))) {
                                if (!property.isTerminalType(passThrough)) {
                                    if (Collection.class.isAssignableFrom(property.returnType)) {
                                        if (enumerateCollections && depth > 1) {
                                            Class collectionType = property.lastGetter.getCollectionType();
                                            property.lastGetter.setType(AccessorType.COLLECTION);
                                            property.returnType = collectionType;
                                            if (property.isTerminalType(passThrough)) {
                                                properties.add(property);
                                            } else {
                                                deferredReturnTypes.add(collectionType);
                                                ArrayList<Accessor> collectionElementGetterChain = (ArrayList<Accessor>) getterChain.clone();
                                                collectionElementGetterChain.add(new Accessor(methods[i], AccessorType.COLLECTION, compareCollectionValues));
                                                deferredGetterChains.add(collectionElementGetterChain);
                                            }
                                        }
                                    } else if (Map.class.isAssignableFrom(property.returnType)) {
                                        if (enumerateMaps && depth > 1) {
                                            Class mapValueType = property.lastGetter.getMapValueType();
                                            property.lastGetter.setType(AccessorType.MAP);
                                            property.returnType = mapValueType;
                                            if (property.isTerminalType(passThrough)) {
                                                properties.add(property);
                                            } else {
                                                deferredReturnTypes.add(mapValueType);
                                                ArrayList<Accessor> mapValueElementGetterChain = (ArrayList<Accessor>) getterChain.clone();
                                                mapValueElementGetterChain.add(new Accessor(methods[i], AccessorType.MAP));
                                                deferredGetterChains.add(mapValueElementGetterChain);
                                            }
                                        }
                                    } else {
                                        if (enumerateReferences && depth > 0) {
                                            deferredReturnTypes.add(property.returnType);
                                            ArrayList<Accessor> associationGetterChain = (ArrayList<Accessor>) getterChain.clone();
                                            associationGetterChain.add(new Accessor(methods[i], AccessorType.REFERENCE));
                                            deferredGetterChains.add(associationGetterChain);
                                        }
                                    }
                                } else {
                                    properties.add(property);
                                }
                            }
                        }
                    }
                }
                for (int i = 0; i < deferredReturnTypes.size(); i++) {
                    appendProperties(clazz, properties, deferredReturnTypes.get(i), deferredGetterChains.get(i), passThrough, depth - 1, transfilter, compareCollectionValues, omitFields, excludedFieldMap, enumerateReferences, enumerateCollections, enumerateMaps, associationPathSeparator);
                }
            }
        }
    }

    private static Stack getIndexKeyStack(ArrayList indexesKeys) {
        Stack indexesKeysStack = new Stack();
        if (indexesKeys != null) {
            for (int i = indexesKeys.size() - 1; i >= 0; i--) {
                indexesKeysStack.push(indexesKeys.get(i));
            }
        }
        return indexesKeysStack;
    }

    private static GraphEnumerator getInstance(Class clazz, Accessor lastGetter, ArrayList<Accessor> getterChain, MethodTransfilter transfilter, String associationPathSeparator) throws Exception {
        GraphEnumerator instance = (GraphEnumerator) clazz.newInstance();
        instance.associationPathSeparator = associationPathSeparator;
        instance.transfilter = transfilter;
        instance.lastGetter = lastGetter;
        instance.getterChain = getterChain;
        instance.returnType = lastGetter.getReturnType();
        instance.fieldName = lastGetter.getFieldName(transfilter);
        return instance;
    }

    private static Object getValueOfHelper(Object graph, Accessor getter, Stack indexesKeys) throws Exception {

    }

    // not thread safe because of Accessors using caching!
    private Accessor lastGetter;

    protected ArrayList<Accessor> getterChain;

    protected Class returnType;

    private String fieldName;

    private String associationPathSeparator;

    protected MethodTransfilter transfilter;

    protected final String getAssociationPath(ArrayList indexesKeys) throws Exception {
        Stack indexesKeysStack = getIndexKeyStack(indexesKeys);
        ArrayList<FieldKeyHelper> helpers = new ArrayList<FieldKeyHelper>();
        helpers.add(new FieldKeyHelper());
        if (getterChain != null) {
            Iterator<Accessor> getterChainIt = getterChain.iterator();
            while (getterChainIt.hasNext()) {
                helpers = updateHelpers(getterChainIt.next(), helpers, indexesKeysStack);
            }
        }
        ArrayList<String> result = new ArrayList<String>();
        Iterator<FieldKeyHelper> helpersIt = updateHelpers(lastGetter, helpers, indexesKeysStack).iterator();
        while (helpersIt.hasNext()) {
            result.add(helpersIt.next().toString());
        }
        return result.get(0);
    }

    protected final ArrayList<String> getAssociationPaths(Object rootGraph) throws Exception {
        ArrayList<FieldKeyHelper> helpers = new ArrayList<FieldKeyHelper>();
        helpers.add(new FieldKeyHelper(rootGraph));
        if (getterChain != null) {
            Iterator<Accessor> getterChainIt = getterChain.iterator();
            while (getterChainIt.hasNext()) {
                helpers = updateHelpers(getterChainIt.next(), helpers, null);
            }
        }
        ArrayList<String> result = new ArrayList<String>();
        Iterator<FieldKeyHelper> helpersIt = updateHelpers(lastGetter, helpers, null).iterator();
        while (helpersIt.hasNext()) {
            result.add(helpersIt.next().toString());
        }
        return result;
    }

    protected final ArrayList<ArrayList<Object>> getIndexKeyChains(Object rootGraph) throws Exception {
        ArrayList<FieldKeyHelper> helpers = new ArrayList<FieldKeyHelper>();
        helpers.add(new FieldKeyHelper(rootGraph));
        if (getterChain != null) {
            Iterator<Accessor> getterChainIt = getterChain.iterator();
            while (getterChainIt.hasNext()) {
                helpers = updateHelpers(getterChainIt.next(), helpers, null);
            }
        }
        ArrayList<ArrayList<Object>> result = new ArrayList<ArrayList<Object>>();
        Iterator<FieldKeyHelper> helpersIt = updateHelpers(lastGetter, helpers, null).iterator();
        while (helpersIt.hasNext()) {
            result.add(helpersIt.next().getIndexKeyChain());
        }
        return result;
    }

    protected final Object getValueOf(Object rootGraph, ArrayList indexesKeys) throws Exception {
        return getValueOf(rootGraph, getIndexKeyStack(indexesKeys));
    }

    private final Object getValueOf(Object rootGraph, Stack indexesKeys) throws Exception {
        Object graph = rootGraph;
        if (getterChain != null) {
            Iterator<Accessor> it = getterChain.iterator();
            while (it.hasNext()) {
                if (graph != null) {
                    graph = getValueOfHelper(graph, it.next(), indexesKeys);
                } else {
                    break;
                }
            }
        }
        if (graph != null) {
            return getValueOfHelper(graph, lastGetter, indexesKeys);
        } else {
            return null;
        }
    }

    private boolean isFieldExcluded(Class graph, String field, HashMap<Class, HashSet<String>> excludedFieldMap) {
        if (excludedFieldMap != null) {
            return excludedFieldMap.containsKey(graph) ? excludedFieldMap.get(graph).contains(field) : false;
        }
        return false;
    }

    protected abstract boolean isFieldOmitted(Class graph, String field);

    protected abstract boolean isTerminalType(Object passThrough);

    private final ArrayList<FieldKeyHelper> updateHelpers(Accessor getter, ArrayList<FieldKeyHelper> helpers, Stack indexesKeys) throws Exception {
        Iterator<FieldKeyHelper> helpersIt = helpers.iterator();
        ArrayList<FieldKeyHelper> newHelpers;
        switch(getter.getType()) {
            case COLLECTION:
                newHelpers = new ArrayList<FieldKeyHelper>();
                if (indexesKeys != null) {
                    newHelpers.add(helpersIt.next().append(getter, ((Integer) indexesKeys.pop()).intValue(), transfilter, associationPathSeparator));
                } else {
                    while (helpersIt.hasNext()) {
                        FieldKeyHelper helper = helpersIt.next();
                        for (int i = 0; i < helper.getCollectionSize(getter); i++) {
                            newHelpers.add(helper.append(getter, i, transfilter, associationPathSeparator));
                        }
                    }
                }
                return newHelpers;
            case MAP:
                newHelpers = new ArrayList<FieldKeyHelper>();
                if (indexesKeys != null) {
                    newHelpers.add(helpersIt.next().append(getter, indexesKeys.pop(), transfilter, associationPathSeparator));
                } else {
                    while (helpersIt.hasNext()) {
                        FieldKeyHelper helper = helpersIt.next();
                        Iterator keysIt = helper.getKeys(getter).iterator();
                        while (keysIt.hasNext()) {
                            newHelpers.add(helper.append(getter, keysIt.next(), transfilter, associationPathSeparator));
                        }
                    }
                }
                return newHelpers;
            case REFERENCE:
            default:
                while (helpersIt.hasNext()) {
                    helpersIt.next().append(getter, transfilter, associationPathSeparator);
                }
                return helpers;
        }
    }
}


switch(this) {
            case LT:
                return GTE;
            case GT:
                return LTE;
            case GTE:
                return LT;
            case LTE:
                return GT;
            case EQ:
                return NE;
            case NE:
                return EQ;
            default:
                throw new ConfusedCFRException("Can't invert CompOp " + this);
        }

package org.benf.cfr.reader.bytecode.analysis.parse.expression;

import org.benf.cfr.reader.bytecode.analysis.parse.expression.misc.Precedence;
import org.benf.cfr.reader.bytecode.opcode.JVMInstr;
import org.benf.cfr.reader.util.ConfusedCFRException;

public enum CompOp {

    LT("<", Precedence.REL_CMP_INSTANCEOF),
    GT(">", Precedence.REL_CMP_INSTANCEOF),
    LTE("<=", Precedence.REL_CMP_INSTANCEOF),
    GTE(">=", Precedence.REL_CMP_INSTANCEOF),
    EQ("==", Precedence.REL_EQ),
    NE("!=", Precedence.REL_EQ);

    private final String showAs;

    private final Precedence precedence;

    CompOp(String showAs, Precedence precedence) {
        this.showAs = showAs;
        this.precedence = precedence;
    }

    public String getShowAs() {
        return showAs;
    }

    public Precedence getPrecedence() {
        return precedence;
    }

    public CompOp getInverted() {

    }

    public static CompOp getOpFor(JVMInstr instr) {
        switch(instr) {
            case IF_ICMPEQ:
            case IF_ACMPEQ:
                return EQ;
            case IF_ICMPLT:
                return LT;
            case IF_ICMPGE:
                return GTE;
            case IF_ICMPGT:
                return GT;
            case IF_ICMPNE:
            case IF_ACMPNE:
                return NE;
            case IF_ICMPLE:
                return LTE;
            case IFEQ:
                return EQ;
            case IFNE:
                return NE;
            case IFLE:
                return LTE;
            case IFLT:
                return LT;
            case IFGE:
                return GTE;
            case IFGT:
                return GT;
            default:
                throw new ConfusedCFRException("Don't know comparison op for " + instr);
        }
    }
}


switch(type) {
            case 1:
                convertView = getViewAdd(position, convertView, parent);
                break;
            default:
                convertView = getViewSelectOne(position, convertView, parent);
                break;
        }

package com.youzu.clan.thread.detail.reply;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import com.kit.app.core.task.DoSomeThing;
import com.kit.imagelib.entity.ImageBean;
import com.kit.utils.AppUtils;
import com.kit.widget.listview.HorizontalListView;
import com.youzu.clan.R;
import com.youzu.clan.base.util.LoadImageUtils;
import com.youzu.clan.base.widget.ViewHolder;
import com.youzu.clan.common.images.PicSelectorActivity;
import java.util.ArrayList;

public class QuickReplyItemPicAdapter extends BaseAdapter {

    public static int LIMIT = 9;

    private Fragment fragment;

    private Context context;

    private HorizontalListView horizontalListView;

    ArrayList<ImageBean> itemList = null;

    ImageBean imageBeanAdd = new ImageBean(com.kit.imagelib.R.drawable.ic_image_select_add);

    public QuickReplyItemPicAdapter(Fragment fragment, HorizontalListView horizontalListView, ArrayList<ImageBean> list) {
        this.fragment = fragment;
        this.context = fragment.getActivity();
        this.horizontalListView = horizontalListView;
        setData(list);
    }

    @Override
    public int getCount() {
        return itemList.size();
    }

    public ArrayList<ImageBean> getRealData() {
        ArrayList<ImageBean> tempBeans = new ArrayList<ImageBean>();
        tempBeans.addAll(itemList);
        tempBeans.remove(imageBeanAdd);
        return tempBeans;
    }

    public void setData(ArrayList<ImageBean> bs) {
        if (itemList == null) {
            itemList = new ArrayList<ImageBean>();
        }
        if (bs == null) {
            bs = new ArrayList<ImageBean>();
        }
        itemList = bs;
        itemList.remove(imageBeanAdd);
        if (getRealCount() < LIMIT) {
            Log.e("ImageSelectAdapter", "imageBeanAdd imageBeanAdd imageBeanAdd imageBeanAdd");
            itemList.add(imageBeanAdd);
        }
        AppUtils.delay(500, new DoSomeThing() {

            @Override
            public void execute(Object... object) {
                horizontalListView.setSelection(getCount() - 1);
            }
        });
        notifyDataSetChanged();
    }

    public int getRealCount() {
        return getRealData().size();
    }

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

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

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        if (itemList.get(position).drawableId > 0) {
            return 1;
        } else
            return super.getItemViewType(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int type = getItemViewType(position);

        return convertView;
    }

    private View getViewAdd(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.item_fragment_quick_reply_item_pic, null);
        }
        ImageView imageView = ViewHolder.get(convertView, R.id.pic);
        ImageView ivDel = ViewHolder.get(convertView, R.id.ivDel);
        ivDel.setVisibility(View.GONE);
        imageView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                int remain = LIMIT - getRealCount();
                PicSelectorActivity.selectPic(fragment, remain);
            }
        });
        return convertView;
    }

    private View getViewSelectOne(final int position, View convertView, ViewGroup parent) {
        ImageBean imageBean = (ImageBean) getItem(position);
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.item_fragment_quick_reply_item_pic, null);
        }
        ImageView imageView = ViewHolder.get(convertView, R.id.pic);
        LoadImageUtils.display(context, imageView, imageBean.path);
        ImageView ivDel = ViewHolder.get(convertView, R.id.ivDel);
        ivDel.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                itemList.remove(position);
                setData(itemList);
            }
        });
        return convertView;
    }
}


switch(func.getNodeID()) {
            case GetterID:
                return TRAIT_Getter;
            case SetterID:
                return TRAIT_Setter;
            default:
                return default_kind;
        }

package org.apache.royale.compiler.internal.as.codegen;

import java.util.Collection;
import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
import org.apache.royale.compiler.internal.tree.as.ClassNode;
import org.apache.royale.compiler.internal.tree.as.FunctionNode;
import org.apache.royale.compiler.internal.tree.as.IdentifierNode;
import org.apache.royale.compiler.internal.tree.as.ImportNode;
import org.apache.royale.compiler.internal.tree.as.InterfaceNode;
import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode;
import org.apache.royale.compiler.internal.tree.as.PackageNode;
import org.apache.royale.compiler.internal.tree.as.VariableNode;
import static org.apache.royale.abc.ABCConstants.TRAIT_Getter;
import static org.apache.royale.abc.ABCConstants.TRAIT_Setter;
import org.apache.royale.compiler.problems.BURMDiagnosticNotAllowedHereProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IFunctionNode;
import org.apache.royale.compiler.tree.mxml.IMXMLDocumentNode;

/**
 * A DirectiveProcessor is the outer shell of the code generator;
 * the DirectiveProcessor contains logic to navigate the directives'
 * ASTs, and subclasses implement "builder" methods to translate
 * specific directives into ABC terms.
 */
class DirectiveProcessor {

    private Collection<ICompilerProblem> problems = null;

    protected DirectiveProcessor(Collection<ICompilerProblem> problems) {
        this.problems = problems;
    }

    /**
     * Translate a ClassNode AST into ABC.
     * Subclasses should override this if
     * they can process class definitions.
     * @param c - the class' AST.
     */
    void declareClass(ClassNode c) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(c));
    }

    /**
     * Translate an InterfaceNode AST into ABC.
     * Subclasses should override this if
     * they can process interface definitions.
     * @param in - the interface's AST.
     */
    void declareInterface(InterfaceNode in) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(in));
    }

    /**
     * Translate a FunctionNode AST into ABC.
     * Subclasses should override this if
     * they can process function definitions.
     * @param f - the function's AST.
     */
    void declareFunction(FunctionNode f) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(f));
    }

    /**
     * Translate a PackageNode AST into ABC.
     * Subclasses should override this if
     * they can process packages.
     * @param p - the package's AST.
     */
    void declarePackage(PackageNode p) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(p));
    }

    /**
     * Translate a VaribleNode AST into ABC.
     * Subclasses should override this if
     * they can process variable definitions.
     * @param var - the variable's AST.
     * @param modifiers - static, const
     */
    void declareVariable(VariableNode var) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(var));
    }

    /**
     * Translate a VaribleNode AST into ABC, and adding bindable support.
     * Subclasses should override this if
     * they can process bindable variable definitions.
     * @param var - the variable's AST.
     * @param modifiers - static, const
     */
    void declareBindableVariable(VariableNode var) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(var));
    }

    /**
     * Translate an MXMLDocumentNode AST into ABC.
     * Subclasses should override this if
     * they can process document nodes.
     * @param c - the document's AST.
     */
    void declareMXMLDocument(IMXMLDocumentNode d) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(d));
    }

    /**
     * Translate other directives' ASTs into ABC.
     * Subclasses should override this if
     * they can process ad-hoc directives.
     * @param n - the directive's AST.
     */
    void processDirective(IASNode n) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(n));
    }

    /**
     * Translate a NamespaceIdentifierNode AST into ABC.
     * Subclasses should override this if
     * they can process namespace identifiers.
     * @param ns - the namespace identifier's AST.
     */
    void processNamespaceIdentifierDirective(NamespaceIdentifierNode ns) {
        problems.add(new BURMDiagnosticNotAllowedHereProblem(ns));
    }

    /**
     * Translate a ImportNode AST into ABC.
     * Subclasses should override this if
     * they can process imports.
     * @param imp - the import's AST.
     */
    void processImportDirective(ImportNode imp) {
        processDirective(imp);
    }

    /**
     * Look through a CONFIG block marker into its contents.
     * @param n - the CONFIG block marker.
     */
    void processConfigBlock(IASNode n) {
        traverse(n);
    }

    /**
     * Traverse the children of a root node and process them.
     * @param root - the root node.  The root is not processed.
     */
    void traverse(IASNode root) {
        for (int i = 0; root != null && i < root.getChildCount(); i++) {
            processNode(root.getChild(i));
        }
    }

    /**
     * Process an individual directive.
     * @param n - the directive's AST.
     */
    void processNode(IASNode n) {
        // In malformed trees, children can be null.
        // For example, an MXMLFileNode may not have an MXMLDocumentNode.
        if (n == null)
            return;
        switch(n.getNodeID()) {
            case ClassID:
                declareClass((ClassNode) n);
                break;
            case InterfaceID:
                declareInterface((InterfaceNode) n);
                break;
            case FunctionID:
            case GetterID:
            case SetterID:
                declareFunction((FunctionNode) n);
                break;
            case ImportID:
                processImportDirective((ImportNode) n);
                break;
            case NamespaceIdentifierID:
                processNamespaceIdentifierDirective((NamespaceIdentifierNode) n);
                break;
            case PackageID:
                declarePackage((PackageNode) n);
                break;
            case VariableID:
                declareVariable((VariableNode) n);
                break;
            case BindableVariableID:
                declareBindableVariable((VariableNode) n);
                break;
            case MXMLDocumentID:
                declareMXMLDocument((IMXMLDocumentNode) n);
                break;
            case MetaTagsID:
                break;
            case ConfigBlockID:
                processConfigBlock(n);
                break;
            default:
                // hack to allow override as a separate keyword in a conditional compile block.
                // the AST thinks it is a property on the class named override.
                // This allows less coding when in SWF the base class has a method that needs
                // overriding but the JS base class does not.  Instead of writing
                // method body
                // an exact copy of method body
                // we want to allow:
                // method body
                if (n.getNodeID() == ASTNodeID.IdentifierID) {
                    IdentifierNode node = (IdentifierNode) n;
                    if (node.getName().equals("override")) {
                        IASNode parent = node.getParent();
                        if (parent.getNodeID() == ASTNodeID.ConfigBlockID) {
                            IASNode parentOfMethods = parent.getParent();
                            int functionCount = parentOfMethods.getChildCount();
                            for (int i = 0; i < functionCount; i++) {
                                IASNode child = parentOfMethods.getChild(i);
                                if (child == parent) {
                                    // examine the next node
                                    child = parentOfMethods.getChild(i + 1);
                                    if (child instanceof IFunctionNode) {
                                        // convince the compiler that this is now an override
                                        IFunctionNode fnode = (IFunctionNode) child;
                                        FunctionDefinition fdef = (FunctionDefinition) fnode.getDefinition();
                                        fdef.setOverride();
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
                processDirective(n);
        }
    }

    /**
     *  Getter/Setter functions need to be declared with a specific
     *  trait kind; do so here.
     *  @param func - a FunctionNode.
     *  @param default_kind - the trait kind to use if the function
     *    is not a getter or setter.  Varies depending on caller's context.
     *  @return the trait kind to use to declare the input function.
     */
    public static int functionTraitKind(FunctionNode func, int default_kind) {


switch(v.getId()) {
            case R.id.clear_btn:
                break;
            case R.id.redo_btn:
                BitmapBuffer.getInstance().redo();
                ViewParams.isRedo = true;
                mView.invalidate();
                break;
            case R.id.restore_btn:
                break;
        }

package cn.teachcourse.view;

import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import cn.teachcourse.R;
import cn.teachcourse.view.custom.DrawingLineView2;
import cn.teachcourse.view.custom.GuaGuaLeView;
import cn.teachcourse.view.custom.PorterDuffXferView;
import cn.teachcourse.view.custom.RadarView;
import cn.teachcourse.view.drawingtool.BitmapBuffer;
import cn.teachcourse.view.drawingtool.DrawingView;
import cn.teachcourse.view.drawingtool.LineDrawer;
import cn.teachcourse.view.drawingtool.OvalDrawer;
import cn.teachcourse.view.drawingtool.PointHistory;
import cn.teachcourse.view.drawingtool.PointModel;
import cn.teachcourse.view.drawingtool.RectDrawer;
import cn.teachcourse.view.drawingtool.ViewParams;

public class PorterDuffXferActivity extends AppCompatActivity implements View.OnClickListener {

    private View mView;

    private Button mClear_btn;

    private Button mRedo_btn;

    private Button mRestore_btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 位图换算
        mView = new PorterDuffXferView(this);
        // 刮刮乐游戏
        mView = new GuaGuaLeView(this);
        // 雷达扫描效果
        mView = new RadarView(this);
        // 绘制面板效果
        mView = new DrawingLineView2(this);
        // 通过注入的方式选择画圆、椭圆、矩形、线条等
        mView = new DrawingView(this);
        setContentView(R.layout.activity_porter_duff_xfer);
        initView();
        addEvent();
    }

    private void addEvent() {
        mClear_btn.setOnClickListener(this);
        mRedo_btn.setOnClickListener(this);
        mRestore_btn.setOnClickListener(this);
    }

    private void initView() {
        mClear_btn = (Button) findViewById(R.id.clear_btn);
        mRedo_btn = (Button) findViewById(R.id.redo_btn);
        mRestore_btn = (Button) findViewById(R.id.restore_btn);
        mView = findViewById(R.id.drawing_view);
        if (mView instanceof DrawingView)
            ((DrawingView) mView).setShapeDrawer(new RectDrawer());
    }

    @Override
    public void onClick(View v) {


switch(status) {
            case TEST_PASS:
                element.setAttribute("status", "true");
                break;
            case TEST_FAIL:
                element.setAttribute("status", "false");
                break;
            case TEST_WARNING:
                element.setAttribute("status", "warning");
                break;
        }

package jsystem.treeui.publisher;

import java.util.Enumeration;
import java.util.Vector;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ElementNode extends DefaultMutableTreeNode implements Comparable<Object> {

    private static final long serialVersionUID = 9088309871545463768L;

    public static final int TYPE_ROOT = 0;

    public static final int TYPE_TEST = 2;

    public static final int TEST_PASS = 0;

    public static final int TEST_FAIL = 1;

    public static final int TEST_WARNING = 2;

    Element element;

    protected int viewMode;

    private int type;

    private int status = TEST_PASS;

    protected String name;

    public ElementNode(Element element, MutableTreeNode parent, int viewMode) {
        super(element);
        setParent(parent);
        this.element = element;
        this.viewMode = viewMode;
        name = element.getAttribute("name");
        setType();
        initStatus();
        initChildrens();
    }

    private void setType() {
        type = TYPE_ROOT;
        String elementName = element.getNodeName();
        if (elementName.equals("test")) {
            type = TYPE_TEST;
        }
    }

    protected void initStatus() {
        String s = element.getAttribute("status");
        if (s.equals("true")) {
            status = TEST_PASS;
        } else if (s.equals("false")) {
            status = TEST_FAIL;
        } else {
            status = TEST_WARNING;
        }
    }

    public void removeElement(ElementNode n) {
        getElement().removeChild(n.getElement());
        children.removeElement(n);
        recalcStatus();
    }

    @SuppressWarnings("unchecked")
    public void initChildrens() {
        if (children == null) {
            children = new Vector();
        }
        NodeList list = element.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node n = list.item(i);
            if (n instanceof Element) {
                Element childElement = (Element) n;
                String status = childElement.getAttribute("status");
                if (status != null && type != TYPE_TEST) {
                    if (viewMode == PublisherTreeModel.VIEW_FAIL_ONLY) {
                        if (!status.equals("false")) {
                            continue;
                        }
                    } else if (viewMode == PublisherTreeModel.VIEW_NOT_SUCCESS) {
                        if (status.equals("true")) {
                            continue;
                        }
                    }
                }
                ElementNode toAdd;
                if (childElement.getNodeName().equals("test")) {
                    toAdd = new TestNode(childElement, this, viewMode);
                } else {
                    toAdd = new ElementNode(childElement, this, viewMode);
                }
                toAdd.initStatus();
                children.addElement(toAdd);
            }
        }
    }

    public void recalcStatus() {
        Enumeration<?> list = children.elements();
        int expectedStatus = TEST_PASS;
        while (list.hasMoreElements()) {
            ElementNode childElement = (ElementNode) list.nextElement();
            if (childElement.getStatus() == TEST_FAIL) {
                expectedStatus = TEST_FAIL;
                break;
            }
        }
        if (status != expectedStatus) {
            setStatus(expectedStatus);
            if (parent != null) {
                ((ElementNode) parent).recalcStatus();
            }
        }
    }

    public String toString() {
        return name;
    }

    /**
     * @return Returns the viewMode.
     */
    public int getViewMode() {
        return viewMode;
    }

    /**
     * @param viewMode
     *            The viewMode to set.
     */
    public void setViewMode(int viewMode) {
        this.viewMode = viewMode;
    }

    /**
     * @return Returns the status.
     */
    public int getStatus() {
        return status;
    }

    /**
     * @param status
     *            The status to set.
     */
    public void setStatus(int status) {
        this.status = status;

    }

    /**
     * @return Returns the type.
     */
    public int getType() {
        return type;
    }

    /**
     * @param type
     *            The type to set.
     */
    public void setType(int type) {
        this.type = type;
    }

    /**
     * @return Returns the element.
     */
    public Element getElement() {
        return element;
    }

    /**
     * @param element
     *            The element to set.
     */
    public void setElement(Element element) {
        this.element = element;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name
     *            The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Object o) {
        return this.getName().compareTo(((ElementNode) o).getName());
    }
}