#include <memory>
#include <string>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
...略...
template<typename FunGetIv, typename FunGetLog>
std::string getGLLogStr(GLuint obj, FunGetIv funGetIv, FunGetLog funGetLog)
{
    GLint len = 0;
    funGetIv(obj, GL_INFO_LOG_LENGTH, &len);
    if(len > 1){
        std::unique_ptr<char[]> infoLog(new char[len]);
        funGetLog(obj, len, nullptr, infoLog.get());
        return std::string(infoLog.get());
    }
    else{
        return std::string();
    }
}
std::string getGLShaderLogInfo(GLuint obj)
{
    return getGLLogStr(obj, glGetShaderiv, glGetShaderInfoLog);
}
std::string getGLProgramLogInfo(GLuint obj)
{
    return getGLLogStr(obj, glGetProgramiv, glGetProgramInfoLog);
}
GLuint loadShader(GLenum type, const char *shaderSrc)
{
    GLuint shader = glCreateShader(type);
    if(shader == 0){
        return 0;
    }
    glShaderSource(shader, 1, &shaderSrc, nullptr);
    glCompileShader(shader);
    GLint compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if(!compiled){
        LOGI("glCompileShader failed.\n Error:(%s)\n", getGLShaderLogInfo(shader).c_str());
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}
class GLEnvironment
{
    EGLDisplay display_;
    EGLSurface surface_; 
    EGLConfig config_; 
    EGLint screenWidth_;
    EGLint screenHeight_;
    EGLContext context_;
public:
    GLEnvironment()
        : display_(EGL_NO_DISPLAY),
          surface_(EGL_NO_SURFACE),
          config_(nullptr),
          screenWidth_(), screenHeight_(),
          context_(EGL_NO_CONTEXT)
    {}
    ~GLEnvironment()
    {
        destroy();
    }
    GLEnvironment(const GLEnvironment &) = delete;
    GLEnvironment &operator=(const GLEnvironment &) = delete;
    bool create(ANativeWindow *window)
    {
        return initContext(window);
    }
    void destroy()
    {
        disconnectDisplay();
    }
    bool recreate(ANativeWindow *window)
    {
        destroy();
        return create(window);
    }
    
    
    
    static const bool preserveEGLContextOnPause = true;
    bool initWindow(ANativeWindow *window)
    {
        LOGI("initWindow");
        if(preserveEGLContextOnPause && hasDisplay() && !hasSurface() && hasContext() && isContextInitialized()){
            
            LOGI("Trying to resume");
            if(createSurface(window, config_) && makeContextCurrent(window)){
                LOGI("resume succeeded");
                return true;
            }
            LOGI("resume failed");
        }
        
        return recreate(window);
    }
    void termWindow()
    {
        if(!preserveEGLContextOnPause){
            destroyContext();
        }
        destroySurface();
    }
    
    
    
    bool presentFrame(ANativeWindow *window)
    {
        if(!isContextInitialized()){
            LOGE("Context not initialized");
            return false;
        }
        drawFrame();
        if(!swap()){
            recreate(window);
            drawFrame();
            if(!swap()){
                LOGE("Swap failed twice");
                return false;
            }
        }
        return true;
    }
private:
    bool swap()
    {
        if(hasSurface()){
            LOGI("swap");
            if(!eglSwapBuffers(display_, surface_)){
                LOGI("eglSwapBuffers failed");
                return false;
            }
        }
        return true;
    }
    virtual void drawFrame(){}
    
    
    
private:
    bool hasDisplay() const {return display_ != EGL_NO_DISPLAY;}
    bool connectDisplay()
    {
        if(display_ == EGL_NO_DISPLAY){
            
            const EGLDisplay uninitializedDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
            if(uninitializedDisplay == EGL_NO_DISPLAY){
                
                LOGE("eglGetDisplay failed");
                return false;
            }
            if(!eglInitialize(uninitializedDisplay, 0, 0)){
                LOGE("eglInitialize failed");
                return false;
            }
            display_ = uninitializedDisplay;
        }
        return true;
    }
    void disconnectDisplay()
    {
        destroyContext();
        destroySurface();
        if(display_ != EGL_NO_DISPLAY){
            LOGI("eglTerminate");
            eglTerminate(display_);
            display_ = EGL_NO_DISPLAY;
        }
    }
    
    
    
private:
    static EGLConfig chooseConfig(EGLDisplay display)
    {
        struct DesiredConfig
        {
            int depth;
            DesiredConfig(int depth):depth(depth){}
        };
        const DesiredConfig desiredConfigs[] = {DesiredConfig(24), DesiredConfig(16)};
        const DesiredConfig * const dcEnd = desiredConfigs + sizeof(desiredConfigs)/sizeof(desiredConfigs[0]);
        const DesiredConfig *dc;
        EGLConfig config = nullptr;
        for(dc = desiredConfigs; dc != dcEnd; ++dc){
            const EGLint desiredAttribs[] = {
                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_BLUE_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_RED_SIZE, 8,
                EGL_DEPTH_SIZE, dc->depth,
                EGL_NONE };
            EGLint numConfigs;
            if(!eglChooseConfig(display, desiredAttribs, &config, 1, &numConfigs)){
                LOGE("eglChooseConfig failed");
                return nullptr;
            }
            if(numConfigs == 1){
                break;
            }
        }
        if(config == nullptr){
            LOGE("no EGL config");
            return nullptr;
        }
        LOGI("match config depth=%d", dc->depth);
        return config;
    }
    
    
    
private:
    bool hasSurface() const { return surface_ != EGL_NO_SURFACE;}
    bool createSurface(ANativeWindow *window, EGLConfig desiredConfig = nullptr)
    {
        if(!connectDisplay()){
            return false;
        }
        if(surface_ == EGL_NO_SURFACE){
            const EGLConfig config = desiredConfig ? desiredConfig : chooseConfig(display_);
            if(!config){
                LOGE("chooseConfig failed");
                return false;
            }
            
            EGLint format;
            eglGetConfigAttrib(display_, config, EGL_NATIVE_VISUAL_ID, &format);
            ANativeWindow_setBuffersGeometry(window, 0, 0, format);
            
            surface_ = eglCreateWindowSurface(display_, config, window, nullptr);
            if(surface_ == EGL_NO_SURFACE){
                LOGE("eglCreateWindowSurface failed. error=%d", eglGetError());
                return false;
            }
            config_ = config;
            eglQuerySurface(display_, surface_, EGL_WIDTH, &screenWidth_);
            eglQuerySurface(display_, surface_, EGL_HEIGHT, &screenHeight_);
            LOGI("screen size %d x %d", screenWidth_, screenHeight_);
        }
        return true;
    }
    void destroySurface()
    {
        
        if(surface_ != EGL_NO_SURFACE){
            LOGI("eglDestroySurface(window surface)");
            eglDestroySurface(display_, surface_);
            surface_ = EGL_NO_SURFACE;
        }
    }
public:
    int getScreenWidth() const {return screenWidth_;}
    int getScreenHeight() const {return screenHeight_;}
    
    
    
private:
    bool hasContext() const{ return context_ != EGL_NO_CONTEXT;}
    bool createContext(ANativeWindow *window)
    {
        if(!createSurface(window)){ 
            return false;
        }
        if(context_ == EGL_NO_CONTEXT){
            const EGLint attribs[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2, 
                EGL_NONE };
            context_ = eglCreateContext(display_, config_, nullptr, attribs);
            if(context_ == EGL_NO_CONTEXT){
                LOGE("eglCreateContext failed");
                return false;
            }
        }
        return true;
    }
    void destroyContext()
    {
        termContext();
        if(context_ != EGL_NO_CONTEXT){
            LOGI("eglMakeCurrent null");
            eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            LOGI("eglDestroyContext");
            eglDestroyContext(display_, context_);
            context_ = EGL_NO_CONTEXT;
        }
    }
        
    bool makeContextCurrent(ANativeWindow *window)
    {
        if(!createSurface(window)){
            return false;
        }
        if(!createContext(window)){
            return false;
        }
        LOGI("eglMakeCurrent");
        if(!eglMakeCurrent(display_, surface_, surface_, context_)){
            LOGE( "eglMakeCurrent failed" );
            return false;
        }
        return true;
    }
    void detachCurrentContext()
    {
        if(display_ != EGL_NO_DISPLAY){
            LOGI("detachCurrentContext");
            eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        }
    }
    bool isContextCurrent()
    {
        return context_ != EGL_NO_CONTEXT && eglGetCurrentContext() == context_;
    }
        
    bool contextInitialized_ = false;
protected:
    bool isContextInitialized() const {return contextInitialized_;}
private:
    bool initContext(ANativeWindow *window)
    {
        if(!makeContextCurrent(window)){
            return false;
        }
        if(!contextInitialized_){
            if(!loadResources()){
                return false;
            }
            initializeContextState();
            contextInitialized_ = true;
        }
        return true;
    }
    void termContext()
    {
        if(contextInitialized_){
            unloadResources();
            contextInitialized_ = false;
        }
    }
    virtual bool loadResources(){return true;}
    virtual void unloadResources(){}
    virtual void initializeContextState(){}
};
class ThisAppGraphics : public GLEnvironment
{
    GLuint vertexShader_;
    GLuint fragmentShader_;
    GLuint programObject_;
public:
    ThisAppGraphics()
        : GLEnvironment(),
          vertexShader_(0),
          fragmentShader_(0),
          programObject_(0)
    {
    }
private:
    virtual bool loadResources() override
    {
        LOGI("loadResources");
        const char vShaderStr[] =
            "attribute vec4 vPosition;"
            "void main(){"
            "  gl_Position = vPosition;"
            "}";
        const char fShaderStr[] =
            "precision mediump float;"
            "void main(){"
            "  gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);"
            "}";
        vertexShader_ = loadShader(GL_VERTEX_SHADER, vShaderStr);
        fragmentShader_ = loadShader(GL_FRAGMENT_SHADER, fShaderStr);
        programObject_ = glCreateProgram();
        glAttachShader(programObject_, vertexShader_);
        glAttachShader(programObject_, fragmentShader_);
        glBindAttribLocation(programObject_, 0, "vPosition");
        glLinkProgram(programObject_);
        GLint linked = 0;
        glGetProgramiv(programObject_, GL_LINK_STATUS, &linked);
        if(!linked){
            LOGI("glLinkProgram failed.\n Error:(%s)\n", getGLProgramLogInfo(programObject_).c_str());
            return false;
        }
        return true;
    }
    virtual void unloadResources() override
    {
        LOGI("unloadResources");
        if(programObject_){
            glDeleteProgram(programObject_);
            programObject_ = 0;
        }
        if(vertexShader_){
            glDeleteShader(vertexShader_);
            vertexShader_ = 0;
        }
        if(fragmentShader_){
            glDeleteShader(fragmentShader_);
            fragmentShader_ = 0;
        }
    }
    virtual void initializeContextState() override
    {
        glEnable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);
        glViewport(0, 0, getScreenWidth(), getScreenHeight());
    }
    virtual void drawFrame() override
    {
        LOGI("drawFrame");
        GLfloat vertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
        };
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(programObject_);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glEnableVertexAttribArray(0);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
};
class ThisApp
{
    ...略...
    ThisAppGraphics gl_;
    ...略...
    void handleCmd(int32_t cmd)
    {
        switch(cmd){
        case APP_CMD_INIT_WINDOW:
            LOGI("handleCmd(APP_CMD_INIT_WINDOW)");
            
            if(app_->window){
                gl_.initWindow(app_->window);
                gl_.presentFrame(app_->window);
            }
            break;
        case APP_CMD_TERM_WINDOW:
            LOGI("handleCmd(APP_CMD_TERM_WINDOW)");
            
            gl_.termWindow();
            break;
    ...略...
 
 他のプロジェクトでも使い回せるようなコードはGLEnvironmentクラスに収め、このプロジェクトに固有な描画コードはThisAppGraphicsクラスに収めてみました。ThisAppクラスからはapp_->windowが有効になったときにinitWindow()やpresentFrame()を、app_->windowが無効になったときにtermWindow()を呼び出すだけです。