How to print a text file using WiFi Direct - java

I have a working Android app that displays user stats. I want to send a small report with plain text format (.txt) to a WiFi Direct printer. I have downloaded the sample demo app from Android. I did the proper modification in order to look for .txt file. But I don't understand why my code is not working. After selecting the file I want to print, nothing happens.
The current configuration for my EPSON printer bellow.
Wi-Fi Direct Mode : On
Communication Mode: AP
Operation Mode: IEEE802.11g/n
Communication Speed: Auto
SSID: DIRECT-D3A36C54
Channel: 7
Security Level: WPA2-PSK(AES)
Link Status: Unknown
This is the DeviceDetailFragment class
public class DeviceDetailFragment extends Fragment implements WifiP2pManager.ConnectionInfoListener {
protected static final int CHOOSE_FILE_RESULT_CODE = 20;
private View mContentView = null;
private WifiP2pDevice device;
private WifiP2pInfo info;
ProgressDialog progressDialog = null;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mContentView = inflater.inflate(R.layout.device_detail, null);
mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.LABEL;
config.wps.pin = "12345677";
// config.groupOwnerIntent = 15;
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",
"Connecting to :" + device.deviceAddress, true, true
// new DialogInterface.OnCancelListener() {
//
// #Override
// public void onCancel(DialogInterface dialog) {
// ((DeviceActionListener) getActivity()).cancelDisconnect();
// }
// }
);
((DeviceListFragment.DeviceActionListener) getActivity()).connect(config);
}
});
mContentView.findViewById(R.id.btn_disconnect).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
((DeviceListFragment.DeviceActionListener) getActivity()).disconnect();
}
});
mContentView.findViewById(R.id.btn_start_client).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
// Allow user to pick a text file from storage or other
// registered apps
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/*");
// intent.setType("image/*");
startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE);
}
});
return mContentView;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// User has picked a text file. Transfer it to group owner i.e peer using
// FileTransferService.
Uri uri = data.getData();
TextView statusText = (TextView) mContentView.findViewById(R.id.status_text);
statusText.setText("Sending: " + uri);
Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri);
Intent serviceIntent = new Intent(getActivity(), FileTransferService.class);
serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE);
serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());
serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,
info.groupOwnerAddress.getHostAddress());
serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8315); //631
getActivity().startService(serviceIntent);
}
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
this.info = info;
this.getView().setVisibility(View.VISIBLE);
// The owner IP is now known.
TextView view = (TextView) mContentView.findViewById(R.id.group_owner);
view.setText(getResources().getString(R.string.group_owner_text)
+ ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)
: getResources().getString(R.string.no)));
// InetAddress from WifiP2pInfo struct.
view = (TextView) mContentView.findViewById(R.id.device_info);
view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());
// After the group negotiation, we assign the group owner as the file
// server. The file server is single threaded, single connection server
// socket.
if (info.groupFormed && info.isGroupOwner) {
new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text))
.execute();
} else if (info.groupFormed) {
// The other device acts as the client. In this case, we enable the
// get file button.
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources()
.getString(R.string.client_text));
}
// hide the connect button
mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE);
}
/**
* Updates the UI with device data
*
* #param device the device to be displayed
*/
public void showDetails(WifiP2pDevice device) {
this.device = device;
this.getView().setVisibility(View.VISIBLE);
TextView view = (TextView) mContentView.findViewById(R.id.device_address);
view.setText(device.deviceAddress);
view = (TextView) mContentView.findViewById(R.id.device_info);
view.setText(device.toString());
}
/**
* Clears the UI fields after a disconnect or direct mode disable operation.
*/
public void resetViews() {
mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE);
TextView view = (TextView) mContentView.findViewById(R.id.device_address);
view.setText(R.string.empty);
view = (TextView) mContentView.findViewById(R.id.device_info);
view.setText(R.string.empty);
view = (TextView) mContentView.findViewById(R.id.group_owner);
view.setText(R.string.empty);
view = (TextView) mContentView.findViewById(R.id.status_text);
view.setText(R.string.empty);
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE);
this.getView().setVisibility(View.GONE);
}
/**
* A simple server socket that accepts connection and writes some data on
* the stream.
*/
public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> {
private Context context;
private TextView statusText;
/**
* #param context
* #param statusText
*/
public FileServerAsyncTask(Context context, View statusText) {
this.context = context;
this.statusText = (TextView) statusText;
}
#Override
protected String doInBackground(Void... params) {
try {
ServerSocket serverSocket = new ServerSocket(8315); //631
Log.d(WiFiDirectActivity.TAG, "Server: Socket opened");
Socket client = serverSocket.accept();
Log.d(WiFiDirectActivity.TAG, "Server: connection done");
// final File f = new File(Environment.getExternalStorageDirectory() + "/"
// + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()
// + ".txt");
final File f = new File(Environment.getExternalStorageDirectory() + "/"
+ context.getPackageName() + "/wifip2pshared-" + ".txt");
File dirs = new File(f.getParent());
if (!dirs.exists())
dirs.mkdirs();
f.createNewFile();
Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString());
InputStream inputstream = client.getInputStream();
copyFile(inputstream, new FileOutputStream(f));
serverSocket.close();
return f.getAbsolutePath();
} catch (IOException e) {
Log.e(WiFiDirectActivity.TAG, e.getMessage());
return null;
}
}
/*
* (non-Javadoc)
* #see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
#Override
protected void onPostExecute(String result) {
if (result != null) {
statusText.setText("File copied - " + result);
// Log.e("...File copied - ", result);
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + result), "text/*");
context.startActivity(intent);
} else {
Log.e("File copied is NULL- ", result);
}
}
/*
* (non-Javadoc)
* #see android.os.AsyncTask#onPreExecute()
*/
#Override
protected void onPreExecute() {
statusText.setText("Opening a server socket");
}
}
public static boolean copyFile(InputStream inputStream, OutputStream out) {
byte buf[] = new byte[1024];
int len;
try {
while ((len = inputStream.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.close();
inputStream.close();
} catch (IOException e) {
Log.d(WiFiDirectActivity.TAG, e.toString());
return false;
}
return true;
}
}
EDIT #1
This is my permission setting
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BIND_PRINT_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
After connecting to the printer, I've got the UI to select the file, after I select it, nothing happens, I just got the below console output (the file I am picking up is located in the SD card)
05-17 10:39:50.994 28659-28659/com.example.ccano.wifidirect E/ViewRootImpl: sendUserActionEvent() mView == null
05-17 10:39:52.314 28659-28659/com.example.ccano.wifidirect D/ViewRootImpl: ViewPostImeInputStage processPointer 0
05-17 10:39:52.384 28659-28659/com.example.ccano.wifidirect D/ViewRootImpl: ViewPostImeInputStage processPointer 1
05-17 10:39:56.484 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: Intent-----------
content://com.android.externalstorage.documents/document/9C33-6BBD%3Asample_file.txt
05-17 10:39:56.514 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: P2P state changed - 2
05-17 10:39:56.514 28659-29309/com.example.ccano.wifidirect D/wifidirectdemo: Opening client socket -
05-17 10:39:56.514 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: Peer status :0
05-17 10:39:56.524 28659-29309/com.example.ccano.wifidirect D/wifidirectdemo: Client socket - true
05-17 10:39:56.524 28659-29309/com.example.ccano.wifidirect E/ccano..copyfile: true
05-17 10:39:56.524 28659-29309/com.example.ccano.wifidirect D/wifidirectdemo: Client: Data written
05-17 10:39:56.534 28659-28659/com.example.ccano.wifidirect I/Timeline: Timeline: Activity_idle id:
android.os.BinderProxy#75dd5e time:4602644
05-17 10:41:01.714 28659-28659/com.example.ccano.wifidirect D/ViewRootImpl: ViewPostImeInputStage processPointer 0
05-17 10:41:01.774 28659-28659/com.example.ccano.wifidirect D/ViewRootImpl: ViewPostImeInputStage processPointer 1
05-17 10:41:02.564 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: P2P peers changed
05-17 10:41:02.574 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: Peer status :3
05-17 10:41:02.594 28659-28659/com.example.ccano.wifidirect D/wifidirectdemo: No devices found
EDIT #2
After adding the below line into my manifiest, still I am getting same result, nothing happens.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Edit #3
After changing WpsInfo.Label to WpsInfo.PBC now, I am getting a different output on the debugger console. (see screen shot below). But, still I the printer is not sending the print job.

It turned out that my code was fine, after several days of testing and doing research, I found out that the issue was the socket class, I was using the default one that Google provides in the demo code (which is the one I was using) but, after reading the official documentation from Wi-Fi.org I could understand that port number matters and, in order to make it work with WiFi Direct only you have to target port #631 and the printer will ask you about the PIN password in case that it is enabled. Now, if you want to use P2P and by-passing the password, you have to use the port #9100.
So, I did it using both ports 9100 and 631 and now I am printing txt files.
Now, if you want to print PDF, you just need to add:
intent.setType("application/pdf");
and
intent.setDataAndType(Uri.parse("file://" + result), "application/pdf");
to the DeviceDetailFragment class (the one is posted above).
I hope this post will provide a good understanding and inside about Android printing with P2P communication.

Related

How can I see the services and the characteristics of the ble module I'm connected to?

I'm making an Android app that is supposed to connect to a BLE-module and should be able to read it's services and characteristics. My app is based on the code of Accent Systems' "iBKS Hello World" application.
I am not using iBeacons, but a different kind of BLE-module. But when I connect to it, the app is not showing a list of services and characteristics as the code indicates should happen.
Can anyone help me fix this problem? Please let me know!
My code:
public class ScanActivity extends AppCompatActivity {
//DEFINE VARS
String TAG = "ScanActivity";
BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mBluetoothGatt;
BluetoothLeScanner scanner;
ScanSettings scanSettings;
private List<String> scannedDeivcesList;
private ArrayAdapter<String> adapter;
//DEFINE LAYOUT
ListView devicesList;
//THIS METHOD RUNS ON APP LAUNCH
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
//Define listview in layout
devicesList = (ListView) findViewById(R.id.devicesList);
//Setup list on device click listener
setupListClickListener();
//Initialize de devices list
scannedDeivcesList = new ArrayList<>();
//Initialize the list adapter for the listview with params: Context / Layout file / TextView ID in layout file / Devices list
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, scannedDeivcesList);
//Set the adapter to the listview
devicesList.setAdapter(adapter);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
SpannableString s = new SpannableString("Scan for modules");
s.setSpan(new com.accent_systems.ibkshelloworld.TypefaceSpan(this, "Khand-Bold.ttf"), 0, s.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new ForegroundColorSpan(Color.parseColor("#3a3c3e")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
setTitle(s);
getSupportActionBar().setDisplayUseLogoEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
//init Bluetooth adapter
initBT();
//Start scan of bluetooth devices
startLeScan(true);
}
#Override
protected void onStop() {
super.onStop();
startLeScan(false);
}
private void initBT(){
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//Create the scan settings
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
//Set scan latency mode. Lower latency, faster device detection/more battery and resources consumption
scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
//Wrap settings together and save on a settings var (declared globally).
scanSettings = scanSettingsBuilder.build();
//Get the BLE scanner from the BT adapter (var declared globally)
scanner = mBluetoothAdapter.getBluetoothLeScanner();
}
private void startLeScan(boolean endis) {
if (endis) {
//********************
//START THE BLE SCAN
//********************
//Scanning parameters FILTER / SETTINGS / RESULT CALLBACK. Filter are used to define a particular
//device to scan for. The Callback is defined above as a method.
scanner.startScan(null, scanSettings, mScanCallback);
}else{
//Stop scan
scanner.stopScan(mScanCallback);
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
//Here all the detected BLE devices will be received . "result" contains the device
//address and name as a BLEPeripheral, the advertising content as a ScanRecord, the Rx RSSI
//and the timestamp when received. Type result.get... to see all the available methods you can call.
//Convert advertising bytes to string for a easier parsing. GetBytes may return a NullPointerException. Treat it right(try/catch).
String advertisingString = byteArrayToHex(result.getScanRecord().getBytes());
//Print the advertising String in the LOG with other device info (ADDRESS - RSSI - ADVERTISING - NAME)
Log.i(TAG, result.getDevice().getAddress()+" - RSSI: "+result.getRssi()+"\t - "+advertisingString+" - "+result.getDevice().getName());
//Check if scanned device is already in the list by mac address
boolean contains = false;
for(int i=0; i<scannedDeivcesList.size(); i++){
if(scannedDeivcesList.get(i).contains(result.getDevice().getAddress())){
//Device already added
contains = true;
//Replace the device with updated values in that position
scannedDeivcesList.set(i, result.getRssi()+" "+result.getDevice().getName()+ "\n ("+result.getDevice().getAddress()+")");
break;
}
}
if(!contains){
//Scanned device not found in the list. NEW => add to list
scannedDeivcesList.add(result.getRssi()+" "+result.getDevice().getName()+ "\n ("+result.getDevice().getAddress()+")");
}
//After modify the list, notify the adapter that changes have been made so it updates the UI.
//UI changes must be done in the main thread
runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
};
//Method to convert a byte array to a HEX. string.
private String byteArrayToHex(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for(byte b: a)
sb.append(String.format("%02x", b & 0xff));
return sb.toString();
}
void setupListClickListener(){
devicesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Stop the scan
Log.i(TAG, "SCAN STOPPED");
scanner.stopScan(mScanCallback);
//Get the string from the item clicked
String fullString = scannedDeivcesList.get(position);
//Get only the address from the previous string. Substring from '(' to ')'
String address = fullString.substring(fullString.indexOf("(")+1, fullString.indexOf(")"));
//Get BLE device with address
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
//******************************
//START CONNECTION WITH DEVICE AND DECLARE GATT
//******************************
Log.i(TAG,"*************************************************");
Log.i(TAG, "CONNECTION STARTED TO DEVICE "+address);
Log.i(TAG,"*************************************************");
//ConnectGatt parameters are CONTEXT / AUTOCONNECT to connect the next time it is scanned / GATT CALLBACK to receive GATT notifications and data
// Note: On Samsung devices, the connection must be done on main thread
mBluetoothGatt = device.connectGatt(ScanActivity.this, false, mGattCallback);
/*
There is also another simplest way to connect to a device. If you already stored
the device in a list (List<BluetoothDevice>) you can retrieve it directly and
connect to it:
mBluetoothGatt = mList.get(position).connectGatt(MainActivity.this, false, mGattCallback);
*/
}
});
}
//Connection callback
BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
//Device connected, start discovering services
Log.i(TAG, "DEVICE CONNECTED. DISCOVERING SERVICES...");
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
//Device disconnected
Log.i(TAG, "DEVICE DISCONNECTED");
}
}
// On discover services method
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//Services discovered successfully. Start parsing services and characteristics
Log.i(TAG, "SERVICES DISCOVERED. PARSING...");
displayGattServices(gatt.getServices());
} else {
//Failed to discover services
Log.i(TAG, "FAILED TO DISCOVER SERVICES");
}
}
//When reading a characteristic, here you receive the task result and the value
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//READ WAS SUCCESSFUL
Log.i(TAG, "ON CHARACTERISTIC READ SUCCESSFUL");
//Read characteristic value like:
//characteristic.getValue();
//Which it returns a byte array. Convert it to HEX. string.
} else {
Log.i(TAG, "ERROR READING CHARACTERISTIC");
}
}
//When writing, here you can check whether the task was completed successfully or not
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "ON CHARACTERISTIC WRITE SUCCESSFUL");
} else {
Log.i(TAG, "ERROR WRITING CHARACTERISTIC");
}
}
//In this method you can read the new values from a received notification
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i(TAG, "NEW NOTIFICATION RECEIVED");
//New notification received. Check the characteristic it comes from and parse to string
/*if(characteristic.getUuid().toString().contains("0000fff3")){
characteristic.getValue();
}*/
}
//RSSI values from the connection with the remote device are received here
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
Log.i(TAG, "NEW RSSI VALUE RECEIVED");
//Read remote RSSI like: mBluetoothGatt.readRemoteRssi();
//Here you get the gatt table where the rssi comes from, the rssi value and the
//status of the task.
}
};
//Method which parses all services and characteristics from the GATT table.
private void displayGattServices(List<BluetoothGattService> gattServices) {
//Check if there is any gatt services. If not, return.
if (gattServices == null) return;
// Loop through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
Log.i(TAG, "SERVICE FOUND: "+gattService.getUuid().toString());
//Loop through available characteristics for each service
for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) {
Log.i(TAG, " CHAR. FOUND: "+gattCharacteristic.getUuid().toString());
}
}
//****************************************
// CONNECTION PROCESS FINISHED!
//****************************************
Log.i(TAG, "*************************************");
Log.i(TAG, "CONNECTION COMPLETED SUCCESFULLY");
Log.i(TAG, "*************************************");
}
}
You may find full description here. And here short example. So you bring list of services after BLE connection and services request. Later you may request characteristics for every service. Main idea when you bring services one by one you have it UUID and reference. If you have know some service that you like to use you should know it before bring all services. And on collecting them save somewhere reference of service test every by known UUID. Later use this reference for bring characteristics and interconnection with device.

