Loading android/ArtiToyVPN/.idea/misc.xml +3 −0 Original line number Diff line number Diff line Loading @@ -5,8 +5,11 @@ <map> <entry key="app/src/main/res/layout/activity_main.xml" value="0.15070921985815602" /> <entry key="app/src/main/res/layout/fragment_home.xml" value="0.1" /> <entry key="app/src/main/res/layout/fragment_log.xml" value="0.1" /> <entry key="app/src/main/res/layout/fragment_notifications.xml" value="0.1" /> <entry key="app/src/main/res/layout/log_item.xml" value="0.1" /> <entry key="app/src/main/res/menu/bottom_nav_menu.xml" value="0.18125" /> <entry key="app/src/main/res/menu/menu_logs.xml" value="0.14479166666666668" /> </map> </option> </component> Loading android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ArtiToyVPNApp.java +3 −0 Original line number Diff line number Diff line Loading @@ -2,14 +2,17 @@ package org.torproject.artitoyvpn; import android.app.Application; import org.torproject.artitoyvpn.ui.logging.LogObservable; import org.torproject.artitoyvpn.vpn.VpnStatusObservable; public class ArtiToyVPNApp extends Application { VpnStatusObservable vpnStatusObservable; LogObservable logObservable; @Override public void onCreate() { super.onCreate(); vpnStatusObservable = VpnStatusObservable.getInstance(); logObservable = LogObservable.getInstance(); } } android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogFragment.java +64 −14 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.torproject.artitoyvpn.R; import org.torproject.artitoyvpn.databinding.FragmentLogBinding; import org.torproject.artitoyvpn.utils.Utils; import java.io.File; import java.util.Locale; public class LogFragment extends Fragment { private LogViewModel logViewModel; private FragmentLogBinding binding; private LogRecyclerViewAdapter recyclerViewAdapter; // duct tape st¥le private boolean showTimestamps = true; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { logViewModel = new ViewModelProvider(this).get(LogViewModel.class); LogObservable logViewModel = LogObservable.getInstance(); binding = FragmentLogBinding.inflate(inflater, container, false); View root = binding.getRoot(); final TextView textView = binding.textLogs; logViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { final RecyclerView logList = binding.logList; LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext()); logList.setLayoutManager(layoutManager); recyclerViewAdapter = new LogRecyclerViewAdapter(); logList.setAdapter(recyclerViewAdapter); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this.getContext(), layoutManager.getOrientation()); logList.addItemDecoration(dividerItemDecoration); logViewModel.getLogListData().observe(getViewLifecycleOwner(), logItems -> recyclerViewAdapter.updateList(logItems)); setHasOptionsMenu(true); return root; } @Override public void onChanged(@Nullable String s) { textView.setText(s); public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_logs, menu); menu.findItem(R.id.action_show_timestamps).setTitle(showTimestamps ? R.string.hide_timestamps : R.string.show_timestamps); } }); return root; @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); Context context = getContext(); if (context == null) { return super.onOptionsItemSelected(item); } //noinspection SimplifiableIfStatement if (id == R.id.action_copy) { String logs = LogObservable.getInstance().getLogStrings(showTimestamps); Utils.writeTextToClipboard(context, logs); Toast.makeText(context.getApplicationContext(), getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show(); return true; } else if (id == R.id.action_show_timestamps) { showTimestamps = !showTimestamps; recyclerViewAdapter.setShowTimeStamps(showTimestamps); item.setTitle(showTimestamps ? R.string.hide_timestamps : R.string.show_timestamps); } return super.onOptionsItemSelected(item); } @Override public void onDestroyView() { super.onDestroyView(); Loading android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogItem.java 0 → 100644 +31 −0 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import android.text.format.DateFormat; import org.torproject.artitoyvpn.utils.Utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class LogItem { public final String content; public final String timestamp; public LogItem(String timestamp, String content) { this.timestamp = timestamp; this.content = content.trim(); } @Override public String toString() { return timestamp + " " + content; } public String toString(boolean showTimestamp) { String result = ""; result += showTimestamp ? timestamp + " " : ""; result += content; return result; } } android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogObservable.java 0 → 100644 +53 −0 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import org.torproject.artitoyvpn.utils.Utils; import java.util.ArrayList; import java.util.Locale; public class LogObservable extends ViewModel { private final MutableLiveData<ArrayList<LogItem>> logListData; private static LogObservable instance; private LogObservable() { logListData = new MutableLiveData<>(new ArrayList<>()); } public static LogObservable getInstance() { if (instance == null) { instance = new LogObservable(); } return instance; } public void addLog(String log) { ArrayList<LogItem> list = logListData.getValue(); if (list != null) { list.add(new LogItem(Utils.getFormattedDate(System.currentTimeMillis(), Locale.getDefault()), log)); logListData.postValue(list); } } public String getLogStrings(boolean showTimestamp) { StringBuilder builder = new StringBuilder(); ArrayList<LogItem> logItemArrayList = logListData.getValue(); if (logItemArrayList == null) { return ""; } for (LogItem item : logItemArrayList) { builder.append(item.toString(showTimestamp)).append("\n"); } return builder.toString(); } public LiveData<ArrayList<LogItem>> getLogListData() { return logListData; } } No newline at end of file Loading
android/ArtiToyVPN/.idea/misc.xml +3 −0 Original line number Diff line number Diff line Loading @@ -5,8 +5,11 @@ <map> <entry key="app/src/main/res/layout/activity_main.xml" value="0.15070921985815602" /> <entry key="app/src/main/res/layout/fragment_home.xml" value="0.1" /> <entry key="app/src/main/res/layout/fragment_log.xml" value="0.1" /> <entry key="app/src/main/res/layout/fragment_notifications.xml" value="0.1" /> <entry key="app/src/main/res/layout/log_item.xml" value="0.1" /> <entry key="app/src/main/res/menu/bottom_nav_menu.xml" value="0.18125" /> <entry key="app/src/main/res/menu/menu_logs.xml" value="0.14479166666666668" /> </map> </option> </component> Loading
android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ArtiToyVPNApp.java +3 −0 Original line number Diff line number Diff line Loading @@ -2,14 +2,17 @@ package org.torproject.artitoyvpn; import android.app.Application; import org.torproject.artitoyvpn.ui.logging.LogObservable; import org.torproject.artitoyvpn.vpn.VpnStatusObservable; public class ArtiToyVPNApp extends Application { VpnStatusObservable vpnStatusObservable; LogObservable logObservable; @Override public void onCreate() { super.onCreate(); vpnStatusObservable = VpnStatusObservable.getInstance(); logObservable = LogObservable.getInstance(); } }
android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogFragment.java +64 −14 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.torproject.artitoyvpn.R; import org.torproject.artitoyvpn.databinding.FragmentLogBinding; import org.torproject.artitoyvpn.utils.Utils; import java.io.File; import java.util.Locale; public class LogFragment extends Fragment { private LogViewModel logViewModel; private FragmentLogBinding binding; private LogRecyclerViewAdapter recyclerViewAdapter; // duct tape st¥le private boolean showTimestamps = true; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { logViewModel = new ViewModelProvider(this).get(LogViewModel.class); LogObservable logViewModel = LogObservable.getInstance(); binding = FragmentLogBinding.inflate(inflater, container, false); View root = binding.getRoot(); final TextView textView = binding.textLogs; logViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { final RecyclerView logList = binding.logList; LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext()); logList.setLayoutManager(layoutManager); recyclerViewAdapter = new LogRecyclerViewAdapter(); logList.setAdapter(recyclerViewAdapter); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this.getContext(), layoutManager.getOrientation()); logList.addItemDecoration(dividerItemDecoration); logViewModel.getLogListData().observe(getViewLifecycleOwner(), logItems -> recyclerViewAdapter.updateList(logItems)); setHasOptionsMenu(true); return root; } @Override public void onChanged(@Nullable String s) { textView.setText(s); public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_logs, menu); menu.findItem(R.id.action_show_timestamps).setTitle(showTimestamps ? R.string.hide_timestamps : R.string.show_timestamps); } }); return root; @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); Context context = getContext(); if (context == null) { return super.onOptionsItemSelected(item); } //noinspection SimplifiableIfStatement if (id == R.id.action_copy) { String logs = LogObservable.getInstance().getLogStrings(showTimestamps); Utils.writeTextToClipboard(context, logs); Toast.makeText(context.getApplicationContext(), getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show(); return true; } else if (id == R.id.action_show_timestamps) { showTimestamps = !showTimestamps; recyclerViewAdapter.setShowTimeStamps(showTimestamps); item.setTitle(showTimestamps ? R.string.hide_timestamps : R.string.show_timestamps); } return super.onOptionsItemSelected(item); } @Override public void onDestroyView() { super.onDestroyView(); Loading
android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogItem.java 0 → 100644 +31 −0 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import android.text.format.DateFormat; import org.torproject.artitoyvpn.utils.Utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class LogItem { public final String content; public final String timestamp; public LogItem(String timestamp, String content) { this.timestamp = timestamp; this.content = content.trim(); } @Override public String toString() { return timestamp + " " + content; } public String toString(boolean showTimestamp) { String result = ""; result += showTimestamp ? timestamp + " " : ""; result += content; return result; } }
android/ArtiToyVPN/app/src/main/java/org/torproject/artitoyvpn/ui/logging/LogObservable.java 0 → 100644 +53 −0 Original line number Diff line number Diff line package org.torproject.artitoyvpn.ui.logging; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import org.torproject.artitoyvpn.utils.Utils; import java.util.ArrayList; import java.util.Locale; public class LogObservable extends ViewModel { private final MutableLiveData<ArrayList<LogItem>> logListData; private static LogObservable instance; private LogObservable() { logListData = new MutableLiveData<>(new ArrayList<>()); } public static LogObservable getInstance() { if (instance == null) { instance = new LogObservable(); } return instance; } public void addLog(String log) { ArrayList<LogItem> list = logListData.getValue(); if (list != null) { list.add(new LogItem(Utils.getFormattedDate(System.currentTimeMillis(), Locale.getDefault()), log)); logListData.postValue(list); } } public String getLogStrings(boolean showTimestamp) { StringBuilder builder = new StringBuilder(); ArrayList<LogItem> logItemArrayList = logListData.getValue(); if (logItemArrayList == null) { return ""; } for (LogItem item : logItemArrayList) { builder.append(item.toString(showTimestamp)).append("\n"); } return builder.toString(); } public LiveData<ArrayList<LogItem>> getLogListData() { return logListData; } } No newline at end of file