migrate to targetSdkVersion 30 (Christian Schneppe)

This commit is contained in:
Arne 2021-12-25 22:04:26 +01:00
parent b111f6263c
commit 1d57c88bbd
11 changed files with 176 additions and 96 deletions

View file

@ -74,7 +74,6 @@ dependencies {
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
implementation 'com.google.android.exoplayer:exoplayer-core:2.15.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.15.0'
implementation 'pub.devrel:easypermissions:3.0.0' // version >= 3.0.0 needs android X libraries
implementation 'com.wefika:flowlayout:0.4.1'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
implementation 'org.jxmpp:jxmpp-jid:1.0.1'
@ -106,11 +105,11 @@ android {
}
}
compileSdkVersion 29
compileSdkVersion 30
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 30
//versionNameSuffix " beta_(2021-12-08)" // " beta_(XXXX-XX-XX)" // activate for beta versions
versionCode 109

View file

@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="28" />
@ -94,7 +95,7 @@
<receiver android:name=".services.AlarmReceiver"></receiver>
<activity
android:name="de.monocles.chat.ui.StartUI"
android:name="de.pixart.messenger.ui.StartUI"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"

View file

@ -0,0 +1,94 @@
package de.pixart.messenger.ui;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.Settings;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import eu.siacs.conversations.R;
import eu.siacs.conversations.utils.Compatibility;
import me.drakeet.support.toast.ToastCompat;
public class PermissionsActivity extends AppCompatActivity
implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int PERMISSION_LENGTH = 2;
public static final int STORAGE_PERMISSION = 0, ALL_FILES_PERMISSION = 1;
private final OnPermissionGranted[] permissionCallbacks = new OnPermissionGranted[PERMISSION_LENGTH];
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == STORAGE_PERMISSION) {
if (Compatibility.hasStoragePermission(PermissionsActivity.this)) {
permissionCallbacks[STORAGE_PERMISSION].onPermissionGranted();
permissionCallbacks[STORAGE_PERMISSION] = null;
} else {
ToastCompat.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
requestStoragePermission(permissionCallbacks[STORAGE_PERMISSION]);
}
}
}
public boolean checkStoragePermission() {
return Compatibility.hasStoragePermission(PermissionsActivity.this);
}
public void requestStoragePermission(@NonNull final PermissionsActivity.OnPermissionGranted onPermissionGranted) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.intro_required_permissions);
builder.setMessage(getString(R.string.no_storage_permission));
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
builder.setPositiveButton(R.string.grant, (dialog, which) -> {
permissionCallbacks[STORAGE_PERMISSION] = onPermissionGranted;
ActivityCompat.requestPermissions(PermissionsActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION);
});
builder.setOnCancelListener(dialog -> finish());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
}
public void requestAllFilesAccess(@NonNull final PermissionsActivity.OnPermissionGranted onPermissionGranted) {
if (Compatibility.runsAndTargetsThirty(this) && !Environment.isExternalStorageManager()) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.intro_required_permissions);
builder.setMessage(getString(R.string.no_manage_storage_permission));
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
builder.setPositiveButton(R.string.grant, (dialog, which) -> {
permissionCallbacks[ALL_FILES_PERMISSION] = onPermissionGranted;
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, ALL_FILES_PERMISSION);
} catch (Exception e) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, ALL_FILES_PERMISSION);
}
});
builder.setOnCancelListener(dialog -> finish());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
} else {
StartUI.next(this);
}
}
public interface OnPermissionGranted {
void onPermissionGranted();
}
}

View file

@ -1,115 +1,66 @@
package de.monocles.chat.ui;
package de.pixart.messenger.ui;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.util.IntroHelper;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class StartUI extends AppCompatActivity
implements EasyPermissions.PermissionCallbacks {
private static final int NeededPermissions = 1000;
String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
};
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.ThemeHelper;
public class StartUI extends PermissionsActivity
implements PermissionsActivity.OnPermissionGranted {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_ui);
setTheme(ThemeHelper.findDialog(this));
IntroHelper.showIntro(this, false);
requestNeededPermissions();
}
@AfterPermissionGranted(NeededPermissions)
private void requestNeededPermissions() {
String PREF_FIRST_START = "FirstStart";
SharedPreferences FirstStart = getApplicationContext().getSharedPreferences(PREF_FIRST_START, Context.MODE_PRIVATE);
long FirstStartTime = FirstStart.getLong(PREF_FIRST_START, 0);
if (EasyPermissions.hasPermissions(this, perms)) {
// Already have permission, start ConversationsActivity
Log.d(Config.LOGTAG, "All permissions granted, starting " + getString(R.string.app_name) + "(" + FirstStartTime + ")");
Intent intent = new Intent(this, ConversationsActivity.class);
intent.putExtra(PREF_FIRST_START, FirstStartTime);
startActivity(intent);
overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
finish();
} else {
// set first start to 0 if there are permissions to request
Log.d(Config.LOGTAG, "Requesting required permissions");
SharedPreferences.Editor editor = FirstStart.edit();
editor.putLong(PREF_FIRST_START, 0);
editor.commit();
// Do not have permissions, request them now
EasyPermissions.requestPermissions(this, getString(R.string.request_permissions_message),
NeededPermissions, perms);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
protected void onStart() {
super.onStart();
requestNeededPermissions();
}
@Override
public void onPermissionsGranted(int requestCode, List<String> list) {
Log.d(Config.LOGTAG, "Permissions granted:" + requestCode);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> list) {
Log.d(Config.LOGTAG, "Permissions denied:" + requestCode);
final AlertDialog dialog = new AlertDialog.Builder(this)
.setMessage(getString(R.string.request_permissions_message_again))
.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
}
})
.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.create();
dialog.show();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
private void requestNeededPermissions() {
if (Compatibility.runsTwentyThree()) {
if (!checkStoragePermission()) {
requestStoragePermission(this);
}
if (Compatibility.runsAndTargetsThirty(this)) {
requestAllFilesAccess(this);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onPermissionGranted() {
next(this);
}
public static void next(final Activity activity) {
String PREF_FIRST_START = "FirstStart";
SharedPreferences FirstStart = activity.getSharedPreferences(PREF_FIRST_START, Context.MODE_PRIVATE);
long FirstStartTime = FirstStart.getLong(PREF_FIRST_START, 0);
Intent intent = new Intent(activity, ConversationsActivity.class);
intent.putExtra(PREF_FIRST_START, FirstStartTime);
activity.startActivity(intent);
activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
activity.finish();
}
}

View file

@ -150,7 +150,7 @@ public class XmppAxolotlMessage {
try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
Cipher cipher = Compatibility.runsTwentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
@ -255,7 +255,7 @@ public class XmppAxolotlMessage {
ciphertext = newCipherText;
key = newKey;
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
final Cipher cipher = Compatibility.runsTwentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);

View file

@ -367,7 +367,7 @@ public class ExportBackupService extends Service {
backupFileHeader.write(dataOutputStream);
dataOutputStream.flush();
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
final Cipher cipher = Compatibility.runsTwentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
final byte[] key = getKey(password, salt);
Log.d(Config.LOGTAG, backupFileHeader.toString());
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);

View file

@ -1862,6 +1862,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (Config.ONLY_INTERNAL_STORAGE && permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && permission.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) {
continue;
}
if (Compatibility.runsAndTargetsThirty(activity)) {
if (Config.ONLY_INTERNAL_STORAGE && permission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
continue;
}
}
if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(permission);
}

View file

@ -59,14 +59,22 @@ public class Compatibility {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
public static boolean runsTwentyThree() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
public static boolean runsTwentyFour() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
public static boolean twentyEight() {
public static boolean runsTwentyEight() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
public static boolean runsThirty() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
}
private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) {
return getPreferences(context).getBoolean(name, context.getResources().getBoolean(res));
}
@ -86,6 +94,17 @@ public class Compatibility {
return true; //when in doubt
}
}
private static boolean targetsThirty(Context context) {
try {
final PackageManager packageManager = context.getPackageManager();
final ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
return applicationInfo == null || applicationInfo.targetSdkVersion >= 30;
} catch (PackageManager.NameNotFoundException e) {
return true; //when in doubt
} catch (RuntimeException e) {
return true; //when in doubt
}
}
private static boolean targetsTwentyFour(Context context) {
try {
@ -98,13 +117,16 @@ public class Compatibility {
return true; //when in doubt
}
}
public static boolean runsAndTargetsTwentyFour(Context context) {
return runsTwentyFour() && targetsTwentyFour(context);
}
public static boolean runsAndTargetsTwentySix(Context context) {
return runsTwentySix() && targetsTwentySix(context);
}
public static boolean runsAndTargetsTwentyFour(Context context) {
return runsTwentyFour() && targetsTwentyFour(context);
public static boolean runsAndTargetsThirty(Context context) {
return runsThirty() && targetsThirty(context);
}
public static boolean keepForegroundService(Context context) {

View file

@ -1162,4 +1162,6 @@
<string name="plain_text_document">Plain text document</string>
<string name="pref_import_settings_summary">Import app settings from settings.dat file within the monocles chat backup directory.</string>
<string name="pref_import_settings">Import app settings</string>
<string name="grant">Grant</string>
<string name="no_manage_storage_permission">Since Android 11, apps needs a special permission to access all file types on the external storage. monocles chat will ask you to grant this permission to continue.</string>
</resources>

View file

@ -163,6 +163,10 @@
<item name="android:buttonStyle">@style/Widget.Conversations.Button.Borderless</item>
</style>
<style name="Conversations.Dialog.Alert.Buttons" parent="Widget.AppCompat.Button.Borderless">
<item name="android:textColor">@color/white</item>
</style>
<style name="ExoMediaButton.Previous">
<item name="android:visibility">gone</item>
</style>

View file

@ -373,7 +373,7 @@
</style>
<!-- blue dialog -->
<style name="ConversationsTheme.Dialog" parent="Theme.MaterialComponents.Light.Dialog">
<style name="ConversationsTheme.Dialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
@ -385,10 +385,11 @@
<item name="TextSizeBody2">14sp</item>
<item name="TextSizeDisplay2">45sp</item>
<item name="android:windowNoTitle">true</item>
<item name="buttonBarButtonStyle">@style/Conversations.Dialog.Alert.Buttons</item>
</style>
<!-- blue dialog - dark -->
<style name="ConversationsTheme.Dark.Dialog" parent="Theme.MaterialComponents.Dialog">
<style name="ConversationsTheme.Dark.Dialog" parent="Theme.AppCompat.Dialog">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
@ -399,6 +400,7 @@
<item name="TextSizeBody2">14sp</item>
<item name="TextSizeDisplay2">45sp</item>
<item name="android:windowNoTitle">true</item>
<item name="buttonBarButtonStyle">@style/Conversations.Dialog.Alert.Buttons</item>
</style>
<!-- blue dialog - black -->