Initial commit
This commit is contained in:
commit
291374b6e8
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
.idea/
|
||||||
|
.gradle/
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
*/build/
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.swp
|
23
SocketFileTransfer/build.gradle
Normal file
23
SocketFileTransfer/build.gradle
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven { url 'http://repo1.maven.org/maven2' }
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:0.4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply plugin: 'android'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile files('libs/android-support-v4.jar')
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 17
|
||||||
|
buildToolsVersion "17.0.0"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 7
|
||||||
|
targetSdkVersion 17
|
||||||
|
}
|
||||||
|
}
|
BIN
SocketFileTransfer/libs/android-support-v4.jar
Normal file
BIN
SocketFileTransfer/libs/android-support-v4.jar
Normal file
Binary file not shown.
54
SocketFileTransfer/src/main/AndroidManifest.xml
Normal file
54
SocketFileTransfer/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.annimon.socketfiletransfer"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk
|
||||||
|
android:minSdkVersion="7"
|
||||||
|
android:targetSdkVersion="17" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/AppTheme" >
|
||||||
|
<activity
|
||||||
|
android:name="com.annimon.socketfiletransfer.MainActivity"
|
||||||
|
android:label="@string/app_name" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="file/*" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name="com.annimon.socketfiletransfer.MessagesActivity"
|
||||||
|
android:label="@string/title_activity_messages" >
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name="com.annimon.socketfiletransfer.CursorActivity"
|
||||||
|
android:label="@string/title_activity_cursor" >
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
BIN
SocketFileTransfer/src/main/ic_launcher-web.png
Normal file
BIN
SocketFileTransfer/src/main/ic_launcher-web.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -0,0 +1,31 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class CursorActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
// Set fullscreen mode.
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
// Set backlight always on.
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
|
||||||
|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
|
||||||
|
TouchpadView view = new TouchpadView(this);
|
||||||
|
view.requestFocus();
|
||||||
|
setContentView(view);
|
||||||
|
|
||||||
|
new SocketTransferTask(OperationListener.MODE_CURSOR_CONTROL).execute(view);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.LinearLayout.LayoutParams;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity implements View.OnClickListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// We don't need to show UI if sharing data.
|
||||||
|
if (checkSharingData()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutParams layoutParam = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
|
||||||
|
LinearLayout layout = new LinearLayout(this);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
layout.setLayoutParams(layoutParam);
|
||||||
|
|
||||||
|
// Create main menu
|
||||||
|
String[] items = getResources().getStringArray(R.array.main_menu);
|
||||||
|
for (int i = 0; i < items.length; i++) {
|
||||||
|
String item = items[i];
|
||||||
|
|
||||||
|
Button button = new Button(this);
|
||||||
|
button.setLayoutParams(layoutParam);
|
||||||
|
button.setTag(i);
|
||||||
|
button.setText(item);
|
||||||
|
button.setOnClickListener(this);
|
||||||
|
|
||||||
|
layout.addView(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.main, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
final int id = (Integer) view.getTag();
|
||||||
|
switch (id) {
|
||||||
|
case OperationListener.MODE_SERVER:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperationListener.MODE_FILE_TRANSFER:
|
||||||
|
onFileTransfer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperationListener.MODE_MESSAGE_TRANSFER:
|
||||||
|
Intent msgIntent = new Intent(this, MessagesActivity.class);
|
||||||
|
startActivity(msgIntent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperationListener.MODE_CURSOR_CONTROL:
|
||||||
|
Intent curIntent = new Intent(this, CursorActivity.class);
|
||||||
|
startActivity(curIntent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (resultCode != Activity.RESULT_OK) return;
|
||||||
|
|
||||||
|
if (requestCode == OperationListener.MODE_FILE_TRANSFER) {
|
||||||
|
sendFile(data.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendFile(Uri uri) {
|
||||||
|
if (uri == null) return;
|
||||||
|
|
||||||
|
String path = uri.getPath();
|
||||||
|
if (uri.getScheme().startsWith("content")) path = getRealPathFromURI(uri);
|
||||||
|
|
||||||
|
File file = new File(path);
|
||||||
|
new SocketTransferTask(OperationListener.MODE_FILE_TRANSFER).execute(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRealPathFromURI(Uri contentUri) {
|
||||||
|
String[] projection = { MediaStore.Images.Media.DATA };
|
||||||
|
Cursor cursor = managedQuery(contentUri, projection, null, null, null);
|
||||||
|
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
return cursor.getString(columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFileTransfer() {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.setType("file/*");
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, OperationListener.MODE_FILE_TRANSFER);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(this, getString(R.string.app_not_found), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkSharingData() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String type = intent.getType();
|
||||||
|
if (Intent.ACTION_SEND.equals(intent.getAction()) && type != null) {
|
||||||
|
if (type.startsWith("file/") || type.startsWith("image/")) {
|
||||||
|
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
|
sendFile(uri);
|
||||||
|
return true;
|
||||||
|
} else if ("text/plain".equals(type)) {
|
||||||
|
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
new SocketTransferTask(OperationListener.MODE_MESSAGE_TRANSFER).execute(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.util.MessageHistory;
|
||||||
|
|
||||||
|
public class MessagesActivity extends Activity implements View.OnClickListener {
|
||||||
|
|
||||||
|
private EditText editText;
|
||||||
|
private TextView messagesHistory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_messages);
|
||||||
|
|
||||||
|
findViewById(R.id.send_text_button).setOnClickListener(this);
|
||||||
|
editText = (EditText) findViewById(R.id.message_edit_text);
|
||||||
|
messagesHistory = (TextView) findViewById(R.id.messages_history);
|
||||||
|
|
||||||
|
messagesHistory.setText(MessageHistory.getAllText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
String text = editText.getText().toString();
|
||||||
|
new SocketTransferTask(OperationListener.MODE_MESSAGE_TRANSFER).execute(text);
|
||||||
|
editText.setText("");
|
||||||
|
|
||||||
|
MessageHistory.addMessage(" >> " + text);
|
||||||
|
messagesHistory.setText(MessageHistory.getAllText());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.operations.MessageOperation;
|
||||||
|
import com.annimon.socketfiletransfer.operations.FileOperation;
|
||||||
|
import com.annimon.socketfiletransfer.operations.Operation;
|
||||||
|
import com.annimon.socketfiletransfer.util.ExceptionHandler;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прослушивание операций с данными.
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class OperationListener {
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
MODE_SERVER = 0,
|
||||||
|
MODE_FILE_TRANSFER = 1,
|
||||||
|
MODE_MESSAGE_TRANSFER = 2,
|
||||||
|
MODE_CURSOR_CONTROL = 3;
|
||||||
|
|
||||||
|
protected DataInputStream dis;
|
||||||
|
protected DataOutputStream dos;
|
||||||
|
|
||||||
|
public void setSocket(Socket socket) {
|
||||||
|
try {
|
||||||
|
dis = new DataInputStream( socket.getInputStream() );
|
||||||
|
dos = new DataOutputStream( socket.getOutputStream() );
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ExceptionHandler.log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listenOperation() throws Exception {
|
||||||
|
int mode = dis.readInt();
|
||||||
|
Operation operation;
|
||||||
|
switch(mode) {
|
||||||
|
case MODE_FILE_TRANSFER:
|
||||||
|
operation = new FileOperation(dis);
|
||||||
|
break;
|
||||||
|
case MODE_MESSAGE_TRANSFER:
|
||||||
|
operation = new MessageOperation(dis);
|
||||||
|
break;
|
||||||
|
// case MODE_CURSOR_CONTROL:
|
||||||
|
// operation = new CursorOperation(dis);
|
||||||
|
// break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (operation != null) {
|
||||||
|
operation.startServerSide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (dis != null) {
|
||||||
|
try {
|
||||||
|
dis.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ExceptionHandler.log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dos != null) {
|
||||||
|
try {
|
||||||
|
dos.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ExceptionHandler.log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.operations.CursorOperation;
|
||||||
|
import com.annimon.socketfiletransfer.operations.FileOperation;
|
||||||
|
import com.annimon.socketfiletransfer.operations.MessageOperation;
|
||||||
|
import com.annimon.socketfiletransfer.operations.Operation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage data operations.
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class OperationManager extends OperationListener {
|
||||||
|
|
||||||
|
public void execute(int mode, Object... params) throws Exception {
|
||||||
|
Operation operation = null;
|
||||||
|
switch (mode) {
|
||||||
|
case MODE_FILE_TRANSFER:
|
||||||
|
operation = new FileOperation(dos);
|
||||||
|
break;
|
||||||
|
case MODE_MESSAGE_TRANSFER:
|
||||||
|
operation = new MessageOperation(dos);
|
||||||
|
break;
|
||||||
|
case MODE_CURSOR_CONTROL:
|
||||||
|
operation = new CursorOperation(dos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (operation != null) {
|
||||||
|
operation.startClientSide(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class SocketTransferTask extends AsyncTask<Object, Void, Boolean> {
|
||||||
|
|
||||||
|
/** Transfer mode */
|
||||||
|
private int mode;
|
||||||
|
|
||||||
|
public SocketTransferTask(int mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Object... objects) {
|
||||||
|
boolean success = true;
|
||||||
|
OperationManager manager = new OperationManager();
|
||||||
|
try {
|
||||||
|
Socket socket = new Socket("192.168.1.35", 7119);
|
||||||
|
manager.setSocket(socket);
|
||||||
|
manager.execute(mode, objects);
|
||||||
|
} catch (Exception e) {
|
||||||
|
success = false;
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("SFT", e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (manager != null) manager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.annimon.socketfiletransfer;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class TouchpadView extends View {
|
||||||
|
|
||||||
|
public TouchpadView(Context context) {
|
||||||
|
super(context);
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TouchpadView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TouchpadView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeActivity() {
|
||||||
|
((Activity)getContext()).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
canvas.drawColor(Color.GRAY);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package com.annimon.socketfiletransfer.operations;
|
||||||
|
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.OperationListener;
|
||||||
|
import com.annimon.socketfiletransfer.TouchpadView;
|
||||||
|
import com.annimon.socketfiletransfer.util.ExceptionHandler;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class CursorOperation implements Operation {
|
||||||
|
|
||||||
|
private static final int
|
||||||
|
NONE = -1,
|
||||||
|
TYPE_MOVE = 1,
|
||||||
|
TYPE_CLICK = 2,
|
||||||
|
TYPE_DRAG = 3,
|
||||||
|
TYPE_RELEASED = 4,
|
||||||
|
TYPE_KEY_RELEASED = 5,
|
||||||
|
STOP = 10;
|
||||||
|
|
||||||
|
private DataInputStream dis;
|
||||||
|
private DataOutputStream dos;
|
||||||
|
|
||||||
|
private boolean running;
|
||||||
|
|
||||||
|
public CursorOperation(DataInputStream dis) {
|
||||||
|
this.dis = dis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CursorOperation(DataOutputStream dos) {
|
||||||
|
this.dos = dos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startServerSide() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startClientSide(Object... params) throws Exception {
|
||||||
|
TouchpadView view = (TouchpadView) params[0];
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
dos.writeInt(OperationListener.MODE_CURSOR_CONTROL);
|
||||||
|
|
||||||
|
// Send view size
|
||||||
|
dos.writeInt(view.getWidth());
|
||||||
|
dos.writeInt(view.getHeight());
|
||||||
|
|
||||||
|
view.requestFocus();
|
||||||
|
view.setOnTouchListener(touchListener);
|
||||||
|
view.setOnKeyListener(keyListener);
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
Thread.sleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
view.closeActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private View.OnTouchListener touchListener = new View.OnTouchListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||||
|
if (!running) return true;
|
||||||
|
|
||||||
|
int x = (int) motionEvent.getX();
|
||||||
|
int y = (int) motionEvent.getY();
|
||||||
|
int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
|
||||||
|
|
||||||
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
try {
|
||||||
|
dos.writeInt(TYPE_MOVE);
|
||||||
|
dos.writeInt(x);
|
||||||
|
dos.writeInt(y);
|
||||||
|
} catch (IOException ex) { }
|
||||||
|
// Click event
|
||||||
|
if (motionEvent.getPointerCount() > 1) {
|
||||||
|
try {
|
||||||
|
dos.writeInt(TYPE_CLICK);
|
||||||
|
int mask = motionEvent.getPointerCount() - 2;
|
||||||
|
dos.writeInt(16 >> mask);
|
||||||
|
} catch (IOException ex) { }
|
||||||
|
}
|
||||||
|
} else if ( (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL) ) {
|
||||||
|
try {
|
||||||
|
dos.writeInt(TYPE_RELEASED);
|
||||||
|
} catch (IOException ex) { }
|
||||||
|
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||||
|
try {
|
||||||
|
dos.writeInt(TYPE_DRAG);
|
||||||
|
dos.writeInt(x);
|
||||||
|
dos.writeInt(y);
|
||||||
|
} catch (IOException ex) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private View.OnKeyListener keyListener = new View.OnKeyListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
|
||||||
|
if (!running) return true;
|
||||||
|
|
||||||
|
if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
try {
|
||||||
|
dos.writeInt(TYPE_KEY_RELEASED);
|
||||||
|
dos.writeInt(convertToAscii(keyCode));
|
||||||
|
} catch (IOException e) {
|
||||||
|
ExceptionHandler.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int convertToAscii(int key) {
|
||||||
|
if (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)
|
||||||
|
return key - KeyEvent.KEYCODE_A + 'A';
|
||||||
|
else if (key >= KeyEvent.KEYCODE_0 && key <= KeyEvent.KEYCODE_9)
|
||||||
|
return key - KeyEvent.KEYCODE_0 + '0';
|
||||||
|
else if (key == KeyEvent.KEYCODE_ENTER) return 10;
|
||||||
|
else if (key == KeyEvent.KEYCODE_SHIFT_LEFT ||
|
||||||
|
key == KeyEvent.KEYCODE_SHIFT_RIGHT) return 0x16;
|
||||||
|
else if (key == KeyEvent.KEYCODE_SPACE) return 32;
|
||||||
|
else if (key == KeyEvent.KEYCODE_DEL) return 8; // Backspace
|
||||||
|
else if (key == KeyEvent.KEYCODE_MENU) return 525; // Context Menu
|
||||||
|
else if (key == KeyEvent.KEYCODE_VOLUME_UP) return 33; // Page up
|
||||||
|
else if (key == KeyEvent.KEYCODE_VOLUME_DOWN) return 34; // Page down
|
||||||
|
else if (key == KeyEvent.KEYCODE_DPAD_LEFT) return 37;
|
||||||
|
else if (key == KeyEvent.KEYCODE_DPAD_UP) return 38;
|
||||||
|
else if (key == KeyEvent.KEYCODE_DPAD_RIGHT) return 39;
|
||||||
|
else if (key == KeyEvent.KEYCODE_DPAD_DOWN) return 40;
|
||||||
|
else return key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.annimon.socketfiletransfer.operations;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.MainActivity;
|
||||||
|
import com.annimon.socketfiletransfer.OperationListener;
|
||||||
|
import com.annimon.socketfiletransfer.OperationManager;
|
||||||
|
import com.annimon.socketfiletransfer.util.ExceptionHandler;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class FileOperation implements Operation {
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 1024;
|
||||||
|
|
||||||
|
private DataInputStream dis;
|
||||||
|
private DataOutputStream dos;
|
||||||
|
|
||||||
|
public FileOperation(DataInputStream dis) {
|
||||||
|
this.dis = dis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileOperation(DataOutputStream dos) {
|
||||||
|
this.dos = dos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startServerSide() {
|
||||||
|
FileOutputStream fout = null;
|
||||||
|
try {
|
||||||
|
String name = dis.readUTF();
|
||||||
|
fout = new FileOutputStream("/mnt/sdcard/" + name);//Config.getTransferDir() + name);
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int count;
|
||||||
|
while ((count = dis.read(buffer, 0, BUFFER_SIZE)) != -1) {
|
||||||
|
fout.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
fout.flush();
|
||||||
|
fout.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ExceptionHandler.log(ex);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fout.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ExceptionHandler.log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startClientSide(Object... params) throws Exception {
|
||||||
|
File file = (File) params[0];
|
||||||
|
|
||||||
|
dos.writeInt(OperationListener.MODE_FILE_TRANSFER);
|
||||||
|
|
||||||
|
String name = file.getName();
|
||||||
|
dos.writeUTF(name);
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int count;
|
||||||
|
while ((count = fis.read(buffer, 0, BUFFER_SIZE)) != -1) {
|
||||||
|
dos.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
dos.flush();
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.annimon.socketfiletransfer.operations;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.annimon.socketfiletransfer.OperationListener;
|
||||||
|
import com.annimon.socketfiletransfer.util.MessageHistory;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class MessageOperation implements Operation {
|
||||||
|
|
||||||
|
private DataInputStream dis;
|
||||||
|
private DataOutputStream dos;
|
||||||
|
|
||||||
|
public MessageOperation(DataInputStream dis) {
|
||||||
|
this.dis = dis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageOperation(DataOutputStream dos) {
|
||||||
|
this.dos = dos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startServerSide() throws IOException {
|
||||||
|
String text = dis.readUTF();
|
||||||
|
MessageHistory.addMessage(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startClientSide(Object... params) throws Exception {
|
||||||
|
String message = (String) params[0];
|
||||||
|
|
||||||
|
dos.writeInt(OperationListener.MODE_MESSAGE_TRANSFER);
|
||||||
|
dos.writeUTF(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.annimon.socketfiletransfer.operations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Operation {
|
||||||
|
|
||||||
|
void startServerSide() throws Exception;
|
||||||
|
|
||||||
|
void startClientSide(Object... params) throws Exception;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.annimon.socketfiletransfer.util;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handling exceptions.
|
||||||
|
* If error is critical or must be show to user, then
|
||||||
|
* call <b>alert</b> method.
|
||||||
|
* In other situations call <b>log</b> method.
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class ExceptionHandler {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
private static final String TAG = "ExceptionHandler";
|
||||||
|
|
||||||
|
public static void alert(Context context, Exception ex) {
|
||||||
|
alert(context, getErrorMessage(ex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void alert(Context context, int resourceId) {
|
||||||
|
alert(context, context.getString(resourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void alert(Context context, String message) {
|
||||||
|
new AlertDialog.Builder(context)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.setTitle(android.R.string.dialog_alert_title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setPositiveButton(android.R.string.ok,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
}).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void log(Exception ex) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(TAG, getErrorMessage(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void log(String message) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.e(TAG, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getErrorMessage(Exception ex) {
|
||||||
|
return ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.annimon.socketfiletransfer.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public class MessageHistory {
|
||||||
|
|
||||||
|
private static String lastMessage = "";
|
||||||
|
private static StringBuilder allText = new StringBuilder();
|
||||||
|
|
||||||
|
public static void addMessage(String message) {
|
||||||
|
lastMessage = message;
|
||||||
|
allText.append(message).append("\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLastMessage() {
|
||||||
|
return lastMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAllText() {
|
||||||
|
return allText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
SocketFileTransfer/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
SocketFileTransfer/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
SocketFileTransfer/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
SocketFileTransfer/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
SocketFileTransfer/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
SocketFileTransfer/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
SocketFileTransfer/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
SocketFileTransfer/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
29
SocketFileTransfer/src/main/res/layout/activity_messages.xml
Normal file
29
SocketFileTransfer/src/main/res/layout/activity_messages.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".MessagesActivity">
|
||||||
|
|
||||||
|
<EditText android:id="@+id/message_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button android:id="@+id/send_text_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/send" />
|
||||||
|
|
||||||
|
<ScrollView android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/messages_history"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
6
SocketFileTransfer/src/main/res/menu/main.xml
Normal file
6
SocketFileTransfer/src/main/res/menu/main.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/action_settings"
|
||||||
|
android:title="@string/action_settings"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
android:showAsAction="never" />
|
||||||
|
</menu>
|
@ -0,0 +1,4 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Customize dimensions originally defined in res/values/dimens.xml (such as
|
||||||
|
screen margins) for sw600dp devices (e.g. 7" tablets) here. -->
|
||||||
|
</resources>
|
@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Customize dimensions originally defined in res/values/dimens.xml (such as
|
||||||
|
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. -->
|
||||||
|
<dimen name="activity_horizontal_margin">128dp</dimen>
|
||||||
|
</resources>
|
11
SocketFileTransfer/src/main/res/values-v11/styles.xml
Normal file
11
SocketFileTransfer/src/main/res/values-v11/styles.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Base application theme for API 11+. This theme completely replaces
|
||||||
|
AppBaseTheme from res/values/styles.xml on API 11+ devices.
|
||||||
|
-->
|
||||||
|
<style name="AppBaseTheme" parent="android:Theme.Holo">
|
||||||
|
<!-- API 11 theme customizations can go here. -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
5
SocketFileTransfer/src/main/res/values/dimens.xml
Normal file
5
SocketFileTransfer/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
19
SocketFileTransfer/src/main/res/values/strings.xml
Normal file
19
SocketFileTransfer/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">SocketFileTransfer</string>
|
||||||
|
<string name="action_settings">Settings</string>
|
||||||
|
<string name="send_message">Send message</string>
|
||||||
|
<string name="app_not_found">Application not found</string>
|
||||||
|
<string name="title_activity_messages">Messages</string>
|
||||||
|
<string name="title_activity_cursor">Cursor control</string>
|
||||||
|
<string name="send">Send</string>
|
||||||
|
|
||||||
|
<string-array name="main_menu">
|
||||||
|
<item>Start server</item>
|
||||||
|
<item>File transfer</item>
|
||||||
|
<item>Send message</item>
|
||||||
|
<item>Cursor control</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
20
SocketFileTransfer/src/main/res/values/styles.xml
Normal file
20
SocketFileTransfer/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Base application theme, dependent on API level. This theme is replaced
|
||||||
|
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
|
||||||
|
-->
|
||||||
|
<style name="AppBaseTheme" parent="android:Theme">
|
||||||
|
<!--
|
||||||
|
Theme customizations available in newer API levels can go in
|
||||||
|
res/values-vXX/styles.xml, while customizations related to
|
||||||
|
backward-compatibility can go here.
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Application theme. -->
|
||||||
|
<style name="AppTheme" parent="AppBaseTheme">
|
||||||
|
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
Loading…
Reference in New Issue
Block a user