I test these codes in Samsung J200F it's working best but when i test these codes in Lenovo 7.0 Android Nought
they does not working.
There is no any exceptions in Logcat.
I am already added these Permissions at runtime and AndroidManifest
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
There is my codes for turning on/off my Wifi Hotspot.
public class HotspotControl {
private static Method getWifiApConfiguration;
private static Method getWifiApState;
private static Method isWifiApEnabled;
private static Method setWifiApEnabled;
private static Method setWifiApConfiguration;
private WifiManager wm;
private String deviceName;
private WifiManager.LocalOnlyHotspotReservation mReservation;
private static HotspotControl instance = null;
static {
for (Method method : WifiManager.class.getDeclaredMethods()) {
switch (method.getName()) {
case "getWifiApConfiguration":
getWifiApConfiguration = method;
break;
case "getWifiApState":
getWifiApState = method;
break;
case "isWifiApEnabled":
isWifiApEnabled = method;
break;
case "setWifiApEnabled":
setWifiApEnabled = method;
break;
case "setWifiApConfiguration":
setWifiApConfiguration = method;
break;
}
}
}
private HotspotControl(Context context) {
wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
}
public boolean turnOnPreOreoHotspot(String name) {
wm.setWifiEnabled(false);
//Create new Open Wifi Configuration
WifiConfiguration wifiConf = new WifiConfiguration();
wifiConf.SSID = "\"" + name + "\"";
wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
wm.addNetwork(wifiConf);
wm.saveConfiguration();
return setHotspotEnabled(wifiConf, true);
}
private boolean setHotspotEnabled(WifiConfiguration config, boolean enabled) {
Object result = invokeSilently(setWifiApEnabled, wm, config, enabled);
if (result == null) {
return false;
}
return (Boolean) result;
}
private static Object invokeSilently(Method method, Object receiver, Object... args) {
try {
return method.invoke(receiver, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
Log.e(TAG, "exception in invoking methods: " + e.getMessage());
}
return null;
}
}
Can you help me in this situation.
Thanks in advance.
There is complete example of asking permission that will work in all versions .
It works in Version<=23 ,7.0(Nought) , Version >=23.
just add checkLocationPermission() at onCreate().
// Check for GPS permission
private boolean checkLocationPermission() {
if (Build.VERSION.SDK_INT >= 23 )
if(getContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED || getContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || getContext().checkSelfPermission(Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED || getContext().checkSelfPermission(Manifest.permission.CHANGE_WIFI_STATE) != PackageManager.PERMISSION_GRANTED ) {
ActivityCompat.requestPermissions(
getActivity(),
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_WIFI_STATE,Manifest.permission.CHANGE_WIFI_STATE},
PERMISSIONS_REQUEST_CODE_ACCESS_COARSE_LOCATION
);
return false;
}
return true;
}
private boolean showWritePermissionSettings() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
if (!Settings.System.canWrite(getContext())) {
Log.v("DANG", " " + !Settings.System.canWrite(getContext()));
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getContext().getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
return false;
}
}
return true; //Permission already given
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSIONS_REQUEST_CODE_ACCESS_COARSE_LOCATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (showWritePermissionSettings())
Logic();
} else {
// else type 1st
// dialog interface start 1st
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION)) {
// dialog
showOptionsDialogWithListners(getString(R.string.p2p_receiver_gps_permission_warning), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
checkLocationPermission();
}
}, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}, "Re-Try", "Yes, Im Sure");
// dialog interface end 1st
} // else type 2nd
else {
// dialog interface 2nd start
showOptionsDialogWithListners(getString(R.string.p2p_receiver_gps_no_permission_prompt), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
} catch (ActivityNotFoundException anf) {
Toast.makeText(getContext(), "Settings activity not found", Toast.LENGTH_SHORT).show();
}
}
}, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
}, getString(R.string.label_settings), getString(R.string.Action_cancel));
// dialog interfaces 2nd ended
}
// else type 2nd start
}
// else 1st ended
break;
}
}
// Dialog with interfaces
public void showOptionsDialogWithListners(String message,
DialogInterface.OnClickListener pListner,
DialogInterface.OnClickListener nListener, String pButtonText,
String nButtonText) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle);
builder.setCancelable(false);
builder.setMessage(Html.fromHtml(message));
builder.setPositiveButton(pButtonText, pListner);
builder.setNegativeButton(nButtonText, null != nListener ? nListener
: new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
return;
}
});
builder.show();
}
Related
Currently i'm implementing the in app update feature from android. I'm using the immediate update method, the problem that I'm facing is that when the UI prompting the user to update shows and the user does not click the update button instead they click the cross button. The UI for the app update just closes and user can continue using the app.
What I want is that when user click the cross button the app immediately closes, until user updates the app then they can use the app as usual. I also uses the java code for the android development.
public class LoginActivity extends AppCompatActivity {
private LoginViewModel loginViewModel;
public static final String MyPREFERENCES = "LoginPrefs" ;
public static final String Name = "nameKey";
public static final String User = "userKey";
public static final String con = "closed";
public static String error = "";
public static int userFlag = 0;
SharedPreferences sharedpreferences;
SharedPreferences.Editor editor;
public TextInputEditText usernameEditText;
public TextInputEditText passwordEditText;
private AppUpdateManager mAppUpdateManager;
private int RC_APP_UPDATE = 999;
private int inAppUpdateType;
private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
private InstallStateUpdatedListener installStateUpdatedListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
loginViewModel = ViewModelProviders.of(this, new LoginViewModelFactory())
.get(LoginViewModel.class);
usernameEditText = findViewById(R.id.user);
passwordEditText = findViewById(R.id.pass);
final Button loginButton = findViewById(R.id.submitBTN);
final TextInputLayout userL = findViewById(R.id.userL);
final TextInputLayout passL = findViewById(R.id.passL);
final JellyToggleButton jtb = findViewById(R.id.jtb);
// Creates instance of the manager.
mAppUpdateManager = AppUpdateManagerFactory.create(this);
// Returns an intent object that you use to check for an update.
appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
//lambda operation used for below listener
//For flexible update
installStateUpdatedListener = installState -> {
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
};
mAppUpdateManager.registerListener(installStateUpdatedListener);
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();
if(userFlag==1){
jtb.setChecked(true);
}
userL.setHint("Enter username");
sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
loginViewModel.getLoginFormState().observe(this, new Observer<LoginFormState>() {
#Override
public void onChanged(#Nullable LoginFormState loginFormState) {
if (loginFormState == null) {
return;
}
loginButton.setEnabled(loginFormState.isDataValid());
if (loginFormState.getUsernameError() != null) {
usernameEditText.setError(getString(loginFormState.getUsernameError()));
loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
}
if (loginFormState.getPasswordError() != null) {
passwordEditText.setError(getString(loginFormState.getPasswordError()));
loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
}
}
});
loginViewModel.getLoginResult().observe(this, new Observer<LoginResult>() {
#Override
public void onChanged(#Nullable LoginResult loginResult) {
if (loginResult == null) {
return;
}
if (loginResult.getError() != null) {
showLoginFailed(loginResult.getError());
}
if (loginResult.getSuccess() != null) {
updateUiWithUser(loginResult.getSuccess());
Intent i = new Intent(LoginActivity.this, user_dashboard.class);
startActivity(i);
}
setResult(Activity.RESULT_OK);
//Complete and destroy login activity once successful
}
});
TextWatcher afterTextChangedListener = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// ignore
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// ignore
}
#Override
public void afterTextChanged(Editable s) {
loginViewModel.loginDataChanged(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
}
};
usernameEditText.addTextChangedListener(afterTextChangedListener);
passwordEditText.addTextChangedListener(afterTextChangedListener);
passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
loginViewModel.login(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
}
return false;
}
});
loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(userFlag==0) {
loginViewModel.login(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
getStaffData();
}
else if(userFlag==1){
loginWorker();
}
}
});
jtb.setOnStateChangeListener(new JellyToggleButton.OnStateChangeListener() {
#Override
public void onStateChange(float process, State state, JellyToggleButton jtb) {
if (state.equals(State.LEFT)) {
userL.setHint("Enter username");
error = "Username cannot be empty";
userFlag = 0;
}
if (state.equals(State.RIGHT)) {
userL.setHint("Enter badge ID");
error = "Badge ID cannot be empty";
userFlag = 1;
}
}
});
}
#Override
protected void onDestroy() {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
finishAndRemoveTask();
super.onDestroy();
}
#Override
protected void onResume() {
try {
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
inAppUpdateType,
this,
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
//For flexible update
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
super.onResume();
}
#Override //For flexible update
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
}
private void updateUiWithUser(LoggedInUserView model) {
String welcome = getString(R.string.welcome);
// TODO : initiate successful logged in experience
Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_LONG).show();
}
private void showLoginFailed(#StringRes Integer errorString) {
Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show();
}
private void getStaffData() {
String username = usernameEditText.getText().toString();
APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
Call<loginList> call3 = apiInterface3.staffData(username);
call3.enqueue(new Callback<loginList>() {
#Override
public void onResponse(Call<loginList> call, Response<loginList> response) {
loginList list = response.body();
if (list!=null && list.getStatusCode()==1) { //response received.
if(list.getStaffList().size()>0){
Log.d("check-in", list.getStatusCode() + " " + list.getStaffList().get(0).getName());
Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();
final String name = list.getStaffList().get(0).getName();
final String badge = list.getStaffList().get(0).getBadge();
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Name,name);
editor.putString(User,badge);
editor.putInt(con,1);
editor.apply();
}
else if(list.getStaffList().size()==0){
}
}
}
#Override
public void onFailure(Call<loginList> call, Throwable t) {
Log.d("fail",t.toString());
}
});
}
private void loginWorker(){
String username = usernameEditText.getText().toString();
String password = passwordEditText.getText().toString();
APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
Call<loginList> call3 = apiInterface3.loginWorker(username,password);
call3.enqueue(new Callback<loginList>() {
#Override
public void onResponse(Call<loginList> call, Response<loginList> response) {
loginList list = response.body();
Log.d("response", response.body().toString());
if (list!=null && list.getStatusCode()==1) { //response received.
if(list.getLoginList().size()>0){
Log.d("check-in", list.getStatusCode() + " " + list.getLoginList().get(0).getName());
Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();
List<login> item = response.body().getLoginList();
final String name = list.getLoginList().get(0).getName();
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Name,name);
editor.putInt(con,1);
editor.apply();
}
String welcome = getString(R.string.welcome);
Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_SHORT).show();
Intent i = new Intent(LoginActivity.this, user_dashboard.class);
startActivity(i);
}
else
Toast.makeText(LoginActivity.this, "wrong ID or password",Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<loginList> call, Throwable t) {
Log.d("fail",t.toString());
}
});
editor = sharedpreferences.edit();
editor.putString(User, username);
editor.commit();
}
#Override
public void onBackPressed() {
new MaterialAlertDialogBuilder(LoginActivity.this,R.style.MyDialogTheme)
.setTitle("Exit")
.setMessage("Confirm to exit?")
.setBackground(getDrawable(R.drawable.alert_dialog))
// Specifying a listener allows you to take an action before dismissing the dialog.
// The dialog is automatically dismissed when a dialog button is clicked.
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Continue with delete
finishAffinity();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
private void inAppUpdate() {
try {
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
#Override
public void onSuccess(AppUpdateInfo appUpdateInfo) {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
// Request the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
inAppUpdateType,
// The current activity making the update request.
LoginActivity.this,
// Include a request code to later monitor this update request.
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException ignored) {
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void popupSnackbarForCompleteUpdate() {
try {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorL),
"An update has just been downloaded.\nRestart to update",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("INSTALL", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.orange));
snackbar.show();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
}
The image I borrowed from google, on the left image as can be seen there is a cross button on top right user can click to close the update process
The most important point I will emphasize is that you should not force users to update the app until it is absolutely necessary (like some security issues etc). Forcing updates to users is considered a very bad user experience.
To the question you asked, you have the answer in your question itself. If you check the code you have something like this in your onActivityResult method-
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
In case when the user cancels resultCode == RESULT_CANCELED or the update fails resultCode == RESULT_IN_APP_UPDATE_FAILED, you can take whatever action you want. You can finish the activity or whatever is suitable in your situation.
I have a QR code scanner in my app and the camera stops when my AlertDialog is being displayed but restarts as soon as I move to another activity and keeps running in the background even when the app isn't in the foreground.
I have the stopCamera() and stopCameraPreview() both but it restarts and stays on as long as the app is open (both foreground and background)
Here's the code for the activity:
public class MainActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
private static final int REQUEST_CAMERA = 1;
private ZXingScannerView scannerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scannerView = new ZXingScannerView(this);
setContentView(scannerView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkPermission()) {
Toast.makeText(MainActivity.this, "Scanner Active", Toast.LENGTH_LONG).show();
} else {
requestPermission();
}
}
}
private boolean checkPermission() {
return (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED);
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{CAMERA}, REQUEST_CAMERA);
}
public void onRequestPermissionsResult(int requestCode, String[] permission, int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA:
if (grantResults.length > 0) {
boolean cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (cameraAccepted) {
Toast.makeText(MainActivity.this, "Permission Granted!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_LONG).show();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(CAMERA)) {
displayAlertMessage("You need to allow access for both permsisions",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{CAMERA}, REQUEST_CAMERA);
}
}
});
return;
}
}
}
break;
}
}
#Override
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkPermission()) {
if (scannerView == null) {
scannerView = new ZXingScannerView(this);
setContentView(scannerView);
}
scannerView.setResultHandler(this);
scannerView.startCamera();
} else {
requestPermission();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
scannerView.stopCamera();
scannerView.stopCameraPreview();
}
public void displayAlertMessage(String message, DialogInterface.OnClickListener listener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", listener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
String[] a = {"ABCD", "PQR"};
String scanned = "";
#Override
public void handleResult(Result result) {
final String scanResult = result.getText();
for (int i = 0; i < 2; i++) {
if (scanResult.equals(a[i])) {
scanned = a[i];
scannerView.stopCamera();
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("QR Scan");
builder.setMessage("Machine Identified!");
builder.setPositiveButton("Continue", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent1 = new Intent(MainActivity.this, buttons.class);
startActivity(intent1);
scannerView.stopCameraPreview();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
scannerView.resumeCameraPreview(MainActivity.this);
}
});
builder.show();
break;
} else {
Toast.makeText(this, "ERROR: Unrecognized QR Code", Toast.LENGTH_LONG).show();
scannerView.resumeCameraPreview(MainActivity.this);
}
}
}
}
"keeps running in the background even when the app isn't in the foreground" - this is due to the Activity lifecycle. Note that onDestroy() is the last method of the class Activity that is called and called only when activity is for sure destroyed.
What it means: when you move to the background onDestroy() is not called. The activity is still alive and waiting to be resumed. Your activity is still alive and it should be. Use instead of onDestroy() method called onPause(). You will reach this method if the activity is killed, and it will return back to the onResume() when appropriate.
Just overwrite onDestroy() to:
#Override
public void onPause() {
super.onPause();
scannerView.stopCamera();
scannerView.stopCameraPreview();
}
I am creating an android application and want to integrate ZXing QR Scanner. When i launch the QRScanner activity from an Intent I get the toast saying "Permission Denied, You cannot access and camera" and the app freezes instead of asking for permission for camera.
Any help to how to correctly implement permission requests or identification of any errors I looked over would be appreciated.
public class Qrscanner extends AppCompatActivity implements
ZXingScannerView.ResultHandler {
private static final int REQUEST_CAMERA = 1;
private ZXingScannerView scannerView;
private static int camId = Camera.CameraInfo.CAMERA_FACING_BACK;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scannerView = new ZXingScannerView(this);
setContentView(scannerView);
int currentApiVersion = Build.VERSION.SDK_INT;
if(currentApiVersion >= Build.VERSION_CODES.M)
{
if(checkPermission())
{
Toast.makeText(getApplicationContext(), "Permission already granted!", Toast.LENGTH_LONG).show();
}
else
{
requestPermission();
}
}
}
private boolean checkPermission()
{
return (ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA) == PackageManager.PERMISSION_GRANTED);
}
private void requestPermission()
{
ActivityCompat.requestPermissions(this, new String[]{CAMERA}, REQUEST_CAMERA);
}
#Override
public void onResume() {
super.onResume();
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.M) {
if (checkPermission()) {
if(scannerView == null) {
scannerView = new ZXingScannerView(this);
setContentView(scannerView);
}
scannerView.setResultHandler(this);
scannerView.startCamera();
} else {
requestPermission();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
scannerView.stopCamera();
}
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
final int req = requestCode;
switch (requestCode) {
case REQUEST_CAMERA:
if (grantResults.length > 0) {
boolean cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (cameraAccepted){
Toast.makeText(getApplicationContext(), "Permission Granted, Now you can access camera", Toast.LENGTH_LONG).show();
}else {
Toast.makeText(getApplicationContext(), "Permission Denied, You cannot access and camera", Toast.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(CAMERA)) {
showMessageOKCancel("You need to allow access to both the permissions",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//
requestPermissions(new String[]{CAMERA},
//
REQUEST_CAMERA);
ActivityCompat.requestPermissions(getParent(), new String[]
{Manifest.permission.CAMERA}, req);
}
}
});
return;
}
}
}
}
break;
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new android.support.v7.app.AlertDialog.Builder(Qrscanner.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
#Override
public void handleResult(final Result result) {
final String myResult = result.getText();
Log.d("QRCodeScanner", result.getText());
Log.d("QRCodeScanner", result.getBarcodeFormat().toString());
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Scan Result");
builder.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
scannerView.resumeCameraPreview(Qrscanner.this);
}
});
builder.setNeutralButton("Confirm", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getApplicationContext(),AddKeys.class);
intent.putExtra("publicKey",result.getText());
startActivity(intent);
}
});
builder.setMessage(result.getText());
AlertDialog alert1 = builder.create();
alert1.show();
}
}
There is a really simple solution to your problem. Inside the onCreate provide runtime permissions. I suggest you use the dexter library as it's the simplest way of implementing runtime permissions. Here is the link to the library:
https://github.com/Karumi/Dexter
I had the same problem after building your cameSource. Add this line to your code, it works for me
final String[] permissions = new String[] Manifest.permission.CAMERA};
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.CAMERA)) {
ActivityCompat.requestPermissions(MainActivity.this, permissions, RC_HANDLE_CAMERA_PERM);
}
i'm trying to implement a SIP Client on Android application using native Session Initiation protocol.
Everything is woking fine if i create an Activity and put the following code :
public class WalkieTalkieActivity extends AppCompatActivity implements View.OnTouchListener {
public String sipAddress = null;
public SipManager manager = null;
public SipProfile me = null;
public SipAudioCall call = null;
public IncomingCallReceiver callReceiver;
private static final int CALL_ADDRESS = 1;
private static final int SET_AUTH_INFO = 2;
private static final int UPDATE_SETTINGS_DIALOG = 3;
private static final int HANG_UP = 4;
private final int PERMISSIONS_REQUEST_RECORD_AUDIO = 1;
private final int PERMISSIONS_REQUEST_USE_SIP = 2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_walkietalkie);
//initRecorderParameters(new int[]{8000, 11025, 16000, 22050, 44100});
//Richiedo i vari permessi
if (!hasUseSipPermission()) { requestUseSipPermission(); }
//Rchiedo i vari permessi
if (!hasRecordAudioPermission()) { requestRecordAudioPermission(); }
if (!SipManager.isVoipSupported(this)) {
//Visualizzo il messaggio voip non supportato e torno alla dahboard
Toast.makeText(WalkieTalkieActivity.this, "Voip non supportato su questo telefono", Toast.LENGTH_LONG).show();
Intent dashboard = new Intent(WalkieTalkieActivity.this,MainActivity.class);
// passo all'attivazione dell'activity page1.java
startActivity(dashboard);
finish();
}
if (!SipManager.isApiSupported(this)) {
//Visualizzo il messaggio voip non supportato e torno alla dahboard
Toast.makeText(WalkieTalkieActivity.this, "API non supportate su questo telefono", Toast.LENGTH_LONG).show();
Intent dashboard = new Intent(WalkieTalkieActivity.this,MainActivity.class);
// passo all'attivazione dell'activity page1.java
startActivity(dashboard);
finish();
}
// "Push to talk" can be a serious pain when the screen keeps turning off.
// Let's prevent that.
//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// use this to start and trigger a service
Intent i = new Intent(this, SipEngine.class);
// potentially add data to the intent
i.putExtra("KEY1", "Value to be used by the service");
this.startService(i);
initializeManager();
}
#Override
public void onStart() {
super.onStart();
// When we get back from the preference setting Activity, assume
// settings have changed, and re-login with new auth info.
initializeManager();
}
#Override
public void onDestroy() {
super.onDestroy();
if (call != null) {
call.close();
}
closeLocalProfile();
if (callReceiver != null) {
this.unregisterReceiver(callReceiver);
}
}
public void initializeManager() {
if(manager == null) {
manager = SipManager.newInstance(this);
}
initializeLocalProfile();
}
/**
* Logs you into your SIP provider, registering this device as the location to
* send SIP calls to for your SIP address.
*/
public void initializeLocalProfile() {
if (manager == null) {
return;
}
if (me != null) {
closeLocalProfile();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
//String username = prefs.getString("namePref", "");
//String domain = prefs.getString("domainPref", "");
//String password = prefs.getString("passPref", "");
String username = "username";
String domain = "domain";
String password = "password";
// Set up the intent filter. This will be used to fire an
// IncomingCallReceiver when someone calls the SIP address used by this
// application.
IntentFilter filter = new IntentFilter();
filter.addAction("android.vohippo.INCOMING_CALL");
callReceiver = new IncomingCallReceiver();
this.registerReceiver(callReceiver, filter);
try {
Log.d("VOHIPPOSERVICE", "PROVO A REGISTRARE");
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
//Costruisco il builder
me = builder.build();
//Intent
Intent i = new Intent();
i.setAction("android.vohippo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
// This listener must be added AFTER manager.open is called,
// Otherwise the methods aren't guaranteed to fire.
manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
updateStatus("Registering with SIP Server...");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Attivazione in corso...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
updateStatus("Ready");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio disponibile");
}
public void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage) {
updateStatus("Registration failed. Please check settings.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio non disponibile");
}
});
} catch (ParseException pe) {
updateStatus("Connection Error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore di connessione");
} catch (SipException se) {
updateStatus("Connection error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore connessione");
}
}
/**
* Closes out your local profile, freeing associated objects into memory
* and unregistering your device from the server.
*/
public void closeLocalProfile() {
if (manager == null) {
return;
}
try {
if (me != null) {
manager.close(me.getUriString());
}
} catch (Exception ee) {
Log.d("WalkieTalkieActivity", "Failed to close local profile.", ee);
}
}
/**
* Make an outgoing call.
*/
public void initiateCall() {
Toast.makeText(WalkieTalkieActivity.this, "Chiamata lanciata", Toast.LENGTH_SHORT).show();
updateStatus(sipAddress);
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// Much of the client's interaction with the SIP Stack will
// happen via listeners. Even making an outgoing call, don't
// forget to set up a listener to set things up once the call is established.
#Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
if (call.isMuted()) {
call.toggleMute();
}
updateStatus(call);
Toast.makeText(WalkieTalkieActivity.this, "Chiamata stabilita", Toast.LENGTH_SHORT).show();
}
#Override
public void onCallEnded(SipAudioCall call) {
Toast.makeText(WalkieTalkieActivity.this, "Chiamata terminata", Toast.LENGTH_SHORT).show();
updateStatus("Ready.");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);
}
catch (Exception e) {
Log.i("WalkieTalkieActivity", "Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
/**
* Updates the status box at the top of the UI with a messege of your choice.
* #param status The String to display in the status box.
*/
public void updateStatus(final String status) {
// Be a good citizen. Make sure UI changes fire on the UI thread.
this.runOnUiThread(new Runnable() {
public void run() {
TextView labelView = (TextView) findViewById(R.id.sipLabel);
labelView.setText(status);
}
});
}
/**
* Updates the status box with the SIP address of the current call.
* #param call The current, active call.
*/
public void updateStatus(SipAudioCall call) {
String useName = call.getPeerProfile().getDisplayName();
if(useName == null) {
useName = call.getPeerProfile().getUserName();
}
updateStatus(useName + "#" + call.getPeerProfile().getSipDomain());
}
/**
* Updates whether or not the user's voice is muted, depending on whether the button is pressed.
* #param v The View where the touch event is being fired.
* #param event The motion to act on.
* #return boolean Returns false to indicate that the parent view should handle the touch event
* as it normally would.
*/
public boolean onTouch(View v, MotionEvent event) {
if (call == null) {
return false;
} else if (event.getAction() == MotionEvent.ACTION_DOWN && call != null && call.isMuted()) {
call.toggleMute();
} else if (event.getAction() == MotionEvent.ACTION_UP && !call.isMuted()) {
call.toggleMute();
}
return false;
}
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, CALL_ADDRESS, 0, "Call someone");
menu.add(0, SET_AUTH_INFO, 0, "Edit your SIP Info.");
menu.add(0, HANG_UP, 0, "End Current Call.");
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CALL_ADDRESS:
showDialog(CALL_ADDRESS);
break;
case SET_AUTH_INFO:
updatePreferences();
break;
case HANG_UP:
if(call != null) {
try {
call.endCall();
} catch (SipException se) {
Log.d("WalkieTalkieActivity",
"Error ending call.", se);
}
call.close();
}
break;
}
return true;
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case CALL_ADDRESS:
LayoutInflater factory = LayoutInflater.from(this);
final View textBoxView = factory.inflate(R.layout.call_address_dialog, null);
return new AlertDialog.Builder(this)
.setTitle("Call Someone.")
.setView(textBoxView)
.setPositiveButton(
android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
EditText textField = (EditText)
(textBoxView.findViewById(R.id.calladdress_edit));
sipAddress = textField.getText().toString();
initiateCall();
}
})
.setNegativeButton(
android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Noop.
}
})
.create();
case UPDATE_SETTINGS_DIALOG:
return new AlertDialog.Builder(this)
.setMessage("Please update your SIP Account Settings.")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
updatePreferences();
}
})
.setNegativeButton(
android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Noop.
}
})
.create();
}
return null;
}
public void updatePreferences() {
//Intent settingsActivity = new Intent(getBaseContext(),
// SipSettings.class);
//startActivity(settingsActivity);
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_USE_SIP:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(WalkieTalkieActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalkieTalkieActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
case PERMISSIONS_REQUEST_RECORD_AUDIO:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(WalkieTalkieActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalkieTalkieActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
}
}
//Controllo che il sistema abbia il permesso di registrare audio
private boolean hasRecordAudioPermission(){
boolean hasPermission = (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED);
Log.d("VOHIPPO", "Permesso di registrare audio? " + hasPermission);
return hasPermission;
}
private void requestRecordAudioPermission(){
String requiredPermission = Manifest.permission.RECORD_AUDIO;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
requiredPermission)) {
Toast.makeText(WalkieTalkieActivity.this, "Quest applicazione richiede il permesso di registrare audio", Toast.LENGTH_SHORT).show();
}
// request the permission.
ActivityCompat.requestPermissions(this,
new String[]{requiredPermission},
PERMISSIONS_REQUEST_RECORD_AUDIO);
}
//Controllo che il sistema abbia il permesso di registrare audio
private boolean hasUseSipPermission(){
boolean hasPermission = (ContextCompat.checkSelfPermission(this,
Manifest.permission.USE_SIP) == PackageManager.PERMISSION_GRANTED);
Log.d("VOHIPPO", "Permesso di usare sip? " + hasPermission);
return hasPermission;
}
private void requestUseSipPermission(){
String requiredPermission = Manifest.permission.USE_SIP;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
requiredPermission)) {
Toast.makeText(WalkieTalkieActivity.this, "Quest applicazione richiede il permesso di usare SIP", Toast.LENGTH_SHORT).show();
}
// request the permission.
ActivityCompat.requestPermissions(this,
new String[]{requiredPermission},
PERMISSIONS_REQUEST_USE_SIP);
}
but if i create a service and try to do the same into this service i have no errors, but my client doesn't register with the server.
This is the code i put into the service :
public class SipEngine extends IntentService {
public String sipAddress = null;
public SipManager manager = null;
public SipProfile me = null;
public SipAudioCall call = null;
public IncomingCallReceiver callReceiver;
private static final int CALL_ADDRESS = 1;
private static final int SET_AUTH_INFO = 2;
private static final int UPDATE_SETTINGS_DIALOG = 3;
private static final int HANG_UP = 4;
private int NOTIFICATION = 10002; //Any unique number for this notification
public SipEngine() {
super("SipEngine");
}
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
initializeManager();
sendNotification();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#Override
//public void onStart() {
// super.onStart();
// When we get back from the preference setting Activity, assume
// settings have changed, and re-login with new auth info.
//initializeManager();
//}
#Override
public void onDestroy() {
super.onDestroy();
if (call != null) {
call.close();
}
closeLocalProfile();
if (callReceiver != null) {
this.unregisterReceiver(callReceiver);
}
}
public void initializeManager() {
if(manager == null) {
manager = SipManager.newInstance(this);
}
initializeLocalProfile();
}
/**
* Logs you into your SIP provider, registering this device as the location to
* send SIP calls to for your SIP address.
*/
public void initializeLocalProfile() {
if (manager == null) {
return;
}
if (me != null) {
closeLocalProfile();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
//String username = prefs.getString("namePref", "");
//String domain = prefs.getString("domainPref", "");
//String password = prefs.getString("passPref", "");
String username = "username";
String domain = "server";
String password = "password";
// Set up the intent filter. This will be used to fire an
// IncomingCallReceiver when someone calls the SIP address used by this
// application.
IntentFilter filter = new IntentFilter();
filter.addAction("android.vohippo.INCOMING_CALL");
callReceiver = new IncomingCallReceiver();
this.registerReceiver(callReceiver, filter);
try {
Log.d("VOHIPPOSERVICE", "PROVO A REGISTRARE");
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
//Costruisco il builder
me = builder.build();
//Intent
Intent i = new Intent();
i.setAction("android.vohippo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
// This listener must be added AFTER manager.open is called,
// Otherwise the methods aren't guaranteed to fire.
manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
//updateStatus("Registering with SIP Server...");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Attivazione in corso...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
//updateStatus("Ready");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio disponibile");
}
public void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage) {
//updateStatus("Registration failed. Please check settings.");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio non disponibile");
}
});
} catch (ParseException pe) {
//updateStatus("Connection Error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore di connessione");
} catch (SipException se) {
//updateStatus("Connection error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore connessione");
}
}
/**
* Closes out your local profile, freeing associated objects into memory
* and unregistering your device from the server.
*/
public void closeLocalProfile() {
if (manager == null) {
return;
}
try {
if (me != null) {
manager.close(me.getUriString());
}
} catch (Exception ee) {
Log.d("WalkieTalkieActivity", "Failed to close local profile.", ee);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Send simple notification using the NotificationCompat API.
*/
public void sendNotification() {
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//icon appears in device notification bar and right hand corner of notification
builder.setSmallIcon(R.drawable.ic_service_sip);
// This intent is fired when notification is clicked
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://stacktips.com/"));
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Set the intent that will fire when the user taps the notification.
builder.setContentIntent(pendingIntent);
// Large icon appears on the left of the notification
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_service_sip));
// Content title, which appears in large type at the top of the notification
builder.setContentTitle("Vohippo");
int color = ContextCompat.getColor(this, R.color.colorPrimary);
builder.setColor(color);
// Content text, which appears in smaller text below the title
builder.setContentText("491023456");
// The subtext, whichppears under the text on newer devices.
// This will show-up in the devices with Android 4.2 and above only
builder.setSubText("Attendere...");
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(NOTIFICATION, builder.build());
}
/**
* Send simple notification using the NotificationCompat API.
*/
public void updateNotification(int color, String status) {
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(color);
// The subtext, which appears under the text on newer devices.
// This will show-up in the devices with Android 4.2 and above only
builder.setSubText(status);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(NOTIFICATION, builder.build());
}
Should i do something different into a service instead of a normal Activity?
Than k you.
For handling permissions in android M+, I want to write a single class, namely PermissionHandler class, to handle all the permission-related work so that I can easily use the same class in any project without making changes to the calling activity by calling only the constructor:
new PermissionHandler(CallingActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE, new PermissionHandler.PermissionGranted() {
#Override
public void onPermissionGranted() {
doWhatever();
}
});
My PermissionHandler is:
public class PermissionHandler implements ActivityCompat.OnRequestPermissionsResultCallback{
.
.
.
public PermissionHandler(AppCompatActivity callingActivity, String permission, PermissionGranted permissionGranted) {
this.permission = permission;
this.permissionGranted = permissionGranted;
this.callingActivity= callingActivity;
askForPermission();
}
private void askForPermission() {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
showAlertDialog();
} else {
ActivityCompat.requestPermissions(callingActivity,permissionsArray, PERMISSION_REQUEST);
}
} else {
permissionGranted.onPermissionGranted();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionGranted.onPermissionGranted();
} else {
onPermissionIsNotGranted();
}
break;
}
}
}
My problem here is that onRequestPermissionsResult which is supposed to be invoked when ActivityCompat.requestPermissions(callingActivity,permissionsArray, PERMISSION_REQUEST) is called is never invoked.
What I found out is that this is due to android calling callingActivity.onRequestPermissionsResult which does not exist in the callingActivity and is passed to PermissionHandler.
I also considered using Reflection and Proxies to resolve this issue at runtime, but no success.
As far as i searched i found that its a bug in android. You can see the live issue here. onRequestPermissionsResult() will not be called in any other class then the activity it is called by.
For more details refer this question : onRequestPermissionsResult not being called in dialog fragment. this user having the same problem as yours.
Creating a Common class for requesting permissions is a really good
attempt but my friend, we need to find an alternative for handling
onRequestPermissionsResult()
according Janki Gadhiya answers, I write procedure like
public static WhatYouWould fCustomRequestPermissionsResult(AppCompatActivity appCompatActivity, #NonNull String[] permissions, #NonNull int[] grantResults,...)
in My PermissionHandler class and do what I would, then in for example MainActivity.java, I call that in onRequestPermissionResult :
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
ActiveUtility.CheckPermission.fCustomRequestPermissionsResult(...)
This is what I am using for requesting single permission on Android 11. compileSdk=30, minSdk=30, targetSdk=30
PermittedTask.java
public abstract class PermittedTask {
private ActivityResultLauncher<String> launcher;
private String permission;
private AppCompatActivity activity;
public PermittedTask(AppCompatActivity activity, String permission) {
this.activity = activity;
this.permission = permission;
this.launcher = activity.registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
new ActivityResultCallback<Boolean>() {
#Override
public void onActivityResult(Boolean result) {
if(result) {
granted();
} else {
denied();
}
}
}
);
}
protected abstract void granted();
protected void denied() {}
private void showRequestPermissionRationale() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Permissions needed")
.setMessage("App needs permissions to do that. You can allow or deny in next screen. Proceed?")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
launcher.launch(permission);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
denied();
}
})
.show();
}
public void run() {
if(ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
granted();
} else if(activity.shouldShowRequestPermissionRationale(permission)) {
showRequestPermissionRationale();
} else {
launcher.launch(permission);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
//...
private PermittedTask scanTask;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
scanTask = new PermittedTask(this, Manifest.permission.ACCESS_FINE_LOCATION) {
#Override
protected void granted() {
startDiscovery();
}
};
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
scanTask.run();
}
});
//...
}
private void startDiscovery() {
//...
}
//...
}
if (checkPermission(PERMISSION_WRITE_EXTERNAL_STORAGE)) {
addToneToStorage();
} else {
requestForPermission(PERMISSION_WRITE_EXTERNAL_STORAGE);
}
BaseActivity
public static final int PERMISSION_RECORD_AUDIO = 1;
public static final int PERMISSION_READ_EXTERNAL_STORAGE = 2;
public static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 3;
public boolean checkPermission(int permission) {
if (ActivityCompat.checkSelfPermission(this, getPermission(permission)) != PackageManager.PERMISSION_GRANTED) {
Debug.e("PERMISSION_CHECK_PERMISSION_FALSE", "-" + permission);
return false;
} else {
Debug.e("PERMISSION_CHECK_PERMISSION_TRUE", "-" + permission);
return true;
}
}
public void requestForPermission(final int permission) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
getPermission(permission))) {
Debug.e("PERMISSION_NEEDED", "-" + permission);
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
// Setting Dialog Message
alertDialog.setMessage(getString(R.string.text_permission_rationale));
// Setting Positive "Yes" Button
alertDialog.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(BaseActivity.this, new String[]{getPermission(permission)}, permission);
}
});
alertDialog.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Write your code here to invoke NO event
dialog.dismiss();
}
});
// Showing Alert Message
alertDialog.show();
} else {
Debug.e("PERMISSION_ALLOW", "-" + permission);
ActivityCompat.requestPermissions(this, new String[]{getPermission(permission)}, permission);
}
// END_INCLUDE(camera_permission_request)
}
#Override
public void onRequestPermissionsResult(final int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Camera permission has been granted, preview can be displayed
Debug.e("PERMISSION_ALLOWED", "-" + requestCode);
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
// Setting Dialog Message
alertDialog.setMessage(getString(R.string.text_permission_granted));
// Setting Positive "Yes" Button
alertDialog.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
// Showing Alert Message
alertDialog.show();
} else {
Debug.e("PERMISSION_DENIED", "-" + requestCode);
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
// Setting Dialog Message
alertDialog.setMessage(getString(R.string.text_permission_not_granted));
// Setting Positive "Yes" Button
alertDialog.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
// Showing Alert Message
alertDialog.show();
}
}
public String getPermission(int permis) {
String permission = null;
switch (permis) {
case PERMISSION_RECORD_AUDIO:
permission = Manifest.permission.RECORD_AUDIO;
Debug.e("PERMISSION", "-" + permission);
return permission;
case PERMISSION_READ_EXTERNAL_STORAGE:
permission = Manifest.permission.READ_EXTERNAL_STORAGE;
Debug.e("PERMISSION", "-" + permission);
return permission;
case PERMISSION_WRITE_EXTERNAL_STORAGE:
permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
Debug.e("PERMISSION", "-" + permission);
return permission;
}
return permission;
}
}