Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
core
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
LibreOffice
core
Commits
4aed4eb1
Kaydet (Commit)
4aed4eb1
authored
Eyl 18, 2012
tarafından
Andrzej J.R. Hunt
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Further improvements to connection and feedback.
Change-Id: I04c48ad3d465e132ea9adaf840e9f858a7096794
üst
988948cb
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
163 additions
and
216 deletions
+163
-216
strings.xml
android/sdremote/res/values/strings.xml
+3
-1
PairingActivity.java
...te/src/org/libreoffice/impressremote/PairingActivity.java
+14
-66
SelectorActivity.java
...e/src/org/libreoffice/impressremote/SelectorActivity.java
+36
-3
BluetoothClient.java
...reoffice/impressremote/communication/BluetoothClient.java
+39
-82
CommunicationService.java
...ice/impressremote/communication/CommunicationService.java
+25
-10
NetworkClient.java
...ibreoffice/impressremote/communication/NetworkClient.java
+46
-54
No files found.
android/sdremote/res/values/strings.xml
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -25,7 +25,9 @@
<string
name=
"selector_noservers"
>
Searching for computers…
</string>
<string
name=
"selector_delete"
>
Remove server
</string>
<string
name=
"selector_choose_a_computer"
>
Choose a Computer
</string>
<string
name=
"selector_dialog_connecting"
>
Attempting to connect to {0}...
</string>
<string
name=
"selector_dialog_connecting"
>
Attempting to connect to {0}…
</string>
<string
name=
"selector_dialog_connectionfailed"
>
Impress Remote couldn"''"t connect to {0}.
</string>
<string
name=
"selector_dialog_connectionfailed_ok"
>
OK
</string>
<string
name=
"pairing_instructions_1"
>
In Impress, click on the "Slideshow" menu and select "Impress Remote".
</string>
<string
name=
"pairing_instructions_2_deviceName"
>
Choose \"{0}\" as your device.
</string>
<string
name=
"pairing_instructions_3"
>
Then input this PIN:
</string>
...
...
android/sdremote/src/org/libreoffice/impressremote/PairingActivity.java
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -10,38 +10,29 @@ package org.libreoffice.impressremote;
import
java.text.MessageFormat
;
import
org.libreoffice.impressremote.communication.CommunicationService
;
import
org.libreoffice.impressremote.communication.CommunicationService.State
;
import
android.content.BroadcastReceiver
;
import
android.content.ComponentName
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.content.IntentFilter
;
import
android.content.ServiceConnection
;
import
android.os.Bundle
;
import
android.os.IBinder
;
import
android.support.v4.content.LocalBroadcastManager
;
import
android.widget.TextView
;
import
com.actionbarsherlock.app.SherlockActivity
;
public
class
PairingActivity
extends
SherlockActivity
{
private
CommunicationService
mCommunicationService
;
private
TextView
mPinText
;
private
ActivityChangeBroadcastProcessor
mBroadcastProcessor
;
/** Called when the activity is first created. */
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_pairing
);
mBroadcastProcessor
=
new
ActivityChangeBroadcastProcessor
(
this
);
bindService
(
new
Intent
(
this
,
CommunicationService
.
class
),
mConnection
,
Context
.
BIND_IMPORTANT
);
IntentFilter
aFilter
=
new
IntentFilter
(
CommunicationService
.
MSG_PAIRING_STARTED
);
aFilter
.
addAction
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
);
IntentFilter
aFilter
=
new
IntentFilter
();
mBroadcastProcessor
=
new
ActivityChangeBroadcastProcessor
(
this
);
mBroadcastProcessor
.
addToFilter
(
aFilter
);
...
...
@@ -51,66 +42,23 @@ public class PairingActivity extends SherlockActivity {
getSupportActionBar
().
setDisplayHomeAsUpEnabled
(
true
);
}
@Override
protected
void
onDestroy
()
{
super
.
onDestroy
();
unbindService
(
mConnection
);
}
private
ServiceConnection
mConnection
=
new
ServiceConnection
()
{
@Override
public
void
onServiceConnected
(
ComponentName
aClassName
,
IBinder
aService
)
{
setContentView
(
R
.
layout
.
activity_pairing
);
mPinText
=
(
TextView
)
findViewById
(
R
.
id
.
pairing_pin
);
mCommunicationService
=
((
CommunicationService
.
CBinder
)
aService
)
.
getService
();
((
TextView
)
findViewById
(
R
.
id
.
pairing_instruction2_deviceName
))
.
setText
(
MessageFormat
.
format
(
getResources
()
.
getString
(
R
.
string
.
pairing_instructions_2_deviceName
),
CommunicationService
.
getDeviceName
()));
if
(
mCommunicationService
.
getState
()
==
State
.
CONNECTING
)
{
mPinText
.
setText
(
mCommunicationService
.
getPairingPin
());
getSupportActionBar
().
setTitle
(
mCommunicationService
.
getPairingDeviceName
());
}
String
aPin
=
getIntent
().
getStringExtra
(
"PIN"
);
String
aServerName
=
getIntent
().
getStringExtra
(
"SERVERNAME"
);
}
((
TextView
)
findViewById
(
R
.
id
.
pairing_pin
)).
setText
(
aPin
);
((
TextView
)
findViewById
(
R
.
id
.
pairing_instruction2_deviceName
))
.
setText
(
MessageFormat
.
format
(
getResources
()
.
getString
(
R
.
string
.
pairing_instructions_2_deviceName
),
aServerName
));
@Override
public
void
onServiceDisconnected
(
ComponentName
aClassName
)
{
mCommunicationService
=
null
;
}
};
getSupportActionBar
().
setTitle
(
aServerName
);
}
private
BroadcastReceiver
mListener
=
new
BroadcastReceiver
()
{
@Override
public
void
onReceive
(
Context
aContext
,
Intent
aIntent
)
{
if
(
mCommunicationService
==
null
)
{
return
;
}
if
(
aIntent
.
getAction
().
equals
(
CommunicationService
.
MSG_PAIRING_STARTED
))
{
String
aPin
=
aIntent
.
getStringExtra
(
"PIN"
);
mPinText
.
setText
(
aPin
);
if
(
mCommunicationService
!=
null
)
getSupportActionBar
().
setTitle
(
mCommunicationService
.
getPairingDeviceName
());
// refreshLists();
}
else
if
(
aIntent
.
getAction
().
equals
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
))
{
Intent
nIntent
=
new
Intent
(
PairingActivity
.
this
,
StartPresentationActivity
.
class
);
startActivity
(
nIntent
);
}
mBroadcastProcessor
.
onReceive
(
aContext
,
aIntent
);
}
};
...
...
android/sdremote/src/org/libreoffice/impressremote/SelectorActivity.java
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -23,6 +23,7 @@ import android.content.BroadcastReceiver;
import
android.content.ComponentName
;
import
android.content.Context
;
import
android.content.DialogInterface
;
import
android.content.DialogInterface.OnCancelListener
;
import
android.content.Intent
;
import
android.content.IntentFilter
;
import
android.content.ServiceConnection
;
...
...
@@ -63,6 +64,7 @@ public class SelectorActivity extends SherlockActivity {
IntentFilter
aFilter
=
new
IntentFilter
(
CommunicationService
.
MSG_SERVERLIST_CHANGED
);
aFilter
.
addAction
(
CommunicationService
.
STATUS_CONNECTION_FAILED
);
mBroadcastProcessor
=
new
ActivityChangeBroadcastProcessor
(
this
);
mBroadcastProcessor
.
addToFilter
(
aFilter
);
...
...
@@ -204,6 +206,32 @@ public class SelectorActivity extends SherlockActivity {
CommunicationService
.
MSG_SERVERLIST_CHANGED
))
{
refreshLists
();
return
;
}
else
if
(
aIntent
.
getAction
().
equals
(
CommunicationService
.
STATUS_CONNECTION_FAILED
))
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
String
aFormat
=
getResources
().
getString
(
R
.
string
.
selector_dialog_connectionfailed
);
String
aDialogText
=
MessageFormat
.
format
(
aFormat
,
mCommunicationService
.
getPairingDeviceName
());
AlertDialog
.
Builder
builder
=
new
AlertDialog
.
Builder
(
SelectorActivity
.
this
);
builder
.
setMessage
(
aDialogText
)
.
setCancelable
(
false
)
.
setPositiveButton
(
R
.
string
.
selector_dialog_connectionfailed_ok
,
new
DialogInterface
.
OnClickListener
()
{
public
void
onClick
(
DialogInterface
dialog
,
int
id
)
{
dialog
.
dismiss
();
}
});
builder
.
show
();
}
}
mBroadcastProcessor
.
onReceive
(
aContext
,
aIntent
);
...
...
@@ -318,9 +346,14 @@ public class SelectorActivity extends SherlockActivity {
mProgressDialog
=
ProgressDialog
.
show
(
SelectorActivity
.
this
,
""
,
aDialogText
,
true
);
// Intent aIntent = new Intent(SelectorActivity.this,
// PairingActivity.class);
// startActivity(aIntent);
mProgressDialog
.
setCancelable
(
true
);
mProgressDialog
.
setOnCancelListener
(
new
OnCancelListener
()
{
@Override
public
void
onCancel
(
DialogInterface
dialog
)
{
mCommunicationService
.
disconnect
();
}
});
}
}
...
...
android/sdremote/src/org/libreoffice/impressremote/communication/BluetoothClient.java
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -9,6 +9,7 @@
package
org
.
libreoffice
.
impressremote
.
communication
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStreamReader
;
import
java.util.UUID
;
...
...
@@ -27,101 +28,57 @@ public class BluetoothClient extends Client {
private
boolean
mBluetoothWasEnabled
;
private
BluetoothAdapter
mAdapter
;
private
BluetoothSocket
mSocket
;
public
BluetoothClient
(
Server
aServer
,
CommunicationService
aCommunicationService
,
Receiver
aReceiver
,
boolean
aBluetoothWasEnabled
)
{
Receiver
aReceiver
,
boolean
aBluetoothWasEnabled
)
throws
IOException
{
super
(
aServer
,
aCommunicationService
,
aReceiver
);
try
{
mAdapter
=
BluetoothAdapter
.
getDefaultAdapter
();
mBluetoothWasEnabled
=
aBluetoothWasEnabled
;
if
(!
mBluetoothWasEnabled
)
{
mAdapter
.
enable
();
}
BluetoothDevice
aDevice
=
mAdapter
.
getRemoteDevice
(
aServer
.
getAddress
());
mAdapter
.
cancelDiscovery
();
BluetoothSocket
aSocket
=
aDevice
.
createRfcommSocketToServiceRecord
(
UUID
.
fromString
(
"00001101-0000-1000-8000-00805F9B34FB"
));
aSocket
.
connect
();
// mSocket = aSocket;
System
.
out
.
println
(
"Connected"
);
mAdapter
=
BluetoothAdapter
.
getDefaultAdapter
();
mBluetoothWasEnabled
=
aBluetoothWasEnabled
;
if
(!
mBluetoothWasEnabled
)
{
mAdapter
.
enable
();
}
mInputStream
=
aSocket
.
getInputStream
();
mReader
=
new
BufferedReader
(
new
InputStreamReader
(
mInputStream
,
CHARSET
));
mOutputStream
=
aSocket
.
getOutputStream
();
BluetoothDevice
aDevice
=
mAdapter
.
getRemoteDevice
(
aServer
.
getAddress
());
mAdapter
.
cancelDiscovery
();
mSocket
=
aDevice
.
createRfcommSocketToServiceRecord
(
UUID
.
fromString
(
"00001101-0000-1000-8000-00805F9B34FB"
));
mSocket
.
connect
();
// mSocket = aSocket;
System
.
out
.
println
(
"Connected"
);
// mOutputStream.write(20);
// mOutputStream.write(20);
// mOutputStream.write(20);
// mOutputStream.flush();
// System.out.println("reading");
// while (true) {
// System.out.println(mInputStream.read());
// }
String
aTemp
=
mReader
.
readLine
();
System
.
out
.
println
(
"SF:waited"
);
if
(!
aTemp
.
equals
(
"LO_SERVER_SERVER_PAIRED"
))
{
return
;
}
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Get rid of extra lines
}
Intent
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
);
LocalBroadcastManager
.
getInstance
(
mCommunicationService
)
.
sendBroadcast
(
aIntent
);
startListening
();
// Pairing.
// Random aRandom = new Random();
// String aPin = "" + (aRandom.nextInt(9000) + 1000);
// while (aPin.length() < 4) {
// aPin = "0" + aPin; // Add leading zeros if necessary
// }
// Intent aIntent = new Intent(
// CommunicationService.MSG_PAIRING_STARTED);
// aIntent.putExtra("PIN", aPin);
// mPin = aPin;
// LocalBroadcastManager.getInstance(mContext).sendBroadcast(aIntent);
// // Send out
// String aName = CommunicationService.getDeviceName(); // TODO: get the proper name
// sendCommand("LO_SERVER_CLIENT_PAIR\n" + aName + "\n" + aPin
// + "\n\n");
//
// // Wait until we get the appropriate string back...
// System.out.println("SF:waiting");
// String aTemp = mReader.readLine();
// System.out.println("SF:waited");
// if (!aTemp.equals("LO_SERVER_SERVER_PAIRED")) {
// return;
// } else {
//
// }
// while (mReader.readLine().length() != 0) {
// // Get rid of extra lines
// System.out.println("SF: empty line");
// }
// System.out.println("SD: empty");
// startListening();
mInputStream
=
mSocket
.
getInputStream
();
mReader
=
new
BufferedReader
(
new
InputStreamReader
(
mInputStream
,
CHARSET
));
mOutputStream
=
mSocket
.
getOutputStream
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
String
aTemp
=
mReader
.
readLine
();
System
.
out
.
println
(
"SF:waited"
);
if
(!
aTemp
.
equals
(
"LO_SERVER_SERVER_PAIRED"
))
{
return
;
}
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Get rid of extra lines
}
Intent
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
);
LocalBroadcastManager
.
getInstance
(
mCommunicationService
).
sendBroadcast
(
aIntent
);
startListening
();
}
@Override
public
void
closeConnection
()
{
//
try {
//
if (mSocket != null)
//
mSocket.close();
//
} catch (IOException e) {
//
// TODO Auto-generated catch block
//
e.printStackTrace();
//
}
try
{
if
(
mSocket
!=
null
)
mSocket
.
close
();
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
protected
void
onDisconnect
()
{
...
...
android/sdremote/src/org/libreoffice/impressremote/communication/CommunicationService.java
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -8,6 +8,7 @@
*/
package
org
.
libreoffice
.
impressremote
.
communication
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.Map
;
...
...
@@ -22,6 +23,7 @@ import android.content.SharedPreferences;
import
android.content.SharedPreferences.Editor
;
import
android.os.Binder
;
import
android.os.IBinder
;
import
android.support.v4.content.LocalBroadcastManager
;
public
class
CommunicationService
extends
Service
implements
Runnable
{
...
...
@@ -88,16 +90,27 @@ public class CommunicationService extends Service implements Runnable {
}
if
(
mStateDesired
==
State
.
CONNECTED
)
{
mState
=
State
.
CONNECTING
;
switch
(
mServerDesired
.
getProtocol
())
{
case
NETWORK:
mClient
=
new
NetworkClient
(
mServerDesired
,
this
,
mReceiver
);
break
;
case
BLUETOOTH:
mClient
=
new
BluetoothClient
(
mServerDesired
,
this
,
mReceiver
,
mBluetoothPreviouslyEnabled
);
break
;
try
{
switch
(
mServerDesired
.
getProtocol
())
{
case
NETWORK:
mClient
=
new
NetworkClient
(
mServerDesired
,
this
,
mReceiver
);
break
;
case
BLUETOOTH:
mClient
=
new
BluetoothClient
(
mServerDesired
,
this
,
mReceiver
,
mBluetoothPreviouslyEnabled
);
break
;
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
mClient
=
null
;
mState
=
State
.
DISCONNECTED
;
Intent
aIntent
=
new
Intent
(
CommunicationService
.
STATUS_CONNECTION_FAILED
);
LocalBroadcastManager
.
getInstance
(
this
)
.
sendBroadcast
(
aIntent
);
return
;
}
mTransmitter
=
new
Transmitter
(
mClient
);
mState
=
State
.
CONNECTED
;
...
...
@@ -194,6 +207,8 @@ public class CommunicationService extends Service implements Runnable {
public
static
final
String
STATUS_PAIRING_PINVALIDATION
=
"STATUS_PAIRING_PINVALIDATION"
;
public
static
final
String
STATUS_CONNECTION_FAILED
=
"STATUS_CONNECTION_FAILED"
;
private
Transmitter
mTransmitter
;
private
Client
mClient
;
...
...
android/sdremote/src/org/libreoffice/impressremote/communication/NetworkClient.java
Dosyayı görüntüle @
4aed4eb1
...
...
@@ -33,66 +33,58 @@ public class NetworkClient extends Client {
public
NetworkClient
(
Server
aServer
,
CommunicationService
aCommunicationService
,
Receiver
aReceiver
)
{
Receiver
aReceiver
)
throws
UnknownHostException
,
IOException
{
super
(
aServer
,
aCommunicationService
,
aReceiver
);
try
{
mName
=
aServer
.
getName
();
mSocket
=
new
Socket
(
aServer
.
getAddress
(),
PORT
);
mInputStream
=
mSocket
.
getInputStream
();
mReader
=
new
BufferedReader
(
new
InputStreamReader
(
mInputStream
,
CHARSET
));
mOutputStream
=
mSocket
.
getOutputStream
();
// Pairing.
String
aPin
=
setupPin
(
aServer
);
Intent
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_STARTED
);
aIntent
.
putExtra
(
"PIN"
,
aPin
);
mPin
=
aPin
;
LocalBroadcastManager
.
getInstance
(
mCommunicationService
)
.
sendBroadcast
(
aIntent
);
// Send out
String
aName
=
CommunicationService
.
getDeviceName
();
// TODO: get the proper name
sendCommand
(
"LO_SERVER_CLIENT_PAIR\n"
+
aName
+
"\n"
+
aPin
+
"\n\n"
);
// Wait until we get the appropriate string back...
String
aTemp
=
mReader
.
readLine
();
while
(!
aTemp
.
equals
(
"LO_SERVER_SERVER_PAIRED"
))
{
if
(
aTemp
.
equals
(
"LO_SERVER_VALIDATING_PIN"
))
{
// Broadcast that we need a pin screen.
aIntent
=
new
Intent
(
CommunicationService
.
STATUS_PAIRING_PINVALIDATION
);
aIntent
.
putExtra
(
"PIN"
,
aPin
);
mPin
=
aPin
;
LocalBroadcastManager
.
getInstance
(
mCommunicationService
)
.
sendBroadcast
(
aIntent
);
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Read off empty lines
}
aTemp
=
mReader
.
readLine
();
}
else
{
return
;
mName
=
aServer
.
getName
();
mSocket
=
new
Socket
(
aServer
.
getAddress
(),
PORT
);
mInputStream
=
mSocket
.
getInputStream
();
mReader
=
new
BufferedReader
(
new
InputStreamReader
(
mInputStream
,
CHARSET
));
mOutputStream
=
mSocket
.
getOutputStream
();
// Pairing.
String
aPin
=
setupPin
(
aServer
);
Intent
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_STARTED
);
aIntent
.
putExtra
(
"PIN"
,
aPin
);
mPin
=
aPin
;
LocalBroadcastManager
.
getInstance
(
mCommunicationService
).
sendBroadcast
(
aIntent
);
// Send out
String
aName
=
CommunicationService
.
getDeviceName
();
// TODO: get the proper name
sendCommand
(
"LO_SERVER_CLIENT_PAIR\n"
+
aName
+
"\n"
+
aPin
+
"\n\n"
);
// Wait until we get the appropriate string back...
String
aTemp
=
mReader
.
readLine
();
while
(!
aTemp
.
equals
(
"LO_SERVER_SERVER_PAIRED"
))
{
if
(
aTemp
.
equals
(
"LO_SERVER_VALIDATING_PIN"
))
{
// Broadcast that we need a pin screen.
aIntent
=
new
Intent
(
CommunicationService
.
STATUS_PAIRING_PINVALIDATION
);
aIntent
.
putExtra
(
"PIN"
,
aPin
);
aIntent
.
putExtra
(
"SERVERNAME"
,
aServer
.
getName
());
mPin
=
aPin
;
LocalBroadcastManager
.
getInstance
(
mCommunicationService
)
.
sendBroadcast
(
aIntent
);
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Read off empty lines
}
aTemp
=
mReader
.
readLine
();
}
else
{
return
;
}
}
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
);
LocalBroadcastManager
.
getInstance
(
mCommunicationService
)
.
sendBroadcast
(
aIntent
);
aIntent
=
new
Intent
(
CommunicationService
.
MSG_PAIRING_SUCCESSFUL
);
LocalBroadcastManager
.
getInstance
(
mCommunicationService
).
sendBroadcast
(
aIntent
);
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Get rid of extra lines
System
.
out
.
println
(
"SF: empty line"
);
}
System
.
out
.
println
(
"SD: empty"
);
startListening
();
}
catch
(
UnknownHostException
e
)
{
// TODO Tell the user we have a problem
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
// TODO As above
e
.
printStackTrace
();
while
(
mReader
.
readLine
().
length
()
!=
0
)
{
// Get rid of extra lines
System
.
out
.
println
(
"SF: empty line"
);
}
System
.
out
.
println
(
"SD: empty"
);
startListening
();
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment