I'm storing some images on Parse Server for my Instagram Clone app. When trying to retrieve them using:
ParseFile file = imageToDisplay.getParseFile("image");
byte[] data = file.getData();
everything works just fine as long as there are still some images in cache but if I reinstall the app or try to get data of other users images I get an exception:
2019-12-12 14:24:44.005 15502-15502/com.example.instagramclone E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.instagramclone, PID: 15502
java.lang.IllegalArgumentException: Expected URL scheme 'http' or 'https' but no colon was found
at okhttp3.HttpUrl$Builder.parse(HttpUrl.java:1333)
at okhttp3.HttpUrl.get(HttpUrl.java:916)
at okhttp3.Request$Builder.url(Request.java:165)
at com.parse.ParseHttpClient.getRequest(ParseHttpClient.java:132)
at com.parse.ParseHttpClient.executeInternal(ParseHttpClient.java:68)
at com.parse.ParseHttpClient.execute(ParseHttpClient.java:57)
at com.parse.ParseRequest$3.then(ParseRequest.java:133)
at com.parse.ParseRequest$3.then(ParseRequest.java:130)
at bolts.Task$15.run(Task.java:917)
at bolts.BoltsExecutors$ImmediateExecutor.execute(BoltsExecutors.java:105)
at bolts.Task.completeAfterTask(Task.java:908)
at bolts.Task.continueWithTask(Task.java:715)
at bolts.Task.continueWithTask(Task.java:726)
at bolts.Task$13.then(Task.java:818)
at bolts.Task$13.then(Task.java:806)
at bolts.Task$15.run(Task.java:917)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Same thing happens when I use:
file.getDataInBackground(new GetDataCallback() {
#Override
public void done(byte[] data, ParseException e) {
// More code
}
});
Exception is in the parameter this time. To note saving and retrieving of anything else, so far worked perfectly.
It seems like urls to my ParseFiles are invalid. I checked that for one image with file.getUrl() and got:
undefined/files/78fc29aeab99c3e9ca6b9739efc4245658b7fd0f/087b5781e3ad8f4c3d7f85f039f6f977_image.png
Path without domain. How can I fix that?
Gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.instagramclone"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "com.github.parse-community.Parse-SDK-Android:parse:1.22.1"
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.instagramclone">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".ServerStarter"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".UserFeedActivity"></activity>
<activity
android:name=".UserDashboardActivity"
android:label="#string/user_dashboard_activity_label" />
<meta-data
android:name="com.parse.SERVER_URL"
android:value="#string/parse_server_url" />
<meta-data
android:name="com.parse.APPLICATION_ID"
android:value="#string/parse_app_id" />
<meta-data
android:name="com.parse.CLIENT_KEY"
android:value="#string/parse_client_key" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I save images to server using:
class SaveImageTask extends AsyncTask<Intent, Void, Void> {
#Override
protected Void doInBackground(Intent... intents) {
Uri selectedImage = intents[0].getData();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedImage);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
ParseFile file = new ParseFile("image.png", byteArray);
ImageParseObject image = new ImageParseObject();
image.put("image", file);
image.put("username", ParseUser.getCurrentUser().getUsername());
image.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null)
Toast.makeText(getApplicationContext(), "Image saved successfully!", Toast.LENGTH_LONG).show();
else {
Toast.makeText(getApplicationContext(), "Image saving failed!", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "Image saving failed!", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
return null;
}
}
Code fragment when the exception is thrown:
Builder parse(#Nullable HttpUrl base, String input) {
int pos = skipLeadingAsciiWhitespace(input, 0, input.length());
int limit = skipTrailingAsciiWhitespace(input, pos, input.length());
// Scheme.
int schemeDelimiterOffset = schemeDelimiterOffset(input, pos, limit);
if (schemeDelimiterOffset != -1) {
if (input.regionMatches(true, pos, "https:", 0, 6)) {
this.scheme = "https";
pos += "https:".length();
} else if (input.regionMatches(true, pos, "http:", 0, 5)) {
this.scheme = "http";
pos += "http:".length();
} else {
throw new IllegalArgumentException("Expected URL scheme 'http' or 'https' but was '"
+ input.substring(0, schemeDelimiterOffset) + "'");
}
} else if (base != null) {
this.scheme = base.scheme;
} else {
throw new IllegalArgumentException(
"Expected URL scheme 'http' or 'https' but no colon was found");
}
...
...
...
Configuartion class
package com.example.instagramclone;
import android.app.Application;
import com.parse.Parse;
import com.parse.ParseACL;
import com.parse.ParseObject;
public class ServerStarter extends Application {
#Override
public void onCreate() {
super.onCreate();
// Enable Local Datastore.
Parse.enableLocalDatastore(this);
ParseObject.registerSubclass(ImageParseObject.class);
// Add your initialization code here
Parse.initialize(new Parse.Configuration.Builder(getApplicationContext())
.applicationId("MY_ACTUAL_APP_ID")
.clientKey("MY_ACTUAL_CLIENT_ID")
.server("http://MY_ACTUAL_DOMAIN/parse/")
.build()
);
// ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
defaultACL.setPublicReadAccess(true);
defaultACL.setPublicWriteAccess(true);
ParseACL.setDefaultACL(defaultACL, true);
}
}
Parse Server Configuartion
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var app = express();
// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var api = new ParseServer({
databaseURI: "mongodb://root:6fDLIHOCRnkC#127.0.0.1:27017/bitnami_parse",
cloud: "./node_modules/parse-server/lib/cloud-code/Parse.Cloud.js",
appId: "78fc29aeab99c3e9ca6b9739efc4245658b7fd0f",
masterKey: "myActualMasterKey",
serverURL: "http://myActualIP:80/parse",
publicServerURL: "http://myActualIP:80/parse"
});
// Serve the Parse API on the /parse URL prefix
app.use('/parse', api);
var port = 1337;
app.listen(port, function() {
console.log('parse-server running on port ' + port);
});
//Parse Dashboard
var ParseDashboard = require('parse-dashboard');
var dashboard = new ParseDashboard({
apps: [
{
appName: "My Bitnami Parse API",
appId: "78fc29aeab99c3e9ca6b9739efc4245658b7fd0f",
masterKey: "myActualMasterKey",
production: true,
serverURL: "http://myActualIP:80/parse",
publicServerURL: "http://myActualIP:80/parse"
}
],
users: [
{
user: "user",
pass: "myActualPassword"
}
], useEncryptedPasswords: true
});
var allowInsecureHTTP = true;
// Serve the Parse Dashboard on the /parsedashboard URL prefix
app.use('/', dashboard);
var portdash = 4040;
app.listen(portdash, function() {
console.log('parse-dashboard running on port ' + portdash);
});
Related
I am sending images to firebase. but when my upload pictures method starts running, I get no auth token printed into my logcat couple of times. I tried using both camera and gallery, but they both make the same output even though my firebase storage rules are:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
And my code is:
if (fragment3.isAdded()) {
EditText plantdetails = (EditText) fragment3.getView().findViewById(R.id.plantdetails);
if (plantdetails.getText().toString().equals("")) {
Toast.makeText(newPlant.this, "I think you forgot something.", Toast.LENGTH_LONG).show();
} else {
plants plantss = new plants();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newPlant.this);
prefs.edit().putString("pldetails", plantdetails.getText().toString()).apply();
String pname = prefs.getString("plname","null");
String pdate = prefs.getString("pldate","null");
String petails = prefs.getString("pldetails","null");
plantss.setPlname(pname);
plantss.setPldate(pdate);
plantss.setPldetails(petails);
reference.child("Plants").child(pname).setValue(plantss);
try {
Fileuploader();
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
if (fragment4.isAdded()){
}
}
});
}
private void Fileuploader() throws FileNotFoundException {
String imageid;
progress.showProgress(newPlant.this,"Loading...",false);
DatabaseHelper databaseHelper = new DatabaseHelper(newPlant.this);
Cursor getimage = databaseHelper.GetPath();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newPlant.this);
String plname = prefs.getString("plname","null");
int count = 0;
int count2 = 0;
if (getimage !=null){
while (getimage.moveToNext()) {
System.out.println("IMAGE IS THIS MY MAN: "+ getimage.getString(0));
Bitmap bm = BitmapFactory.decodeFile(getimage.getString(0));
if (bm == null){
return;
}else {
ByteArrayOutputStream out = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 35, out);
imageid = System.currentTimeMillis() + "_" + (count++) + "." + getExtension(uri);
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Plants").child(plname).child("PlantImages");
String imagekey = reference.push().getKey();
reference.child(imagekey).child("ImageID").setValue(imageid);
reference.child(imagekey).child("ID").setValue(count2++);
System.out.println("IMAGES UPLOADEDDDD: " + imageid);
byte[] data = out.toByteArray();
StorageReference Ref = mStorageRef.child(imageid);
Ref.putBytes(data)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// Get a URL to the uploaded content
//Uri downloadUrl = taskSnapshot.getDownloadUrl();
//Toast.makeText(profuctadd.this,"Image uploaded",Toast.LENGTH_LONG).show();
progress.hideProgress();
Intent intent = new Intent(newPlant.this, Donenewplant.class);
startActivity(intent);
finish();
DatabaseHelper mDatabaseHelper = new DatabaseHelper(newPlant.this);
Cursor cursor2 = mDatabaseHelper.DeleteDataOfTableImagesAr();
cursor2.moveToNext();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle unsuccessful uploads
// ...
Toast.makeText(newPlant.this, "Failed", Toast.LENGTH_LONG).show();
System.out.println("FAILED:::: "+exception);
}
});
}
}
}
}
Mainfest file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.varroxsystems.plant">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="#drawable/plant"
android:label="#string/app_name"
android:roundIcon="#drawable/plant"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="#style/AppTheme">
<activity android:name=".Donenewplant"></activity>
<activity android:name=".newPlant" />
<activity android:name=".MainActivity" />
<activity android:name=".Splash_screen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.varroxsystems.plant.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
</manifest>
So does anyone know a solution for that, or is it just a bug, because I use the same exact code in another app and it works just fine.
EDIT: The other app I use uses a different database, not the same one.
I was trying to download an image from a server and save it in the external memory, but in Android 11 it gives me an error when I try to create the file.
I have granted permission to access the external storage.
i searched a bit on the internet and they suggested me to put this code in the manifest, but it didn't work for android 11
android:requestLegacyExternalStorage="true"
manifest
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.TestDwonloadImgApp"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity">
</activity>
</application>
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView img = findViewById(R.id.img);
ImmagineInterface ii = RetrofitManager.retrofit.create(ImmagineInterface.class);
Call<ResponseBody> call = ii.downloadFile("/immaginimusei/arte-scienza.jpg");
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.code() == 200) {
boolean result = writeResponseBody(response.body(), "/immaginimusei/arte-scienza.jpg");
if(result) {
Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/ArtHunter/immaginimusei/arte-scienza.jpg");
img.setImageBitmap(bitmap);
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/ArtHunter/immaginimusei/arte-scienza.jpg");
img.setImageBitmap(bitmap);
}
});
}
}
writeResponseBody
public static boolean writeResponseBody(ResponseBody body, String dir1) {
try {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// todo change the file location/name according to your needs
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/ArtHunter";
String path1 = path + dir1;
File f = new File(path1);
String path2 = f.getPath();
String nome = f.getName();
path2 = path2.replaceAll("/" + nome, "");
File directory = new File(path2);
if (!directory.exists())
directory.mkdirs();
File img = new File(path2, nome);
if (img.exists())
return true;
img.createNewFile();
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
inputStream = body.byteStream();
outputStream = new FileOutputStream(img); //error here!
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
}
outputStream.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
error
/System.err: java.io.FileNotFoundException: /storage/emulated/0/Download/ArtHunter/immaginimusei/arte-scienza.jpg: open failed: EEXIST (File exists)
W/System.err: at libcore.io.IoBridge.open(IoBridge.java:492)
at java.io.FileOutputStream.<init>(FileOutputStream.java:236)
at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
at com.theapplegeek.testdwonloadimgapp.MainActivity.writeResponseBody(MainActivity.java:93)
at com.theapplegeek.testdwonloadimgapp.MainActivity$1.onResponse(MainActivity.java:47)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCall$1$hVGjmafRi6VitDIrPNdoFizVAdk.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:245)
at android.app.ActivityThread.main(ActivityThread.java:8004)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
Caused by: android.system.ErrnoException: open failed: EEXIST (File exists)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:254)
W/System.err: at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7865)
at libcore.io.IoBridge.open(IoBridge.java:478)
... 13 more
In Android 11 android:requestLegacyExternalStorage="true" will simply be ignored, since it was an ad-hoc solution for Android < 11 to not break old apps.
Now, you must use
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
Also you could just use SAF to avoid all this 'permissions' hassle. This is what Google recommends for apps that do not need to manage most internal storage data. Refer to:
https://developer.android.com/guide/topics/providers/document-provider
However, if you don't want to break you app and lose all your hard work, consider
if(Environment.isExternalStorageManager())
{
internal = new File("/sdcard");
internalContents = internal.listFiles();
}
else
{
Intent permissionIntent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(permissionIntent);
}
This will bring up a settings page where you will be able to give storage access to your app. If the app already has permission, then you will be able to access the directory. Place this at the very beginning of onCreate() method after setting layout resource.
It's best not to do this for any future apps you build.
Go to your mobile setting -> apps -> select your app -> permissions -> storage -> select Allow managment of all files
It works for me.
Android has become too complex when it comes to creating folders.
If you want to avoid so many problems and not use things like android:requestLegacyExternalStorage="true" and adding many permissions like MANAGE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE which can be a nightmare, even when it comes to publishing your app to the marketplace.
In addition to that, from Android version 30 onwards, you ca NOT extend android:requestLegacyExternalStorage="true". So you will have problems again.
What is recommended is to start to save your recordings inside applications dedicated folder, because in the future Android will no longer let you create folders anymore, even with legacy methods. You can use standard directories in which to place any file like DIRECTORY_MUSIC, DIRECTORY_PICTURES, etc.
For instance, I created my own method for saving audio recording in Android 33 and it works perfectly. I don't need to add anything to my Manifest.
override fun startRecording(): Boolean {
try {
val recordingStoragePath = "${app.getExternalFilesDir(Environment.DIRECTORY_MUSIC)}"
recordingFilePath = "$recordingStoragePath/${fileRecordingUtils.generateFileName()}"
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
// Audio Quality Normal
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
mediaRecorder.setAudioSamplingRate(8000)
// Set path
mediaRecorder.setOutputFile(recordingFilePath)
mediaRecorder.prepare()
mediaRecorder.start()
Toast.makeText(app, "Recording Started", Toast.LENGTH_SHORT).show()
} catch (e: IOException) {
Toast.makeText(app, "Recording Failed. Problem accessing storage.", Toast.LENGTH_SHORT).show()
mediaRecorder.reset()
return false
} catch (e: Exception) {
mediaRecorder.reset()
return false
}
return true
}
I am making a cleaner version of a research drone android app that utilises DJI SDK/Hardware and attempting to structure the app more appropriately such that it can be expanded later. The application allows user selection of hardware (currently mock/test, DJI and ToDo) from a config pop up in the main display (class UserInterface extends AppCompatActivity). This is to allow the app to be used without registering or check perms when it is not being used for DJI. Previously I registered the DJI SDK when the app first opens, but it was my first real app and was a mess to follow its flow and didn't manage screen real estate very well.
When the DJI option is actioned from the pop up "config" window on the main interface from UI class, the UI class saves the config to a SettingsManager instance and calls the EventsManager instance with method runSettings(). The method actions the relevant SDK by try/catch{new DJI SDK class} and passes both the instances of EventsManager and UI.
The difference of this major version compared to my first version (which functions correctly) is the API key (changed package name, so new key), I am not doing the SDK registration in the first main activity and the SDK registration is not a viewable class, it only forwards strings to run on UI thread for showToast method in the UI class.
App does not return any errors.
showToast for messages in the DJI SDK class appear on the display but nothing inside the
SDKManagerCallback occurs.
Unsure of constructor in a class with extends AppCompatActivity, context methods seem unavailable when
tried with different class extensions.
I have tried creating two DJI developer keys, no difference.
Unsure of DJI class listing in Manifest as it is not an activity started by an intent with bundle but
does require context.
DJI SDK class is extracted from DJISDKDemo (I am fairly new to android app development, not sure I
could write my own version from the limited explanation on DJI dev site).
Thank you greatly for your help and any other tips appreciated.
The SDKRegistration class:
package com.research.droneapp; // mock package name for SO
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.sdkmanager.DJISDKInitEvent;
import dji.sdk.sdkmanager.DJISDKManager;
public class SoftwareDevelopmentKitDJI extends AppCompatActivity {
private static final String TAG = "SoftwareDevelopmentKit";
public UserInterface userInterface;
public EventsManager eventsManager;
public static final String FLAG_CONNECTION_CHANGE = "dji_sdk_connection_change";
private static BaseProduct mProduct;
private Handler mHandler;
public boolean isConnected = false; // Variable for start up flag
private static final String[] REQUIRED_PERMISSION_LIST = new String[]{
Manifest.permission.VIBRATE,
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.WAKE_LOCK,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE,
};
private List<String> missingPermission = new ArrayList<>();
private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false);
private static final int REQUEST_PERMISSION_CODE = 12345;
public SoftwareDevelopmentKitDJI(UserInterface userInterface, EventsManager eventsManager) {
Log.d(TAG, "SoftwareDevelopmentKitDJI");
this.userInterface = userInterface;
this.eventsManager = eventsManager;
// Permission checked true actions SDK registration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkAndRequestPermissions();
}
// Handle DJI SDK hardware changes in background thread??
// ToDo: Receive hardware changes in EventsManager
mHandler = new Handler(Looper.getMainLooper());
}
/**
* Checks if there is any missing permissions, and
* requests runtime permission if needed.
*/
private void checkAndRequestPermissions() {
Log.d(TAG, "checkAndRequestPermissions: S");
// Check for permissions
for (String eachPermission : REQUIRED_PERMISSION_LIST) {
if (ContextCompat.checkSelfPermission(userInterface, eachPermission) !=
PackageManager.PERMISSION_GRANTED) {
missingPermission.add(eachPermission);
}
}
// Request for missing permissions
if (missingPermission.isEmpty()) {
Log.d(TAG, "notMissingPerms");
startSDKRegistration();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.d(TAG, "missingPerms");
passToastToUI("Need permissions!");
ActivityCompat.requestPermissions(userInterface,
missingPermission.toArray(new String[missingPermission.size()]),
REQUEST_PERMISSION_CODE);
}
Log.d(TAG, "checkAndRequestPermissions: E");
}
/**
* Result of runtime permission request
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Check for granted permission and remove from missing list
if (requestCode == REQUEST_PERMISSION_CODE) {
for (int i = grantResults.length - 1; i >= 0; i--) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
missingPermission.remove(permissions[i]);
}
}
}
// If no missing permission, start SDK registration
if (missingPermission.isEmpty()) {
startSDKRegistration();
} else {
passToastToUI("Missing permissions!!!");
}
}
private void startSDKRegistration() {
Log.d(TAG, "startSDKRegistration: S");
if (isRegistrationInProgress.compareAndSet(false, true)) {
AsyncTask.execute(new Runnable() {
#Override
public void run() {
passToastToUI("registering, pls wait...");
Log.d(TAG, "startSDKRegistration: run");
// ToDO: Investigate why SDKManagerCallback's don't occur
// (is getApplicationContext() correct?)
DJISDKManager.getInstance().registerApp(getApplicationContext(),
new DJISDKManager.SDKManagerCallback() {
#Override
public void onRegister(DJIError djiError) {
Log.d(TAG, "onRegister: S");
if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
passToastToUI("Register Success");
DJISDKManager.getInstance().startConnectionToProduct();
}
else {
passToastToUI("Register sdk failed!");
}
Log.v(TAG, djiError.getDescription());
Log.d(TAG, "onRegister: E");
}
#Override
public void onProductDisconnect() {
Log.d(TAG, "onProductDisconnect");
passToastToUI("Product Disconnected");
notifyStatusChange();
isConnected = false; // Set hardware connection flag
}
#Override
public void onProductConnect(BaseProduct baseProduct) {
Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct));
passToastToUI("Product Connected");
notifyStatusChange();
isConnected = true; // Set hardware connection flag
}
#Override
public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent,
BaseComponent newComponent) {
if (newComponent != null) {
newComponent.setComponentListener(new BaseComponent.ComponentListener() {
#Override
public void onConnectivityChange(boolean isConnected) {
Log.d(TAG, "onComponentConnectivityChanged: " + isConnected);
notifyStatusChange();
}
});
}
Log.d(TAG, String.format(
"onComponentChange key:%s, oldComponent:%s, newComponent:%s",
componentKey, oldComponent, newComponent));
}
#Override
public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) {
Log.d(TAG, "startSDKRegistration: onInitProcess");
}
#Override
public void onDatabaseDownloadProgress(long l, long l1) {
Log.d(TAG, "startSDKRegistration: onDatabaseDownloadProgress");
}
});
}
});
}
Log.d(TAG, "startSDKRegistration: E");
}
private void notifyStatusChange() {
mHandler.removeCallbacks(updateRunnable);
mHandler.postDelayed(updateRunnable, 500);
}
private Runnable updateRunnable = () -> {
Intent intent = new Intent(FLAG_CONNECTION_CHANGE);
sendBroadcast(intent);
};
private void passToastToUI(String toastMsg) {
runOnUiThread(() -> {
userInterface.showToast(toastMsg);
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.research.droneapp"> <!-- mock package name for SO -->
<!-- Permissions and features -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<uses-feature
android:name="android.hardware.usb.accessory"
android:required="true" />
<!-- App Activity Process -->
<application
android:name="com.research.droneapp.MApplication"
android:allowBackup="true"
android:icon="#mipmap/hmu_icon"
android:label="#string/app_name"
android:roundIcon="#mipmap/hmu_icon_round"
android:supportsRtl="true"
android:theme="#style/Theme.AppCompat.NoActionBar">
<!-- Main Display -->
<activity android:name="com.research.droneapp.UserInterface" />
<!-- DJI SDK -->
<uses-library android:name="com.android.future.usb.accessory" />
<meta-data
android:name="com.dji.sdk.API_KEY"
android:value="*************" /> <!-- removed for SO -->
<activity
android:name="dji.sdk.sdkmanager.DJIAoaControllerActivity"
android:theme="#android:style/Theme.Translucent">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="#xml/accessory_filter" />
</activity>
<service android:name="dji.sdk.sdkmanager.DJIGlobalService">
</service>
<activity android:name="com.research.droneapp.SoftwareDevelopmentKitDJI" />
<!-- Splash Screen at Launch -->
<activity android:name="com.research.droneapp.Splash" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The method that starts the SDK class:
/**
* Settings changes to start relevant classes
*/
public void runSettings() {
Log.d(TAG, "runSettings: S");
// Get hardware selection from settings
int hardwarePosition = settingsManager.getHardwareInt();
/*ToDo: Set Test Environment*/
if (hardwarePosition == 0) { // Operate settings for test environment
Log.d(TAG, "runSettings: hardPos Test");
userInterface.showToast("runSetting: TEST");
}
else if (hardwarePosition == 1) { // Operate settings for DJI
Log.d(TAG, "runSettings: hardPos MavP2");
try {
this.softwareDevelopmentKitDJI = new SoftwareDevelopmentKitDJI(
userInterface, this);
Log.d(TAG, "runSettings: DJI Launched");
userInterface.showToast("runSetting: DJI");
} catch (Exception e) {
Log.d(TAG, "runSettings: DJI Error: "+ e.toString());
userInterface.showToast("runSetting: Error");
}
} // Operate settings for...?
else if (hardwarePosition == 2) { /*ToDo*/
Log.d(TAG, "runSettings: hardPos ToDo");
userInterface.showToast("runSetting: ToDo");
}
else { // Unknown hardware
Log.d(TAG, "runSettings: Error");
userInterface.showToast("runSetting:Error");
}
Log.d(TAG, "runSettings: E");
}
The config window click listener for action changes button, within the method of UI class for config pop up window:
// Action changes
Button btnAction = popupView.findViewById(R.id.btnAction);
btnAction.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "displayConfigOverlay: onClick -> configAction");
// Update hardware setting
String hardware =
(btnTest.isChecked())?settingsManager.HARDWARE_OPTIONS[0]:
(btnMavP2.isChecked())?settingsManager.HARDWARE_OPTIONS[1]:
(btnToDo.isChecked())?settingsManager.HARDWARE_OPTIONS[2]:
"NoSelection!";
settingsManager.setHardware(hardware);
// Update port number
String port = serverPortEdit.getText().toString();
settingsManager.setServerPort(port);
// Update WSS launch setting
boolean autoLunchWSS = swAutoWSS.isChecked();
settingsManager.setAutoLaunchWSS(autoLunchWSS);
// Display to user
showToast("Hardware: " + hardware + "\nPort: " + port + "\nAutoWSS: " +
autoLunchWSS);
// Push settings
eventsManager.runSettings();
// Close config pop up
popupConfig.dismiss();
}
});
Apologises for the lack of and mixed commenting styles. Again any tips appreciated, still new to android and java.
Min API23 setting in gradle allowed code to run but did not allow SDK to download. Unsure why errors not returned.
Solved by changing gradle to following:
...
android {
compileSdkVersion 29
buildToolsVersion '28.0.3'
useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
multiDexEnabled true
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
...
Additionally found a potential context issue within layout, tools:context="..." was not set to the Activity interacting with this layout.
...
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mainConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserInterface" >
...
I'm trying to connect a device (an oximeter) to a Android smartphone via UART cable. So I need to build an app capable of reading data from this device. For this, I'm using Android Things, but I haven't been able to make this app to run, 'cause I get this error: "Install failed.
Installation failed
Rerun"
Pretty sure this tag "uses-library android:name="com.google.android.things" android:required="true"" is getting me this error, without it it runs but crashes.
MainActivity:
public class MainActivity extends Activity {
String TAG = "Log";
private static final String UART_DEVICE_NAME = null;
private UartDevice mDevice;
private TextView tv;
private List<String> serial;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = findViewById(R.id.serial);
serial = new ArrayList<>();
PeripheralManager manager = PeripheralManager.getInstance();
List<String> deviceList = manager.getUartDeviceList();
if (deviceList.isEmpty()) {
Log.i(TAG, "No UART port available on this device.");
} else {
Log.i(TAG, "List of available devices: " + deviceList);
}
// Attempt to access the UART device
try {
mDevice = manager.openUartDevice(deviceList.get(0));
} catch (IOException e) {
Log.w(TAG, "Unable to access UART device", e);
}
try {
setFlowControlEnabled(mDevice, false);
} catch (IOException e) {
e.printStackTrace();
}
try {
configureUartFrame(mDevice);
} catch (IOException e) {
e.printStackTrace();
}
try {
writeUartData(mDevice);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mDevice != null) {
try {
mDevice.close();
mDevice = null;
} catch (IOException e) {
Log.w(TAG, "Unable to close UART device", e);
}
}
}
public void configureUartFrame(UartDevice uart) throws IOException {
// Configure the UART port
uart.setBaudrate(57600);
uart.setDataSize(8);
uart.setParity(UartDevice.PARITY_NONE);
uart.setStopBits(1);
}
public void setFlowControlEnabled(UartDevice uart, boolean enable) throws
IOException {
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
}
public void writeUartData(UartDevice uart) throws IOException {
byte[] raw = new byte[]{(byte) 0xA5, (byte) 0x10, (byte) 0x02, (byte)
0x1A, (byte) 0x00, (byte) 0x00};
byte cs = (byte) ((raw[1] ^ raw[2] ^ raw[3] ^ raw[4]) & 0xFF);
raw[5] = cs;
int count = uart.write(raw, raw.length);
Log.d(TAG, "Wrote " + count + " bytes to peripheral");
readUartBuffer(uart);
}
public void readUartBuffer(UartDevice uart) throws IOException {
// Maximum amount of data to read at one time
final int maxCount = 9600;
byte[] buffer = new byte[maxCount];
int count;
while ((count = uart.read(buffer, buffer.length)) > 0) {
Log.d(TAG, "Read " + count + " bytes from peripheral");
serial.add(String.valueOf(buffer[count]) + " ");
}
StringBuilder data = new StringBuilder();
for (int i = 0; i < serial.size(); i++) {
data.append(serial.get(i));
}
tv.setText(data);
}
}
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.oximetro">
<uses-permission
android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library android:name="com.google.android.things"
android:required="true"/>
</application>
</manifest>
build.Gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.0"
defaultConfig {
applicationId "com.example.oximetro"
minSdkVersion 27
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-
optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.things:androidthings:+'
}
I appreciate any help, even alternatives to do what I'm trying to do, which is reading data from an oximeter via UART with an Android smartphone. Running this code I get the error mentioned above, so the app does not even open on device.
You can't run the Android Things library on your phone. Your phone doesn't have the library hooks to make the hardware connections. The phone, knowing it doesn't have everything it needs to run the app, should refuse the installation.
I am following the Spotify tutorial on android sdk beta22-noconnect-2.20b to just get the groundwork on an app that I want to to write and I am having trouble getting my application to authenticate. I am sure that I have signed my app and inserted the correct fingerprint into the "My Applications" tab on spotify. My URI is correct. I am also fairly certain that I followed the tutorial 100% properly. Every time I run the program I get this error.
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
D/com.spotify.sdk.android.authentication.LoginActivity: https://accounts.spotify.com/authorize?client_id=7c59d84c8b7f4e35b3c85a7a5289db1b&response_type=token&redirect_uri=spotifymixer%3A%2F%2Fcallback&show_dialog=true&scope=user-read-private%20streaming
D/SpotifyAuthHandler: start
D/com.spotify.sdk.android.authentication.LoginActivity: Error authenticating
D/SpotifyAuthHandler: stop
I don't know how to proceed. Could I have used an incorrect signature for the program?
My MainActivity.java:
package com.example.mammo.spotifyplayer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import com.spotify.sdk.android.authentication.AuthenticationClient;
import com.spotify.sdk.android.authentication.AuthenticationRequest;
import com.spotify.sdk.android.authentication.AuthenticationResponse;
import com.spotify.sdk.android.player.Config;
import com.spotify.sdk.android.player.ConnectionStateCallback;
import com.spotify.sdk.android.player.Error;
import com.spotify.sdk.android.player.Player;
import com.spotify.sdk.android.player.PlayerEvent;
import com.spotify.sdk.android.player.Spotify;
import com.spotify.sdk.android.player.SpotifyPlayer;
public class MainActivity extends Activity implements
SpotifyPlayer.NotificationCallback, ConnectionStateCallback
{
// TODO: Replace with your client ID
private static final String CLIENT_ID = "7c59d84c8b7f4e35b3c85a7a5289db1b";
// TODO: Replace with your redirect URI
private static final String REDIRECT_URI = "spotifymixer://callback";
private Player mPlayer;
private static final int REQUEST_CODE = 1337;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AuthenticationRequest.Builder builder = new AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI);
builder.setScopes(new String[]{"user-read-private", "streaming"});
AuthenticationRequest request = builder.build();
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request);
}
public void tryAgain(View view){
AuthenticationRequest.Builder builder = new AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI);
builder.setScopes(new String[]{"user-read-private", "streaming"});
AuthenticationRequest request = builder.build();
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if(requestCode == REQUEST_CODE){
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, intent);
if(response.getType() == AuthenticationResponse.Type.TOKEN) {
Config playerConfig = new Config(this, response.getAccessToken(), CLIENT_ID);
Spotify.getPlayer(playerConfig, this, new SpotifyPlayer.InitializationObserver() {
#Override
public void onInitialized(SpotifyPlayer spotifyPlayer) {
mPlayer = spotifyPlayer;
mPlayer.addConnectionStateCallback(MainActivity.this);
mPlayer.addNotificationCallback(MainActivity.this);
}
#Override
public void onError(Throwable throwable) {
Log.e("MainActivity", "Could not initialize player: " + throwable.getMessage());
}
});
}
}
}
#Override
protected void onDestroy() {
Spotify.destroyPlayer(this);
super.onDestroy();
}
#Override
public void onPlaybackEvent(PlayerEvent playerEvent) {
Log.d("MainActivity", "Playback event received: " + playerEvent.name());
switch (playerEvent) {
// Handle event type as necessary
default:
break;
}
}
#Override
public void onPlaybackError(Error error) {
Log.d("MainActivity", "Playback error received: " + error.name());
switch (error) {
// Handle error type as necessary
default:
break;
}
}
#Override
public void onLoggedIn() {
Log.d("MainActivity", "User logged in");
mPlayer.playUri(null, "spotify:track:2TpxZ7JUBn3uw46aR7qd6V", 0, 0);
}
#Override
public void onLoggedOut() {
Log.d("MainActivity", "User logged out");
}
#Override
public void onLoginFailed(int i) {
Log.d("MainActivity", "Login failed");
}
#Override
public void onTemporaryError() {
Log.d("MainActivity", "Temporary error occurred");
}
#Override
public void onConnectionMessage(String message) {
Log.d("MainActivity", "Received connection message: " + message);
}
}
My build.gradle (Module:app):
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.example.mammo.spotifyplayer"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
mavenCentral()
flatDir {
dirs 'libs'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
// This library handles authentication and authorization
compile 'com.spotify.sdk:spotify-auth:beta22-noconnect-2.20b#aar'
// This library handles music playback
compile 'com.spotify.sdk:spotify-player:beta22-noconnect-2.20b#aar'
compile 'com.android.support:appcompat-v7:24.2.1'
testCompile 'junit:junit:4.12'
}
And my AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mammo.spotifyplayer">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Needed for LoginActivity to work -->
<activity
android:name="com.spotify.sdk.android.authentication.LoginActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar"/>
</application>
</manifest>