I am new to Android app development and I have been asked to make a video splitter app. I am trying to use FFMPEG, but the library size is massive and makes the .APK file 140MB. How can I solve this? Similar apps are around 15MBs in size.
Also, the framerate starts at ~30FPS and drops to around 2.2FPS over time when trying to split a 30 second long video into two parts. How can I solve this? This is my code currently:
package splicer.com.splicer;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaMetadataRetriever;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.FFmpeg;
import com.github.hiteshsondhi88.libffmpeg.LoadBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegNotSupportedException;
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private FFmpeg ffmpeg;
static {
System.loadLibrary("native-lib");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ffmpeg = FFmpeg.getInstance(getApplicationContext());
try {
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
#Override
public void onStart() {}
#Override
public void onFailure() {}
#Override
public void onSuccess() {}
#Override
public void onFinish() {}
});
} catch(FFmpegNotSupportedException e) {
e.printStackTrace();
}
textView = (TextView) findViewById(R.id.textView);
textView.setY(200);
textView.setHeight(700);
textView.setMovementMethod(new ScrollingMovementMethod());
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openGallery();
}
});
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public void openGallery() {
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String [] {Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
}
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String [] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(gallery, 100);
}
public String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if(resultCode == RESULT_OK && requestCode == 100) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(getBaseContext(), intent.getData());
String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long splitCount = Long.valueOf(time) / 1000 / 15;
if(splitCount > 1) {
final String path = getRealPathFromURI(getBaseContext(), Uri.parse(intent.getData().toString()));
for(int a = 0, start = 0; a < splitCount; ++a, start += 15) {
// I am only testing with .mp4s atm, this will change before production
final String targetPath = path.replace(".mp4", "_" + (a + 1) + ".mp4");
ffmpeg.execute(new String [] {
"-r",
"1",
"-i",
path,
"-ss",
String.valueOf(start),
"-t",
String.valueOf(start + 15),
"-r",
"24",
targetPath
}, new ExecuteBinaryResponseHandler() {
#Override
public void onStart() {}
#Override
public void onProgress(String message) {
textView.setText("onProcess: " + message);
}
#Override
public void onFailure(String message) {
textView.setText("onFailure: " + message + " --- " + path);
}
#Override
public void onSuccess(String message) {
textView.setText("onSuccess:" + message);
MediaScannerConnection.scanFile(getBaseContext(),
new String [] { targetPath }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {}
});
}
#Override
public void onFinish() {}
});
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {
retriever.release();
}
}
}
}
I don't believe everything here is as optimal as it could be, but I'm just trying to prove the concept at the moment. Any help in the right direction would be amazing, thank you!
Related
Context:
I used a template of an android app from https://github.com/pramod722445/Custom_Object_Detection_App
and added java files to detect images from a video. I trained the model on Google Colab and converted it to a tflite file and renamed it as bline_model.tflite with a text folder with the label bline_label.txt in Android Studio. When I run the application, it stops/crashes after the video to image phase is done. I think the error originates from either objectDetectorClass.java or PreviewImageActivity.java. How do I fix it?
CODE
MainActivity.java
package bline.detector.com.ffmpegvideoeditor.activity;
import android.Manifest;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.FFmpeg;
import com.github.hiteshsondhi88.libffmpeg.LoadBinaryResponseHandler;
import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegCommandAlreadyRunningException;
import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegNotSupportedException;
import org.florescu.android.rangeseekbar.RangeSeekBar;
import org.opencv.android.OpenCVLoader;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import bline.detector.com.ffmpegvideoeditor.R;
public class MainActivity extends AppCompatActivity {
static {
if (!OpenCVLoader.initDebug())
Log.d("ERROR", "Unable to load OpenCV");
else
Log.d("SUCCESS", "OpenCV loaded");
}
private Button uploadVideo;
private Button extractImages;
private VideoView videoView;
private String filePath;
private static final String FILEPATH = "filepath";
private Uri selectedVideoUri;
private FFmpeg ffmpeg;
private static final int REQUEST_TAKE_GALLERY_VIDEO = 100;
private static final String TAG = "amirah";
private int choice = 0;
private ProgressDialog progressDialog;
private RangeSeekBar rangeSeekBar;
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
uploadVideo = (Button) findViewById(R.id.uploadVideo);
extractImages = (Button) findViewById(R.id.extractImages);
videoView = (VideoView) findViewById(R.id.videoView);
rangeSeekBar = (RangeSeekBar) findViewById(R.id.rangeSeekBar);
progressDialog = new ProgressDialog(this);
progressDialog.setTitle(null);
progressDialog.setCancelable(false);
rangeSeekBar.setEnabled(false);
loadFFMpegBinary();
uploadVideo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= 23)
getPermission();
else
uploadVideo();
}
});
extractImages.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
choice = 1;
if (selectedVideoUri != null) {
extractImagesVideo(rangeSeekBar.getSelectedMinValue().intValue() * 1000, rangeSeekBar.getSelectedMaxValue().intValue() * 1000);
} else
Toast.makeText(MainActivity.this, "Please Upload a Video", Toast.LENGTH_LONG).show();
}
});
}
private void getPermission() {
String[] params = null;
String writeExternalStorage = Manifest.permission.WRITE_EXTERNAL_STORAGE;
String readExternalStorage = Manifest.permission.READ_EXTERNAL_STORAGE;
int hasWriteExternalStoragePermission = ActivityCompat.checkSelfPermission(this, writeExternalStorage);
int hasReadExternalStoragePermission = ActivityCompat.checkSelfPermission(this, readExternalStorage);
List<String> permissions = new ArrayList<String>();
if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED)
permissions.add(writeExternalStorage);
if (hasReadExternalStoragePermission != PackageManager.PERMISSION_GRANTED)
permissions.add(readExternalStorage);
if (!permissions.isEmpty()) {
params = permissions.toArray(new String[permissions.size()]);
}
if (params != null && params.length > 0) {
ActivityCompat.requestPermissions(MainActivity.this,
params,
100);
} else
uploadVideo();
}
/**
* Handling response for permission request
*/
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
uploadVideo();
}
}
break;
}
}
private void uploadVideo() {
try {
Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Video"), REQUEST_TAKE_GALLERY_VIDEO);
} catch (Exception e) {
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_TAKE_GALLERY_VIDEO) {
selectedVideoUri = data.getData();
videoView.setVideoURI(selectedVideoUri);
videoView.start();
}
}
}
private void loadFFMpegBinary() {
try {
if (ffmpeg == null) {
ffmpeg = FFmpeg.getInstance(this);
}
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
#Override
public void onFailure() { showUnsupportedExceptionDialog();
}
#Override
public void onSuccess() {
Log.d(TAG, "ffmpeg: successfully Loaded");
}
});
} catch (FFmpegNotSupportedException e) {
showUnsupportedExceptionDialog();
} catch (Exception e) {
Log.d(TAG, "exception: " + e);
}
}
private void showUnsupportedExceptionDialog() {
new AlertDialog.Builder(MainActivity.this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Not Supported")
.setMessage("Device Not Supported")
.setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.finish();
}
})
.create()
.show();
}
private void extractImagesVideo(int startMs, int endMs) {
File moviesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
);
String filePrefix = "extract_picture";
String fileExtn = ".jpg";
String yourRealPath = getPath(MainActivity.this, selectedVideoUri);
File dir = new File(moviesDir, "VideoEditor");
int fileNo = 0;
while (dir.exists()) {
fileNo++;
dir = new File(moviesDir, "VideoEditor" + fileNo);
}
dir.mkdir();
filePath = dir.getAbsolutePath();
File dest = new File(dir, filePrefix + "%03d" + fileExtn);
Log.d(TAG, "startTrim: src: " + yourRealPath);
Log.d(TAG, "startTrim: dest: " + dest.getAbsolutePath());
String[] complexCommand = {"-y", "-i", yourRealPath, "-an", "-r", "1", "-ss", "" + startMs / 1000, "-t", "" + (endMs - startMs) / 1000, dest.getAbsolutePath()};
execFFmpegBinary(complexCommand);
}
//-y overwrite output files without asking
//-i ffmpeg reads from an arbitrary number of input "files" specified by the -i option
//-an disable audio recordings
//-r 1/2 will extract one image frame from every 2 second of video
//-r 1/2 will extract one image frame from every 2 second of video.Similarly, -r 1 will extract one image frame from every second of the video.
//remove -r option if u want to extract all video frames as images from the specified time duration
//-ss seeks to position
//-t limit the duration of data read from the input file
private void execFFmpegBinary(final String[] command) {
try {
ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
#Override
public void onFailure(String message) {
Log.d(TAG, "FAILED with output: " + message);
}
#Override
public void onSuccess(String s) {
Log.d(TAG, "SUCCESS with output: " + s);
Intent intent = new Intent(MainActivity.this, PreviewImageActivity.class);
intent.putExtra(FILEPATH, filePath);
startActivity(intent);
}
#Override
public void onProgress(String s) {
Log.d(TAG, "Started command : ffmpeg " + command);
if (choice == 1)
progressDialog.setMessage("progress: " + s);
Log.d(TAG, "progress : " + s);
}
#Override
public void onStart() {
Log.d(TAG, "Started command : ffmpeg " + command);
progressDialog.setMessage("Processing...");
progressDialog.show();
}
#Override
public void onFinish() {
Log.d(TAG, "Finished command : ffmpeg " + command);
if (choice == 1) {
progressDialog.dismiss();
}
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// do nothing for now
}
}
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
if (children != null) {
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
}
return dir.delete();
}
private String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri.
*/
private String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
}
objectDetectorClass.java
package bline.detector.com.ffmpegvideoeditor.activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.gpu.GpuDelegate;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class objectDetectorClass {
// used to load model and predict
private Interpreter interpreter;
// store all label in array
private List<String> labelList;
private int INPUT_SIZE;
private int PIXEL_SIZE=3; // for RGB
private int IMAGE_MEAN=0;
private float IMAGE_STD=255.0f;
// use to initialize gpu in app
private GpuDelegate gpuDelegate;
private int height=0;
private int width=0;
objectDetectorClass(AssetManager assetManager,String modelPath, String labelPath,int inputSize) throws IOException{
INPUT_SIZE=inputSize;
// use to define gpu or cpu // no. of threads
Interpreter.Options options=new Interpreter.Options();
gpuDelegate=new GpuDelegate();
options.addDelegate(gpuDelegate);
options.setNumThreads(4); // set it according to your phone
// loading model
interpreter=new Interpreter(loadModelFile(assetManager,modelPath),options);
// load labelmap
labelList=loadLabelList(assetManager,labelPath);
}
private List<String> loadLabelList(AssetManager assetManager, String labelPath) throws IOException {
// to store label
List<String> labelList=new ArrayList<>();
// create a new reader
BufferedReader reader=new BufferedReader(new InputStreamReader(assetManager.open(labelPath)));
String line;
// loop through each line and store it to labelList
while ((line=reader.readLine())!=null){
labelList.add(line);
}
reader.close();
return labelList;
}
private ByteBuffer loadModelFile(AssetManager assetManager, String modelPath) throws IOException {
// use to get description of file
AssetFileDescriptor fileDescriptor=assetManager.openFd(modelPath);
FileInputStream inputStream=new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel=inputStream.getChannel();
long startOffset =fileDescriptor.getStartOffset();
long declaredLength=fileDescriptor.getDeclaredLength();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,startOffset,declaredLength);
}
public Mat recognizeVideo(Mat mat_image){
Bitmap bitmap=null;
bitmap=Bitmap.createBitmap(mat_image.cols(),mat_image.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat_image,bitmap);
height=bitmap.getHeight();
width=bitmap.getWidth();
// scale the bitmap to input size of model
Bitmap scaledBitmap=Bitmap.createScaledBitmap(bitmap,INPUT_SIZE,INPUT_SIZE,false);
// convert bitmap to bytebuffer as model input should be in it
ByteBuffer byteBuffer=convertBitmapToByteBuffer(scaledBitmap);
// defining output
Object[] input=new Object[1];
input[0]=byteBuffer;
Map<Integer,Object> output_map=new TreeMap<>();
// create treemap of three array (boxes,score,classes)
float[][][]boxes =new float[1][10][4];
// 10: top 10 objects detected
// 4: coordinates in image
float[][] scores=new float[1][10];
// stores scores of 10 object
float[][] classes=new float[1][10];
// stores class of object
// add it to object_map;
output_map.put(0,boxes);
output_map.put(1,classes);
output_map.put(2,scores);
// now predict
interpreter.runForMultipleInputsOutputs(input,output_map);
Object value=output_map.get(0);
Object Object_class=output_map.get(1);
Object score=output_map.get(2);
// loop through each object
// as output has only 10 boxes
for (int i=0;i<10;i++){
float class_value=(float) Array.get(Array.get(Object_class,0),i);
float score_value=(float) Array.get(Array.get(score,0),i);
// define threshold for score
// Here you can change threshold according to your model
if(score_value>0.8){
Object box1=Array.get(Array.get(value,0),i);
// multiply it with Original height and width of frame
float top=(float) Array.get(box1,0)*height;
float left=(float) Array.get(box1,1)*width;
float bottom=(float) Array.get(box1,2)*height;
float right=(float) Array.get(box1,3)*width;
// draw rectangle in Original frame // starting point //ending point of box //color of box thickness
Imgproc.rectangle(mat_image,new Point(left,top),new Point(right,bottom),new Scalar(0, 255, 0, 255),2);
// write text on frame
// string of class name of object // starting point // color of text // size of text
Imgproc.putText(mat_image,labelList.get((int) class_value),new Point(left,top),3,1,new Scalar(255, 0, 0, 255),2);
}
}
return mat_image;
}
private ByteBuffer convertBitmapToByteBuffer(Bitmap bitmap) {
ByteBuffer byteBuffer;
int quant=1;
int size_images=INPUT_SIZE;
if(quant==0){
byteBuffer=ByteBuffer.allocateDirect(1*size_images*size_images*3);
}
else {
byteBuffer=ByteBuffer.allocateDirect(4*1*size_images*size_images*3);
}
byteBuffer.order(ByteOrder.nativeOrder());
int[] intValues=new int[size_images*size_images];
bitmap.getPixels(intValues,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
int pixel=0;
for (int i=0;i<size_images;++i){
for (int j=0;j<size_images;++j){
final int val=intValues[pixel++];
if(quant==0){
byteBuffer.put((byte) ((val>>16)&0xFF));
byteBuffer.put((byte) ((val>>8)&0xFF));
byteBuffer.put((byte) (val&0xFF));
}
else {
byteBuffer.putFloat((((val >> 16) & 0xFF))/255.0f);
byteBuffer.putFloat((((val >> 8) & 0xFF))/255.0f);
byteBuffer.putFloat((((val) & 0xFF))/255.0f);
}
}
}
return byteBuffer;
}
}
PreviewImageActivity.java
package bline.detector.com.ffmpegvideoeditor.activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewSwitcher;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import bline.detector.com.ffmpegvideoeditor.R;
public class PreviewImageActivity extends AppCompatActivity {
private static final String FILEPATH = "filepath";
private objectDetectorClass objectDetectorClass;
private ImageSwitcher imageSwitcher;
private Button prev;
private Button next;
int placement = 0;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preview_image);
TextView tvInstruction = (TextView) findViewById(R.id.tvInstruction);
imageSwitcher = (ImageSwitcher) findViewById(R.id.imageSwitcher);
prev = findViewById(R.id.prev);
next = findViewById(R.id.next);
ArrayList<Bitmap> bitmap = new ArrayList<Bitmap>();
ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
ArrayList<Drawable> drawables = new ArrayList<Drawable>();
try {
objectDetectorClass = new objectDetectorClass(getAssets(), "bline_model.tflite", "bline_label.txt", 300);
Log.d("PreviewImageActivity", "Model is successfully loaded");
} catch (IOException e) {
Log.d("PreviewImageActivity", "Getting some error");
e.printStackTrace();
}
imageSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
#Override
public View makeView() {
ImageView imageView = new ImageView(getApplicationContext());
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
return imageView;
}
});
prev.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (placement > 0) {
placement--;
imageSwitcher.setImageDrawable(drawables.get(placement));
} else {
Toast.makeText(PreviewImageActivity.this, "No Previous Images", Toast.LENGTH_SHORT).show();
}
}
});
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (placement < drawables.size() - 1) {
placement++;
imageSwitcher.setImageDrawable(drawables.get(placement));
} else {
Toast.makeText(PreviewImageActivity.this, "No More Images", Toast.LENGTH_SHORT).show();
}
}
});
String filePath = getIntent().getStringExtra(FILEPATH);
ArrayList<String> f = new ArrayList<String>();
File dir = new File(filePath);
tvInstruction.setText("Images stored at path " + filePath);
File[] listFile;
listFile = dir.listFiles();
for (File e : listFile) {
f.add(e.getAbsolutePath());
}
if (f != null) {
Log.d("PreviewImageAdapter", "filepath: " + f);
for (int position = 0; position < f.size(); position++) {
Bitmap bmp = BitmapFactory.decodeFile(f.get(position));
bitmap.add(bmp);
Log.d("PreviewImageAdapter", "bitmap: " + bitmap);
if (bitmap.size() == f.size()) {
for (int m = 0; m < bitmap.size(); m++) {
Mat selected_image = new Mat(bitmap.get(m).getHeight(), bitmap.get(m).getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(bitmap.get(m), selected_image);
selected_image = objectDetectorClass.recognizeVideo(selected_image);
Bitmap bitmap1 = Bitmap.createBitmap(selected_image.cols(), selected_image.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(selected_image, bitmap1);
bitmaps.add(bitmap1);
Log.d("PreviewImageAdapter", "detected images: " + bitmaps);
}
if (bitmaps.size() == bitmap.size()) {
for (int n = 0; n < bitmaps.size(); n++) {
Drawable drawable = new BitmapDrawable(bitmaps.get(n));
drawables.add(drawable);
Log.d("PreviewImageAdapter", "drawables: " + drawables);
}
}
imageSwitcher.setImageDrawable(drawables.get(0));
placement = 0;
}
}
}
}
}
I am also new to java and am sorry if my question is silly.
So I have an application which has to monitor and range after beacons and than calculates the position of the user. After calculating this , the value is passed to the Wayfindigoverlayactivity.class where the value should be putt on the map with the blue dot.
I don know how to assign the value to the blue dot but before that my application is working on an endless loop and is opening the activity on ranging about 100x .
RangingActivity:
package com.indooratlas.android.sdk.examples.wayfinding;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.widget.EditText;
import android.content.Context;
import com.google.android.gms.maps.model.LatLng;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.examples.R;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
public class RangingActivity extends Activity implements BeaconConsumer,Runnable{
protected static final String TAG = "RangingActivity";
public LatLng center;
private final BlockingQueue queue;
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranging);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.bind(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onBeaconServiceConnect() {
RangeNotifier rangeNotifier = new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
int beacon_number = beacons.size();
Beacon[] beacon_array = beacons.toArray(new Beacon[beacons.size()]);
Beacon device1 = null, device2 = null, device3 = null;
Constants constants = new Constants();
float txPow1 = 0;
double RSSI1Unfiltered = 0;
double RSSI2Unfiltered = 0;
float txPow2 = 0;
double RSSI3Unfiltered = 0;
float txPow3 = 0;
if (beacon_number == 4) {
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE1_UUID) {
device1 = beacon_array[1];
} else {
device1 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE2_UUID) {
device2 = beacon_array[1];
} else {
device2 = beacon_array[2];
}
if (beacon_array[0].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[0];
} else if (beacon_array[1].getIdentifier(0).toString() == constants.DEVICE3_UUID) {
device3 = beacon_array[1];
} else {
device3 = beacon_array[2];
}
RSSI1Unfiltered = device1.getRssi();
RSSI2Unfiltered = device2.getRssi();
RSSI3Unfiltered = device3.getRssi();
txPow1 = device1.getTxPower();
txPow2 = device2.getTxPower();
txPow3 = device3.getTxPower();
} else if (beacon_number > 0) {
Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: " + beacons.size());
for (int i = 0; i < beacon_number; i++) {
Beacon nextBeacon = beacon_array[i];
Log.d(TAG, "The next beacon " + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
logToDisplay("The next beacon" + nextBeacon.getIdentifier(0) + " is about " + nextBeacon.getDistance() + " meters away." + "RSSI is: " + nextBeacon.getRssi());
}
}
Log.d(TAG, "FLOAT!!!!!!!!" + txPow1);
LocationFinder locationFinder = new LocationFinder();
//pass location
center = locationFinder.findLocation(RSSI1Unfiltered, txPow1, RSSI2Unfiltered, txPow2, RSSI3Unfiltered, txPow3);
Log.d(TAG, "Current coordinates: asta e asta e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + center.toString());
Bundle args = new Bundle();
args.putParcelable("b", center);
Intent intent00 = new Intent(RangingActivity.this, WayfindingOverlayActivity.class);
intent00.putExtras(args);
startActivity(intent00);
}
private void logToDisplay(final String s) {
runOnUiThread(new Runnable() {
#Override
public void run() {
EditText editText = RangingActivity.this.findViewById(R.id.textView3);
editText.append(s+"\n");
}
});
}
};
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
beaconManager.addRangeNotifier(rangeNotifier);
} catch (RemoteException e) {
}
}
/* Blockinqueue try---not working
RangingActivity(BlockingQueue q)
{
queue = q;
}
public void run() {
LatLng res;
try
{
res = center;
queue.put(res);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
}
Everything works fine here , until I open the next class where my map is the WayfindingOverlayActivity
package com.indooratlas.android.sdk.examples.wayfinding;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.google.android.material.snackbar.Snackbar;
import androidx.fragment.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.indooratlas.android.sdk.IALocation;
import com.indooratlas.android.sdk.IALocationListener;
import com.indooratlas.android.sdk.IALocationManager;
import com.indooratlas.android.sdk.IALocationRequest;
import com.indooratlas.android.sdk.IAOrientationListener;
import com.indooratlas.android.sdk.IAOrientationRequest;
import com.indooratlas.android.sdk.IAPOI;
import com.indooratlas.android.sdk.IARegion;
import com.indooratlas.android.sdk.IARoute;
import com.indooratlas.android.sdk.IAWayfindingListener;
import com.indooratlas.android.sdk.IAWayfindingRequest;
import com.indooratlas.android.sdk.examples.R;
import com.indooratlas.android.sdk.examples.SdkExample;
import com.indooratlas.android.sdk.resources.IAFloorPlan;
import com.indooratlas.android.sdk.resources.IALatLng;
import com.indooratlas.android.sdk.resources.IALocationListenerSupport;
import com.indooratlas.android.sdk.resources.IAVenue;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import com.squareup.picasso.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
#SdkExample(description = R.string.example_wayfinding_description)
public class WayfindingOverlayActivity extends FragmentActivity
implements GoogleMap.OnMapClickListener, OnMapReadyCallback ,Runnable{
private final BlockingQueue queue;
private static final String TAG = "IndoorAtlasExample";
/* used to decide when bitmap should be downscaled */
private static final int MAX_DIMENSION = 2048;
//kalman filter
private static final double KALMAN_R = 0.125d;
private static final double KALMAN_Q = 0.5d;
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
private Circle mCircle;
private IARegion mOverlayFloorPlan = null;
private GroundOverlay mGroundOverlay = null;
private IALocationManager mIALocationManager;
private Target mLoadTarget;
private boolean mCameraPositionNeedsUpdating = true; // update on first location
private Marker mDestinationMarker;
private Marker mHeadingMarker;
private IAVenue mVenue;
private List<Marker> mPoIMarkers = new ArrayList<>();
private List<Polyline> mPolylines = new ArrayList<>();
private IARoute mCurrentRoute;
private IAWayfindingRequest mWayfindingDestination;
private IAWayfindingListener mWayfindingListener = new IAWayfindingListener() {
#Override
public void onWayfindingUpdate(IARoute route) {
mCurrentRoute = route;
if (hasArrivedToDestination(route)) {
// stop wayfinding
showInfo("You're there!");
mCurrentRoute = null;
mWayfindingDestination = null;
mIALocationManager.removeWayfindingUpdates();
}
updateRouteVisualization();
}
};
private IAOrientationListener mOrientationListener = new IAOrientationListener() {
#Override
public void onHeadingChanged(long timestamp, double heading) {
updateHeading(heading);
}
#Override
public void onOrientationChange(long timestamp, double[] quaternion) {
// we do not need full device orientation in this example, just the heading
}
};
private int mFloor;
// circle
private void showLocationCircle(LatLng center, double accuracyRadius) {
if (mCircle == null) {
// location can received before map is initialized, ignoring those updates
if (mMap != null) {
mCircle = mMap.addCircle(new CircleOptions()
.center(center)
.radius(accuracyRadius)
.fillColor(0x201681FB)
.strokeColor(0x500A78DD)
.zIndex(1.0f)
.visible(true)
.strokeWidth(5.0f));
mHeadingMarker = mMap.addMarker(new MarkerOptions()
.position(center)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.map_blue_dot))
.anchor(0.5f, 0.5f)
.flat(true));
}
} else {
// move existing markers position to received location
mCircle.setCenter(center);
mHeadingMarker.setPosition(center);
mCircle.setRadius(accuracyRadius);
}
}
private void updateHeading(double heading) {
if (mHeadingMarker != null) {
mHeadingMarker.setRotation((float) heading);
}
}
private IALocationListener mListener = new IALocationListenerSupport() {
public void onLocationChanged(IALocation location) {
Log.d(TAG, "NEW" + location.getLatitude() + " " + location.getLongitude());
if (mMap == null) {
return;
}
final LatLng center = new LatLng(location.getLatitude(),location.getLongitude());
final int newFloor = location.getFloorLevel();
if (mFloor != newFloor) {
updateRouteVisualization();
}
mFloor = newFloor;
showLocationCircle(center, location.getAccuracy());
if (mCameraPositionNeedsUpdating) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(center, 15.5f));
mCameraPositionNeedsUpdating = false;
}
}
};
/**
* Listener that changes overlay if needed
*/
private IARegion.Listener mRegionListener = new IARegion.Listener() {
#Override
public void onEnterRegion(final IARegion region) {
if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
Log.d(TAG, "enter floor plan " + region.getId());
mCameraPositionNeedsUpdating = true; // entering new fp, need to move camera
if (mGroundOverlay != null) {
mGroundOverlay.remove();
mGroundOverlay = null;
}
mOverlayFloorPlan = region; // overlay will be this (unless error in loading)
fetchFloorPlanBitmap(region.getFloorPlan());
//setupPoIs(mVenue.getPOIs(), region.getFloorPlan().getFloorLevel());
} else if (region.getType() == IARegion.TYPE_VENUE) {
mVenue = region.getVenue();
}
}
#Override
public void onExitRegion(IARegion region) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// prevent the screen going to sleep while app is on foreground
findViewById(android.R.id.content).setKeepScreenOn(true);
// instantiate IALocationManager
mIALocationManager = IALocationManager.create(this);
// Try to obtain the map from the SupportMapFragment.
((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map))
.getMapAsync(this);
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
Intent intent00 = getIntent();
LatLng center = intent00.getParcelableExtra("b");
Log.d(TAG,"Location!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + center);
}
#Override
protected void onDestroy() {
super.onDestroy();
// remember to clean up after ourselves
mIALocationManager.destroy();
}
/*Some blockingqueue---does not work
public class BlockinQueueExample
{
public void main(String[] args) throws Exception
{
BlockingQueue q = new ArrayBlockingQueue(1000);
RangingActivity producer = new RangingActivity(q);
WayfindingOverlayActivity consumer = new WayfindingOverlayActivity(q);
new Thread(producer).start();
new Thread(consumer).start();
}
}
WayfindingOverlayActivity(BlockingQueue q)
{
this.queue = q;
}
public void run() {
try{
queue.take();
Log.d(TAG,"BIANCABICA"+queue.take());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
*/
#Override
protected void onResume() {
super.onResume();
// start receiving location updates & monitor region changes
mIALocationManager.requestLocationUpdates(IALocationRequest.create(), mListener);
mIALocationManager.registerRegionListener(mRegionListener);
mIALocationManager.registerOrientationListener(
// update if heading changes by 1 degrees or more
new IAOrientationRequest(1, 0),
mOrientationListener);
if (mWayfindingDestination != null) {
mIALocationManager.requestWayfindingUpdates(mWayfindingDestination, mWayfindingListener);
}
}
EDIT , LAUNCHER ACTIVITY
package com.indooratlas.android.sdk.examples;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.indooratlas.android.sdk.examples.imageview.ImageViewActivity;
import com.indooratlas.android.sdk.examples.wayfinding.MonitoringActivity;
import com.indooratlas.android.sdk.examples.wayfinding.RangingActivity;
import com.indooratlas.android.sdk.examples.wayfinding.WayfindingOverlayActivity;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.powersave.BackgroundPowerSaver;
import org.altbeacon.beacon.startup.BootstrapNotifier;
import org.altbeacon.beacon.startup.RegionBootstrap;
public class Bianca extends Activity implements BootstrapNotifier {
private static final String TAG = "RANGE";
private RegionBootstrap regionBootstrap;
private Button button;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedBeaconsSinceBoot = false;
private MonitoringActivity monitoringActivity = null;
private String cumulativeLog = "";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
//--------------------------------meniu -------------------------------
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct();
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct2();
}
});
Button button3 = findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct3();
}
});
Button button4 = findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openAct4();
}
});
//-----------------------------meniu----------------------------------
Notification.Builder builder = new Notification.Builder(this);
Intent intent = new Intent(this,WayfindingOverlayActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
public void openAct()
{
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
startActivity(intent);
}
public void openAct2()
{
Intent intent2 = new Intent(this, RangingActivity.class);
startActivity(intent2);
}
public void openAct3()
{
Intent intent4 = new Intent(this, ImageViewActivity.class);
startActivity(intent4);
}
public void openAct4()
{
Intent intent5 = new Intent(this,RegionsActivity.class);
startActivity(intent5);
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap((BootstrapNotifier) this, region);
}
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever a Beacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
if (!haveDetectedBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an beacon, we launch the
// MainActivity
Intent intent = new Intent(this, WayfindingOverlayActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: make sure to add android:launchMode="singleInstance" in the manifest
// to keep multiple copies of this activity from getting created if the user has
// already manually launched the app.
this.startActivity(intent);
haveDetectedBeaconsSinceBoot = true;
} else {
if (monitoringActivity != null) {
// If the Monitoring Activity is visible, we log info about the beacons we have
// seen on its display
Log.d(TAG, "I see a beacon again");
} else {
// If we have already seen beacons before, but the monitoring activity is not in
// the foreground, we send a notification to the user on subsequent detections.
Log.d(TAG, "Sending notification.");
}
}
}
public void didExitRegion(Region arg0) {
Log.d(TAG,"I no longer see a beacon.");
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
}
The second class is not fully posted , only where I make changes.
The intent in the second class is in the OnCreate part
The location is calculated in the logcat , the only problem is that the application is working in a loop
Please help me , I am stuck. Thanks
I guess, you only need to open RangingActivity from onCreate() of WayFindingOverlayActivity if center is null. This means, we need to open RangingActivity to get the value for center. Doing null check for the center will also ensure that the application doesn't go in loop and proceed when we have the value for center. The code in the onCreate() of WayFindingOverlayActivity may look like this :
// edited here
Bundle extras = getIntent().getExtras();
if(extras == null){
Intent myIntent = new Intent(this, RangingActivity.class);
this.startActivity(myIntent);
} else{
LatLng center = extras.getParcelable("b");
Log.d("Location!!", center);
// call showLocationCircle() to show the blue dot
showLocationCircle(center, yourAccuracyRadius);
}
Hi I am currently facing issues with connection and reading of BLE devices. I think I have connected as the code below prints a message for connection, however the reading of values from the BLE device does not seem to work. The service uuid returns a null value.
package com.example.asp_sqllite;
import android.Manifest;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PlayActivity extends AppCompatActivity {
private static final int REQUEST_ENABLE_BT =1 ;
private Button btnPlay;
private Button btnConnect;
private ListView btList;
SQLiteDatabase db;
private Handler handler;
private ArrayList<String> deviceList = new ArrayList<>();
private ArrayAdapter<String> testAdapter;
private ArrayAdapter<String> deviceAdapter;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bleScanner;
private BluetoothGatt bleGatt;
private ArrayList<ScanResult> results = new ArrayList<>();
private ScanSettings settings;
private Intent intent;
private ListView bluetoothList;
private boolean completed = false;
static final UUID HR_SERVICE_UUID = UUID.fromString("0000110a-0000-1000-8000-00805f9b34fb");
private static final UUID HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
this.handler= new Handler();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
checkPermission();
}
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
this.btList = (ListView) findViewById(R.id.btlist);
deviceAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, android.R.id.text1);
testAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, android.R.id.text1);
intent = getIntent();
db = openOrCreateDatabase("myDB.db", MODE_PRIVATE, null);
checkBluetooth();
this.btnPlay = (Button) findViewById(R.id.btnPlay);
this.btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(bleGatt!=null) {
final String username = intent.getStringExtra("username");
System.out.println(username+"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
String sqlStatement = "insert into records (Name) values( '" + username + "')";
String result = updateTable(sqlStatement);
/*
* Run query to get recid to be passed over to the next activity
*
* */
final Cursor cursor = db.rawQuery("SELECT recID From records", null);
int num = 0;
if (cursor != null) {
cursor.moveToLast();
num = cursor.getInt(0);
cursor.close();
db.close();
}
Intent intent = new Intent(PlayActivity.this, testPlayActivity.class);
intent.putExtra("ID", Integer.toString(num));
startActivity(intent);
}
else
Toast.makeText(getApplicationContext(),"Connect to BLE device", Toast.LENGTH_LONG).show();
//finish();
}
});
this.btnConnect = (Button) findViewById(R.id.connect);
this.btnConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startScan();
Dialog d = new Dialog(PlayActivity.this); //open up dialog box with listview
d.setContentView(R.layout.bluetooth_device);
d.setTitle("Devices");
d.show();
//stopScan();
Button scanBtn = d.findViewById(R.id.scanBluetooth);
bluetoothList = d.findViewById(R.id.bluetoothDeviceList);
bluetoothList.setAdapter(deviceAdapter);
bluetoothList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
ScanResult device = results.get(i);
Toast.makeText(getApplicationContext(), device.getDevice().getName(), Toast.LENGTH_LONG).show();
bleGatt = device.getDevice().connectGatt(getApplicationContext(), false, bleGattCallback);
System.out.println("##############################################testing 123");
//finish();
try {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
Method getUuidsMethod = BluetoothAdapter.class.getDeclaredMethod("getUuids", null);
ParcelUuid[] uuids = (ParcelUuid[]) getUuidsMethod.invoke(adapter, null);
if(uuids != null) {
for (ParcelUuid uuid : uuids) {
System.out.println(uuid.getUuid().toString()+"#############################");
}
}else{
System.out.println("fail");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
scanBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) { //clear all list and adapters before scanning again
deviceList.clear();
deviceAdapter.clear();
results.clear();
startScan();
//stopScan();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stopScan();
}
},5000);
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
stopScan();
}
},5000);
}
});
}
public void checkPermission(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
){//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
123);
}
}
private void checkBluetooth()
{
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
private String updateTable(String sql) {
try {
db.beginTransaction();
db.execSQL(sql);
db.setTransactionSuccessful();
db.endTransaction();
} catch (Exception e) {
System.out.println(e.toString());
return ("Error");
}
Toast.makeText(this, "DB updated", Toast.LENGTH_LONG).show();
return ("Welcome");
}
private void stopScan(){
bleScanner = bluetoothAdapter.getBluetoothLeScanner();
bleScanner.stopScan(scanCallback);
}
private void startScan() {
bleScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bleScanner != null) { //setting up of scanner
final ScanFilter scanFilter =new ScanFilter.Builder().build();
settings =new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
bleScanner.startScan(Arrays.asList(scanFilter), settings, scanCallback);
//stopScan();
}
else
checkBluetooth();
}
private ScanCallback scanCallback = new ScanCallback() { //scan and return device results
#Override
public void onScanResult(int callbackType, ScanResult result) {
System.out.println("######### "+callbackType + result);
if (bleScanner != null && !deviceList.contains(result.getDevice().getName())) {
deviceList.add(result.getDevice().getName());
String device = result.getDevice().getName() + "\n" + result.getDevice().getAddress();
deviceAdapter.add(device); //Store device name and address
results.add(result); //records found devices as ScanResult
}
}
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e("TAG","onScanFailed");
}
};
private BluetoothGattCallback bleGattCallback = new BluetoothGattCallback()
{
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState== BluetoothProfile.STATE_CONNECTED){
System.out.println("#################################################################Connected");
}
else if (newState == BluetoothProfile.STATE_DISCONNECTED)
{
System.out.println("################################################################Not Connected");
}
gatt.discoverServices();
super.onConnectionStateChange(gatt, status, newState);
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
BluetoothGattService service = gatt.getService(HR_SERVICE_UUID);
System.out.println(service+"!!!!!!!!!!!!!!!!!!!!!!");
BluetoothGattCharacteristic temperatureCharacteristic = service.getCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID);
gatt.readCharacteristic(temperatureCharacteristic);
super.onServicesDiscovered(gatt, status);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
final String value = characteristic.getStringValue(0);
runOnUiThread(new Runnable() {
#Override
public void run() {
if(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
//Toast.makeText(getApplicationContext(), "Correct Bluetooth: " + value, Toast.LENGTH_LONG).show();
System.out.println("###########################################################correct");
} else {
//Toast.makeText(getApplicationContext(), "Wrong Bluetooth", Toast.LENGTH_LONG).show();
System.out.println("##############################################################wrong");
}
}
});
BluetoothGattService service = gatt.getService(HR_SERVICE_UUID);
//readNextCharacteristic(gatt, characteristic);
super.onCharacteristicRead(gatt, characteristic, status);
}
};
}
The code is an example in arduino library(BLE_Example/BLE_HRM)
/*
* Copyright (c) 2016 RedBear
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/**
* #note This demo is Nordic HRM example.
* You could use nRF toolbox tool to test it.
*/
#include <nRF5x_BLE_API.h>
#define DEVICE_NAME "Nordic_HRM"
BLE ble;
Ticker ticker_task1;
static uint8_t hrmCounter = 100;
static uint8_t bpm[2] = {0x00, hrmCounter};
static const uint8_t location = 0x03;
static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
// Create characteristic and service
GattCharacteristic hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, bpm, sizeof(bpm), sizeof(bpm), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR,(uint8_t *)&location, sizeof(location), sizeof(location),GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic *hrmChars[] = {&hrmRate, &hrmLocation, };
GattService hrmService(GattService::UUID_HEART_RATE_SERVICE, hrmChars, sizeof(hrmChars) / sizeof(GattCharacteristic *));
void disconnectionCallBack(const Gap::DisconnectionCallbackParams_t *params) {
Serial.println("Disconnected!");
Serial.println("Restarting the advertising process");
ble.startAdvertising();
}
void periodicCallback() {
if (ble.getGapState().connected) {
// Update the HRM measurement
// First byte = 8-bit values, no extra info, Second byte = uint8_t HRM value
// See --> https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
hrmCounter++;
if (hrmCounter == 175)
hrmCounter = 100;
bpm[1] = hrmCounter;
ble.updateCharacteristicValue(hrmRate.getValueAttribute().getHandle(), bpm, sizeof(bpm));
}
}
void setup() {
// put your setup code here, to run once
Serial.begin(9600);
Serial.println("Nordic_HRM Demo ");
// Init timer task
ticker_task1.attach(periodicCallback, 1);
// Init ble
ble.init();
ble.onDisconnection(disconnectionCallBack);
// setup adv_data and srp_data
ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
ble.accumulateAdvertisingPayload(GapAdvertisingData::HEART_RATE_SENSOR_HEART_RATE_BELT);
ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
// set adv_type
ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
// add service
ble.addService(hrmService);
// set device name
ble.setDeviceName((const uint8_t *)DEVICE_NAME);
// set tx power,valid values are -40, -20, -16, -12, -8, -4, 0, 4
ble.setTxPower(4);
// set adv_interval, 100ms in multiples of 0.625ms.
ble.setAdvertisingInterval(160);
// set adv_timeout, in seconds
ble.setAdvertisingTimeout(0);
// start advertising
ble.startAdvertising();
}
void loop() {
// put your main code here, to run repeatedly:
ble.waitForEvent();
}
I created an app with database in assets folder . I wrote a code to copy database to SD Card and of course for android 6 + it needs run time permission. My problem : on first run after granting permission database isn't loaded but on second run there is no problem. Please help me to solve this issue .
UPDATE: Problem solved! now I have problem with favorite section. when I add something to favorite it can't be updated and I have to restart app and also with each run data is shown more than one time.
Here's my code :
package farmani.com.essentialwordsforielts.mainPage;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import farmani.com.essentialwordsforielts.R;
import farmani.com.essentialwordsforielts.search.ActivitySearch;
public class MainActivity extends AppCompatActivity {
public static Context context;
public static ArrayList<Structure> list = new ArrayList<>();
public static ArrayList<Structure> favorite = new ArrayList<>();
DrawerLayout drawerLayout;
NavigationView navigationView;
ImageView hamburger;
SQLiteDatabase database;
String destPath;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navigation_activity_main);
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this
, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE
, Manifest.permission.WRITE_EXTERNAL_STORAGE}
, 1);
} else if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this
, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE
, Manifest.permission.WRITE_EXTERNAL_STORAGE}
, 1);
} else {
setupDB();
selectList();
selectFavorite();
Toast.makeText(MainActivity.this, "You grandet earlier",
Toast.LENGTH_LONG).show();
}
}
if (!favorite.isEmpty()){
favorite.clear();
selectFavorite();
} else if (!list.isEmpty()){
list.clear();
selectList();
}
context = getApplicationContext();
setTabOption();
drawerLayout = findViewById(R.id.navigation_drawer);
navigationView = findViewById(R.id.navigation_view);
hamburger = findViewById(R.id.hamburger);
hamburger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
drawerLayout.openDrawer(Gravity.START);
}
});
navigationView.setNavigationItemSelectedListener(new
NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
if (id == R.id.exit) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(
MainActivity.this);
alertDialog.setTitle(R.string.exit);
alertDialog.setMessage(R.string.exit_ask);
alertDialog.setCancelable(false);
alertDialog.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int
which) {
finish();
}
});
alertDialog.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int
which) {
dialog.cancel();
}
});
alertDialog.show();
}
if (id == R.id.search) {
Intent intent = new Intent(MainActivity.this,
ActivitySearch.class);
MainActivity.this.startActivity(intent);
}
return true;
}
});
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[]
permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case 1: {
if (grantResults.length >= 2 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED && grantResults[1] ==
PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "Access granted",
Toast.LENGTH_LONG).show();
}
}
}
}
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(Gravity.START)) {
drawerLayout.closeDrawer(Gravity.START);
} else {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(
MainActivity.this);
alertDialog.setTitle(R.string.exit);
alertDialog.setMessage(R.string.exit_ask);
alertDialog.setCancelable(false);
alertDialog.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
alertDialog.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alertDialog.show();
}
}
private void setTabOption() {
ViewPager viewPager = findViewById(R.id.viewpager);
viewPager.setAdapter(new AdapterFragment(getSupportFragmentManager(),
context));
TabLayout tabStrip = findViewById(R.id.tabs);
tabStrip.setupWithViewPager(viewPager);
}
private void setupDB() {
try {
destPath =
Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/ielts/";
File file = new File(destPath);
if (!file.exists()) {
file.mkdirs();
file.createNewFile();
CopyDB(getBaseContext().getAssets().open("md_book.db"),
new FileOutputStream(destPath + "/md_book.db"));
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
if (!favorite.isEmpty()){
favorite.clear();
selectFavorite();
} else if (!list.isEmpty()){
list.clear();
selectList();
}
}
private void CopyDB(InputStream inputStream, OutputStream outputStream)
throws IOException {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
}
private void selectFavorite() {
database = SQLiteDatabase.openOrCreateDatabase(destPath + "/md_book.db",
null);
Cursor cursor = database.rawQuery("SELECT * FROM main WHERE fav = 1",
null);
while (cursor.moveToNext()) {
String word = cursor.getString(cursor.getColumnIndex("word"));
String definition =
cursor.getString(cursor.getColumnIndex("definition"));
String trans = cursor.getString(cursor.getColumnIndex("trans"));
String img = cursor.getString(cursor.getColumnIndex("img"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
Structure struct = new Structure(word, definition, trans, img, id);
struct.setWord(word);
struct.setDefinition(definition);
struct.setTrans(trans);
struct.setImg(img);
struct.setId(id);
favorite.add(struct);
}
}
private void selectList() {
database = SQLiteDatabase.openOrCreateDatabase(destPath + "/md_book.db",
null);
Cursor cursor = database.rawQuery("SELECT * FROM main", null);
while (cursor.moveToNext()) {
String word = cursor.getString(cursor.getColumnIndex("word"));
String definition =
cursor.getString(cursor.getColumnIndex("definition"));
String trans = cursor.getString(cursor.getColumnIndex("trans"));
String img = cursor.getString(cursor.getColumnIndex("img"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
Structure struct = new Structure(word, definition, trans, img, id);
struct.setWord(word);
struct.setDefinition(definition);
struct.setTrans(trans);
struct.setImg(img);
struct.setId(id);
list.add(struct);
}
}
}
You have just shown a toast after permissions are granted for the first time i.e. inside onRequestPermissionsResult. You will need to place the code to perform necessary operations inside there too.
I'd like to check GPS both when the app is started and when the refresh button is hit, and use those data points, in the form of mLatitude and mLongitude to call the weather api. Eventually I'm going to geocode the city but right now for debugging purposes I'm outputting the GPS coordinates to the locationLabel textview.
my MainActivity.java:
package com.example.paxie.stormy.ui;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.paxie.stormy.GPS_Service;
import com.example.paxie.stormy.R;
import com.example.paxie.stormy.weather.Current;
import com.example.paxie.stormy.weather.Day;
import com.example.paxie.stormy.weather.Forecast;
import com.example.paxie.stormy.weather.Hour;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
public static final String DAILY_FORECAST = "DAILY_FORECAST";
public static final String HOURLY_FORECAST = "HOURLY_FORECAST";
private Forecast mForecast;
private double mLatitude;
private double mLongitude;
private BroadcastReceiver broadcastReceiver;
#Bind(R.id.timeLabel)
TextView mTimeLabel;
#Bind(R.id.temperatureLabel)
TextView mTemperatureLabel;
#Bind(R.id.humidityValue)
TextView mHumidityValue;
#Bind(R.id.precipValue)
TextView mPrecipValue;
#Bind(R.id.summaryLabel)
TextView mSummaryLabel;
#Bind(R.id.iconImageView)
ImageView mIconImageView;
#Bind(R.id.refreshImageView)
ImageView mRefreshImageView;
#Bind(R.id.progressBar)
ProgressBar mProgressBar;
#Bind(R.id.locationLabel)
TextView mLocationlabel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getForecast();
}
});
getForecast();
Log.d(TAG, "Main UI code is running!");
}
private void getForecast() {
if(!runtime_permissions())
checkGPS();
String apiKey = "1621390f8c36997cb1904914b726df52";
String forecastUrl = "https://api.forecast.io/forecast/" + apiKey +
"/" + mLatitude + "," + mLongitude;
if (isNetworkAvailable()) {
toggleRefresh();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(forecastUrl)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError();
}
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
try {
String jsonData = response.body().string();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
} else {
alertUserAboutError();
}
} catch (IOException e)
{
Log.e(TAG, "Exception caught: ", e);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
} else {
Toast.makeText(this, "Network is currently unavailable!", Toast.LENGTH_LONG).show();
}
}
private void toggleRefresh() {
if (mProgressBar.getVisibility() == View.INVISIBLE) {
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImageView.setVisibility(View.INVISIBLE);
} else {
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImageView.setVisibility(View.VISIBLE);
}
}
private void updateDisplay() {
mLocationlabel.setText(mLatitude + " " + mLongitude);
Current current = mForecast.getCurrent();
mTemperatureLabel.setText(current.getTemperature() + "");
mTimeLabel.setText("At " + current.getFormattedTime() + " it will be:");
mHumidityValue.setText(current.getHumidity() + "");
mPrecipValue.setText(current.getPrecipChance() + "%");
mSummaryLabel.setText(current.getSummary());
Drawable drawable = ContextCompat.getDrawable(this, current.getIconId());
mIconImageView.setImageDrawable(drawable);
}
private Forecast parseForecastDetails(String jsonData) throws JSONException {
Forecast forecast = new Forecast();
forecast.setCurrent(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyForecast(jsonData));
forecast.setDailyForecast(getDailyForecast(jsonData));
return forecast;
}
private Day[] getDailyForecast(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray data = daily.getJSONArray("data");
Day[] days = new Day[data.length()];
for (int i = 0; i < data.length(); i++) {
JSONObject jsonDay = data.getJSONObject(i);
Day day = new Day();
day.setSummary(jsonDay.getString("summary"));
day.setIcon(jsonDay.getString("icon"));
day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
day.setTime(jsonDay.getLong("time"));
day.setTimeZone(timezone);
days[i] = day;
}
return days;
}
private Hour[] getHourlyForecast(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray data = hourly.getJSONArray("data");
Hour[] hours = new Hour[data.length()];
for (int i = 0; i < data.length(); i++) {
JSONObject jsonHour = data.getJSONObject(i);
Hour hour = new Hour();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon(jsonHour.getString("icon"));
hour.setTime(jsonHour.getLong("time"));
hour.setTimeZone(timezone);
hours[i] = hour;
}
return hours;
}
private Current getCurrentDetails(String jsonData) throws JSONException {
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
Log.i(TAG, "From JSON: " + timezone);
JSONObject currently = forecast.getJSONObject("currently");
Current current = new Current();
current.setHumidity(currently.getDouble("humidity"));
current.setTime(currently.getLong("time"));
current.setIcon(currently.getString("icon"));
current.setPrecipChance(currently.getDouble("precipProbability"));
current.setSummary(currently.getString("summary"));
current.setTemperature(currently.getDouble("temperature"));
current.setTimeZone(timezone);
Log.d(TAG, current.getFormattedTime());
return current;
}
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if (networkInfo != null && networkInfo.isConnected()) {
isAvailable = true;
}
return isAvailable;
}
private void alertUserAboutError() {
AlertDialogFragment dialog = new AlertDialogFragment();
dialog.show(getFragmentManager(), "error_dialog");
}
#OnClick(R.id.dailyButton)
public void startDailyActivity(View view) {
Intent intent = new Intent(this, DailyForecastActivity.class);
intent.putExtra(DAILY_FORECAST, mForecast.getDailyForecast());
startActivity(intent);
}
#OnClick(R.id.hourlyButton)
public void startHourlyActivity(View view) {
Intent intent = new Intent(this, HourlyForecastActivity.class);
intent.putExtra(HOURLY_FORECAST, mForecast.getHourlyForecast());
startActivity(intent);
}
private void checkGPS() {
if (broadcastReceiver == null) {
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mLatitude = (double) intent.getExtras().get("latitude");
mLongitude = (double) intent.getExtras().get("longitude");
}
};
}
registerReceiver(broadcastReceiver, new IntentFilter("location_update"));
}
private boolean runtime_permissions() {
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 100);
return true;
}
return false;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 100) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
getForecast();
} else {
runtime_permissions();
}
}
}
#Override
protected void onResume() {
super.onResume();
checkGPS();
}
}
my GPSservice.java:
package com.example.paxie.stormy;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.annotation.Nullable;
/**
* Created by paxie on 10/27/16.
*/
public class GPS_Service extends Service {
private LocationListener listener;
private LocationManager locationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
listener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Intent i = new Intent("location_update");
i.putExtra("coordinates",location.getLongitude()+" "+location.getLatitude());
i.putExtra("longitude", location.getLongitude());
i.putExtra("latitude", location.getLatitude());
sendBroadcast(i);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
//noinspection MissingPermission
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,3000,0,listener);
}
#Override
public void onDestroy() {
super.onDestroy();
if(locationManager != null){
//noinspection MissingPermission
locationManager.removeUpdates(listener);
}
}
}
Here are two things you can try:
1. Please ensure you have included the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions in your manifest. If your app targets Android API 23 and above, you will need to include runtime permission handling.
2. Instead of having a LocationListener object as a class member, you should have your Service implement it as an interface and implement its onLocationChanged() method.
Apart from this, you really should consider using the location APIs' in Google Play Services, rather than those in AOSP (i.e. android.location).
See Making Your App Location-Aware