AsyncTask kills istelf without any error message while debugging

I'm having a very weird problem with AsyncTask which I use to download a zip file in my android application. It was working flawlessly until I decided to use strings.xml resource for every string linked to this task.
when I click on the download button inside my app, the progressbar of the AsyncTask shows for a second or less then dismisses itself and the task goes to the onPostExecute() state.
I tried debuging the app on my test device and there is no error about the task. I even added some stubs with Log.d tag, I've included the logcat results:
275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: path set
2019-04-04 20:19:22.484 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: Try block
2019-04-04 20:19:22.487 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: file url got
2019-04-04 20:19:22.490 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: opening connection
2019-04-04 20:19:22.515 495-528/? D/SurfaceFlinger: duplicate layer name: changing xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity to xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:22.585 495-794/? D/SurfaceFlinger: duplicate layer name: changing Surface(name=cafcbf6 xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity)/#0x3604cd - animation-leash to Surface(name=cafcbf6 xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity)/#0x3604cd - animation-leash#1
2019-04-04 20:19:22.614 15275-15275/xmc.androidexpert35.com.xtrememusicchecker I/ViewRootImpl: CPU Rendering VSync enable = true
2019-04-04 20:19:22.672 495-528/? W/SurfaceFlinger: Attempting to set client state on removed layer: xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:22.672 495-528/? W/SurfaceFlinger: Attempting to destroy on removed layer: xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:24.758 15275-15314/xmc.androidexpert35.com.xtrememusicchecker D/FA: Logging event (FE): user_engagement(_e), Bundle[{firebase_event_origin(_o)=auto, engagement_time_msec(_et)=3635, firebase_screen_class(_sc)=SettingsActivity, firebase_screen_id(_si)=-6495914915605520780}]
2019-04-04 20:19:26.125 829-3715/? W/NotificationService: Toast already killed. pkg=xmc.androidexpert35.com.xtrememusicchecker callback=android.app.ITransientNotification$Stub$Proxy#3b51651
This is my AsyncTask code, if anyone can help me find the issue? or suggest a useful debug solution to discover it?
Thanks, any help is very appreciated!
public class DownloadFile extends AsyncTask<String, String, String> {
private static String file_url;
private Context context;
private ProgressDialog dialog;
private String dialogString;
private File path;
private String xmpath;
private boolean canceled = false;
public DownloadFile(Context cxt) {
context = cxt;
dialog = new ProgressDialog(context);
}
#Override
protected void onPreExecute() {
dialog.setMessage(context.getString(R.string.xm_downloading));
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false);
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.xm_cancel), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
path.delete();
canceled = true;
dialog.dismiss();
}
});
dialog.show();
super.onPreExecute();
}
#Override
protected String doInBackground(String... aurl) {
int count;
if (SettingsActivity.isMagisk){
file_url = "http://androidexpert35developer.altervista.org/Xtrememusic-versions/XTREMEMusic_MAGISK_OFICIAL_By_androidexpert35.zip";
path= new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download/XTREMEMusic_Magisk.zip");
}else{
file_url = "http://androidexpert35developer.altervista.org/Xtrememusic-versions/XTREMEMusic_OFFICIAL_By_androidexpert35.zip";
path = new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download/XTREMEMusic.zip");
Log.d("ANDRO_ASYNC","path set");
}
try {
Log.d("ANDRO_ASYNC","Try block");
URL url = new URL(file_url);
Log.d("ANDRO_ASYNC","file url got");
URLConnection conexion = url.openConnection();
Log.d("ANDRO_ASYNC","opening connection");
conexion.connect();
Log.d("ANDRO_ASYNC","Connected");
int lenghtOfFile = conexion.getContentLength();
InputStream is = url.openStream();
File testDirectory = new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download");
Log.d("ANDRO_ASYNC","making directory");
if (!testDirectory.exists()) {
testDirectory.mkdir();
}
FileOutputStream fos;
Log.d("ANDRO_ASYNC","Stream");
if(SettingsActivity.isMagisk) {
fos = new FileOutputStream(testDirectory + "/" + ("XTREMEMusic_Magisk") + ".zip");
Log.d("ANDRO_ASYNC","Downloading");
}else{
fos = new FileOutputStream(testDirectory + "/" + ("XTREMEMusic") + ".zip");
Log.d("ANDRO_ASYNC","Downloading");
}
byte data[] = new byte[1024];
long total = 0;
int progress = 0;
while ((count = is.read(data)) != -1) {
total += count;
int progress_temp = (int) total * 100 / lenghtOfFile;
publishProgress(""+ progress_temp);
if (progress_temp % 10 == 0 && progress != progress_temp) {
progress = progress_temp;
}
fos.write(data, 0, count);
}
is.close();
fos.close();
} catch (Exception e) {}
return null;
}
protected void onProgressUpdate(String... progress) {
Log.d("ANDRO_ASYNC",progress[0]);
dialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String unused) {
dialog.dismiss();
if(SettingsActivity.isInstall) {
installer();
}else if (canceled) {
Toast.makeText(context, R.string.xm_cancelled, Toast.LENGTH_LONG).show();
} else{
xmpath = path.toString();
Toast.makeText(context, context.getString(R.string.xm_downloaded, xmpath), Toast.LENGTH_LONG).show();
}
}

