public class TwitterConstants {
    public static final String CONSUMER_KEY = "tf4fb34h8P2ZDXXepNJMjZQgv";

    public static final String CONSUMER_SECRET = "HWHXaIpVK6zaSh7p7E0ls7RmMvfC6NXuDHVxKbF6T4NF47fgKp";



} public class DbHelper extends SQLiteOpenHelper implements IDbOperations {

    private static final String SQL_TABLE_CREATE_TEMPLATE = "CREATE TABLE IF NOT EXISTS %s (%s);";
    private static final String SQL_TABLE_CREATE_FIELD_TEMPLATE = "%s %s";

    public DbHelper(final Context context, final String name, final int version) {
        super(context, name, null, version);
    }

    @Nullable
    public static String getTableName(final AnnotatedElement clazz) {
        final Table table = clazz.getAnnotation(Table.class);

        if (table != null) {
            return table.name();
        } else {
            return null;
        }
    }

    @Nullable
    public static String getTableCreateQuery(final Class<?> clazz) {
        final Table table = clazz.getAnnotation(Table.class);

        if (table != null) {
            try {
                final String name = table.name();

                final StringBuilder builder = new StringBuilder();
                final Field[] fields = clazz.getFields();

                for (int i = 0; i < fields.length; i++) {
                    final Field field = fields[i];

                    final Annotation[] annotations = field.getAnnotations();

                    String type = null;

                    for (final Annotation annotation : annotations) {
                        if (annotation instanceof dbInteger) {
                            type = ((dbInteger) annotation).value();
                        } else if (annotation instanceof dbLong) {
                            type = ((dbLong) annotation).value();
                        } else if (annotation instanceof dbString) {
                            type = ((dbString) annotation).value();
                        }
                    }

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

                    final String value = (String) field.get(null);

                    builder.append(String.format(Locale.US, SQL_TABLE_CREATE_FIELD_TEMPLATE, value, type));

                    if (i < fields.length - 1) {
                        builder.append(",");
                    }
                }

                return String.format(Locale.US, SQL_TABLE_CREATE_TEMPLATE, name, builder);
            } catch (final Exception e) {
                return null;
            }
        } else {
            return null;
        }
    }

    @Override
    public void onCreate(final SQLiteDatabase db) {
        for (final Class<?> clazz : Contract.MODELS) {
            final String sql = getTableCreateQuery(clazz);

            if (sql != null) {
                db.execSQL(sql);
            }
        }
    }

    @Override
    public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {

    }

    @Override
    public Cursor query(final String sql, final String... args) {
        final SQLiteDatabase database = getReadableDatabase();

        return database.rawQuery(sql, args);
    }

    @Override
    public long insert(final Class<?> table, final ContentValues values) {
        final String name = getTableName(table);

        if (name != null) {
            final SQLiteDatabase database = getWritableDatabase();
            long id;

            try {
                database.beginTransaction();

                id = database.insert(name, null, values);

                database.setTransactionSuccessful();
            } finally {
                database.endTransaction();
            }

            return id;
        } else {
            throw new RuntimeException();
        }
    }

    @Override
    public int bulkInsert(final Class<?> table, final List<ContentValues> values) {
        final String name = getTableName(table);

        if (name != null) {
            final SQLiteDatabase database = getWritableDatabase();
            int count = 0;

            try {
                database.beginTransaction();

                for (final ContentValues value : values) {
                    database.insert(name, null, value);

                    count++;
                }

                database.setTransactionSuccessful();
            } finally {
                database.endTransaction();
            }

            return count;
        } else {
            throw new RuntimeException();
        }
    }

    @Override
    public int delete(final Class<?> table, final String sql, final String... args) {
        final String name = getTableName(table);

        if (name != null) {
            final SQLiteDatabase database = getWritableDatabase();
            int count = 0;

            try {
                database.beginTransaction();

                count = database.delete(name, sql, args);

                database.setTransactionSuccessful();
            } finally {
                database.endTransaction();
            }

            return count;
        } else {
            throw new RuntimeException();
        }
    }
} public class DateConverter implements JsonDeserializer<Date> {

    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        try {
            return simpleDateFormat.parse(json.getAsString());
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return null;
    }
} public class HttpRequestModel {

    private String url;

    private Map<String, String> headers;

    private String body;


    public void setUrl(String url) {
        this.url = url;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getUrl() {
        return url;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public String getBody() {
        return body;
    }
} public class TwitterSearchResponse {

    private List<TwitterSearchStatus> mTwitterSearchStatuses = new ArrayList<>();

    public TwitterSearchResponse(String json) throws JSONException {
        JSONObject jsonObject = new JSONObject(json);

        if (jsonObject.has("statuses")) {
            JSONArray statusesJsonArray = jsonObject.getJSONArray("statuses");
            for (int i = 0; i < statusesJsonArray.length(); i++) {
                JSONObject statusObject = statusesJsonArray.getJSONObject(i);

                TwitterSearchStatus twitterSearchStatus = new TwitterSearchStatus(statusObject);
                mTwitterSearchStatuses.add(twitterSearchStatus);

            }

        }
    }

    public List<TwitterSearchStatus> getTwitterSearchStatuses() {
        return mTwitterSearchStatuses;
    }
} @Table(name = "USER")
public final class User {

    @dbInteger
    public static final String ID = "id";

    @dbString
    public static final String TITLE = "title";

    @dbLong
    public static final String DATE = "date";

} public class TwitterSearchGSONResponse {

    @SerializedName("statuses")
    private List<TwitterSearchStatuses> mStatuses;

    public List<TwitterSearchStatuses> getStatuses() {
        return mStatuses;
    }
} public class CustomContentProvider extends ContentProvider {

    public static final String AUTH = "content://com.example.mikhail_sianko.myapplication.provider.CustomContentProvider";

    private IDbOperations mOperations;

    @Override
    public boolean onCreate() {
        mOperations = IDbOperations.Imp.newInstance(getContext(), "db", 1);

        return true;
    }

    private Class<?> getTable(final Uri pUri) {
        return User.class;
    }

    @Nullable
    @Override
    public Cursor query(final Uri pUri, final String[] pStrings, final String pS, final String[] pStrings1, final String pS1) {
        return mOperations.query("select * from "+DbHelper.getTableName(getTable(pUri)), pStrings1);
    }

    @Nullable
    @Override
    public String getType(final Uri pUri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(final Uri pUri, final ContentValues pContentValues) {
        long id = mOperations.insert(getTable(pUri), pContentValues);
        final Uri uri = Uri.parse(AUTH + "/" + DbHelper.getTableName(User.class) + "/" + id);
        getContext().getContentResolver().notifyChange(uri,null);
        return uri;
    }

    @Override
    public int delete(final Uri pUri, final String pS, final String[] pStrings) {
        return mOperations.delete(getTable(pUri), pS, pStrings);
    }

    @Override
    public int bulkInsert(final Uri uri, final ContentValues[] values) {
        return super.bulkInsert(uri, values);
    }

    @Override
    public int update(final Uri pUri, final ContentValues pContentValues, final String pS, final String[] pStrings) {
        return 0;
    }
} public interface OnResultCallback<Result, Progress> extends ProgressCallback<Progress> {

    void onSuccess(Result result);

    void onError(Exception e);
} public interface ProgressCallback<Progress> {

    void onProgressChanged(Progress progress);

} public class ThreadManager {

    public static final int COUNT_CORE = 3;
    private final ExecutorService executorService;

    public ThreadManager() {
        this(Executors.newFixedThreadPool(3));
    }

    public ThreadManager(final ExecutorService executorService) {
        this.executorService = executorService;
    }

    public<Params, Progress, Result> void execute(final Operation<Params, Progress, Result> operation, final Params param, final OnResultCallback<Result, Progress> onResultCallback) {
        final Handler handler = new Handler();
        executorService.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    final Result result = operation.doing(param, new ProgressCallback<Progress>() {

                        @Override
                        public void onProgressChanged(final Progress progress) {
                            handler.post(new Runnable() {

                                @Override
                                public void run() {
                                    onResultCallback.onProgressChanged(progress);
                                }
                            });
                        }
                    });
                    handler.post(new Runnable() {

                        @Override
                        public void run() {
                            onResultCallback.onSuccess(result);
                        }
                    });
                } catch (Exception e) {
                    onResultCallback.onError(e);
                }
            }
        });
    }

} public class DBOperation implements Operation<Integer, Integer, String> {

    public static final String TAG = DBOperation.class.getSimpleName();

    @Override
    public String doing(final Integer count, final ProgressCallback<Integer> progressCallback) throws Exception {
        for (int i = 0; i < count; i++) {
            Thread.currentThread().sleep(1000L);
            Log.d(TAG, "I'm sleeping " + i);
            progressCallback.onProgressChanged(i);
        }
        return "I slept " + count + " sec " ;
    }
} public class HttpGetRequest implements Operation<HttpRequestModel, Void, String> {

    public HttpGetRequest() {

    }

    @Override
    public String doing(HttpRequestModel requestModel, ProgressCallback<Void> progressCallback) throws Exception {
        HttpClient httpClient = new HttpClient();


        return httpClient.get(requestModel.getUrl(), requestModel.getHeaders());
    }
} public class HttpPostOperation implements Operation<HttpRequestModel, Void, String> {

    @Override
    public String doing(HttpRequestModel inputRequestModel, ProgressCallback<Void> progressCallback) throws Exception {
        HttpClient httpClient = new HttpClient();
        if (inputRequestModel != null) {
            return httpClient.post(inputRequestModel.getUrl(), inputRequestModel.getHeaders(), inputRequestModel.getBody());
        } else {
            throw new IllegalArgumentException("httpRequestModel is null");
        }
    }
} public class WorkerOperation implements Operation<String, Integer, WorkerOperation.Result> {

    public static final int N = 3;

    @Override
    public Result doing(final String whatYouDoing, final ProgressCallback<Integer> progressCallback) throws Exception {
        for (int i = 0; i < N; i++) {
            try {
                Thread.currentThread().sleep(1000L);
            } catch (InterruptedException e) {
                throw new IOException(e);
            }
            Log.d(TAG, whatYouDoing + i);
            progressCallback.onProgressChanged(i);
        }
        Result result = new Result();
        result.n = N;
        result.result = "I did " + whatYouDoing + N + " sec ";
        return  result;
    }

    public static class Result {
        private int n;

        private String result;

        public int getN() {
            return n;
        }

        public String getResult() {
            return result;
        }

        @Override
        public String toString() {
            return "Result{" +
                    "n=" + n +
                    ", result='" + result + '\'' +
                    '}';
        }
    }

    public static final String TAG = WorkerOperation.class.getSimpleName();

}
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.mikhail_sianko.myapplication.BuildConfig;
import com.example.mikhail_sianko.myapplication.R;
import com.example.mikhail_sianko.myapplication.constants.TwitterConstants;
import com.example.mikhail_sianko.myapplication.db.DbHelper;
import com.example.mikhail_sianko.myapplication.gson.DateConverter;
import com.example.mikhail_sianko.myapplication.model.HttpRequestModel;
import com.example.mikhail_sianko.myapplication.model.TwitterSearchResponse;
import com.example.mikhail_sianko.myapplication.model.contract.User;
import com.example.mikhail_sianko.myapplication.model.gson.TwitterSearchGSONResponse;
import com.example.mikhail_sianko.myapplication.provider.CustomContentProvider;
import com.example.mikhail_sianko.myapplication.threads.OnResultCallback;
import com.example.mikhail_sianko.myapplication.threads.ProgressCallback;
import com.example.mikhail_sianko.myapplication.threads.ThreadManager;
import com.example.mikhail_sianko.myapplication.threads.operation.DBOperation;
import com.example.mikhail_sianko.myapplication.threads.operation.HttpGetRequest;
import com.example.mikhail_sianko.myapplication.threads.operation.HttpPostOperation;
import com.example.mikhail_sianko.myapplication.threads.operation.WorkerOperation;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
package com.example.mikhail_sianko.myapplication.ui;





public class MainActivity extends AppCompatActivity implements Contract.View {

    private static final String TAG = MainActivity.class.getSimpleName();
    public static final int ASYNC_LOADER_ID = 0;
    public static final int CURSOR_LOADER_ID = 1;
    private Contract.Presenter presenter;

    private TextView responseView;
    private ProgressBar progressBar;

    private String mAccessToken;

    //TODO should be singleton
    private ThreadManager threadManager = new ThreadManager(Executors.newFixedThreadPool(ThreadManager.COUNT_CORE));