From 0e8d900ed4f6197eaeeb473859966fd9ed43d57d Mon Sep 17 00:00:00 2001
From: str4d <str4d@mail.i2p>
Date: Mon, 10 Nov 2014 01:56:35 +0000
Subject: [PATCH] I2PTunnel secondary action to open linkable tunnels

---
 .../i2ptunnel/TunnelDetailFragment.java       |   4 +-
 .../i2p/android/i2ptunnel/TunnelEntry.java    |  82 +++++++++++++-----
 .../android/i2ptunnel/TunnelEntryAdapter.java |  32 +++++--
 .../ic_open_in_browser_white_24dp.png         | Bin 0 -> 276 bytes
 .../ic_open_in_browser_white_24dp.png         | Bin 0 -> 234 bytes
 .../ic_open_in_browser_white_24dp.png         | Bin 0 -> 348 bytes
 .../ic_open_in_browser_white_24dp.png         | Bin 0 -> 480 bytes
 .../main/res/layout/listitem_i2ptunnel.xml    |  22 ++++-
 8 files changed, 106 insertions(+), 34 deletions(-)
 create mode 100644 app/src/main/res/drawable-hdpi/ic_open_in_browser_white_24dp.png
 create mode 100644 app/src/main/res/drawable-mdpi/ic_open_in_browser_white_24dp.png
 create mode 100644 app/src/main/res/drawable-xhdpi/ic_open_in_browser_white_24dp.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/ic_open_in_browser_white_24dp.png

