Should the Presenter know about the Activity in Android MVP architecture? - java

I am wondering if the Activity should be referenced or not within Presenter code when using the Android MVP architecture?
The example MVP architecture that I have found so far doesn't reference it, but in my code it's not a property on the Presenter, but an argument in some methods. Could this lead to issues? Does this not follow Android MVP?
Here is a code example from one Presenter:
package com.example.example;
import android.net.Uri;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.FileProvider;
import com.example.example.util.Constants;
import com.example.example.util.ImageFile;
import java.io.IOException;
/**
* Presenter from home screen, (Main), of the app
*/
public class MainPresenter implements MainContract.Presenter {
private final MainContract.View mView;
private final ImageFile mImageFile;
public MainPresenter(MainContract.View mainView, ImageFile imageFile) {
mView = mainView;
mImageFile = imageFile;
}
#Override
public void takePicture(FragmentActivity activity) throws IOException {
mImageFile.create(activity);
Uri photoUri = FileProvider.getUriForFile(
activity,
Constants.FILE_PROVIDER_PATH,
mImageFile.getFile());
mView.openCamera(photoUri);
}
Uri getImageFileUri() {
return mImageFile.getUri();
}
}

In proper MVP implementation, Presenter should not know about the activity. If we'll use activity then we'll have to mock the activity during testing, that'll make the testing difficult. So, in your case, you should pass your mImageFile to activity through the view reference and create the URI inside activity class.

Related

How to start an Intent in an outsourced static function?

I'm following this tutorial to get started with Bluetooth:
https://www.youtube.com/watch?v=y8R2C86BIUc
I want to outscource the bluetooth enable to an separated class and call it from the MainActivity.
I made the new Intent, but following the Video I'm not possible to start the Intent.
I tried importing:
android.support.v7.app.AppCompatActivity;
android.support.v4.content.ContextCompat;
But in both cases it didn't worked out.
Without any imports Android Studio is telling "Can't resolve method"
MAIN
package com.example.lenkzeitapplikation_01;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startBT.switch_BT_ON();
}
}
STARTBT
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.support.v4.content.ContextCompat;
import com.fleetboard.sdk.lib.android.log.Log;
public class startBT {
private static final String Tag ="StartBT";
static BluetoothAdapter mBluetoothAdapter;
public static void switch_BT_ON(){
if(mBluetoothAdapter == null){
Log.d(Tag, "No BT adapter");
}if(!mBluetoothAdapter.isEnabled()){
Intent enableBTIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBTIntent);
//mBluetoothAdapter.enable();
}
}
}
Using
import android.support.v4.content.ContextCompat:
Error:
method startActivity in class ContextCompat cannot be applied to given types;
required: Context,Intent,Bundle
found: Intent
reason: actual and formal argument lists differ in length
Important
Ok, first things first:
Activity is one of the different types of Context.
AND:
startActivity is a method that Context objects have.
Explanation
If you want to start an Activity, you must use a Context object. That's why it was working in the first place, in your MainActivity.
Now that you moved the code to another class, if you wan to use the method startActivity, you must have a reference to a Context object.
But... How?
public class startBT {
public static void switch_BT_ON(Context context){
//... Your logic
context.startActivity(intent);
}
}
In your activity:
startBT.switch_BT_ON(this);
The this parameter is the MainActivity itself, which is a Context by definition.
It means that:
switch_BT_ON requires a Context.
MainActivity is saying: "Here, use me".
Recommendations
This is classic, basic OOP thinking. Study about Object-Oriented Programming, classes and inheritance to learn why the startActivity method worked on the Acivity and not outside of it, passing objects around and handling different scopes.
Read the quick answer about what is an Android Context. Or adventure yourself through the documentation.

codename native Android application subclassing issues

