content: /*
   * Copyright (C) 2015-2023 Emanuel Moecklin
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.onegravity.rteditor.media;
  
  import android.app.Activity;
  import android.app.ProgressDialog;
  import android.os.Bundle;
  import android.os.Handler;
  import androidx.appcompat.app.AppCompatActivity;
  
  import com.onegravity.rteditor.R;
  import com.onegravity.rteditor.api.RTApi;
  
  import java.util.ArrayList;
  
  public class MonitoredActivity extends AppCompatActivity {
  
      protected Handler mHandler;
  
      private final ArrayList<LifeCycleListener> mListeners = new ArrayList<>();
  
      // ****************************************** MonitoredActivity Methods *******************************************
  
      public interface LifeCycleListener {
          void onActivityCreated(Activity activity);
  
          void onActivityDestroyed(Activity activity);
  
          void onActivityPaused(Activity activity);
  
          void onActivityResumed(Activity activity);
  
          void onActivityStarted(Activity activity);
  
          void onActivityStopped(Activity activity);
      }
  
      public static class LifeCycleAdapter implements LifeCycleListener {
          public void onActivityCreated(Activity activity) {
          }
  
          public void onActivityDestroyed(Activity activity) {
          }
  
          public void onActivityPaused(Activity activity) {
          }
  
          public void onActivityResumed(Activity activity) {
          }
  
          public void onActivityStarted(Activity activity) {
          }
  
          public void onActivityStopped(Activity activity) {
          }
      }
  
      public void addLifeCycleListener(LifeCycleListener listener) {
          if (!mListeners.contains(listener)) {
              mListeners.add(listener);
          }
      }
  
      public void removeLifeCycleListener(LifeCycleListener listener) {
          mListeners.remove(listener);
      }
  
      // ****************************************** Lifecycle Methods *******************************************
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          // set theme
          setTheme(RTApi.useDarkTheme() ? R.style.RTE_BaseThemeDark : R.style.RTE_BaseThemeLight);
  
          if (!isFinishing()) {
              mHandler = new Handler();
  
              for (LifeCycleListener listener : mListeners) {
                  listener.onActivityCreated(this);
              }
          }
      }
  
      @Override
      protected void onStart() {
          super.onStart();
  
          for (LifeCycleListener listener : mListeners) {
              listener.onActivityStarted(this);
          }
      }
  
      @Override
      protected void onPause() {
          super.onPause();
  
          for (LifeCycleListener listener : mListeners) {
              listener.onActivityPaused(this);
          }
      }
  
      @Override
      protected void onResume() {
          super.onResume();
  
          for (LifeCycleListener listener : mListeners) {
              listener.onActivityResumed(this);
          }
      }
  
      @Override
      protected void onStop() {
          super.onStop();
  
          for (LifeCycleListener listener : mListeners) {
              listener.onActivityStopped(this);
          }
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
  
          for (LifeCycleListener listener : mListeners) {
              listener.onActivityDestroyed(this);
          }
      }
  
      // ****************************************** Foreground Jobs (Modal) *******************************************
  
      /**
       * Start a foreground job showing a progress bar as long as the job runs.
       * A foreground job shows a modal dialog that can't be cancelled while the app
       * does some processing. This should be used for short jobs only that block the ui.
       * Of course this contradicts many of the Android guidelines but with short jobs
       * we mean < 1 second and only in rare cases should it me more (e.g. when the garbage
       * collector kicks in which would normally lead to stuttering).
       * Using foreground jobs gives a better user experience in these rare cases.
       * The user has to wait but he/she knows it because there's a progess bar.
       */
      public <T> T startForegroundJob(int msgId, ForegroundJob<T> job) {
          // make the progress dialog uncancelable, so that we can guarantee
          // that the thread is done before the activity gets destroyed
          ProgressDialog dialog = ProgressDialog.show(this, null, getString(msgId), true, false);
          Job<T> managedJob = new Job<>(job, dialog);
          return managedJob.runForegroundJob();
      }
  
      /**
       * Start a background job showing a progress bar as long as the job runs.
       * This seems contradictory but with background job we mean one that runs off
       * the ui thread to prevent an ANR.
       * We still have to wait for the processing to be done because we need the result.
       */
      public void startBackgroundJob(int msgId, Runnable runnable) {
          // make the progress dialog uncancelable, so that we can guarantee
          // that the thread is done before the activity gets destroyed
          ProgressDialog dialog = ProgressDialog.show(this, null, getString(msgId), true, false);
          Job<Object> managedJob = new Job<>(runnable, dialog);
          managedJob.runBackgroundJob();
      }
  
      public interface ForegroundJob<T> {
          T runForegroundJob();
      }
  
      private class Job<T> extends LifeCycleAdapter implements ForegroundJob<T> {
  
          private final ProgressDialog mDialog;
          private final Runnable mRunnable;
          private final ForegroundJob<T> mJob;
  
          private final Runnable mCleanupRunner = new Runnable() {
              public void run() {
                  removeLifeCycleListener(Job.this);
                  if (mDialog.getWindow() != null) {
                      mDialog.dismiss();
                  }
              }
          };
  
          public Job(ForegroundJob<T> job, ProgressDialog dialog) {
              mDialog = dialog;
              mJob = job;
              mRunnable = null;
              addLifeCycleListener(this);
          }
  
          public Job(Runnable runnable, ProgressDialog dialog) {
              mDialog = dialog;
              mJob = null;
              mRunnable = runnable;
              addLifeCycleListener(this);
          }
  
          @Override
          public T runForegroundJob() {
              try {
                  return mJob.runForegroundJob();
              } finally {
                  mHandler.post(mCleanupRunner);
              }
          }
  
          public void runBackgroundJob() {
              try {
                  mRunnable.run();
              } finally {
                  mHandler.post(mCleanupRunner);
              }
          }
  
          @Override
          public void onActivityDestroyed(Activity activity) {
              // We get here only when the onDestroyed being called before
              // the mCleanupRunner. So, run it now and remove it from the queue
              mCleanupRunner.run();
              mHandler.removeCallbacks(mCleanupRunner);
          }
  
          @Override
          public void onActivityStopped(Activity activity) {
              mDialog.hide();
          }
  
          @Override
          public void onActivityStarted(Activity activity) {
              mDialog.show();
          }
      }
  
  }