protected void loadHotspotTexture(
     PLIHotspot hotspot, String filename, String urlBase, PLTextureColorFormat colorFormat) {
   if (filename != null) {
     String url = this.buildURL(filename, urlBase);
     if (mHotspotTextures.containsKey(url)) hotspot.addTexture(mHotspotTextures.get(url));
     else {
       boolean isHTTPURL = this.isHTTPURL(url);
       PLITexture texture =
           new PLTexture(
               isHTTPURL ? new PLImage() : this.getLocalImageAsynchronously(url, colorFormat));
       mHotspotTextures.put(url, texture);
       hotspot.addTexture(texture);
       if (isHTTPURL)
         mView
             .getDownloadManager()
             .add(
                 new PLHTTPFileDownloader(
                     url, new PLImageFileDownloaderListener(texture.getImage(), colorFormat)));
     }
   }
 }
  protected void parseJSON(byte[] jsonData) {
    try {
      mJSON = new JSONObject(new String(jsonData, "utf-8"));
      String urlBase = mJSON.getString("urlBase").trim();
      if (urlBase == null) throw new RuntimeException("urlBase property not exists");
      else if (!this.isHTTPURL(urlBase)
          && !urlBase.startsWith("res://")
          && !urlBase.startsWith("file://"))
        throw new RuntimeException("urlBase property is wrong");
      String type = mJSON.getString("type").trim();
      final PLIPanorama panorama;
      PLPanoramaType panoramaType = PLPanoramaType.PLPanoramaTypeUnknow;
      if (type != null) {
        if (type.equals("spherical")) {
          panoramaType = PLPanoramaType.PLPanoramaTypeSpherical;
          panorama = new PLSphericalPanorama();
        } else if (type.equals("spherical2")) {
          panoramaType = PLPanoramaType.PLPanoramaTypeSpherical2;
          panorama = new PLSpherical2Panorama();
        } else if (type.equals("cubic")) {
          panoramaType = PLPanoramaType.PLPanoramaTypeCubic;
          panorama = new PLCubicPanorama();
        } else if (type.equals("cylindrical")) {
          panoramaType = PLPanoramaType.PLPanoramaTypeCylindrical;
          panorama = new PLCylindricalPanorama();
        } else throw new RuntimeException("Panorama type is wrong");
      } else throw new RuntimeException("type property not exists");
      PLTextureColorFormat colorFormat = PLTextureColorFormat.PLTextureColorFormatRGBA8888;
      if (mJSON.has("imageColorFormat")) {
        String imageColorFormat = mJSON.getString("imageColorFormat").trim().toUpperCase(Locale.US);
        if (imageColorFormat.equals("RGB565"))
          colorFormat = PLTextureColorFormat.PLTextureColorFormatRGB565;
        else if (imageColorFormat.equals("RGBA4444"))
          colorFormat = PLTextureColorFormat.PLTextureColorFormatRGBA4444;
      }
      if (panoramaType == PLPanoramaType.PLPanoramaTypeCylindrical && mJSON.has("height"))
        ((PLCylindricalPanorama) panorama).setHeight((float) mJSON.getDouble("height"));
      if (mJSON.has("divisions") && panorama instanceof PLIQuadricPanorama) {
        JSONObject divisions = mJSON.getJSONObject("divisions");
        if (divisions != null) {
          PLIQuadricPanorama quadricPanorama = (PLIQuadricPanorama) panorama;
          if (divisions.has("preview")) quadricPanorama.setPreviewDivs(divisions.getInt("preview"));
          if (divisions.has("panorama")) quadricPanorama.setDivs(divisions.getInt("panorama"));
        }
      }
      PLIPanorama oldPanorama = mView.getPanorama();
      mKeepParameters =
          (oldPanorama != null && !(oldPanorama instanceof PLBlankPanorama) && mJSON.has("keep")
              ? PLViewParameterType.checkViewParametersWithStringMask(mJSON.getString("keep"))
              : PLViewParameterType.checkViewParametersWithMask(
                  PLViewParameterType.PLViewParameterTypeNone));
      if (!mKeepParameters.reset && mJSON.has("reset")) {
        JSONObject reset = mJSON.getJSONObject("reset");
        if (reset != null) {
          if (reset.has("enabled")) mView.setResetEnabled(reset.getBoolean("enabled"));
          if (reset.has("numberOfTouches"))
            mView.setNumberOfTouchesForReset(reset.getInt("numberOfTouches"));
          if (reset.has("shake")) {
            JSONObject shake = reset.getJSONObject("shake");
            if (shake != null) {
              if (shake.has("enabled")) mView.setShakeResetEnabled(shake.getBoolean("enabled"));
              if (shake.has("threshold"))
                mView.setShakeThreshold((float) shake.getDouble("threshold"));
            }
          }
        }
      }
      if (!mKeepParameters.scrolling && mJSON.has("scrolling")) {
        JSONObject scrolling = mJSON.getJSONObject("scrolling");
        if (scrolling != null) {
          if (scrolling.has("enabled")) mView.setScrollingEnabled(scrolling.getBoolean("enabled"));
          if (scrolling.has("minDistanceToEnableScrolling"))
            mView.setMinDistanceToEnableScrolling(scrolling.getInt("minDistanceToEnableScrolling"));
        }
      }
      if (!mKeepParameters.inertia && mJSON.has("inertia")) {
        JSONObject inertia = mJSON.getJSONObject("inertia");
        if (inertia != null) {
          if (inertia.has("enabled")) mView.setInertiaEnabled(inertia.getBoolean("enabled"));
          if (inertia.has("interval"))
            mView.setInertiaInterval((float) inertia.getDouble("interval"));
        }
      }
      if (!mKeepParameters.accelerometer && mJSON.has("accelerometer")) {
        JSONObject accelerometer = mJSON.getJSONObject("accelerometer");
        if (accelerometer != null) {
          if (accelerometer.has("enabled"))
            mView.setAccelerometerEnabled(accelerometer.getBoolean("enabled"));
          if (accelerometer.has("interval"))
            mView.setAccelerometerInterval((float) accelerometer.getDouble("interval"));
          if (accelerometer.has("sensitivity"))
            mView.setAccelerometerSensitivity((float) accelerometer.getDouble("sensitivity"));
          if (accelerometer.has("leftRightEnabled"))
            mView.setAccelerometerLeftRightEnabled(accelerometer.getBoolean("leftRightEnabled"));
          if (accelerometer.has("upDownEnabled"))
            mView.setAccelerometerUpDownEnabled(accelerometer.getBoolean("upDownEnabled"));
        }
      }
      boolean hasPreviewImage = false;
      JSONObject images = mJSON.getJSONObject("images");
      if (images != null) {
        if (images.has("preview")) {
          String previewURL = this.buildURL(images.getString("preview"), urlBase);
          if (this.isHTTPURL(previewURL)) {
            byte[] previewData = new PLHTTPFileDownloader(previewURL).download();
            if (previewData != null) {
              panorama.setPreviewImage(new PLImage(previewData));
              hasPreviewImage = true;
            }
          } else {
            PLIImage previewImage = this.getLocalImage(previewURL, colorFormat);
            if (previewImage != null) {
              panorama.setPreviewImage(previewImage);
              hasPreviewImage = true;
            }
          }
        }
        if (mHotspotTextures.size() > 0) mHotspotTextures.clear();
        JSONArray hotspots = mJSON.getJSONArray("hotspots");
        if (hotspots != null) {
          for (int i = 0, hotspotsCount = hotspots.length(); i < hotspotsCount; i++) {
            JSONObject hotspot = hotspots.getJSONObject(i);
            if (hotspot != null) {
              if (hotspot.has("image")) {
                long identifier = (hotspot.has("id") ? hotspot.getLong("id") : -1);
                float atv = (hotspot.has("atv") ? (float) hotspot.getDouble("atv") : 0.0f);
                float ath = (hotspot.has("ath") ? (float) hotspot.getDouble("ath") : 0.0f);
                float width =
                    (hotspot.has("width")
                        ? (float) hotspot.getDouble("width")
                        : PLConstants.kDefaultHotspotSize);
                float height =
                    (hotspot.has("height")
                        ? (float) hotspot.getDouble("height")
                        : PLConstants.kDefaultHotspotSize);
                PLIHotspot currentHotspot = new PLHotspot(identifier, atv, ath, width, height);
                if (hotspot.has("alpha")) {
                  currentHotspot.setDefaultAlpha((float) hotspot.getDouble("alpha"));
                  currentHotspot.setAlpha(currentHotspot.getDefaultAlpha());
                }
                if (hotspot.has("overAlpha")) {
                  currentHotspot.setDefaultOverAlpha((float) hotspot.getDouble("overAlpha"));
                  currentHotspot.setOverAlpha(currentHotspot.getDefaultOverAlpha());
                }
                if (hotspot.has("onClick")) currentHotspot.setOnClick(hotspot.getString("onClick"));
                this.loadHotspotTexture(
                    currentHotspot, hotspot.getString("image"), urlBase, colorFormat);
                panorama.addHotspot(currentHotspot);
              }
            }
          }
          mHotspotTextures.clear();
        }
        if (panoramaType == PLPanoramaType.PLPanoramaTypeCubic) {
          PLCubicPanorama cubicPanorama = (PLCubicPanorama) panorama;
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationFront,
              images,
              "front",
              urlBase,
              hasPreviewImage,
              colorFormat);
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationBack,
              images,
              "back",
              urlBase,
              hasPreviewImage,
              colorFormat);
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationLeft,
              images,
              "left",
              urlBase,
              hasPreviewImage,
              colorFormat);
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationRight,
              images,
              "right",
              urlBase,
              hasPreviewImage,
              colorFormat);
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationUp,
              images,
              "up",
              urlBase,
              hasPreviewImage,
              colorFormat);
          this.loadCubicPanoramaImage(
              cubicPanorama,
              PLCubeFaceOrientation.PLCubeFaceOrientationDown,
              images,
              "down",
              urlBase,
              hasPreviewImage,
              colorFormat);
        } else {
          if (images.has("image")) {
            String imageURL = this.buildURL(images.getString("image"), urlBase);
            if (this.isHTTPURL(imageURL))
              mView
                  .getDownloadManager()
                  .add(
                      new PLHTTPFileDownloader(
                          imageURL,
                          new PLPanoramaImageFileDownloaderListener(panorama, colorFormat)));
            else if (panoramaType == PLPanoramaType.PLPanoramaTypeSpherical2)
              ((PLSpherical2Panorama) panorama).setImage(this.getLocalImage(imageURL, colorFormat));
            else if (panorama instanceof PLIQuadricPanorama)
              ((PLIQuadricPanorama) panorama)
                  .setImage(this.getLocalImageAsynchronously(imageURL, colorFormat));
          } else if (!hasPreviewImage)
            throw new RuntimeException("images.image and images.preview properties not exist");
        }
        if (images.has("preload")) mIsPreloadingImages = images.getBoolean("preload");
      } else throw new RuntimeException("images property not exists");
      if (mIsPreloadingImages) mView.getDownloadManager().start();
      mView
          .getActivity()
          .runOnUiThread(
              new Runnable() {
                @Override
                public void run() {
                  mView.reset(false);
                  if (mTransition != null && mView.getPanorama() != null) {
                    mTransition
                        .getListeners()
                        .add(
                            new PLTransitionListener() {
                              @Override
                              public boolean isRemovableListener() {
                                return true;
                              }

                              @Override
                              public void didBeginTransition(PLITransition transition) {
                                synchronized (transition) {
                                  parseCameraJSON(transition.getNewPanorama());
                                }
                              }

                              @Override
                              public void didProcessTransition(
                                  PLITransition transition, int progressPercentage) {}

                              @Override
                              public void didStopTransition(
                                  PLITransition transition, int progressPercentage) {
                                if (parseSensorialRotationJSON()) {
                                  mView.getDownloadManager().removeAll();
                                  didStop(true);
                                }
                              }

                              @Override
                              public void didEndTransition(PLITransition transition) {
                                if (parseSensorialRotationJSON()) {
                                  if (!mIsPreloadingImages) mView.getDownloadManager().start();
                                  didComplete(true);
                                }
                              }
                            });
                    mView.startTransition(mTransition, panorama);
                  } else {
                    if (parseCameraJSON(panorama)) {
                      mView.setPanorama(panorama);
                      if (parseSensorialRotationJSON()) {
                        if (!mIsPreloadingImages) mView.getDownloadManager().start();
                        didComplete(false);
                      }
                    }
                  }
                }
              });
    } catch (Throwable e) {
      this.didError(e);
    }
  }