My codenameone application crashes anything I use this native code
package com.mycompany.interfaces;
import android.app.Application;
import android.content.Context;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
public class InitialiseApp extends Application{
private static Context context;
public static Context getContext() {
return context;
}
#Override
public void onCreate()
{
super.onCreate();
context = getApplicationContext();
try
{
FirebaseApp.initializeApp(this, new FirebaseOptions.Builder().
setApiKey("XXXXXXXXXXXXXX").
setApplicationId("XXXXXXXX").
setGcmSenderId("XXXXXXXXXX")
.build());
FirebaseInstanceId.getInstance().deleteInstanceId();
FirebaseInstanceId.getInstance().getToken("XXXXXXXXXX",FirebaseMessaging.INSTANCE_ID_SCOPE);
FirebaseMessaging.getInstance().subscribeToTopic("test");
}
catch(Exception c)
{
c.printStackTrace();
}
}
}
I declare the class in the android.xapplication_attr android:name="com.mycompany.interfaces.InitialiseApp"
Need a assistance
Are you putting this in the native interface stub or in the CN1 part of the code?
Also, I don’t think that’s how you get a context in CN1. Look in the developer guide and video tutorials for Native Interfaces. I also recall a series of blog posts about native interfaces that dive into writing the Android code. You’ll need to use something from the AndroidNativeUtil class like: AndroidNativeUtil.getActivity().

Global class to register listeners for all the activities in android

