commit b88cdd9118e3a5f658ad8969d89b2f6cb09c72a8
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Jan 27 16:09:56 2022 +0100

    Download Update icons.

diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
index eeee4676..65c97f77 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogUpdateView.java
@@ -20,6 +20,7 @@
 package org.distorted.dialogs;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.Button;
@@ -37,10 +38,7 @@ import static android.view.View.GONE;
 
 public class RubikDialogUpdateView
   {
-  public RubikDialogUpdateView()
-    {
-
-    }
+  private ImageView mIcon;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -54,10 +52,10 @@ public class RubikDialogUpdateView
     TextView description = view.findViewById(R.id.updates_pane_description);
     description.setText(info.mDescription);
 
-    ImageView image = view.findViewById(R.id.updates_pane_image);
-    image.setBackgroundResource(R.drawable.unknown_icon);
+    mIcon = view.findViewById(R.id.updates_pane_image);
+    mIcon.setImageResource(R.drawable.unknown_icon);
 
-    image.setLayoutParams(pImage);
+    mIcon.setLayoutParams(pImage);
     view.setLayoutParams(pView);
 
     title.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
@@ -95,4 +93,11 @@ public class RubikDialogUpdateView
 
     return view;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setIcon(Bitmap icon)
+    {
+    mIcon.setImageBitmap(icon);
+    }
   }
diff --git a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java b/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
index a067f07c..570b1b19 100644
--- a/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
+++ b/src/main/java/org/distorted/dialogs/RubikDialogUpdates.java
@@ -19,8 +19,11 @@
 
 package org.distorted.dialogs;
 
+import java.util.ArrayList;
+
 import android.app.Dialog;
 import android.content.DialogInterface;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -49,6 +52,8 @@ public class RubikDialogUpdates extends AppCompatDialogFragment implements Rubik
   private LinearLayout mLayout;
   private int mMargin, mSize, mFontSize, mPadding;
 
+  private ArrayList<RubikDialogUpdateView> mPanes;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private View createView(FragmentActivity act, LayoutInflater inflater, float size, int minH)
@@ -72,6 +77,8 @@ public class RubikDialogUpdates extends AppCompatDialogFragment implements Rubik
   @Override
   public Dialog onCreateDialog(Bundle savedInstanceState)
     {
+    if( mPanes==null ) mPanes = new ArrayList<>();
+
     FragmentActivity act = getActivity();
     LayoutInflater inflater = act.getLayoutInflater();
     AlertDialog.Builder builder = new AlertDialog.Builder(act);
@@ -166,6 +173,7 @@ public class RubikDialogUpdates extends AppCompatDialogFragment implements Rubik
           RubikDialogUpdateView rubikView = new RubikDialogUpdateView();
           View pane = rubikView.createView(act,info,mFontSize,mPadding,pI,pV,pT,pB);
           mLayout.addView(pane);
+          mPanes.add(rubikView);
           }
 
         int numS = updates.getStartedNumber();
@@ -176,6 +184,7 @@ public class RubikDialogUpdates extends AppCompatDialogFragment implements Rubik
           RubikDialogUpdateView rubikView = new RubikDialogUpdateView();
           View pane = rubikView.createView(act,info,mFontSize,mPadding,pI,pV,pT,pB);
           mLayout.addView(pane);
+          mPanes.add(rubikView);
           }
 
         RubikNetwork network = RubikNetwork.getInstance();