Select random image to send to server

I am new to Android development. I created my first app which is a camera with the ability to capture an image, query the user for a Car ID related to the image, preview the captured image, and upload that image to the server.
I am interested in randomly sending out one image from possibly multiple such as from the image directory to the server. I do NOT want to send multiple images to the server--just one random image.
Upon much research on this website as well as on numerous others, I determined the following: I must obtain the image directory to where the captured images are uploaded to, I must randomly select an image from said directory using the random capability in Android, I must set up the image bitmap based on the file uri of the image, and I must convert the bitmap to a blob data type by base64 encoding.
I did the above, but my bitmap returned as null since I receive the message, "Please select image", which was the toast message for that instance.
Here is my code:
public class MainActivity extends Activity {
// Activity request codes.
private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100;
public static final int MEDIA_TYPE_IMAGE = 1;
Bitmap bitmap;
ProgressDialog pd;
InputStream is;
//Directory name to store captured images.
private static final String IMAGE_DIRECTORY_NAME = "Hello Camera";
private Uri fileUri; //File url to store captured images.
private ImageView imgPreview;
private Button btnCapturePicture;
private Button upload;
Button mButton;
EditText mEdit;
/* Initializes activity, defines UI layout, and retrieves widgets
* to interact with.
* #param savedInstanceState - data is caught here and saved for
* future app use.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.button);
mEdit = (EditText)findViewById(R.id.edittext);
mButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Log.v("EditText", mEdit.getText().toString());
}
});
imgPreview = (ImageView) findViewById(R.id.imgPreview);
btnCapturePicture = (Button) findViewById(R.id.btnCapturePicture);
upload = (Button) findViewById(R.id.upload);
upload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (bitmap == null) {
Toast.makeText(getApplicationContext(), "Please Select Image", Toast.LENGTH_LONG).show();
} else {
new ImageUpload().execute();
}
}
});
/**
* Captures image button click event in order to capture a photo.
* #param new View.OnClickListener- call to constructor
*/
btnCapturePicture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Captures an image.
captureImage();
}
});
//Checks for camera availability.
if (!isDeviceSupportCamera()) {
Toast.makeText(getApplicationContext(),
"Sorry! Your device doesn't support camera", Toast.LENGTH_LONG).show();
//Closes if a camera is not available on the device.
finish();
}
}
/**
* Checks if device has camera hardware.
*
* #return - true or false.
*/
private boolean isDeviceSupportCamera() {
if (getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
//Device has a camera.
return true;
} else {
// Device does not have a camera.
return false;
}
}
/*
* Launches camera app request image capture.
*/
private void captureImage() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
//Starts the image capture Intent.
startActivityForResult(intent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);
}
/**
* Gets and stores the file url. Will be null after returning from camera
* app.
*
* #param outState - Holds per-instance state from activity.
*/
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Saves the file url in bundle. It will be null on screen orientation changes.
outState.putParcelable("file_uri", fileUri);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//Gets the file url.
fileUri = savedInstanceState.getParcelable("file_uri");
}
/**
* Receives activity result method. Will be called after closing the camera.
*
* #param requestCode - Requests to capture an image.
* #param resultCode - Based on success or failure of image capturing, shows
* an image preview or one of two error messages.
* #param data - Carries the result data(successfully captured image).
* #return - An image preview, or one of two error messages.
*/
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//If the result is the image capturing event,
if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
//the image was successfully captured
// and will be displayed in the preview.
previewCapturedImage();
} else if (resultCode == RESULT_CANCELED) {
//The user cancelled the image capturing.
Toast.makeText(getApplicationContext(),
"User cancelled image capture", Toast.LENGTH_SHORT).show();
} else {
//Image capturing failed, but the user did not actually cancel it.
Toast.makeText(getApplicationContext(),
"Sorry! Failed to capture image", Toast.LENGTH_SHORT).show();
}
}
}
/**
* Displays the image from a path to ImageView.
* Downsizes an image if necessary and throws exception
* if the image is too large.
*/
private void previewCapturedImage() {
try {
imgPreview.setVisibility(View.VISIBLE);
upload.setVisibility(View.VISIBLE);
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
IMAGE_DIRECTORY_NAME);
Log.v("Directory", mediaStorageDir.toString());
mediaStorageDir.listFiles(new FileFilter() {
public boolean accept(File file) {
return !file.isDirectory();
}
});
File[] listFiles = mediaStorageDir.listFiles();
Log.v("File listFiles", listFiles.toString());
Random r = new Random ();
Log.v("Random r", r.toString());
File randomPicture = listFiles[r.nextInt(listFiles.length)];
Log.v("Random Picture File", randomPicture.toString());
Uri pictureUri = Uri.fromFile(randomPicture);
Log.v("PictureUri", pictureUri.toString());
BitmapFactory.Options options = new BitmapFactory.Options();
// Downsizes images. Will throw OutOfMemory Exception for larger images.
options.inSampleSize = 8;
Bitmap bitmap = BitmapFactory.decodeFile(pictureUri.getPath(), options);
Log.v("Bitmap", bitmap.toString());
ImageView imgView = (ImageView) findViewById(R.id.imgPreview);
Log.v("ImgView", imgView.toString());
imgView.setImageBitmap(bitmap);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
/**
* Creates the file uri to store an image.
*
* #param type - Refers to the captured image.
* #return Uri.fromFile - The file uri to store an image.
*/
public Uri getOutputMediaFileUri(int type) {
return Uri.fromFile(getOutputMediaFile(type));
}
/**
* Returns the captured image.
*
* #param type - Refers to the captured image.
* #return null or mediaFile - mediaFile: Captured
* image inside of timestamped file.
*/
private static File getOutputMediaFile(int type) {
// External sdcard location.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
IMAGE_DIRECTORY_NAME);
Log.v("Directory Two", mediaStorageDir.toString());
// Create the storage directory if it does not exists
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d(IMAGE_DIRECTORY_NAME, "Oops! Failed create " + IMAGE_DIRECTORY_NAME + " directory");
return null;
}
}
// Creates a media file name corresponding to a timestamp.
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
} else {
return null;
}
return mediaFile;
}
/*
* Uploads the saved and captured image to the server.
*/
public class ImageUpload extends AsyncTask<String, String,String> {
String postUrl = "";
public void postURL(String url) {
postUrl = url;
}
/**
* Compresses the selected gallery image and encodes
* compressed image to a base64 string.
*
* #param params
*/
#Override
protected String doInBackground(String... params) {
String result = null;
try {
HttpClient client = new DefaultHttpClient();
String postURL = "";
HttpPost post = new HttpPost("http://IP here/files/justine/image_save.php");
ArrayList<NameValuePair> nameValuePair = new ArrayList<NameValuePair>();
String msg = mEdit.getText().toString();
nameValuePair.add(new BasicNameValuePair("carid",msg));
//Converts image file uri to blob base64
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] byte_arr = stream.toByteArray();
String image_str = new String(Base64.encode(byte_arr, Base64.DEFAULT));
nameValuePair.add(new BasicNameValuePair("carimage", image_str));
UrlEncodedFormEntity ent = new UrlEncodedFormEntity(nameValuePair, HTTP.UTF_8);
post.setEntity(ent);
HttpResponse responsePOST = client.execute(post);
HttpEntity resEntity = responsePOST.getEntity();
result = EntityUtils.toString(resEntity);
if (resEntity != null) {
Log.i("RESPONSE", EntityUtils.toString(resEntity));
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Toast.makeText(getApplicationContext(),
"Successfully uploaded", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(),
"Sorry! Failed to upload", Toast.LENGTH_SHORT).show();
}
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.justin.myapplication" >
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="22" />
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true">
</supports-screens>
<!-- Accessing camera hardware -->
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.screen.portrait" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboard|keyboardHidden"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Logcat:
07-15 13:34:52.998 22775-22775/? D/dalvikvm﹕ Late-enabling CheckJNI
07-15 13:34:53.154 22775-22775/com.example.justin.myapplication D/libEGL﹕ loaded /vendor/lib/egl/libEGL_POWERVR_SGX540_120.so
07-15 13:34:53.162 22775-22775/com.example.justin.myapplication D/libEGL﹕ loaded /vendor/lib/egl/libGLESv1_CM_POWERVR_SGX540_120.so
07-15 13:34:53.170 22775-22775/com.example.justin.myapplication D/libEGL﹕ loaded /vendor/lib/egl/libGLESv2_POWERVR_SGX540_120.so
07-15 13:34:53.256 22775-22775/com.example.justin.myapplication D/OpenGLRenderer﹕ Enabling debug mode 0
07-15 13:35:08.795 22775-22775/com.example.justin.myapplication V/EditText﹕ 77869548059
07-15 13:35:10.779 22775-22775/com.example.justin.myapplication V/Directory Two﹕ /storage/emulated/0/Pictures/Hello Camera
07-15 13:35:11.490 22775-22775/com.example.justin.myapplication W/IInputConnectionWrapper﹕ showStatusIcon on inactive InputConnection
07-15 13:35:18.350 22775-22775/com.example.justin.myapplication V/Directory﹕ /storage/emulated/0/Pictures/Hello Camera
07-15 13:35:18.412 22775-22775/com.example.justin.myapplication V/File listFiles﹕ [Ljava.io.File;#427723e8
07-15 13:35:18.412 22775-22775/com.example.justin.myapplication V/Random r﹕ java.util.Random#4277d730
07-15 13:35:18.412 22775-22775/com.example.justin.myapplication V/Random Picture File﹕ /storage/emulated/0/Pictures/Hello Camera/IMG_20150713_143026.jpg
07-15 13:35:18.412 22775-22775/com.example.justin.myapplication V/PictureUri﹕ file:///storage/emulated/0/Pictures/Hello%20Camera/IMG_20150713_143026.jpg
07-15 13:35:18.443 22775-22775/com.example.justin.myapplication D/dalvikvm﹕ GC_FOR_ALLOC freed 207K, 3% free 8778K/9024K, paused 19ms, total 21ms
07-15 13:35:18.443 22775-22775/com.example.justin.myapplication I/dalvikvm-heap﹕ Grow heap (frag case) to 8.897MB for 314944-byte allocation
07-15 13:35:18.459 22775-22784/com.example.justin.myapplication D/dalvikvm﹕ GC_FOR_ALLOC freed 6K, 3% free 9079K/9332K, paused 18ms, total 18ms
07-15 13:35:18.607 22775-22775/com.example.justin.myapplication V/Bitmap﹕ android.graphics.Bitmap#4275f650
07-15 13:35:18.607 22775-22775/com.example.justin.myapplication V/ImgView﹕ android.widget.ImageView{42748e50 V.ED.... ......I. 0,0-0,0 #7f0c0051 app:id/imgPreview}
I am confused as to why the bitmap is returning null. I appreciate any insight as to why as well as any suggestions of how to improve my current code for my interested goal. Thank you greatly.
I think you are missing a reference to your created directory. Haven't tried this myself, but this might work in your previewCapturedImage() method:
File mediaStorageDir = new File(Environment.getExternalStorageDirectory().toString() + IMAGE_DIRECTORY_NAME);
I think you failed to add IMAGE_DIRECTORY_NAME since I believe you are making that a directory.
mediaStorageDir.listFiles()
Returns a list of files, but it can also contain directories. To only retrieve files, use:
mediaStorageDir.listFiles(new FileFilter() {
public boolean accept(File file) {
return !file.isDirectory();
}
});

Bluetooth enabling disrupts broadcastreceiver

My app is designed to append all scanned Bluetooth devices to a textview. This works great if the phones Bluetooth is on. However, if my app checks to see if the phones Bluetooth is off and turns it on if it is off, then start my the discovery process my broadcastreciever does not pick up an event and so my textview does not populate. Any help is appreciated!
Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blue_tooth_main);
txtResults = (TextView) this.findViewById(R.id.txtResults);
mBlueToothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!(mBlueToothAdapter.isEnabled())) {
mBlueToothAdapter.enable();
}
mBlueToothAdapter.startDiscovery();
MY RECEIVER:
public static class BlueToothBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
blueToothName = device.getName();
blueToothAddress = device.getAddress();
blueToothClass = device.getBluetoothClass();
blueToothBondState = device.getBondState();
GetBondStateStr(blueToothBondState);
blueToothUUIDS = device.getUuids();
paramsBlueTooth
.add(new BasicNameValuePair("Name: ", blueToothName));
paramsBlueTooth.add(new BasicNameValuePair("Address: ",
blueToothAddress));
paramsBlueTooth.add(new BasicNameValuePair("Class: ", String
.valueOf(blueToothClass)));
paramsBlueTooth.add(new BasicNameValuePair("Bond State: ",
blueToothBondStateStr));
paramsBlueTooth.add(new BasicNameValuePair("UUIDS: ", String
.valueOf(blueToothUUIDS)));
showBlueToothData();
}
ShowBlueToothData():
private void showBlueToothData() {
StringBuilder results = new StringBuilder();
results.append("-----BLUETOOTH DEVICE INFORMATION-----\n");
results.append("Name: " + blueToothName + "\n");
results.append("Address: " + blueToothAddress + "\n");
results.append("Class: " + blueToothClass + "\n");
results.append("Bond State: " + blueToothBondStateStr + "\n");
results.append("UUIDS: " + blueToothUUIDS + "\n");
txtResults.append(new String(results));
txtResults.append("\n");
}
Your chosen method of enabling the Bluetooth radio via the BluetoothAdapter.enable() method is an asynchronous call. As you can see from the method's documentation, you cannot assume that the radio is up and active as soon as the method returns. You have to wait for the ACTION_STATE_CHANGED broadcast to know that the radio is ready for you to try a scan.
If you read the documentation, notice also that doing it this was is a poor experience because there is no notification to the user. A better choice is the method of sending the user to Settings to enable Bluetooth themselves. You could modify your startup logic to be something more like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blue_tooth_main);
txtResults = (TextView) this.findViewById(R.id.txtResults);
mBlueToothAdapter = BluetoothAdapter.getDefaultAdapter();
…
}
#Override
protected void onResume() {
super.onResume();
if (!(mBlueToothAdapter.isEnabled())) {
//Take the user to settings first!
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
} else {
mBlueToothAdapter.startDiscovery();
}
}
This modification will check for Bluetooth every time you come to the foreground, and only trigger the scan if you are ready to do so.
Try doing:
Intent enable_intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enable_intent, REQUEST_ENABLE_BT);
inside if (!(mBlueToothAdapter.isEnabled())){...}
OR:
for(int i=0;i<5;i++){
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
Thread.sleep(100);
}else{
//display error, unsuccessfull , try manually
}

