mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge remote-tracking branch 'upstream/main' into lite
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
@@ -6,4 +6,6 @@
|
||||
<color name="reaction_pill_background_selected">#ff282828</color>
|
||||
<color name="reaction_pill_border_selected">@color/reaction_pill_border</color>
|
||||
<color name="reaction_pill_text_color_selected">@color/reaction_pill_text_color</color>
|
||||
|
||||
<color name="action_mode_status_bar">@color/gray95</color>
|
||||
</resources>
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
<item name="actionBarStyle">@style/TextSecure.DarkActionBar</item>
|
||||
<item name="actionBarTabBarStyle">@style/TextSecure.DarkActionBar.TabBar</item>
|
||||
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||
<item name="actionModeBackground">@color/gray50</item>
|
||||
<item name="actionModeBackground">@color/gray78</item>
|
||||
<item name="android:textColor">@color/text_color_dark_theme</item>
|
||||
<item name="android:textColorSecondary">@color/text_color_secondary_dark_theme</item>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class ConversationListAdapter extends RecyclerView.Adapter {
|
||||
private static final int MESSAGE_TYPE_INBOX_ZERO = 3;
|
||||
|
||||
private final WeakReference<Context> context;
|
||||
private final @NonNull DcContext dcContext;
|
||||
private @NonNull DcContext dcContext;
|
||||
private @NonNull DcChatlist dcChatlist;
|
||||
private final @NonNull GlideRequests glideRequests;
|
||||
private final @NonNull Locale locale;
|
||||
@@ -195,7 +195,16 @@ class ConversationListAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
void changeData(@Nullable DcChatlist chatlist) {
|
||||
dcChatlist = chatlist == null ? new DcChatlist(0, 0) : chatlist;
|
||||
Context context = this.context.get();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
if (chatlist == null) {
|
||||
dcChatlist = new DcChatlist(0, 0);
|
||||
} else {
|
||||
dcChatlist = chatlist;
|
||||
dcContext = DcHelper.getContext(context);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +254,8 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
@SuppressLint({"StaticFieldLeak", "NewApi"})
|
||||
private void updateReminders() {
|
||||
// by the time onPostExecute() is asynchronously run, getActivity() might return null, so get the activity here:
|
||||
Activity activity = getActivity();
|
||||
new AsyncTask<Context, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Context... params) {
|
||||
@@ -270,7 +272,6 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
Activity activity = ConversationListFragment.this.getActivity();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (!Prefs.getBooleanPreference(activity, Prefs.ASKED_FOR_NOTIFICATION_PERMISSION, false)) {
|
||||
Prefs.setBooleanPreference(activity, Prefs.ASKED_FOR_NOTIFICATION_PERMISSION, true);
|
||||
@@ -278,7 +279,7 @@ public class ConversationListFragment extends Fragment
|
||||
.request(Manifest.permission.POST_NOTIFICATIONS)
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
DozeReminder.maybeAskDirectly(getActivity());
|
||||
DozeReminder.maybeAskDirectly(activity);
|
||||
})
|
||||
.onAnyDenied(() -> {
|
||||
final DcContext dcContext = DcHelper.getContext(activity);
|
||||
@@ -290,10 +291,10 @@ public class ConversationListFragment extends Fragment
|
||||
.execute();
|
||||
}
|
||||
} else {
|
||||
DozeReminder.maybeAskDirectly(getActivity());
|
||||
DozeReminder.maybeAskDirectly(activity);
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, activity);
|
||||
}
|
||||
|
||||
private void handlePinAllSelected() {
|
||||
|
||||
@@ -56,6 +56,7 @@ public class LogViewActivity extends BaseActionBarActivity {
|
||||
case R.id.save_log:
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
boolean success = logViewFragment.saveLogFile();
|
||||
|
||||
@@ -305,6 +305,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> {
|
||||
|
||||
@@ -86,6 +86,7 @@ public abstract class MessageSelectorFragment
|
||||
|
||||
Permissions.with(getActivity())
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> performSave(messageRecords))
|
||||
|
||||
@@ -151,6 +151,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
|
||||
private void requestPermissionForFiles(List<Uri> streamExtras) {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk33()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> resolveUris(streamExtras))
|
||||
|
||||
@@ -170,6 +170,7 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
|
||||
private void startImportBackup() {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> {
|
||||
|
||||
@@ -446,6 +446,10 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
public static void selectGallery(Activity activity, int requestCode) {
|
||||
// to enable camera roll,
|
||||
// we're asking for "gallery permissions" also on newer systems that do not strictly require that.
|
||||
// (asking directly after tapping "attachment" would be not-so-good as the user may want to attach sth. else
|
||||
// and asking for permissions is better done on-point)
|
||||
Permissions.with(activity)
|
||||
.request(Permissions.galleryPermissions())
|
||||
.ifNecessary()
|
||||
@@ -466,6 +470,7 @@ public class AttachmentManager {
|
||||
public static void selectAudio(Activity activity, int requestCode) {
|
||||
Permissions.with(activity)
|
||||
.request(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> selectMediaType(activity, "audio/*", null, requestCode))
|
||||
|
||||
@@ -80,6 +80,8 @@ public class Permissions {
|
||||
|
||||
private boolean condition = true;
|
||||
|
||||
private boolean alwaysGranted = false;
|
||||
|
||||
PermissionsBuilder(PermissionObject permissionObject) {
|
||||
this.permissionObject = permissionObject;
|
||||
}
|
||||
@@ -100,6 +102,20 @@ public class Permissions {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PermissionsBuilder alwaysGrantOnSdk30() {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
alwaysGranted = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PermissionsBuilder alwaysGrantOnSdk33() {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
alwaysGranted = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PermissionsBuilder withRationaleDialog(@NonNull String message, @NonNull @DrawableRes int... headers) {
|
||||
this.rationalDialogHeader = headers;
|
||||
this.rationaleDialogMessage = message;
|
||||
@@ -147,16 +163,9 @@ public class Permissions {
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE does not exist on modern androids
|
||||
// as file access is done by pickers
|
||||
String[] r = requestedPermissions;
|
||||
Arrays.sort(r);
|
||||
if ( (r.length == 1 && (r[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE) || r[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)))
|
||||
|| (r.length == 2 && r[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE) && r[1].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) ) {
|
||||
allGrantedListener.run();
|
||||
return;
|
||||
}
|
||||
if (alwaysGranted) {
|
||||
allGrantedListener.run();
|
||||
return;
|
||||
}
|
||||
|
||||
PermissionsRequest request = new PermissionsRequest(allGrantedListener, anyDeniedListener, anyPermanentlyDeniedListener, anyResultListener,
|
||||
|
||||
@@ -177,7 +177,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode != RESULT_OK) return;
|
||||
if (requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS_KEYS) {
|
||||
exportKeys();
|
||||
manageKeys();
|
||||
} else if (requestCode == PICK_SELF_KEYS) {
|
||||
Uri uri = (data != null ? data.getData() : null);
|
||||
if (uri == null) {
|
||||
@@ -330,15 +330,16 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
boolean result = ScreenLockUtil.applyScreenLock(getActivity(), getString(R.string.pref_manage_keys), getString(R.string.enter_system_secret_to_continue), REQUEST_CODE_CONFIRM_CREDENTIALS_KEYS);
|
||||
if (!result) {
|
||||
exportKeys();
|
||||
manageKeys();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void exportKeys() {
|
||||
private void manageKeys() {
|
||||
Permissions.with(getActivity())
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> {
|
||||
@@ -359,7 +360,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||
}
|
||||
else {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
AttachmentManager.selectMediaType(getActivity(), "application/pgp-keys", new String[]{"text/plain"}, PICK_SELF_KEYS, StorageUtil.getDownloadUri());
|
||||
AttachmentManager.selectMediaType(getActivity(), "*/*", null, PICK_SELF_KEYS, StorageUtil.getDownloadUri());
|
||||
} else {
|
||||
String path = DcHelper.getImexDir().getAbsolutePath();
|
||||
showImportKeysDialog(path, path);
|
||||
|
||||
@@ -38,7 +38,6 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
Button galleryButton;
|
||||
Button defaultButton;
|
||||
MenuItem acceptMenuItem;
|
||||
ImageView preview;
|
||||
|
||||
String tempDestinationPath;
|
||||
@@ -80,7 +79,6 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
menu.clear();
|
||||
inflater.inflate(R.menu.chat_background, menu);
|
||||
acceptMenuItem = menu.findItem(R.id.apply_background);
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -173,7 +171,6 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
|
||||
if (data != null && context != null && resultCode == RESULT_OK && requestCode == ApplicationPreferencesActivity.REQUEST_CODE_SET_BACKGROUND) {
|
||||
imageUri = data.getData();
|
||||
if (imageUri != null) {
|
||||
acceptMenuItem.setEnabled(true);
|
||||
Thread thread = new Thread(){
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -266,7 +266,8 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
|
||||
private void performBackup() {
|
||||
Permissions.with(getActivity())
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) // READ_EXTERNAL_STORAGE required to read folder contents and to generate backup names
|
||||
.alwaysGrantOnSdk30()
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> {
|
||||
|
||||
@@ -40,7 +40,7 @@ class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.Search
|
||||
private SearchResult searchResult = SearchResult.EMPTY;
|
||||
|
||||
final Context context;
|
||||
final DcContext dcContext;
|
||||
final DcContext dcContext; // reset on account switching is not needed because SearchFragment and SearchListAdapter are recreated in every search start
|
||||
|
||||
SearchListAdapter(Context context,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
|
||||
Reference in New Issue
Block a user