diff --git a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelDetailFragment.java b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelDetailFragment.java
index 6d51f3054..f23b3a00e 100644
--- a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelDetailFragment.java
+++ b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelDetailFragment.java
@@ -97,10 +97,10 @@ public class TunnelDetailFragment extends Fragment {
             description.setText(mTunnel.getDescription());
 
             TextView targetIfacePort = (TextView) v.findViewById(R.id.tunnel_target_interface_port);
-            targetIfacePort.setText(mTunnel.getIfacePort());
+            targetIfacePort.setText(mTunnel.getTunnelLink(false));
 
             TextView accessIfacePort = (TextView) v.findViewById(R.id.tunnel_access_interface_port);
-            accessIfacePort.setText(mTunnel.getIfacePort());
+            accessIfacePort.setText(mTunnel.getTunnelLink(false));
 
             CheckBox autoStart = (CheckBox) v.findViewById(R.id.tunnel_autostart);
             autoStart.setChecked(mTunnel.startAutomatically());
diff --git a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntry.java b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntry.java
index d2e1d982d..4c8a4550b 100644
--- a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntry.java
+++ b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntry.java
@@ -90,6 +90,16 @@ public class TunnelEntry {
         else return NOT_RUNNING;
     }
 
+    public boolean isRunning() {
+        switch (getStatus()) {
+            case STANDBY:
+            case RUNNING:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     public boolean isClient() {
         return TunnelUtil.isClient(mController.getType());
     }
@@ -100,18 +110,42 @@ public class TunnelEntry {
         return Boolean.parseBoolean(mController.getSharedClient());
     }
 
+    /**
+     * Call this to see if it is okay to linkify getClientLink()
+     * @return true if getClientLink() can be linkified, false otherwise.
+     */
+    public boolean isClientLinkValid() {
+        return ("ircclient".equals(mController.getType())) &&
+                mController.getListenOnInterface() != null &&
+                mController.getListenPort() != null;
+    }
+
+    /**
+     * @return valid host:port only if isClientLinkValid() is true
+     */
+    public String getClientLink(boolean linkify) {
+        String host = getClientInterface();
+        String port = getClientPort();
+        String link =  host + ":" + port;
+        if (linkify) {
+            if ("ircclient".equals(mController.getType()))
+                link = "irc://" + link;
+        }
+        return link;
+    }
+
     public String getClientInterface() {
+        String rv;
         if ("streamrclient".equals(mController.getType()))
-            return mController.getTargetHost();
+            rv = mController.getTargetHost();
         else
-            return mController.getListenOnInterface();
+            rv = mController.getListenOnInterface();
+        return rv != null ? rv : "";
     }
 
     public String getClientPort() {
         String rv = mController.getListenPort();
-        if (rv != null)
-            return rv;
-        return "";
+        return rv != null ? rv : "";
     }
 
     public String getClientDestination() {
@@ -128,10 +162,10 @@ public class TunnelEntry {
     /* Server tunnel data */
 
     /**
-     * Call this to see if it is okay to linkify getServerTarget()
-     * @return true if getServerTarget() can be linkified, false otherwise.
+     * Call this to see if it is okay to linkify getServerLink()
+     * @return true if getServerLink() can be linkified, false otherwise.
      */
-    public boolean isServerTargetLinkValid() {
+    public boolean isServerLinkValid() {
         return ("httpserver".equals(mController.getType()) ||
                 "httpbidirserver".equals(mController.getType())) &&
                 mController.getTargetHost() != null &&
@@ -139,9 +173,9 @@ public class TunnelEntry {
     }
 
     /**
-     * @return valid host:port only if isServerTargetLinkValid() is true
+     * @return valid host:port only if isServerLinkValid() is true
      */
-    public String getServerTarget() {
+    public String getServerLink(boolean linkify) {
         String host;
         if ("streamrserver".equals(getInternalType()))
             host = mController.getListenOnInterface();
@@ -152,7 +186,13 @@ public class TunnelEntry {
         if (port == null) port = "";
         if (host.indexOf(':') >= 0)
             host = '[' + host + ']';
-        return host + ":" + port;
+        String link =  host + ":" + port;
+        if (linkify) {
+            if ("httpserver".equals(mController.getType()) ||
+                    "httpbidirserver".equals(mController.getType()))
+                link = "http://" + link;
+        }
+        return link;
     }
 
     public String getDestinationBase64() {
@@ -183,18 +223,14 @@ public class TunnelEntry {
 
     /* Other output formats */
 
-    public String getIfacePort() {
-        if (isClient()) {
-            String host;
-            if ("streamrclient".equals(getInternalType()))
-                host = mController.getTargetHost();
-            else
-                host = mController.getListenOnInterface();
-            String port = mController.getListenPort();
-            if (host == null) host = "";
-            if (port == null) port = "";
-            return host + ":" + port;
-        } else return getServerTarget();
+    public boolean isTunnelLinkValid() {
+        if (isClient()) return isClientLinkValid();
+        else return isServerLinkValid();
+    }
+
+    public String getTunnelLink(boolean linkify) {
+        if (isClient()) return getClientLink(linkify);
+        else return getServerLink(linkify);
     }
 
     public String getDetails() {
diff --git a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntryAdapter.java b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntryAdapter.java
index b1139969d..223d4c448 100644
--- a/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntryAdapter.java
+++ b/app/src/main/java/net/i2p/android/i2ptunnel/TunnelEntryAdapter.java
@@ -1,9 +1,9 @@
 package net.i2p.android.i2ptunnel;
 
-import java.util.List;
-
-import net.i2p.android.router.R;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -11,6 +11,10 @@ import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import net.i2p.android.router.R;
+
+import java.util.List;
+
 public class TunnelEntryAdapter extends ArrayAdapter<TunnelEntry> {
     private final LayoutInflater mInflater;
 
@@ -31,11 +35,14 @@ public class TunnelEntryAdapter extends ArrayAdapter<TunnelEntry> {
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         View v = mInflater.inflate(R.layout.listitem_i2ptunnel, parent, false);
-        TunnelEntry tunnel = getItem(position);
+        final TunnelEntry tunnel = getItem(position);
 
         ImageView status = (ImageView) v.findViewById(R.id.tunnel_status);
         status.setImageDrawable(tunnel.getStatusIcon());
-        status.setBackground(tunnel.getStatusBackground());
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
+            status.setBackgroundDrawable(tunnel.getStatusBackground());
+        else
+            status.setBackground(tunnel.getStatusBackground());
 
         TextView name = (TextView) v.findViewById(R.id.tunnel_name);
         name.setText(tunnel.getName());
@@ -44,7 +51,20 @@ public class TunnelEntryAdapter extends ArrayAdapter<TunnelEntry> {
         type.setText(tunnel.getDescription());
 
         TextView ifacePort = (TextView) v.findViewById(R.id.tunnel_interface_port);
-        ifacePort.setText(tunnel.getIfacePort());
+        ifacePort.setText(tunnel.getTunnelLink(false));
+
+        if (tunnel.isRunning() && tunnel.isTunnelLinkValid()) {
+            View open = v.findViewById(R.id.tunnel_open);
+            open.setVisibility(View.VISIBLE);
+            open.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    Intent i = new Intent(Intent.ACTION_VIEW);
+                    i.setData(Uri.parse(tunnel.getTunnelLink(true)));
+                    getContext().startActivity(i);
+                }
+            });
+        }
 
         return v;
     }
diff --git a/app/src/main/res/drawable-hdpi/ic_open_in_browser_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_open_in_browser_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5b0f1b632059eb0f914a8c8007dbd01f8d3f19c
GIT binary patch
literal 276
zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;Lb6AYF9SoB8UsT^3j@P1pisjL
z28L1t28LG&3=CE?7#PG0=Ijcz0ZK3>dAqwX{BQ3+vmeOgEbxddW?<k3n!+v2$S`Y;
z1W>TY)5S5w<M`XFXE_@Tcvv62Q?%+x2-nK{z$S9nb<efucfZIl)$r9<z3~1*i%Xzw
zaY*wkhgsUX!H=J~y=`KXJJZ{|w}r?1kOzmWz&>WhoezF6v-q0{d!0Eh=WuN~OVjQv
zI|cs4PG(lT({R;px8r(;&C9O{d_S>b>S24nw{K!zXY5+L*dS$RLC)^~w=b|h{#5Lg
T*mCJT&|M6ku6{1-oD!M<vQcA8

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-mdpi/ic_open_in_browser_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_open_in_browser_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..1585ff18dff3327d71c82398df20015032cf1e68
GIT binary patch
literal 234
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1g=CK)Uj~LMH3o);76yi2K%s^g
z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tW<QX}S>O=~G=W<fgc%uT%>kL6
z<muuVVsSe8$9V^-6p1}jO#k=)>F3|`OTJ+h<NhLniq2Jy5)M1rYUVB7*T$xx;^@pg
z<G#b1Hntt+(j6BsGzb~-9{I(nUN4Yc(K?M~LMBH=<0?f4&AmyNKRnw1e!=-gF3t>>
XV((sL@(NG{+Q#7N>gTe~DWM4fIZ#Q|

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xhdpi/ic_open_in_browser_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_open_in_browser_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..2da8b3feeb6ed5b38df3d61afeae4ca6b131355a
GIT binary patch
literal 348
zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tg=CK)Uj~LMH3o);76yi2K%s^g
z3=E|}g|8AA7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^+;1OBOz`!jG!i)^F=12eq
zuXwsRhD02Gd)<+%$w8#yVf{7H*u%;@%H%JmX3c#2%IuL<RC&t_p}-rr#8}Th=y@W|
z+hYE}spbFVzz>;OZLw~QtjE|@-khAlneFelW@k%di}B|Z(fcOfYT*{qslGAw(z+c7
zocCCMTlJ=k`^(oOy-XJy1lzJ%|1mt7pRAye&i$szMdib}%T6wg_iJW!Fx;u>5MVyL
zXTAW_zdii|j1Oxjchvc^UKb5WC~-Kt<g0QV`?-3#JziqA46Liy9nKej;C5<jZjJo}
nLs2GCceAp%`h_9^3=dc*E||y|AE9mr^dEz#tDnm{r-UW|7#n}E

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxhdpi/ic_open_in_browser_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_open_in_browser_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3fedfa5c3ce8c56fb79d08eb0db8b7b9df2ca15
GIT binary patch
literal 480
zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw6p}rHd>I(3)EF2VS{N990fib~
zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g&?
zz`z*c>EalYaqsPov%ZHN1Xv%Ww-m6LE@9qrk!2B!R0Fe9z`@t7fjll8B0j1MCKO2K
zaXcxt;eK7{kpFGhx4Kzh&z}ivJKLqpp)$dNiBly&Frb(F&LdW{0}+qp!e^AMnX@M?
zt617<Ue2tj-J3$n(kwV`%w=njsND9(q=os-<1^oR=C;@#?3@}D{jF=S!UgFv+XW|-
zHeG5DTu{${>s&22S4-w;7NHmHjvH=R-;jzw96t5ZnKLiu?EUsn)#1;av%U-;Z`btk
zCatb`K0`t6H)kJfQhN7cLx-5#rG`>R?zv5vsIaRl*sbwJ-gHUEq%ijLcYc5Wni`tM
z`s4LaEqfLr4~0e+t%6@0o$gi#P5gcSmDQJ$)EAaIvc3Ez!fd;~m97)cu6ixwUwc`c
zf7|6a+o=mx`+`pD$n;wr+pX_x5Rp6GZ1YhC$*3E1=PuuLIn?l#H6$1=#52`0)O|bF
U;9Fbd3=DV%Pgg&ebxsLQ0EnQzrT_o{

literal 0
HcmV?d00001

diff --git a/app/src/main/res/layout/listitem_i2ptunnel.xml b/app/src/main/res/layout/listitem_i2ptunnel.xml
index afb819226..8833f51c2 100644
--- a/app/src/main/res/layout/listitem_i2ptunnel.xml
+++ b/app/src/main/res/layout/listitem_i2ptunnel.xml
@@ -30,18 +30,32 @@
         android:text="Tunnel name"
         android:textAppearance="@style/TextAppearance.AppCompat.Primary" />
 
+    <!-- Open link -->
+    <ImageView
+        android:id="@+id/tunnel_open"
+        android:layout_width="@dimen/listitem_icon_size"
+        android:layout_height="@dimen/listitem_icon_size"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+        android:layout_marginStart="@dimen/listitem_horizontal_margin"
+        android:contentDescription="Open"
+        android:src="@drawable/ic_open_in_browser_white_24dp"
+        android:visibility="gone" />
+
     <!-- Interface:port the tunnel listens on or points to -->
     <TextView
         android:id="@+id/tunnel_interface_port"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentRight="true"
         android:layout_alignTop="@id/tunnel_name"
         android:layout_marginLeft="4dp"
         android:layout_marginStart="4dp"
         android:layout_toEndOf="@id/tunnel_name"
+        android:layout_toLeftOf="@id/tunnel_open"
         android:layout_toRightOf="@id/tunnel_name"
+        android:layout_toStartOf="@id/tunnel_open"
         android:ellipsize="start"
         android:gravity="right"
         android:maxLines="1"
@@ -57,6 +71,8 @@
         android:layout_alignParentBottom="true"
         android:layout_alignStart="@+id/tunnel_name"
         android:layout_marginBottom="@dimen/listitem_text_bottom_margin_two_lines"
+        android:layout_toLeftOf="@id/tunnel_open"
+        android:layout_toStartOf="@id/tunnel_open"
         android:ellipsize="end"
         android:maxLines="1"
         android:text="Tunnel description"
-- 
GitLab