Login in Android app from web server and save registrationId of the device

I am very new in android and web services. I am trying to login into my android app where the data is coming from web service and at the same time I want to register the Id of android device using GCM in web server database. Until now I have done so far. If possible please give me your time to assets me.
Config.Java
public class Config {
static final String SERVER_URL = "http://example/stich/wsTest/?c=profile&func=login";
static final String GOOGLE_SENDER_ID = "499319758496";
static final String TAG = "GCM";
static final String DISPLAY_MESSAGE_ACTION = "com.google.android.gcm.demo.app.DISPLAY_MESSAGE";
static final String EXTRA_MESSAGE = "message";
}
Controller.java
public class Controller extends Application {
private final int MAX_ATTEMPTS = 5;
private final int BACKOFF_MILLI_SECONDS = 2000;
private final Random random = new Random();
/**
* Creating post request and store GCM RegistrationID, email and password in
* database on our web server for later use.
*
* #param context
* #param email
* #param password
* #param RegistrationID
*/
void register(final Context context, String email, String password,
final String RegistrationID) {
Log.i(Config.TAG, "registering device (RegistrationID = "
+ RegistrationID + ")");
String serverUrl = Config.SERVER_URL;
Map<String, String> params = new HashMap<String, String>();
params.put("androidToken", RegistrationID);
params.put("email", email);
params.put("password", password);
long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
// Once GCM returns a RegistrationID, we need to register on our server.
// As the server might be down, we will retry it a couple of times.
for (int i = 1; i <= MAX_ATTEMPTS; i++) {
Log.d(Config.TAG, "Attempt #" + i + "to register");
try {
// Send Broadcast to Show message on screen
displayMessageOnScreen(context, context.getString(
R.string.server_registering, i, MAX_ATTEMPTS));
// Post registration values to web server
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, true);
// Send Broadcast to Show message on screen
String message = context.getString(R.string.server_registered);
displayMessageOnScreen(context, message);
return;
} catch (IOException e) {
// Here we are simplifying and retrying on any error; in a real
// application, it should retry only on unrecoverable errors
// (like HTTP error code 503).
Log.e(Config.TAG, "Failed to register on attempt " + i + ":"
+ e);
if (i == MAX_ATTEMPTS) {
break;
}
try {
Log.d(Config.TAG, "Sleeping for " + backoff
+ " ms before retry");
Thread.sleep(backoff);
} catch (InterruptedException e1) {
// Activity finished before we complete - exit.
Log.d(Config.TAG,
"Thread interrupted: abort remaining retries!");
Thread.currentThread().interrupt();
return;
}
// increase backoff exponentially
backoff *= 2;
}
}
String message = context.getString(R.string.server_register_error,
MAX_ATTEMPTS);
// Send Broadcast to show message on screen
displayMessageOnScreen(context, message);
}
/**
* Unregister device from GCM server and also creating a post request on
* server to delete stored RegistrationID from database on our web server.
*
* #param context
* #param RegistrationID
*/
void unregister(final Context context, final String RegistrationID) {
Log.i(Config.TAG, "unregistering device (regId =" + RegistrationID
+ ")");
String serverUrl = Config.CLIPME_SERVER_URL + "/unregister";
Map<String, String> params = new HashMap<String, String>();
params.put("regId", RegistrationID);
try {
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, false);
String message = context.getString(R.string.server_unregistered);
displayMessageOnScreen(context, message);
} catch (IOException e) {
// At this point the device is unregistered from GCM, but still
// registered in the our server.
// We could try to unregister again, but it is not necessary:
// if the server tries to send a message to the device, it will get
// a "NotRegistered" error message and should unregister the device.
String message = context.getString(
R.string.server_unregister_error, e.getMessage());
displayMessageOnScreen(context, message);
}
}
/**
* Create HTTP Post request to server, requested url is defined in
* Config.java.
*
* #param endpoint
* #param params
* #throws IOException
*/
private void post(String endpoint, Map<String, String> params) throws IOException {
URL url;
try {
url = new URL(endpoint);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid url: " + endpoint);
}
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
// constructs the POST body using the parameters
while (iterator.hasNext()) {
Entry<String, String> param = iterator.next();
bodyBuilder.append(param.getKey()).append('=')
.append(param.getValue());
if(iterator.hasNext()){
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString();
Log.v(Config.TAG, "Posting'" +body + "'to"+url);
byte[] bytes = body.getBytes();
HttpURLConnection conn = null;
try{
Log.e("URL",">" + url);
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
//Post the request
OutputStream out = conn.getOutputStream();
out.write(bytes);
out.close();
//handle the response
int status = conn.getResponseCode();
//If response is not success
if(status != 200){
throw new IOException("Get failed with error code" + status);
}
}finally{
if (conn != null){
conn.disconnect();
}
}
}
/**
* Checks internet connectivity.
* #return false
*/
public boolean isConnectingToInternet(){
ConnectivityManager connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivity != null){
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if(info != null){
for(int i = 0; i < info.length; i++)
if(info[i].getState() == NetworkInfo.State.CONNECTED);
{
return true;
}
}
}
return false;
}
/**
* Send broadcast to show message on activity, broadcast receiver mHandleMessageReceiver
* defined in Login.java.
* #param context
* #param message
*/
void displayMessageOnScreen(Context context, String message) {
Intent intent = new Intent(Config.DISPLAY_MESSAGE_ACTION);
intent.putExtra(Config.EXTRA_MESSAGE, message);
// Send Broadcast to Broadcast receiver with message
context.sendBroadcast(intent);
}
/**
* It displays the simple Alert dialog
* #param context
* #param title
* #param message
* #param status
*/
#SuppressWarnings("deprecation")
public void showAlertDialog(Context context, String title, String message,
Boolean status) {
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
//Set Dialog Title
alertDialog.setTitle(title);
//Set Dialog Message
alertDialog.setMessage(message);
if(status != null)
//set alert dialog icon
alertDialog.setIcon((status) ? R.drawable
.ic_launcher : R.drawable.ic_launcher);
//Set OK button
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(final DialogInterface dialog, final int which) {
}
});
alertDialog.show();
}
private PowerManager.WakeLock wakeLock;
/**
* Device wakeup when any push notification reveived.
* #param context
*/
#SuppressWarnings("deprecation")
#SuppressLint("Wakelock")
public void acquireWakeLock (Context context){
if (wakeLock != null) wakeLock.release();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, "WakeLock");
wakeLock.acquire();
}
public void releaseWakeLock(){
if (wakeLock != null) wakeLock.release();
wakeLock =null;
}
}
LoginActivity.java
public class LoginActivity extends Activity {
EditText inputEmail;
EditText inputPassword;
Button loginBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
final Controller controller = (Controller) getApplicationContext();
// Check if the internet coneection is present
if (!controller.isConnectingToInternet()) {
// Internet connection is not present
controller.showAlertDialog(LoginActivity.this,
"Internet Connection Error!!", "Please connect your"
+ "device to internet.", false);
// Stop executing code by return
return;
}
// Check if GCM configuration is set
if (Config.SERVER_URL == null || Config.GOOGLE_SENDER_ID == null
|| Config.SERVER_URL.length() == 0
|| Config.GOOGLE_SENDER_ID.length() == 0) {
// GCM sender id / server url is missing
controller.showAlertDialog(LoginActivity.this,
"Configuration Error!",
"Please set your Server URL and GCM Sender Id", false);
// Stop executing code by return
return;
}
inputEmail = (EditText) findViewById(R.id.email);
inputPassword = (EditText) findViewById(R.id.password);
loginBtn = (Button) findViewById(R.id.btnLogin);
loginBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Get data from EditText
String email = inputEmail.getText().toString();
String password = inputPassword.getText().toString();
// Check if the user filled the form
if (email.trim().length() > 0 && password.trim().length() > 0) {
// Launch MainActivity
Intent i = new Intent(getApplicationContext(), MainActivity.class);
// Registering user to our server
// Sending registration details to MainActivity
i.putExtra("email", email);
startActivity(i);
finish();
} else {
// User don´t filled that data
controller.showAlertDialog(LoginActivity.this,
"Login Error",
"Please enter your full details", false);
}
}
});
}
}
GCMIntentService.java
public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = "GCMIntentService";
private Controller controller = null;
public GCMIntentService() {
// Call extended class Constructor GCMBaseIntentService
super(Config.GOOGLE_SENDER_ID);
}
/**
* Method called on Error
**/
#Override
protected void onError(Context context, String errorId) {
if(controller == null)
controller = (Controller) getApplicationContext();
Log.i(TAG, "Received error: " + errorId);
controller.displayMessageOnScreen(context,
getString(R.string.gcm_error, errorId));
}
/**
* Method called on Receiving a new message from GCM server
**/
#Override
protected void onMessage(Context context, Intent intent) {
if(controller == null)
controller = (Controller) getApplicationContext();
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("price");
controller.displayMessageOnScreen(context, message);
// notifies user
generateNotification(context, message);
}
#Override
protected boolean onRecoverableError(Context context, String errorId) {
if(controller == null)
controller = (Controller) getApplicationContext();
// log message
Log.i(TAG, "Received recoverable error: " + errorId);
controller.displayMessageOnScreen(context,
getString(R.string.gcm_recoverable_error,
errorId));
return super.onRecoverableError(context, errorId);
}
/**
* Create a notification to inform the user that server has sent a message.
*/
#SuppressWarnings("deprecation")
private void generateNotification(Context context, String message) {
int icon = R.drawable.ic_launcher;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MainActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
//notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Play default notification sound
// notification.defaults |= Notification.DEFAULT_SOUND;
//notification.sound = Uri.parse(
// "android.resource://"
// + context.getPackageName()
// + "your_sound_file_name.mp3");
// Vibrate if vibrate is enabled
notification.defaults |= Notification.DEFAULT_VIBRATE;
notificationManager.notify(0, notification);
}
/**
* Method called on device registered
**/
#Override
protected void onRegistered(Context context, String RegistrationID) {
// Get Global Controller Class object (see application tag in
// AndroidManifest.xml)
if (controller == null)
controller = (Controller) getApplicationContext();
Log.i(TAG, "Device registered: regId = " + RegistrationID);
controller.displayMessageOnScreen(context,
"Your device registred with GCM");
Log.d("NAME", MainActivity.email);
controller.register(context, MainActivity.password, MainActivity.email,
RegistrationID);
}
/**
* Method called on device unregistred
**/
#Override
protected void onUnregistered(Context context, String RegistrationID) {
if (controller == null)
controller = (Controller) getApplicationContext();
Log.i(TAG, "Device unregistered");
controller.displayMessageOnScreen(context,
getString(R.string.gcm_unregistered));
controller.unregister(context, RegistrationID);
}
}
MainActivity.java
public class MainActivity extends Activity {
// label to display gcm message
TextView lblMessage;
Controller controller;
// Asyntask
AsyncTask<Void, Void, Void> mRegisterTask;
static String email;
static String password;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
controller = (Controller) getApplicationContext();
// Check if Internet present
if (!controller.isConnectingToInternet()) {
// Internet Connection is not present
controller.showAlertDialog(MainActivity.this,
"Internet Connection Error",
"Please connect to Internet connection", false);
// stop executing code by return
return;
}
// Getting email from intent
Intent i = getIntent();
email = i.getStringExtra("email");
password = i.getStringExtra("password");
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest permissions was properly set
GCMRegistrar.checkManifest(this);
lblMessage = (TextView) findViewById(R.id.lblMessage);
// Register custom Broadcast receiver to show messages on activity
registerReceiver(mHandleMessageReceiver, new IntentFilter(
Config.DISPLAY_MESSAGE_ACTION));
// Get GCM registration id
final String regId = GCMRegistrar.getRegistrationId(this);
// Check if regid already presents
if (regId.equals("")) {
// Register with GCM
GCMRegistrar.register(this, Config.GOOGLE_SENDER_ID);
} else {
// Device is already registered on GCM Server
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
Toast.makeText(getApplicationContext(),
"Already registered with GCM Server", Toast.LENGTH_LONG)
.show();
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// Register on our server
// On server creates a new user
controller.register(context, email, regId, regId);
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
// execute AsyncTask
mRegisterTask.execute(null, null, null);
}
}
}
private BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(
Config.EXTRA_MESSAGE);
// Waking up mobile if it is sleeping
controller.acquireWakeLock(getApplicationContext());
// Display message on the screen
lblMessage.append(newMessage + "");
Toast.makeText(getApplicationContext(),
"Got Message: " + newMessage, Toast.LENGTH_LONG).show();
// Releasing wake lock
controller.releaseWakeLock();
}
};
#Override
protected void onDestroy() {
// Cancel AsyncTask
if (mRegisterTask != null) {
mRegisterTask.cancel(true);
}
try {
// Unregister Broadcast Receiver
unregisterReceiver(mHandleMessageReceiver);
// Clear internal resources.
GCMRegistrar.onDestroy(this);
} catch (Exception e) {
Log.e("UnRegister Receiver Error", "> " + e.getMessage());
}
super.onDestroy();
}
}
Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.appname"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
<!-- GCM connects to Internet Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!--
Creates a custom permission so only this app can receive its messages.
NOTE: the permission *must* be called PACKAGE.permission.C2D_MESSAGE,
where PACKAGE is the application's package name.
-->
<permission
android:name="com.example.appname.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.appname.permission.C2D_MESSAGE" />
<!-- This app has permission to register and receive data message. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Network State Permissions to detect Internet status -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Permission to vibrate -->
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name ="com.example.appname.Controller"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.appname.LoginActivity"
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.VIEW" />
<action android:name="android.intent.action.DELETE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="com.idrivecare.familypro" />
</intent-filter>
</activity>
<activity android:name="com.example.appname.MainActivity" />
<!--
BroadcastReceiver that will receive intents from GCM
services and handle them to the custom IntentService.
The com.google.android.c2dm.permission.SEND permission is necessary
so only GCM services can send data messages for the app.
-->
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<!-- <category android:name="com.google.android.gcm.demo.app" /> -->
<category android:name="com.example.appname" />
</intent-filter>
</receiver>
<service android:name="com.example.appname.gcm.GCMIntentService" />
</application>
After I login I get this in LogCat;
05-03 12:02:06.386: D/GCMRegistrar(20407): resetting backoff for com.example.appname
05-03 12:02:06.386: V/GCMRegistrar(20407): Registering app com.example.appname of senders 499319758496
05-03 12:02:06.456: V/GCMBroadcastReceiver(20407): onReceive: com.google.android.c2dm.intent.REGISTRATION
05-03 12:02:06.456: V/GCMBroadcastReceiver(20407): GCM IntentService class: com.example.appname.GCMIntentService
05-03 12:02:06.456: V/GCMBaseIntentService(20407): Acquiring wakelock
Help me if you find any problems in the code.
You are using the old way of registering to GCM, which is deprecated. The new way of registering simply requires calling GoogleCloudMessaing.register method, which is blocking, and therefore you don't have to get the result in the intent service.
You can follow the official GCM demo app if you wish to use the new way.
If you wish to stick with the old way, it can still work, but you'll have to make some fixes :
If your app's package name is com.example.appname, the manifest should use it consistently. Therefore, instead of :
<permission
android:name="com.example.clipme.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.appname.permission.C2D_MESSAGE" />
you should have
<permission
android:name="com.example.appname.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.appname.permission.C2D_MESSAGE" />
In addition, your intent service class com.example.appname.gcm.GCMIntentService is in the package com.example.appname.gcm, but the broadcast receiver you are using - com.google.android.gcm.GCMBroadcastReceiver - looks for it by default in the main package (com.example.appname.GCMIntentService). Therefore it doesn't find it. You shuold either move the service class to the main package or override the broadcast receiver class to change where it expects to find the intent service class.

Categories

Resources