I have two Activity classes. Activity A and Activity B. Later on I added Activity C which gets launched when user shakes the device being on Activity A or Activity B. Now if I register the ShakeListener in the Activity A and Acitivity B, I can achieve my goal
But what I want now, is a different thing, I do not want to change Activity A and Activity B. I want to write a different class, which runs for the whole app, and registers the ShakeListener for all the activities in the app. How can I do that? What kind of class should that be?
I tried extending BroadcastReceiver and registering the ShakeListener in the onReceive method, but used BOOT_EVENT which gets fired only when the device boots and not the starting of the application. So could not achieve my goal.
Then I was suggested by an SO user, to extend the Application class and registering the listener there. Now the listener gets registered, but now I need the currently running Activity and context to be passed to the Activity C. Here I'm back to zero again, because I don't want to add code in the Activity A or B. Also, AFAIK, the Application class is called before any of the Activity gets initiated, so is it possible to get the currently running Activity in the foreground?
Then I thought to move the code to find the activity in the Listener itself. Here also I needed to get the current activity and context. The context was the application context and then I tried to access all the currently open activities, following this thread. Based on the version the code is a bit different. And this is not the recommended way and gives me error.
This is the class:
package com.something.someotherthing;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.List;
/**
* Created by Inquisitive on 20/5/15.
*/
public class ShakeEventListener implements SensorEventListener {
private Context context;
private Activity activity;
private String[] getPreLollipop() {
try {
#SuppressWarnings("deprecation")
List<ActivityManager.RunningTaskInfo> tasks =
activityManager().getRunningTasks(1);
ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
ComponentName currentActivity = currentTask.topActivity;
return new String[]{currentActivity.getClassName()};
}
catch(Exception e){
Log.d("version","Exception" +e.getClass());
String str[]= {"abc","def"};
return str;
}
}
private String[] getLollipop() {
final int PROCESS_STATE_TOP = 2;
try {
Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
List<ActivityManager.RunningAppProcessInfo> processes =
activityManager().getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (
// Filters out most non-activity processes
process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&&
// Filters out processes that are just being
// _used_ by the process with the activity
process.importanceReasonCode == 0
) {
int state = processStateField.getInt(process);
if (state == PROCESS_STATE_TOP)
/*
If multiple candidate processes can get here,
it's most likely that apps are being switched.
The first one provided by the OS seems to be
the one being switched to, so we stop here.
*/
return process.pkgList;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return new String[] { };
}
private ActivityManager activityManager() {
return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
public String[] get() {
if (Build.VERSION.SDK_INT < 21) {
Log.d("Version","Pre-lollipop");
for(String str:getPreLollipop())
Log.d("Version",str);
return getPreLollipop();
}
else {
Log.d("Version","Lollipop");
for(String str:getLollipop())
Log.d("Version",str);
return getLollipop();
}
}
public ShakeEventListener(Context context){
Log.d("ACCELEROMETER","inside the constructor of shake event listener");
this.context=context;
String str[] = get();
try {
Class<?> myClass = Class.forName(str[0]);
activity = (Activity) myClass.newInstance();
}catch(Exception e){
//do something
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy){
}
public void onSensorChanged(SensorEvent se){
Log.d("SENSOR","On sensor changed");
//need the activity and context here..
}
}
When I try with a device having version below lollipop. It catches the SecurityException. With a device having version above lollipop, it gives the package name of my application, but not the particular class.
1 what is the correct way to achieve my goal? Whatever approach I'm following is correct? in that case, I will try to debug the code to find activity
2 If not, what are the other alternatives by which I can achieve my goal of having a global listener that listens from any activity within the app.
3 Is it achievable at all, without changing the activities?
Please help.
In your case, you need to implement a Service to listen to sensors in the background. Go through documentation:Service
You cannot just use Broadcastreceiver to accomplish this task.
Reference:
Android sensor listening when app in background
Refer this link for help:
http://code.tutsplus.com/tutorials/android-barometer-logger-acquiring-sensor-data--mobile-10558
Create a main activity class which will be extended by Activity A, B and C.
You can register your ShakeListener in this main activity.
You can define your own ActivityStack in Application class, properly manage by adding and removing activities. Finally refer to this stack from activity C.
There is example how ActivityStack works:
Back Stack Example

Simple MainActivity and BroadcastReceiver derived class communication Issue

This should be very simple and I'm really not sure what is going wrong. I have created a SMSReceiver java class that basically receives messages and then gives them to my MainActivity. When the onReceive() function fires, the app crashes because it is having issues sending the data back to MainActivity. It has been throwing a NullPointerException on the Zout() call in onReceive().
Here is the SmsReceiver class:
package ...; //you don't need to see my dumb package name.
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
public class SmsReceiver extends BroadcastReceiver {
ArrayList<String> phoneNumbers;
ArrayList<String> textMessages;
private Activity mainAct; //our reference to MainActivity
//very simple constructor
public SmsReceiver() {
}
//this function is called in onCreate() in MainActivity to provide reference to MainActivity
public void SetSMSReceiverActivity(Activity a){
mainAct = a;
//talk to our main activity
MainActivity mainComm = (MainActivity)mainAct;
mainComm.Zout("SMSReceiver initialized bro"); //this works just fine
}
#Override
public void onReceive(Context context, Intent intent) {
//THIS IS WHAT CAUSES ISSUES! This call to MainActivity does NOT work for whatever reason. Rude.
MainActivity mainComm = (MainActivity)mainAct;
mainComm.Zout("This message won't send"); //this does NOT work fine for whatever reason. Throws NullPointerException.
//...more code down here that actually works!
}
}
The function Zout() in my MainActivity is a simple print to TextView for debugging purposes. I will be using a function in MainActivity to receive the data once onReceive() stops being moody.
I'm sorry if this is a dumb question. I haven't programmed in Java for a few years and I'm quite rusty. I have no idea why this isn't working. I'll be tossing out +1's to whoever can help!
I believe it is because you have not initialized your mainAct variable.
for onCreate(){}
you do initialize
mainAct = a;
but not for onReceive.

solution for getSystemService function at Android

I want to test something about usb devices, I try to write a small program, I am sure that it is wrong but this is not the point of this question.
I am sure that my imports are OK but the Android Studio refused to build this class with an error about the GetSystemService(). I have the message:
Error:(65, 43) error: cannot find symbol method getSystemService(String).
I used also an example from http://android-er.blogspot.de/2013/10/list-attached-usb-devices-in-usb-host.html and the Android Studio also has the same error but if i install the Apk from this website then it is running on my device, so i supposed that something is wrong at Android Studio.
Any good idea?
OFFTOPIC "QT Creator is light years better"
import android.content.Context;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbAccessory;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileDescriptor;
import android.util.Log;
public class DeviceOpenActivity {
private static final String TAG = "DeviceOpenActivity";
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
public static int fibonacci(int n) {
if (n<2) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
private void openAccessory() {
Log.d(TAG, "openAccessory: " + mAccessory);
mFileDescriptor = usbManager.openAccessory(mAccessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
}
}
}
If you check the example that you provided, you will verify that there is a MainActivity class that extends Activity class, which by its turn extends indirectly from Context. In order to call getSystemService() you need to have an available Context. If you make your DeviceOpenActivity extend Activity, Android Studio will not complain anymore about your call.
Just leave your class declaration like this:
public class MainActivity extends Activity
Don't forget that you need to provide a XML layout for your Activity, as well as the Activity methods, like onCreate().

Categories