This commit is contained in:
Daniel Pollithy 2018-01-18 19:51:39 +01:00
parent 9931d2f192
commit 2e22f5ed99
12 changed files with 294 additions and 81 deletions

View File

@ -19,8 +19,10 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Switch;
import android.widget.Toast;
@ -50,6 +52,13 @@ public class MainActivity extends AppCompatActivity
updateUIReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
try {
// hide progress bar
ProgressBar progressConnection = (ProgressBar) findViewById(R.id.progressConnection);
progressConnection.setVisibility(View.VISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
if (intent.getAction().equals("com.flashwifi.wifip2p.start_roaming")) {
startRoamingView(intent.getStringExtra("peer_mac_address"),
intent.getStringExtra("ssid"),

View File

@ -1,5 +1,6 @@
package com.flashwifi.wifip2p;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@ -26,6 +27,7 @@ import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@ -37,6 +39,8 @@ import com.flashwifi.wifip2p.broadcast.WiFiDirectBroadcastService;
import com.flashwifi.wifip2p.datastore.PeerStore;
import com.flashwifi.wifip2p.protocol.NegotiationFinalization;
import org.w3c.dom.Text;
public class RoamingActivity extends AppCompatActivity {
private static final String TAG = "RoamingActivity";
@ -59,6 +63,7 @@ public class RoamingActivity extends AppCompatActivity {
private Button stopButton;
private boolean endRoamingFlag = false;
private boolean initiatedEnd;
@Override
protected void onStart() {
@ -106,6 +111,11 @@ public class RoamingActivity extends AppCompatActivity {
flashEstablished.setChecked(true);
} else if (message.equals("Billing")) {
updateBillingCard();
} else if (message.equals("Channel closed")) {
exitRoaming();
if (mService.isInRoleHotspot()) {
showRetransferCard();
}
}
}
@ -118,25 +128,44 @@ public class RoamingActivity extends AppCompatActivity {
}
private void showRetransferCard() {
CardView cardView = (CardView) findViewById(R.id.card_view_tangle_attachment);
cardView.setVisibility(View.VISIBLE);
}
@SuppressLint("DefaultLocale")
private void updateBillingCard() {
CardView summaryView = (CardView) findViewById(R.id.card_view_overview);
if (summaryView.getVisibility() != View.VISIBLE) {
summaryView.setVisibility(View.VISIBLE);
}
String minutes = Integer.toString(Accountant.getInstance().getTotalDurance());
String megabytes_max = Integer.toString(Accountant.getInstance().getBookedMegabytes());
String megabytes_used = Integer.toString(Accountant.getInstance().getTotalMegabytes());
String iotas_transferred = Integer.toString(Accountant.getInstance().getTotalIotaPrice());
int minutes = Accountant.getInstance().getTotalDurance() / 60;
int minutes_max = Accountant.getInstance().getBookedMinutes();
int megabytes_max = Accountant.getInstance().getBookedMegabytes();
int megabytes_used = Accountant.getInstance().getTotalMegabytes();
int iotas_transferred = Accountant.getInstance().getTotalIotaPrice();
int iotas_max = Accountant.getInstance().getTotalIotaDeposit();
TextView summaryMinutes = (TextView) findViewById(R.id.summaryMinutes);
summaryMinutes.setText(String.format("%s minutes active", minutes));
summaryMinutes.setText(String.format("%d/%d minutes active", minutes, minutes_max));
TextView summaryMegabytes = (TextView) findViewById(R.id.summaryMegabytes);
summaryMegabytes.setText(String.format("%s/%s Megabytes roamed", megabytes_used, megabytes_max));
summaryMegabytes.setText(String.format("%d/%d Megabytes roamed", megabytes_used, megabytes_max));
TextView summaryIota = (TextView) findViewById(R.id.summaryIota);
summaryMegabytes.setText(String.format("%s Iota transferred", iotas_transferred));
summaryIota.setText(String.format("%d/%d Iota transferred", iotas_transferred, iotas_max));
ProgressBar progressMinutes = (ProgressBar) findViewById(R.id.progressbarDurance);
progressMinutes.setMax(minutes_max);
progressMinutes.setProgress(minutes);
ProgressBar progressMegabytes = (ProgressBar) findViewById(R.id.progressbarMegabytes);
progressMegabytes.setMax(megabytes_max);
progressMegabytes.setProgress(megabytes_used);
ProgressBar progressIota = (ProgressBar) findViewById(R.id.progressbarIota);
progressIota.setProgress(iotas_transferred);
progressIota.setMax(iotas_max);
}
@ -227,30 +256,7 @@ public class RoamingActivity extends AppCompatActivity {
private void initUI() {
//final EditText input = (EditText) findViewById(R.id.chat_input);
//Button button = (Button) findViewById(R.id.btn_send);
/*button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
addMessageRight(name, input.getText().toString());
// send the message to the peer
//mService.sendMessageToSocketServer(groupOwnerAddress, input.getText().toString());
}
});*/
/*FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
toggleHotspot();
}
});
listView = (ListView) findViewById(R.id.peer_list);
arrayList = new ArrayList<>();
listAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arrayList);
listView.setAdapter(listAdapter);*/
updateBillingCard();
}
/** Defines callbacks for service binding, passed to bindService() */
@ -298,6 +304,17 @@ public class RoamingActivity extends AppCompatActivity {
}
private void endRoaming() {
if (!initiatedEnd) {
initiatedEnd = true;
Accountant.getInstance().setCloseAfterwards(true);
// the next bill will send the close request
// meanwhile show a loading icon
ProgressBar stopProgressBar = (ProgressBar) findViewById(R.id.stopProgressBar);
stopProgressBar.setVisibility(View.VISIBLE);
}
}
private void exitRoaming() {
endRoamingFlag = true;
cancelNotification();
if (mService.isInRoleHotspot()){
@ -306,6 +323,16 @@ public class RoamingActivity extends AppCompatActivity {
mService.disconnectAP();
}
mService.setRoaming(false);
// hide the spinner and the stop button
ProgressBar stopProgressBar = (ProgressBar) findViewById(R.id.stopProgressBar);
stopProgressBar.setVisibility(View.GONE);
Button stopButton = (Button) findViewById(R.id.stopRoamingButton);
stopButton.setVisibility(View.GONE);
TextView stopText = (TextView) findViewById(R.id.stopText);
stopText.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Press BACK now", Toast.LENGTH_LONG).show();
}

View File

@ -21,6 +21,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;
import android.widget.ToggleButton;
@ -223,6 +224,11 @@ public class SearchFragment extends Fragment {
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (!busy) {
busy = true;
// show progress bar
ProgressBar progressConnection = (ProgressBar) getActivity().findViewById(R.id.progressConnection);
progressConnection.setVisibility(View.VISIBLE);
PeerInformation peer = PeerStore.getInstance().getPeerArrayList().get(i);
peer.setSelected(true);

View File

@ -12,7 +12,11 @@ public class Accountant {
private int totalIotaPrice;
private int totalDurance;
private int bookedMegabytes;
private int bookedMinutes;
private int totalIotaDeposit;
private int timeoutMinutes;
private boolean closeAfterwards;
private long startTime;
private FlashChannelHelper flashChannelHelper;
@ -25,19 +29,39 @@ public class Accountant {
private Accountant() {
}
public void start(int bookedMegabytes, int timeoutMinutes){
public void start(int bookedMegabytes, int timeoutMinutes, int bookedMinutes, int totalIotaDeposit){
if (closed) {
bills = new ArrayList<Bill>();
this.bookedMegabytes = bookedMegabytes;
this.timeoutMinutes = timeoutMinutes;
this.totalIotaDeposit = totalIotaDeposit;
totalMegabytes = 0;
totalIotaPrice = 0;
totalDurance = 0;
flashChannelHelper = new FlashChannelHelper();
closeAfterwards = false;
startTime = System.currentTimeMillis() / 1000L;
this.bookedMinutes = bookedMinutes;
closed = false;
}
}
private long getLastTime() {
if (bills.isEmpty()) {
return startTime;
} else {
return bills.get(bills.size()-1).getTime();
}
}
public boolean isCloseAfterwards() {
return closeAfterwards;
}
public void setCloseAfterwards(boolean closeAfterwards) {
this.closeAfterwards = closeAfterwards;
}
public int getNextBillNumber() {
return bills.size() + 1;
}
@ -56,20 +80,22 @@ public class Accountant {
totalMegabytes += b.getMegabytesUsed();
totalIotaPrice += b.getPriceInIota();
totalDurance += b.getDuranceInMinutes();
totalDurance += b.getDuranceInSeconds();
// ToDo: apply transfer to flash channel
return true;
}
public Bill createBill(int megaByte, int priceInIota, int duranceMinutes){
public Bill createBill(int megaByte, int priceInIota){
if (!closed) {
Bill b = new Bill(getNextBillNumber(), totalDurance, duranceMinutes, megaByte, priceInIota);
long now = System.currentTimeMillis() / 1000L;
long duranceInSeconds = now - getLastTime();
Bill b = new Bill(getNextBillNumber(), now, duranceInSeconds, megaByte, priceInIota);
totalMegabytes += megaByte;
totalIotaPrice += priceInIota;
totalDurance += duranceMinutes;
totalDurance += duranceInSeconds;
// 1) modify flash channel
applyTransferToFlashChannel(priceInIota);
@ -114,4 +140,16 @@ public class Accountant {
public int getBookedMegabytes() {
return bookedMegabytes;
}
public int getTimeoutMinutes() {
return timeoutMinutes;
}
public int getBookedMinutes() {
return bookedMinutes;
}
public int getTotalIotaDeposit() {
return totalIotaDeposit;
}
}

View File

@ -3,17 +3,17 @@ package com.flashwifi.wifip2p.billing;
public class Bill {
private int index;
private int minuteStart;
private int duranceInMinutes = 1;
private int megabytesUsed;
private int priceInIota;
private long duranceInSeconds;
private long time;
private boolean acceptedByPeer;
public Bill(int index, int minuteStart, int duranceInMinutes, int megabytesUsed, int priceInIota) {
public Bill(int index, long time, long duranceInSeconds, int megabytesUsed, int priceInIota) {
this.index = index;
this.minuteStart = minuteStart;
this.duranceInMinutes = duranceInMinutes;
this.time = time;
this.duranceInSeconds = duranceInSeconds;
this.megabytesUsed = megabytesUsed;
this.priceInIota = priceInIota;
}
@ -30,12 +30,13 @@ public class Bill {
return index;
}
public int getMinuteStart() {
return minuteStart;
}
public int getDuranceInMinutes() {
return duranceInMinutes;
return (int) duranceInSeconds/60;
}
public int getDuranceInSeconds() {
return (int) duranceInSeconds;
}
public int getMegabytesUsed() {
@ -45,4 +46,8 @@ public class Bill {
public int getPriceInIota() {
return priceInIota;
}
public long getTime() {
return time;
}
}

View File

@ -1,5 +1,6 @@
package com.flashwifi.wifip2p.billing;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@ -74,15 +75,17 @@ public class BillingClient {
String hotspotStateLine = socketWrapper.getLineThrowing();
if (hotspotStateLine.contains("INITIAL") || hotspotStateLine.contains("NOT_PAIRED")) {
// ask the hotspot to open the flash channel
// ToDo: get real digests
String[] digests = new String[]{"1234", "2345", "3456"};
billingOpenChannel = new BillingOpenChannel(100, 100, "clientAddress", 8, digests, 20 * 60 * 1000);
// ToDo: replace magic numbers
billingOpenChannel = new BillingOpenChannel(100, 100, "clientAddress", 8, digests, 20 * 60 * 1000, 60);
String billingOpenChannelString = gson.toJson(billingOpenChannel);
socketWrapper.sendLine(billingOpenChannelString);
// receive the hotspot details for the flash channel
String billingOpenChannelAnswerString = socketWrapper.getLineThrowing();
billingOpenChannelAnswer = gson.fromJson(billingOpenChannelAnswerString, BillingOpenChannelAnswer.class);
// now create the flash channel on our side
Accountant.getInstance().start(billingOpenChannel.getTotalMegabytes(), billingOpenChannel.getTimeoutMinutesClient());
Accountant.getInstance().start(billingOpenChannel.getTotalMegabytes(), billingOpenChannel.getTimeoutMinutesClient(), billingOpenChannel.getTotalMinutes(), billingOpenChannelAnswer.getClientDepositIota());
sendUpdateUIBroadcastWithMessage("Channel established");
state = State.ROAMING;
} else {
@ -106,7 +109,7 @@ public class BillingClient {
// ToDo: flash object -> diff()
// ToDo: sign flash transaction
sendUpdateUIBroadcastWithMessage("Billing");
latestBillAnswer = new BillMessageAnswer("id", true, "", false);
latestBillAnswer = new BillMessageAnswer("id", true, "", Accountant.getInstance().isCloseAfterwards());
latestBillAnswerString = gson.toJson(latestBillAnswer);
socketWrapper.sendLine(latestBillAnswerString);
@ -128,6 +131,8 @@ public class BillingClient {
String billingCloseChannelAnswerString = gson.toJson(billingCloseChannelAnswer);
socketWrapper.sendLine(billingCloseChannelAnswerString);
sendUpdateUIBroadcastWithMessage("Channel closed");
state = State.CLOSED;
}

View File

@ -2,6 +2,8 @@ package com.flashwifi.wifip2p.billing;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.text.format.Formatter;
import android.util.Log;
import com.flashwifi.wifip2p.negotiation.SocketWrapper;
@ -20,6 +22,8 @@ import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import static android.content.Context.WIFI_SERVICE;
/**
* 1) This class keeps the socket connection alive.
* 2) It tracks the state of the communication.
@ -48,9 +52,9 @@ public class BillingServer {
context.sendBroadcast(local);
}
public BillingServer(int bookedMegabytes, int timeoutMinutes, Context context){
public BillingServer(int bookedMegabytes, int timeoutMinutes, int maxMinutes, int iotaDepositClient, Context context){
this.context = context;
Accountant.getInstance().start(bookedMegabytes,timeoutMinutes);
Accountant.getInstance().start(bookedMegabytes, timeoutMinutes, maxMinutes, iotaDepositClient);
gson = new GsonBuilder().create();
}
@ -59,6 +63,10 @@ public class BillingServer {
createDeadlineGuard();
// 1) create a socket
Log.d(TAG, "start: Billing server has been started");
// ToDo: receive end of roaming broadcast
while (state != State.CLOSED) {
try {
// create server socket
@ -117,10 +125,9 @@ public class BillingServer {
Log.d(TAG, "start: Good morning!");
// create new bill
// ToDo: integrate real network data
// ToDo: calculate time correctly
b = Accountant.getInstance().createBill(0,0,1);
b = Accountant.getInstance().createBill(3,9);
// ToDo: integrate real flash channel
latestBill = new BillMessage(b, "", false);
latestBill = new BillMessage(b, "<flash obj>", Accountant.getInstance().isCloseAfterwards());
latestBillString = gson.toJson(latestBill);
socketWrapper.sendLine(latestBillString);
@ -136,11 +143,14 @@ public class BillingServer {
if (latestBill.isCloseAfterwards() || latestBillAnswer.isCloseAfterwards()) {
state = State.CLOSE;
}
sendUpdateUIBroadcastWithMessage("Billing");
}
}
if (state == State.CLOSE) {
Log.d(TAG, "start: state is CLOSE now");
// ToDo: handle the final deposit of the flash channel
// ToDo: sign the transaction
BillingCloseChannel billingCloseChannel = new BillingCloseChannel(0,0,0,0,"", "", "");
@ -150,6 +160,9 @@ public class BillingServer {
String billingCloseChannelAnswerString = socketWrapper.getLineThrowing();
BillingCloseChannelAnswer billingCloseChannelAnswer = gson.fromJson(billingCloseChannelAnswerString, BillingCloseChannelAnswer.class);
// ToDo: validate the signature
// change the ui
sendUpdateUIBroadcastWithMessage("Channel closed");
state = State.CLOSED;
}
@ -162,7 +175,7 @@ public class BillingServer {
state = State.FULLY_ATTACHED;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

View File

@ -101,8 +101,9 @@ public class WiFiDirectBroadcastService extends Service {
Runnable task = new Runnable() {
@Override
public void run() {
Log.d(TAG, "run: instantiate billing server");
// ToDo: remove magic numbers
BillingServer billingServer = new BillingServer(100, 20, getApplicationContext());
BillingServer billingServer = new BillingServer(100, 20, 60, 100, getApplicationContext());
try {
billingServer.start();
@ -115,8 +116,10 @@ public class WiFiDirectBroadcastService extends Service {
};
Log.d(TAG, "startBillingServer");
Thread thread = new Thread(task);
//asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
//AsyncTask.execute(thread);
threads.add(thread);
AsyncTask.execute(thread);
thread.start();
}
}

View File

@ -3,6 +3,7 @@ package com.flashwifi.wifip2p.protocol;
public class BillingOpenChannel {
private int totalMegabytes;
private int totalMinutes;
private int iotaPerMegabyte;
private int timeoutMinutesClient;
private String clientRefundAddress;
@ -10,8 +11,10 @@ public class BillingOpenChannel {
private String[] clientDigests;
public BillingOpenChannel(int totalMegabytes, int iotaPerMegabyte, String clientRefundAddress,
int treeDepth, String[] clientDigests, int timeoutMinutesClient) {
int treeDepth, String[] clientDigests, int timeoutMinutesClient,
int totalMinutes) {
this.totalMegabytes = totalMegabytes;
this.totalMinutes = totalMinutes;
this.iotaPerMegabyte = iotaPerMegabyte;
this.clientRefundAddress = clientRefundAddress;
this.treeDepth = treeDepth;
@ -42,4 +45,8 @@ public class BillingOpenChannel {
public String[] getClientDigests() {
return clientDigests;
}
public int getTotalMinutes() {
return totalMinutes;
}
}

View File

@ -60,21 +60,88 @@
android:clickable="false"
android:text="Flash channel" />
<Button
android:id="@+id/stopRoamingButton"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="STOP"
android:orientation="horizontal"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp" />
android:layout_marginBottom="10dp" >
<Button
android:id="@+id/stopRoamingButton"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:text="STOP"
/>
<ProgressBar
android:id="@+id/stopProgressBar"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:indeterminate="true"
android:visibility="gone"
/>
</LinearLayout>
<TextView
android:id="@+id/stopText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/channelClosed"
android:visibility="invisible"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view_tangle_attachment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_gravity="center"
android:visibility="gone"
card_view:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Retransfer deposits"
android:textSize="20sp" />
<ProgressBar
android:id="@+id/progressbarRetransfer"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="5"
/>
<TextView
android:id="@+id/retransferText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/initRetransfer"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view_overview"
@ -98,14 +165,14 @@
android:textSize="20sp" />
<TextView
<ProgressBar
android:id="@+id/progressbarMegabytes"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:id="@+id/summaryMinutes"
android:text="0 minutes open"
android:textSize="15sp" />
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
/>
<TextView
android:id="@+id/summaryMegabytes"
@ -116,25 +183,44 @@
android:text="0/0 Megabytes"
android:textSize="15sp" />
<ProgressBar
android:id="@+id/progressbarDurance"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="20dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:id="@+id/summaryMinutes"
android:text="0/0 seconds"
android:textSize="15sp" />
<ProgressBar
android:id="@+id/progressbarIota"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="20dp"
/>
<TextView
android:id="@+id/summaryIota"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="0 Iota transferred"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="0.1 Miota still locked"
android:text="0/0 Iota transferred"
android:textSize="15sp"
android:layout_marginBottom="20dp"/>
android:paddingBottom="20dp"/>
</LinearLayout>
@ -142,6 +228,8 @@
</LinearLayout>

View File

@ -34,7 +34,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginRight="50dp"
android:layout_weight="1"
android:layout_marginRight="20dp"
android:text="Device name"
android:textAppearance="@style/TextAppearance.AppCompat.Headline" />
@ -83,6 +84,15 @@
</LinearLayout>
<ProgressBar
android:id="@+id/progressConnection"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
/>

View File

@ -64,5 +64,7 @@
<string name="startSearch">Start Search</string>
<string name="roaming_title">Roaming (This is your hotspot)</string>
<string name="roaming_title_client">Roaming (You are consuming)</string>
<string name="channelClosed">The payment channel was closed.</string>
<string name="initRetransfer">Initializing retransfer</string>
</resources>