summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-10-22 23:36:34 +0200
committercyBerta <cyberta@riseup.net>2021-10-22 23:36:34 +0200
commit213e42d82d360cdf7f5632782a9a0cb879c1b4f5 (patch)
treed5f5e98bb2f5294b2fe260719c8b148a344f3bd8
parent726f06f564e2743048baea5624daa96a511aace3 (diff)
show tor and snowflake connection status in provider setup screens
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java161
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java23
-rw-r--r--app/src/main/res/drawable/ic_snowflake.pngbin0 -> 2653 bytes
-rw-r--r--app/src/main/res/drawable/ic_tor.pngbin0 -> 10221 bytes
-rw-r--r--app/src/main/res/drawable/v_vertical_gradient.xml8
-rw-r--r--app/src/main/res/layout-xlarge/v_loading_screen.xml138
-rw-r--r--app/src/main/res/layout/v_add_provider.xml57
-rw-r--r--app/src/main/res/layout/v_loading_screen.xml139
-rw-r--r--app/src/main/res/layout/v_log_item.xml16
-rw-r--r--app/src/main/res/values/strings.xml1
10 files changed, 474 insertions, 69 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
index 7d452200..a0c046de 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
@@ -5,20 +5,28 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import java.util.List;
import java.util.Observable;
import java.util.Observer;
@@ -31,8 +39,14 @@ import se.leap.bitmaskclient.tor.TorStatusObservable;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getStringForCurrentStatus;
/**
* Base Activity for configuration wizard activities
@@ -55,6 +69,32 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
protected LinearLayout loadingScreen;
@Nullable
+ @BindView(R.id.btn_connection_detail)
+ protected Button connectionDetailBtn;
+
+ @Nullable
+ @BindView(R.id.connection_detail_container)
+ protected RelativeLayout connectionDetailContainer;
+
+ @Nullable
+ @BindView(R.id.log_container)
+ protected RelativeLayout logsContainer;
+
+ @Nullable
+ @BindView(R.id.tor_state)
+ protected AppCompatTextView torState;
+
+ @Nullable
+ @BindView(R.id.snowflake_state)
+ protected AppCompatTextView snowflakeState;
+
+ @Nullable
+ @BindView(R.id.connection_detail_logs)
+ protected RecyclerView connectionDetailLogs;
+
+ private TorLogAdapter torLogAdapter;
+
+ @Nullable
@BindView(R.id.progressbar)
protected ProgressBar progressBar;
@@ -157,7 +197,7 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
super.onResume();
isActivityShowing = true;
TorStatusObservable.getInstance().addObserver(this);
- setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(this));
+ setProgressbarDescription(getStringForCurrentStatus(this));
}
protected void restoreState(Bundle savedInstanceState) {
@@ -178,10 +218,48 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
providerHeaderView.setTitle(providerHeaderText);
}
+ protected void showConnectionDetails() {
+ if (loadingScreen == null) {
+ return;
+ }
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+ connectionDetailLogs.setLayoutManager(layoutManager);
+ connectionDetailLogs.addItemDecoration( new DividerItemDecoration(this, layoutManager.getOrientation()));
+ torLogAdapter = new TorLogAdapter(getLastLogs());
+ connectionDetailLogs.setAdapter(torLogAdapter);
+
+ connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (newState != SCROLL_STATE_IDLE) {
+ torLogAdapter.postponeUpdate = true;
+ } else if (newState == SCROLL_STATE_IDLE && getFirstVisibleItemPosion() == 0) {
+ torLogAdapter.postponeUpdate = false;
+ }
+ }
+ });
+
+ snowflakeState.setText(getLastSnowflakeLog());
+ torState.setText(getLastTorLog());
+ connectionDetailBtn.setOnClickListener(v -> {
+ connectionDetailBtn.setVisibility(GONE);
+ logsContainer.setVisibility(VISIBLE);
+ });
+ connectionDetailContainer.setVisibility(VISIBLE);
+ }
+
+ private int getFirstVisibleItemPosion() {
+ return ((LinearLayoutManager)connectionDetailLogs.getLayoutManager()).findFirstVisibleItemPosition();
+ }
+
protected void hideProgressBar() {
if (loadingScreen == null) {
return;
}
+ connectionDetailBtn.setVisibility(VISIBLE);
+ connectionDetailContainer.setVisibility(GONE);
+ logsContainer.setVisibility(GONE);
loadingScreen.setVisibility(GONE);
content.setVisibility(VISIBLE);
}
@@ -195,19 +273,28 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
}
protected void setProgressbarTitle(@StringRes int progressbarTitle) {
- if (this.progressbarTitle == null) {
+ if (loadingScreen == null) {
return;
}
this.progressbarTitle.setText(progressbarTitle);
}
protected void setProgressbarDescription(String progressbarDescription) {
- if (this.progressbarDescription == null) {
+ if (loadingScreen == null) {
return;
}
this.progressbarDescription.setText(progressbarDescription);
}
+ protected void setConfigProgress(int value) {
+ if (loadingScreen == null) {
+ return;
+ }
+
+ progressBar.setProgress(value, true);
+ progressBar.setIndeterminate(value >= 100 || value < 0);
+ }
+
protected void showCompactLayout() {
if (isCompactLayout) {
@@ -307,7 +394,73 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple
@Override
public void update(Observable o, Object arg) {
if (o instanceof TorStatusObservable) {
- runOnUiThread(() -> setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(ConfigWizardBaseActivity.this)));
+ runOnUiThread(() -> {
+ if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF && loadingScreen != null) {
+ if (connectionDetailContainer.getVisibility() == GONE) {
+ showConnectionDetails();
+ } else {
+ setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs());
+ }
+ }
+ setProgressbarDescription(getStringForCurrentStatus(ConfigWizardBaseActivity.this));
+ setConfigProgress(getBootstrapProgress());
+ });
+ }
+ }
+
+ protected void setLogs(String torLog, String snowflakeLog, List<String> lastLogs) {
+ if (loadingScreen == null) {
+ return;
+ }
+ torLogAdapter.updateData(lastLogs);
+ torState.setText(torLog);
+ snowflakeState.setText(snowflakeLog);
+ }
+
+ static class TorLogAdapter extends RecyclerView.Adapter<TorLogAdapter.ViewHolder> {
+ private List<String> values;
+ private boolean postponeUpdate;
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ public AppCompatTextView logTextLabel;
+ public View layout;
+
+ public ViewHolder(View v) {
+ super(v);
+ layout = v;
+ logTextLabel = v.findViewById(android.R.id.text1);
+ }
+ }
+
+ public void updateData(List<String> data) {
+ values = data;
+ if (!postponeUpdate) {
+ notifyDataSetChanged();
+ }
+ }
+
+ public TorLogAdapter(List<String> data) {
+ values = data;
+ }
+
+ @NonNull
+ @Override
+ public TorLogAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(
+ parent.getContext());
+ View v = inflater.inflate(R.layout.v_log_item, parent, false);
+ return new TorLogAdapter.ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(TorLogAdapter.ViewHolder holder, final int position) {
+ final String log = values.get(position);
+ holder.logTextLabel.setText(log);
+ }
+
+ @Override
+ public int getItemCount() {
+ return values.size();
}
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
index 449955af..281b21c0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
@@ -55,7 +55,7 @@ public class TorStatusObservable extends Observable {
addLog(message);
getInstance().lastSnowflakeLog = message;
if (getInstance().status != TorStatus.OFF) {
- getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress());
+ getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress());
}
instance.setChanged();
instance.notifyObservers();
@@ -72,15 +72,15 @@ public class TorStatusObservable extends Observable {
snowflakeIcon + ": " + snowflakeLog;
}
- private static int getNotificationProgress() {
+ public static int getBootstrapProgress() {
return getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1;
}
private static void addLog(String message) {
if (instance.lastLogs.size() > 100) {
- instance.lastLogs.remove(0);
+ instance.lastLogs.remove(99);
}
- instance.lastLogs.add(message);
+ instance.lastLogs.add(0, message.trim());
}
public static void updateState(Context context, String status) {
@@ -102,7 +102,7 @@ public class TorStatusObservable extends Observable {
getInstance().lastTorLog = getStringFor(context, logKey);
addLog(getInstance().lastTorLog);
}
- getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress());
+ getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress());
}
instance.setChanged();
@@ -168,8 +168,17 @@ public class TorStatusObservable extends Observable {
@Nullable
- public String getLastError() {
- return lastError;
+ public static String getLastTorLog() {
+ return getInstance().lastTorLog;
+ }
+
+ @Nullable
+ public static String getLastSnowflakeLog() {
+ return getInstance().lastSnowflakeLog;
+ }
+
+ public static Vector<String> getLastLogs() {
+ return getInstance().lastLogs;
}
public static String getStringForCurrentStatus(Context context) {
diff --git a/app/src/main/res/drawable/ic_snowflake.png b/app/src/main/res/drawable/ic_snowflake.png
new file mode 100644
index 00000000..992662ee
--- /dev/null
+++ b/app/src/main/res/drawable/ic_snowflake.png
Binary files differ
diff --git a/app/src/main/res/drawable/ic_tor.png b/app/src/main/res/drawable/ic_tor.png
new file mode 100644
index 00000000..a5f9ae89
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tor.png
Binary files differ
diff --git a/app/src/main/res/drawable/v_vertical_gradient.xml b/app/src/main/res/drawable/v_vertical_gradient.xml
new file mode 100644
index 00000000..877634b5
--- /dev/null
+++ b/app/src/main/res/drawable/v_vertical_gradient.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:angle="270"
+ android:startColor="#00FFFFFF"
+ android:endColor="#FFFFFFFF"
+ android:type="linear" />
+</shape>
diff --git a/app/src/main/res/layout-xlarge/v_loading_screen.xml b/app/src/main/res/layout-xlarge/v_loading_screen.xml
index ed25f07b..0b71099e 100644
--- a/app/src/main/res/layout-xlarge/v_loading_screen.xml
+++ b/app/src/main/res/layout-xlarge/v_loading_screen.xml
@@ -56,4 +56,142 @@
android:layout_marginTop="@dimen/standard_margin"
/>
+ <RelativeLayout
+ android:id="@+id/connection_detail_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="30dp"
+ android:visibility="gone"
+ tools:visibility="visible"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/connection_details_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/connection_details"
+ android:gravity="start"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
+ android:paddingBottom="@dimen/stdpadding"
+ android:paddingStart="4dp"
+ android:paddingLeft="4dp"
+ android:paddingEnd="4dp"
+ android:paddingRight="4dp"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/tor_icon"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:src="@drawable/ic_tor"
+ android:layout_below="@id/connection_details_title"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="@dimen/stdpadding"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/tor_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/connection_details_title"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:text="@string/configuring_provider"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+ android:layout_alignBottom="@id/tor_icon"
+ android:layout_toEndOf="@id/tor_icon"
+ android:layout_toRightOf="@+id/tor_icon"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:gravity="bottom"
+ tools:text="test 12321 123 \n sdf,sdf,m\nn 123 "
+ android:ellipsize="end"
+
+ tools:visibility="visible"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/snowflake_icon"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:src="@drawable/ic_snowflake"
+ android:layout_below="@id/tor_icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="@dimen/stdpadding"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/snowflake_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/tor_state"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:text="@string/configuring_provider"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+ android:layout_alignTop="@id/snowflake_icon"
+ android:layout_alignBottom="@+id/snowflake_icon"
+ android:layout_toEndOf="@+id/snowflake_icon"
+ android:layout_toRightOf="@+id/snowflake_icon"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:paddingBottom="1dp"
+ android:gravity="bottom"
+ tools:text="test \n another \n and a third \n blkud"
+ android:ellipsize="end"
+ tools:visibility="visible"
+ />
+ <Button
+ android:id="@+id/btn_connection_detail"
+ android:layout_marginTop="30dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/snowflake_icon"
+ android:text="@string/show_log"
+ android:visibility="visible"
+ />
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/log_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:visibility="visible"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/connection_detail_logs_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/log_fragment_title"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
+ android:layout_alignParentTop="true"
+ android:paddingStart="4dp"
+ android:paddingLeft="4dp"
+ android:paddingEnd="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="@dimen/stdpadding"
+ android:paddingBottom="@dimen/stdpadding"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/connection_detail_logs"
+ android:layout_below="@+id/connection_detail_logs_title"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/v_log_item"
+ android:isScrollContainer="false"
+ />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/connection_detail_logs"
+ android:layout_alignTop="@id/connection_detail_logs"
+ android:src="@drawable/v_vertical_gradient"
+ android:layout_marginTop="200dp"
+ android:importantForAccessibility="no"
+ />
+ </RelativeLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_add_provider.xml b/app/src/main/res/layout/v_add_provider.xml
deleted file mode 100644
index 933f19d0..00000000
--- a/app/src/main/res/layout/v_add_provider.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/loading_screen"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:visibility="gone"
- tools:visibility="visible">
-
- <androidx.appcompat.widget.AppCompatImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- app:tint="@color/colorPrimary"
- app:srcCompat="@drawable/action_history"
- android:layout_marginTop="@dimen/loading_screen_icon_vertical_margin"
- android:layout_marginBottom="@dimen/loading_screen_icon_vertical_margin"
- />
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/progressbar_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fadingEdge="horizontal"
- android:singleLine="true"
- android:text="@string/introduce_new_provider"
- android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
- android:layout_marginTop="@dimen/standard_margin"
- android:layout_marginBottom="@dimen/standard_margin"
- />
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/progressbar_description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fadingEdge="horizontal"
- android:maxLines="2"
- android:text="@string/configuring_provider"
- android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
- android:layout_marginTop="@dimen/standard_margin"
- android:layout_marginBottom="@dimen/standard_margin"
- tools:text="test"
- tools:visibility="visible"
- />
-
- <ProgressBar
- android:id="@+id/progressbar"
- style="@style/Widget.AppCompat.ProgressBar.Horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:indeterminate="true"
- android:layout_marginTop="@dimen/standard_margin"
- />
-
-</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_loading_screen.xml b/app/src/main/res/layout/v_loading_screen.xml
index f7f58e7b..9942a1c5 100644
--- a/app/src/main/res/layout/v_loading_screen.xml
+++ b/app/src/main/res/layout/v_loading_screen.xml
@@ -30,7 +30,6 @@
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
android:layout_marginTop="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_margin"
- tools:text="title"
/>
<androidx.appcompat.widget.AppCompatTextView
@@ -56,4 +55,142 @@
android:layout_marginTop="@dimen/standard_margin"
/>
+ <RelativeLayout
+ android:id="@+id/connection_detail_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="30dp"
+ android:visibility="gone"
+ tools:visibility="visible"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/connection_details_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/show_log"
+ android:gravity="start"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
+ android:paddingBottom="@dimen/stdpadding"
+ android:paddingStart="4dp"
+ android:paddingLeft="4dp"
+ android:paddingEnd="4dp"
+ android:paddingRight="4dp"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/tor_icon"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:src="@drawable/ic_tor"
+ android:layout_below="@id/connection_details_title"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="@dimen/stdpadding"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/tor_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/connection_details_title"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:text="@string/configuring_provider"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+ android:layout_alignBottom="@id/tor_icon"
+ android:layout_toEndOf="@id/tor_icon"
+ android:layout_toRightOf="@+id/tor_icon"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:gravity="bottom"
+ tools:text="test 12321 123 \n sdf,sdf,m\nn 123 "
+ android:ellipsize="end"
+
+ tools:visibility="visible"
+ />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/snowflake_icon"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:src="@drawable/ic_snowflake"
+ android:layout_below="@id/tor_icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="@dimen/stdpadding"
+ />
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/snowflake_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/tor_state"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:text="@string/configuring_provider"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+ android:layout_alignTop="@id/snowflake_icon"
+ android:layout_alignBottom="@+id/snowflake_icon"
+ android:layout_toEndOf="@+id/snowflake_icon"
+ android:layout_toRightOf="@+id/snowflake_icon"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:paddingBottom="1dp"
+ android:gravity="bottom"
+ tools:text="test \n another \n and a third \n blkud"
+ android:ellipsize="end"
+ tools:visibility="visible"
+ />
+ <Button
+ android:id="@+id/btn_connection_detail"
+ android:layout_marginTop="30dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/snowflake_icon"
+ android:text="@string/show_log"
+ android:visibility="visible"
+ />
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/log_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:visibility="visible"
+ >
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/connection_detail_logs_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/log_fragment_title"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
+ android:layout_alignParentTop="true"
+ android:paddingStart="4dp"
+ android:paddingLeft="4dp"
+ android:paddingEnd="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="@dimen/stdpadding"
+ android:paddingBottom="@dimen/stdpadding"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/connection_detail_logs"
+ android:layout_below="@+id/connection_detail_logs_title"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/v_log_item"
+ android:isScrollContainer="false"
+ />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/connection_detail_logs"
+ android:layout_alignTop="@id/connection_detail_logs"
+ android:src="@drawable/v_vertical_gradient"
+ android:layout_marginTop="200dp"
+ android:importantForAccessibility="no"
+ />
+ </RelativeLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/v_log_item.xml b/app/src/main/res/layout/v_log_item.xml
new file mode 100644
index 00000000..c3039f46
--- /dev/null
+++ b/app/src/main/res/layout/v_log_item.xml
@@ -0,0 +1,16 @@
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
+ android:gravity="center_vertical"
+ tools:text="test \ntest2\ntest3"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
+ /> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4c5cffd4..0b3d1d9b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -7,6 +7,7 @@
<string name="switch_provider_menu_option">Switch provider</string>
<string name="info">info</string>
<string name="show_connection_details">Show connection details</string>
+ <string name="connection_details">Connection details</string>
<string name="routes_info">Routes: %s</string>
<string name="routes_info6">IPv6 routes: %s</string>
<string name="error_empty_username">The username must not be empty.</string>