@@ -216,9 +225,22 @@ public class RubikDialogUpdates extends AppCompatDialogFragment implements Rubik
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void iconDownloaded()
+  public void iconDownloaded(int ordinal, Bitmap icon)
     {
+    FragmentActivity act = getActivity();
 
+    if( act!=null )
+      {
+      act.runOnUiThread(new Runnable()
+        {
+        @Override
+        public void run()
+          {
+          RubikDialogUpdateView view = mPanes.get(ordinal);
+          if( view!=null ) view.setIcon(icon);
+          }
+        });
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/network/RubikNetwork.java b/src/main/java/org/distorted/network/RubikNetwork.java
index e41a2f91..2a6f1ce6 100644
--- a/src/main/java/org/distorted/network/RubikNetwork.java
+++ b/src/main/java/org/distorted/network/RubikNetwork.java
@@ -20,6 +20,7 @@
 package org.distorted.network;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
@@ -30,6 +31,8 @@ import java.security.NoSuchAlgorithmException;
 
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 
 import androidx.fragment.app.FragmentActivity;
 
@@ -52,7 +55,7 @@ public class RubikNetwork
 
   public interface Updatee
     {
-    void iconDownloaded();
+    void iconDownloaded(int ordinal, Bitmap bitmap);
     void receiveUpdate(RubikUpdates update);
     void errorUpdate();
     }
@@ -655,6 +658,67 @@ public class RubikNetwork
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private Bitmap downloadIcon(String url)
+    {
+    try
+      {
+      java.net.URL connectURL = new URL(url);
+      HttpURLConnection conn = (HttpURLConnection) connectURL.openConnection();
+      conn.setDoInput(true);
+      conn.connect();
+      InputStream input = conn.getInputStream();
+      return BitmapFactory.decodeStream(input);
+      }
+    catch (IOException e)
+      {
+      android.util.Log.e("D", "Failed to download "+url);
+      android.util.Log.e("D", e.getMessage() );
+      return null;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void iconThread()
+    {
+    int numC = mUpdates.getCompletedNumber();
+    int numS = mUpdates.getStartedNumber();
+
+    for(int c=0; c<numC; c++)
+      {
+      Bitmap icon = mUpdates.getCompletedIcon(c);
+
+      if( icon==null )
+        {
+        String url = mUpdates.getCompletedURL(c);
+        icon = downloadIcon(url);
+        }
+      if( icon!=null )
+        {
+        mUpdates.setCompletedIcon(c,icon);
+        mUpdatee.iconDownloaded(c,icon);
+        }
+      }
+
+    for(int s=0; s<numS; s++)
+      {
+      Bitmap icon = mUpdates.getStartedIcon(s);
+
+      if( icon==null )
+        {
+        String url = mUpdates.getStartedURL(s);
+        icon = downloadIcon(url);
+        }
+      if( icon!=null )
+        {
+        mUpdates.setStartedIcon(s,icon);
+        mUpdatee.iconDownloaded(numC+s,icon);
+        }
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private RubikNetwork()
@@ -778,7 +842,18 @@ public class RubikNetwork
 
   public void downloadIcons(Updatee updatee)
     {
+    initializeStatics();
+    mUpdatee = updatee;
+
+    Thread thread = new Thread()
+      {
+      public void run()
+        {
+        iconThread();
+        }
+      };
 
+    thread.start();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/network/RubikUpdates.java b/src/main/java/org/distorted/network/RubikUpdates.java
index d2cb5ec5..e3e959e7 100644
--- a/src/main/java/org/distorted/network/RubikUpdates.java
+++ b/src/main/java/org/distorted/network/RubikUpdates.java
@@ -19,11 +19,11 @@
 
 package org.distorted.network;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
+import java.util.ArrayList;
+import android.graphics.Bitmap;
 import org.distorted.objects.RubikObjectList;
 
-import java.util.ArrayList;
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class RubikUpdates
 {
@@ -37,6 +37,7 @@ public class RubikUpdates
     public final int mPercent;
     public final boolean mUpdateObject;
     public final boolean mUpdateExtras;
+    public Bitmap mIcon;
 
     public UpdateInfo(String shortName, String longName, String description, int objectMinor,
                       int extrasMinor, int percent, boolean updateO, boolean updateE)
@@ -49,6 +50,8 @@ public class RubikUpdates
       mPercent            = percent;
       mUpdateObject       = updateO;
       mUpdateExtras       = updateE;
+
+      mIcon = null;
       }
     }
 
@@ -133,6 +136,8 @@ public class RubikUpdates
     if( numLines>=1 )
       {
       mUrl = lines[0];
+      if( !mUrl.endsWith("/") ) mUrl += "/";
+
       for(int line=1; line<numLines; line++)
         {
         String[] elements = lines[line].split(",");
@@ -183,6 +188,52 @@ public class RubikUpdates
     return mStarted.size();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Bitmap getCompletedIcon(int ordinal)
+    {
+    return mCompleted.get(ordinal).mIcon;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Bitmap getStartedIcon(int ordinal)
+    {
+    return mStarted.get(ordinal).mIcon;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getCompletedURL(int ordinal)
+    {
+    UpdateInfo info = mCompleted.get(ordinal);
+    return info!=null ? mUrl + info.mObjectShortName + ".png" : null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getStartedURL(int ordinal)
+    {
+    UpdateInfo info = mStarted.get(ordinal);
+    return info!=null ? mUrl + info.mObjectShortName + ".png" : null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setCompletedIcon(int ordinal, Bitmap icon)
+    {
+    UpdateInfo info = mCompleted.get(ordinal);
+    info.mIcon = icon;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setStartedIcon(int ordinal, Bitmap icon)
+    {
+    UpdateInfo info = mStarted.get(ordinal);
+    info.mIcon = icon;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void showDebug()
diff --git a/src/main/java/org/distorted/screens/RubikScreenPlay.java b/src/main/java/org/distorted/screens/RubikScreenPlay.java
index 97ec7150..1b6ae9ec 100644
--- a/src/main/java/org/distorted/screens/RubikScreenPlay.java
+++ b/src/main/java/org/distorted/screens/RubikScreenPlay.java
@@ -25,6 +25,7 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Build;
 import android.os.Bundle;
@@ -747,7 +748,7 @@ public class RubikScreenPlay extends RubikScreenBase implements RubikNetwork.Upd
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void iconDownloaded()
+  public void iconDownloaded(int ordinal, Bitmap bitmap)
     {
     // empty
     }
