Browse Source

cleaned up layout stuff

master
Beau Kujath 3 days ago
parent
commit
a309db6d6d
  1. 6
      NetworkGenie/app/build.gradle
  2. BIN
      NetworkGenie/app/src/main/cpp/.networkgenie.c.swp
  3. 3
      NetworkGenie/app/src/main/cpp/icmp.c
  4. 19
      NetworkGenie/app/src/main/cpp/ip.c
  5. 64
      NetworkGenie/app/src/main/cpp/networkgenie.c
  6. 6
      NetworkGenie/app/src/main/cpp/networkgenie.h
  7. 10
      NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/ActivityMain.java
  8. 8
      NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/AdapterRule.java
  9. 2
      NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/Rule.java
  10. 55
      NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/ServiceSinkhole.java
  11. 3
      NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/Util.java
  12. 2
      NetworkGenie/app/src/main/res/layout/activity_genie.xml
  13. 1
      NetworkGenie/app/src/main/res/layout/main.xml
  14. 9
      NetworkGenie/app/src/main/res/layout/rule.xml

6
NetworkGenie/app/build.gradle

@ -29,7 +29,7 @@ android {
buildConfigField "boolean", "PLAY_STORE_RELEASE", "false"
buildConfigField "String", "HOSTS_FILE_URI", "\"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts\""
buildConfigField "String", "GITHUB_LATEST_API", "\"https://github.com/beaukuj15/NetworkGenie\""
buildConfigField "String", "GITHUB_LATEST_API", "\"https://api.github.com/repos/M66B/NetGuard/releases/latest\""
}
@ -39,7 +39,7 @@ android {
buildConfigField "boolean", "PLAY_STORE_RELEASE", "false"
buildConfigField "String", "HOSTS_FILE_URI", "\"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts\""
buildConfigField "String", "GITHUB_LATEST_API", "\"https://github.com/beaukuj15/NetworkGenie\""
buildConfigField "String", "GITHUB_LATEST_API", "\"https://api.github.com/repos/M66B/NetGuard/releases/latest\""
}
}
@ -67,6 +67,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.3.1'
@ -80,6 +81,7 @@ dependencies {
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
implementation libs.appcompat
implementation libs.material
implementation libs.activity
implementation libs.constraintlayout

BIN
NetworkGenie/app/src/main/cpp/.networkgenie.c.swp

3
NetworkGenie/app/src/main/cpp/icmp.c

@ -163,7 +163,6 @@ jboolean handle_icmp(const struct arguments *args,
} else {
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
pkt_ttl = 244;
}
log_android(ANDROID_LOG_ERROR, "GEE ICMP handling icmp from %s to %s, ttl: %u", source, dest, pkt_ttl);
@ -213,6 +212,7 @@ jboolean handle_icmp(const struct arguments *args,
// Open UDP socket
s->socket = open_icmp_socket(args, &s->icmp, pkt_ttl);
log_android(ANDROID_LOG_ERROR, "GEE ICMP new socket open res: %d", s->socket);
if (s->socket < 0) {
ng_free(s, __FILE__, __LINE__);
return 0;
@ -323,7 +323,6 @@ int open_icmp_socket(const struct arguments *args, const struct icmp_session *cu
} else {
log_android(ANDROID_LOG_ERROR, "GEE ICMP successfully set ttl to custom val: %d", ttl);
}
return sock;
}

19
NetworkGenie/app/src/main/cpp/ip.c

@ -141,7 +141,7 @@ void handle_ip(const struct arguments *args,
uint8_t version = (*pkt) >> 4;
if (version == 4) {
if (length < sizeof(struct iphdr)) {
log_android(ANDROID_LOG_WARN, "IP4 packet too short length %d", length);
log_android(ANDROID_LOG_ERROR, "IP4 packet too short length %d", length);
return;
}
@ -251,7 +251,7 @@ void handle_ip(const struct arguments *args,
// TODO checksum (IPv6)
} else if (protocol == IPPROTO_TCP) {
if (length - (payload - pkt) < sizeof(struct tcphdr)) {
log_android(ANDROID_LOG_WARN, "TCP packet too short");
log_android(ANDROID_LOG_ERROR, "!! GEE IP TCP packet too short");
return;
}
@ -304,6 +304,7 @@ void handle_ip(const struct arguments *args,
if (get_sni(data, datalen, server_name)) {
log_android(ANDROID_LOG_INFO, "TLS server name: %s", server_name);
uid = get_uid(version, protocol, saddr, sport, daddr, dport);
dns_resolved(args, server_name, server_name, dest, -1, uid);
}
@ -322,6 +323,9 @@ void handle_ip(const struct arguments *args,
uid = get_uid_q(args, version, protocol, source, sport, dest, dport);
}
if (*server_name != 0) {
log_android(ANDROID_LOG_ERROR, "GEE NEW TLSS found sni name: %s from app uid: %d w dest ip %s:%u", server_name, uid, dest, dport, ttl);
}
log_android(ANDROID_LOG_ERROR,
"GEE IP handling IP Packet v%d %s/%u > %s/%u proto %d flags %s uid %d ttl %u",
version, source, sport, dest, dport, protocol, flags, uid, ttl);
@ -331,9 +335,12 @@ void handle_ip(const struct arguments *args,
// Check if allowed
int allowed = 0;
struct allowed *redirect = NULL;
if (protocol == IPPROTO_UDP && has_udp_session(args, pkt, payload))
bool is_dns = (dport == 53 || sport == 53);
if (protocol == IPPROTO_UDP && has_udp_session(args, pkt, payload) && (!is_dns))
allowed = 1; // could be a lingering/blocked session
else if (protocol == IPPROTO_TCP && (!syn || (uid == 0 && dport == 53)) && *server_name == 0)
//else if (protocol == IPPROTO_TCP && (!syn || (uid == 0 && dport == 53))) // assumes each tcp packet that is not a syn is allowed by Netguard
else if (protocol == IPPROTO_TCP && (uid == 0 && dport == 53))
allowed = 1; // assume existing session
else {
jobject objPacket = create_packet(
@ -343,6 +350,10 @@ void handle_ip(const struct arguments *args,
allowed = redirect->bypass;
new_sender = redirect->send_addr;
//if (allowed == 12) {
// // TODO try to swap out the ip ttl or some other value in real packet
//}
// if there was a gui bypass action, then update the send address in debug_conn
if (allowed == 3 || allowed == 5 || allowed == 7 || allowed == 9 || allowed == 10) {
debug_set_send(new_sender);

64
NetworkGenie/app/src/main/cpp/networkgenie.c

@ -198,6 +198,68 @@ Java_com_breakpointingbad_networkgenie_ServiceSinkhole_jni_1get_1mtu(JNIEnv *env
return get_mtu();
}
JNIEXPORT jint JNICALL
Java_com_breakpointingbad_networkgenie_ServiceSinkhole_jni_1get_1genie_1val(JNIEnv *env, jobject instance) {
return 2;
}
JNIEXPORT jstring JNICALL
Java_com_breakpointingbad_networkgenie_ServiceSinkhole_getNativeString(JNIEnv *env, jobject instance) {
// TODO: implement getNativeString()
return (*env)->NewStringUTF(env,"Hello World! From native code!");
}
JNIEXPORT jint JNICALL
Java_com_breakpointingbad_networkgenie_ServiceSinkhole_jni_1get_1conn_1stats(
JNIEnv *env, jobject instance, jlong context) {
struct context *ctx = (struct context *) context;
if (pthread_mutex_lock(&ctx->lock))
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
jintArray jarray = (*env)->NewIntArray(env, 5);
jint *jcount = (*env)->GetIntArrayElements(env, jarray, NULL);
struct ng_session *s = ctx->ng_session;
int active_count = 0;
while (s != NULL) {
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
if (!s->icmp.stop) {
jcount[0]++;
active_count ++;
}
} else if (s->protocol == IPPROTO_UDP) {
if (s->udp.state == UDP_ACTIVE) {
jcount[1]++;
active_count ++;
}
} else if (s->protocol == IPPROTO_TCP) {
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) {
jcount[2]++;
active_count ++;
}
}
s = s->next;
}
log_android(ANDROID_LOG_ERROR, "GENIE NEW final num active conns: %d", active_count);
return active_count;
}
JNIEXPORT jintArray JNICALL
Java_com_breakpointingbad_networkgenie_ServiceSinkhole_jni_1get_1stats(
JNIEnv *env, jobject instance, jlong context) {
@ -1131,3 +1193,5 @@ Java_com_breakpointingbad_networkgenie_Util_dump_1memory_1profile(JNIEnv *env, j
#endif
}

6
NetworkGenie/app/src/main/cpp/networkgenie.h

@ -452,12 +452,15 @@ jboolean handle_tcp(const struct arguments *args,
#define DEBUG_SRC_IP "10.1.10.69"
#define DEBUG_SRC_IP "10.1.10.222"
int debug_socket_init(const struct arguments *args, int epoll_fd);
void debug_set_send(char* new_dest);
char * get_app_con_info(const struct arguments *args, int uid);
void write_debug_socket(const struct arguments *args, int epoll_fd, const uint8_t *buffer, size_t length);
struct ng_session *get_debug_session(const struct arguments *args);
@ -517,6 +520,7 @@ ssize_t write_tcp(const struct arguments *args, const struct tcp_session *cur,
const uint8_t *data, size_t datalen,
int syn, int ack, int fin, int rst);
uint8_t char2nible(const char c);
void hex2bytes(const char *hex, uint8_t *buffer);

10
NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/ActivityMain.java

@ -86,6 +86,9 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
private ImageView ivQueue;
private Button genieButton;
public Button genieConnButton;
public TextView genieConnText;
private SwitchCompat swEnabled;
private ImageView ivMetered;
private SwipeRefreshLayout swipeRefresh;
@ -168,6 +171,9 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
genieButton = actionView.findViewById(R.id.genieButton);
genieButton.setOnClickListener(this::openGenie);
//genieConnButton.setOnClickListener(this::getPerAppInfo);
// Icon
@ -327,6 +333,9 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
adapter = new AdapterRule(this, findViewById(R.id.vwPopupAnchor));
rvApplication.setAdapter(adapter);
// Swipe to refresh
TypedValue tv = new TypedValue();
getTheme().resolveAttribute(R.attr.colorPrimary, tv, true);
@ -484,6 +493,7 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
checkExtras(getIntent());
}
public void openGenie(View view) {
startActivity(new Intent(ActivityMain.this, GenieActivity.class));
}

8
NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/AdapterRule.java

@ -201,10 +201,13 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
tvInternet = itemView.findViewById(R.id.tvInternet);
tvDisabled = itemView.findViewById(R.id.tvDisabled);
btnRelated = itemView.findViewById(R.id.btnRelated);
ibSettings = itemView.findViewById(R.id.ibSettings);
ibLaunch = itemView.findViewById(R.id.ibLaunch);
cbApply = itemView.findViewById(R.id.cbApply);
llScreenWifi = itemView.findViewById(R.id.llScreenWifi);
@ -342,6 +345,7 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
final boolean filter = prefs.getBoolean("filter", false);
final boolean notify_access = prefs.getBoolean("notify_access", false);
// Get rule
final Rule rule = listFiltered.get(position);
@ -365,6 +369,7 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
holder.ivIcon.setImageResource(android.R.drawable.sym_def_app_icon);
else {
Uri uri = Uri.parse("android.resource://" + rule.packageName + "/" + rule.icon);
// TODO: fix Glide app import
GlideApp.with(holder.itemView.getContext())
.applyDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565))
.load(uri)
@ -372,6 +377,7 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
//.skipMemoryCache(true)
.override(iconSize, iconSize)
.into(holder.ivIcon);
}
// Show application label
@ -483,6 +489,8 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
}
});
// Launch application settings
if (rule.expanded) {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);

2
NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/Rule.java

@ -121,6 +121,8 @@ public class Rule {
return cacheEnabled.get(info);
}
public static void clearCache(Context context) {
Log.i(TAG, "Clearing cache");
synchronized (context.getApplicationContext()) {

55
NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/ServiceSinkhole.java

@ -19,6 +19,7 @@ package com.breakpointingbad.networkgenie;
Copyright 2015-2024 by Marcel Bokhorst (M66B)
*/
import android.Manifest;
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.ForegroundServiceStartNotAllowedException;
@ -144,10 +145,12 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
private static String currConnInfo = "none";
private static String currGenieDestIp = "1.1.1.1"; //207.x
private Map<Integer, ArrayList<String>> genieAppConnList = new HashMap<>();
private static Object jni_lock = new Object();
private static long jni_context = 0;
private Thread tunnelThread = null;
private ServiceSinkhole.Builder last_builder = null;
private Builder last_builder = null;
private ParcelFileDescriptor vpn = null;
private boolean temporarilyStopped = false;
@ -566,7 +569,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
List<Rule> listAllowed = getAllowedRules(listRule);
ServiceSinkhole.Builder builder = getBuilder(listAllowed, listRule);
Builder builder = getBuilder(listAllowed, listRule);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
last_builder = builder;
@ -1140,7 +1143,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
Log.d(TAG, "Start foreground state=" + state.toString());
} else {
if (Util.canNotify(ServiceSinkhole.this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -2120,17 +2123,14 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
currConnInfo = cinfo;
}
int dport = packet.dport;
if (dport == 53 || dport == 443 || dport == 80) {
genieLogPktInfo(packet);
}
allowed.sender = sendConn;
System.out.println("Genie handling sink pkt with daddr: " + packet.daddr + ", current genie dest: " + currGenieDestIp);
System.out.println("return some allowed object: " + allowed.toString() + " for packet: " + packet);
System.out.println("return allowed object: " + allowed.toString() + " for packet: " + packet);
return allowed;
}
public static String getLastInfo() {
return genieInfo;
}
@ -2168,28 +2168,9 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
private String readRom()
{
return "blahh";
return "test";
}
private void genieLogPktInfo(Packet packet) {
int dport = packet.dport;
int sport = packet.sport;
String ptype = "using something";
if (dport == 80) {
ptype = "cleartext HTTP";
} else if(dport == 443) {
ptype = "HTTPS";
} else if(dport == 53) {
ptype = "DNS reply";
} else if(sport == 53) {
ptype = "DNS response";
}
DatabaseHelper dh = DatabaseHelper.getInstance(this);
String dname = dh.getQName(packet.uid, packet.daddr);
String cinfo = packet.saddr + ">" + sport + ">" + packet.daddr + ">" + dport + " TO: " + dname;
System.out.println("Genie found a packet " + ptype + ": " + cinfo + " from uid: " + packet.uid);
}
// Called from native code
private void accountUsage(Usage usage) {
@ -2550,7 +2531,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
// Build Wi-Fi action
Intent riWifi = new Intent(this, ServiceSinkhole.class);
riWifi.putExtra(ServiceSinkhole.EXTRA_COMMAND, ServiceSinkhole.Command.set);
riWifi.putExtra(ServiceSinkhole.EXTRA_COMMAND, Command.set);
riWifi.putExtra(ServiceSinkhole.EXTRA_NETWORK, "wifi");
riWifi.putExtra(ServiceSinkhole.EXTRA_UID, uid);
riWifi.putExtra(ServiceSinkhole.EXTRA_PACKAGE, packages[0]);
@ -2566,7 +2547,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
// Build mobile action
Intent riOther = new Intent(this, ServiceSinkhole.class);
riOther.putExtra(ServiceSinkhole.EXTRA_COMMAND, ServiceSinkhole.Command.set);
riOther.putExtra(ServiceSinkhole.EXTRA_COMMAND, Command.set);
riOther.putExtra(ServiceSinkhole.EXTRA_NETWORK, "other");
riOther.putExtra(ServiceSinkhole.EXTRA_UID, uid);
riOther.putExtra(ServiceSinkhole.EXTRA_PACKAGE, packages[0]);
@ -2582,7 +2563,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
// Show notification
if (internet) {
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3197,7 +3178,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
notification.bigText(getString(R.string.msg_revoked));
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3228,7 +3209,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
notification.bigText(getString(R.string.msg_always_on_lockdown));
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3263,7 +3244,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
notification.bigText(getString(R.string.msg_autostart));
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3294,7 +3275,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
notification.setSummaryText(message);
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3387,7 +3368,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}
@ -3414,7 +3395,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
.setVisibility(NotificationCompat.VISIBILITY_SECRET);
if (Util.canNotify(this))
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.checkSelfPermission(ServiceSinkhole.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
System.out.println("unable to get permission to post notificatoins..");
return;
}

3
NetworkGenie/app/src/main/java/com/breakpointingbad/networkgenie/Util.java

@ -120,6 +120,9 @@ public class Util {
private static native String jni_getprop(String name);
public static native String getNativeString();
private static native boolean is_numeric_address(String ip);
private static native void dump_memory_profile();

2
NetworkGenie/app/src/main/res/layout/activity_genie.xml

@ -258,7 +258,7 @@
android:inputType="number"
android:text="ttl"
android:textAlignment="inherit"
android:textColor="#ff5722"
android:textColor="@color/colorTealPrimary"
android:textSize="26sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/destPortInput"

1
NetworkGenie/app/src/main/res/layout/main.xml

@ -97,6 +97,7 @@
android:textAppearance="@style/TextSmall" />
</LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"

9
NetworkGenie/app/src/main/res/layout/rule.xml

@ -289,15 +289,6 @@
android:layout_marginTop="4dp"
android:text="@string/title_apply" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="14dp"
android:textColor="#ff0011"
android:text="Genie # of active connections:"
android:textAppearance="@style/TextTitle" />
<TextView
android:layout_width="wrap_content"

Loading…
Cancel
Save