Merge branch 'dev' of https://github.com/loki-project/session-android into attachment-in-conversation-preview

This commit is contained in:
Ryan ZHAO
2020-09-03 13:17:03 +10:00
308 changed files with 2481 additions and 2436 deletions

View File

@@ -16,17 +16,18 @@
*/
package org.thoughtcrime.securesms;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.multidex.MultiDexApplication;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.google.firebase.iid.FirebaseInstanceId;
@@ -72,6 +73,7 @@ import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities;
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
@@ -114,10 +116,10 @@ import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderK
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities;
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink;
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol;
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate;
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink;
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol;
import java.io.File;
@@ -143,11 +145,10 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
*
* @author Moxie Marlinspike
*/
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate,
public class ApplicationContext extends Application implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate,
SessionManagementProtocolDelegate, SharedSenderKeysImplementationDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName();
private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB
private ExpiringMessageManager expiringMessageManager;
private TypingStatusRepository typingStatusRepository;
@@ -215,6 +216,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
publicChatManager = new PublicChatManager(this);
updateOpenGroupProfilePicturesIfNeeded();
registerForFCMIfNeeded(false);
// Set application UI mode (day/night theme) to the user selected one.
UiModeUtilities.setupUiModeToUserSelected(this);
// ========
initializeJobManager();
initializeMessageRetrieval();

View File

@@ -247,7 +247,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp));
Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp));
Drawable publicKey = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_24dp));
Drawable qrCode = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_qr_code));
Drawable qrCode = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_qr_code_24));
Drawable linkDevice = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_link));
Drawable seed = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_seedling));

View File

@@ -32,7 +32,6 @@ import java.util.Date;
import network.loki.messenger.R;
public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
private BroadcastReceiver broadcastReceiver;
@@ -122,6 +121,9 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
ActivityCompat.startActivity(this, intent, bundle);
}
//TODO AC: I don't think we need this method,
// setting any colors directly will most likely clash with the current theme.
@Deprecated
@TargetApi(VERSION_CODES.LOLLIPOP)
protected void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@@ -455,7 +455,7 @@ public class ConversationListFragment extends Fragment
switch (item.getItemId()) {
case R.id.menu_select_all: handleSelectAllThreads(); return true;
case R.id.menu_delete_selected: handleDeleteAllSelected(); return true;
case R.id.menu_archive_selected: handleArchiveAllSelected(); return true;
// case R.id.menu_archive_selected: handleArchiveAllSelected(); return true;
}
return false;

View File

@@ -48,8 +48,6 @@ import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicRegistrationTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -85,7 +83,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
public static final String NEXT_INTENT = "next_intent";
public static final String EXCLUDE_SYSTEM = "exclude_system";
private final DynamicTheme dynamicTheme = new DynamicRegistrationTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Inject SignalServiceAccountManager accountManager;
@@ -107,7 +104,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
setContentView(R.layout.profile_create_activity);
@@ -125,7 +121,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}

View File

@@ -25,21 +25,6 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.google.android.material.tabs.TabLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -49,7 +34,23 @@ import android.view.Window;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import com.google.android.material.tabs.TabLayout;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
@@ -62,8 +63,6 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.Util;
@@ -87,7 +86,6 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
public static final String ADDRESS_EXTRA = "address";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private Toolbar toolbar;
@@ -97,14 +95,12 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle bundle, boolean ready) {
setContentView(R.layout.media_overview_activity);
getWindow().setNavigationBarColor(getResources().getColor(R.color.navigation_bar_background));
initializeResources();
initializeToolbar();
@@ -116,7 +112,6 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}

View File

@@ -19,6 +19,8 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
@@ -150,7 +152,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(this::initializeActionBar);
Util.runOnMain(this::updateActionBar);
}
@Override
@@ -164,7 +166,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
@SuppressWarnings("ConstantConditions")
private void initializeActionBar() {
private void updateActionBar() {
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem != null) {
@@ -222,6 +224,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
caption = findViewById(R.id.media_preview_caption);
captionContainer = findViewById(R.id.media_preview_caption_container);
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
setSupportActionBar(findViewById(R.id.toolbar));
}
private void initializeResources() {
@@ -476,7 +481,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
MediaItem item = adapter.getMediaItemFor(position);
if (item.recipient != null) item.recipient.addListener(MediaPreviewActivity.this);
viewModel.setActiveAlbumRailItem(MediaPreviewActivity.this, position);
initializeActionBar();
updateActionBar();
}
}

View File

@@ -55,7 +55,6 @@ import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.DynamicIntroTheme;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -70,7 +69,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private static final String TAG = PassphrasePromptActivity.class.getSimpleName();
private DynamicIntroTheme dynamicTheme = new DynamicIntroTheme();
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
private View passphraseAuthContainer;
@@ -92,7 +90,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate()");
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
@@ -105,7 +102,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
setLockTypeVisibility();

View File

@@ -74,7 +74,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
public static final String EXTRA_ADDRESS_MARSHALLED = "address_marshalled";
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private ContactSelectionListFragment contactsFragment;
@@ -87,7 +86,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@@ -121,7 +119,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
public void onResume() {
Log.i(TAG, "onResume()");
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import android.text.TextUtils;
@@ -19,8 +18,6 @@ public class TransportOption implements Parcelable {
TEXTSECURE
}
private final int drawable;
private final int backgroundColor;
private final @NonNull String text;
private final @NonNull Type type;
private final @NonNull String composeHint;
@@ -29,19 +26,15 @@ public class TransportOption implements Parcelable {
private final @NonNull Optional<Integer> simSubscriptionId;
public TransportOption(@NonNull Type type,
@DrawableRes int drawable,
int backgroundColor,
@NonNull String text,
@NonNull String composeHint,
@NonNull CharacterCalculator characterCalculator)
{
this(type, drawable, backgroundColor, text, composeHint, characterCalculator,
this(type, text, composeHint, characterCalculator,
Optional.<CharSequence>absent(), Optional.<Integer>absent());
}
public TransportOption(@NonNull Type type,
@DrawableRes int drawable,
int backgroundColor,
@NonNull String text,
@NonNull String composeHint,
@NonNull CharacterCalculator characterCalculator,
@@ -49,8 +42,6 @@ public class TransportOption implements Parcelable {
@NonNull Optional<Integer> simSubscriptionId)
{
this.type = type;
this.drawable = drawable;
this.backgroundColor = backgroundColor;
this.text = text;
this.composeHint = composeHint;
this.characterCalculator = characterCalculator;
@@ -60,8 +51,6 @@ public class TransportOption implements Parcelable {
TransportOption(Parcel in) {
this(Type.valueOf(in.readString()),
in.readInt(),
in.readInt(),
in.readString(),
in.readString(),
CharacterCalculator.readFromParcel(in),
@@ -85,14 +74,6 @@ public class TransportOption implements Parcelable {
return characterCalculator.calculateCharacters(messageBody);
}
public @DrawableRes int getDrawable() {
return R.drawable.ic_arrow_up;
}
public int getBackgroundColor() {
return backgroundColor;
}
public @NonNull String getComposeHint() {
return composeHint;
}
@@ -119,8 +100,6 @@ public class TransportOption implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(type.name());
dest.writeInt(drawable);
dest.writeInt(backgroundColor);
dest.writeString(text);
dest.writeString(composeHint);
CharacterCalculator.writeToParcel(dest, characterCalculator);

View File

@@ -2,6 +2,10 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.content.Context;
import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -147,8 +151,7 @@ public class TransportOptions {
private List<TransportOption> initializeAvailableTransports(boolean isMediaMessage) {
List<TransportOption> results = new LinkedList<>();
results.add(new TransportOption(Type.TEXTSECURE, R.drawable.ic_send_push_white_24dp,
context.getResources().getColor(R.color.textsecure_primary),
results.add(new TransportOption(Type.TEXTSECURE,
context.getString(R.string.ConversationActivity_transport_signal),
context.getString(R.string.conversation_activity__type_message_push),
new PushCharacterCalculator()));
@@ -171,13 +174,11 @@ public class TransportOptions {
}
if (subscriptions.size() < 2) {
results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp,
context.getResources().getColor(R.color.grey_600),
results.add(new TransportOption(Type.SMS,
text, composeHint, characterCalculator));
} else {
for (SubscriptionInfoCompat subscriptionInfo : subscriptions) {
results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp,
context.getResources().getColor(R.color.grey_600),
results.add(new TransportOption(Type.SMS,
text, composeHint, characterCalculator,
Optional.of(subscriptionInfo.getDisplayName()),
Optional.of(subscriptionInfo.getSubscriptionId())));
@@ -202,6 +203,6 @@ public class TransportOptions {
}
public interface OnTransportChangedListener {
public void onChange(TransportOption newTransport, boolean manuallySelected);
void onChange(TransportOption newTransport, boolean manuallySelected);
}
}

View File

@@ -1,8 +1,6 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.graphics.PorterDuff.Mode;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,6 +8,8 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.List;
@@ -60,8 +60,7 @@ public class TransportOptionsAdapter extends BaseAdapter {
TextView textView = ViewUtil.findById(convertView, R.id.text);
TextView subtextView = ViewUtil.findById(convertView, R.id.subtext);
imageView.getBackground().setColorFilter(transport.getBackgroundColor(), Mode.MULTIPLY);
imageView.setImageResource(transport.getDrawable());
imageView.setImageResource(R.drawable.ic_arrow_up_circle_24);
textView.setText(transport.getDescription());
if (transport.getSimName().isPresent()) {

View File

@@ -10,6 +10,7 @@ import android.widget.LinearLayout;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ThemeUtil;
import java.util.List;
@@ -41,7 +42,10 @@ public class ConversationTypingView extends LinearLayout {
}
Recipient typist = typists.get(0);
bubble.getBackground().setColorFilter(getResources().getColor(R.color.received_message_background), PorterDuff.Mode.MULTIPLY);
bubble.getBackground().setColorFilter(
ThemeUtil.getThemedColor(getContext(), R.attr.message_received_background_color),
PorterDuff.Mode.MULTIPLY);
if (isGroupThread) {
avatar.setAvatar(glideRequests, typist, false);

View File

@@ -210,7 +210,8 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
// We use the raw color resource because Android 4.x was struggling with tints here
quoteBarView.setImageResource(R.color.accent);
mainView.setBackgroundColor(getResources().getColor(outgoing ? R.color.received_message_background : R.color.sent_message_background));
mainView.setBackgroundColor(ThemeUtil.getThemedColor(getContext(),
outgoing ? R.attr.message_received_background_color : R.attr.message_sent_background_color));
}
private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) {

View File

@@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import android.util.AttributeSet;
@@ -10,14 +12,16 @@ import org.thoughtcrime.securesms.TransportOption;
import org.thoughtcrime.securesms.TransportOptions;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.TransportOptionsPopup;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import network.loki.messenger.R;
public class SendButton extends AppCompatImageButton
implements TransportOptions.OnTransportChangedListener,
TransportOptionsPopup.SelectedListener,
View.OnLongClickListener
{
View.OnLongClickListener {
private final TransportOptions transportOptions;
@@ -102,7 +106,19 @@ public class SendButton extends AppCompatImageButton
@Override
public void onChange(TransportOption newTransport, boolean isManualSelection) {
setImageResource(newTransport.getDrawable());
// Map send icon drawable resource from a transport type.
//TODO These values should come from XML layout as view's attributes and not be resolved in code like this.
@DrawableRes final int sendDrawable;
switch (newTransport.getType()) {
case SMS:
case TEXTSECURE:
default: {
sendDrawable = ThemeUtil.getThemedDrawableResId(
getContext(), R.attr.conversation_transport_sms_indicator);
}
}
setImageResource(sendDrawable);
setContentDescription(newTransport.getDescription());
}

View File

@@ -1,12 +1,12 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import android.util.AttributeSet;
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -46,18 +46,16 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
}
private void initialize() {
int attributes[] = new int[] {R.attr.conversation_emoji_toggle,
R.attr.conversation_sticker_toggle,
R.attr.conversation_keyboard_toggle};
TypedArray drawables = getContext().obtainStyledAttributes(new int[] {
R.attr.conversation_emoji_toggle,
R.attr.conversation_sticker_toggle,
R.attr.conversation_keyboard_toggle});
TypedArray drawables = getContext().obtainStyledAttributes(attributes);
this.emojiToggle = drawables.getDrawable(0);
this.stickerToggle = drawables.getDrawable(1);
this.imeToggle = drawables.getDrawable(2);
this.mediaToggle = emojiToggle;
setImageTintList(ColorStateList.valueOf(getResources().getColor(R.color.text)));
drawables.recycle();
setToMedia();
}

View File

@@ -19,20 +19,17 @@ package org.thoughtcrime.securesms.conversation;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import androidx.lifecycle.ViewModelProviders;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
import android.net.Uri;
import android.os.AsyncTask;
@@ -42,17 +39,6 @@ import android.os.Handler;
import android.os.Vibrator;
import android.provider.Browser;
import android.provider.Telephony;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.view.MenuItemCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -75,6 +61,20 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.view.MenuItemCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.loader.app.LoaderManager;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus;
@@ -145,7 +145,6 @@ import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
@@ -208,7 +207,6 @@ import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -346,7 +344,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private int keyboardHeight = 0;
private final IdentityRecordList identityRecords = new IdentityRecordList();
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
// Message status bar
@@ -365,7 +362,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@@ -375,12 +371,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setContentView(R.layout.conversation_activity);
TypedArray typedArray = obtainStyledAttributes(new int[] {R.attr.conversation_background});
int color = typedArray.getColor(0, Color.WHITE);
typedArray.recycle();
getWindow().getDecorView().setBackgroundColor(color);
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale());
registerMessageStatusObserver("calculatingPoW");
@@ -528,7 +518,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
EventBus.getDefault().register(this);
@@ -760,6 +749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
inflater.inflate(R.menu.conversation_block, menu);
}
inflater.inflate(R.menu.conversation_copy_session_id, menu);
} else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
// inflater.inflate(R.menu.conversation_group_options, menu);
@@ -863,16 +853,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
case R.id.menu_unblock: handleUnblock(); return true;
case R.id.menu_block: handleBlock(); return true;
case R.id.menu_copy_session_id: handleCopySessionID(); return true;
case R.id.menu_view_media: handleViewMedia(); return true;
case R.id.menu_add_shortcut: handleAddShortcut(); return true;
case R.id.menu_search: handleSearch(); return true;
case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
// case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
case R.id.menu_edit_group: handleEditPushGroup(); return true;
@@ -1096,6 +1087,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}).show();
}
private void handleCopySessionID() {
if (recipient.isGroupRecipient()) { return; }
String sessionID = recipient.getAddress().toPhoneString();
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Session ID", sessionID);
clipboard.setPrimaryClip(clip);
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
}
private void handleViewMedia() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
@@ -1282,9 +1282,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleAddAttachment() {
if (this.isMmsEnabled || isSecureText) {
if (attachmentTypeSelector == null) {
attachmentTypeSelector = new AttachmentTypeSelector(this, getSupportLoaderManager(), new AttachmentTypeListener(), keyboardHeight);
attachmentTypeSelector = new AttachmentTypeSelector(
this,
LoaderManager.getInstance(this),
new AttachmentTypeListener(),
keyboardHeight);
}
attachmentTypeSelector.keyboardHeight = keyboardHeight;
attachmentTypeSelector.show(this, attachButton);
} else {
handleManualMmsRequired();
@@ -1733,7 +1736,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected void initializeActionBar() {
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.getOverflowIcon().setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
setSupportActionBar(toolbar);
ActionBar supportActionBar = getSupportActionBar();
@@ -2080,13 +2082,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void setActionBarColor(MaterialColor color) {
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar == null) throw new AssertionError();
supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.action_bar_background)));
setStatusBarColor(getResources().getColor(R.color.action_bar_background));
//TODO AC: As we're trying to theme everything properly this method seems a bit broken
// and it's not used anyway so it's a subject to be deleted.
// ActionBar supportActionBar = getSupportActionBar();
// if (supportActionBar == null) throw new AssertionError();
// supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.action_bar_background)));
// setStatusBarColor(getResources().getColor(R.color.action_bar_background));
}
// FIXME: This name is confusing because we also have updateInputPanel and setInputPanelEnabled
private void updateInputUI(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
if (recipient.isGroupRecipient() && !isActiveGroup()) {
unblockButton.setVisibility(View.GONE);
@@ -2549,7 +2552,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Permissions.with(this)
.request(Manifest.permission.RECORD_AUDIO)
.ifNecessary()
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_mic_white_48dp)
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
.execute();
}
@@ -2750,7 +2753,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Permissions.with(ConversationActivity.this)
.request(Manifest.permission.CAMERA)
.ifNecessary()
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_photo_camera_white_48dp)
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
.onAllGranted(() -> {
composeText.clearFocus();

View File

@@ -352,11 +352,10 @@ public class ConversationItem extends LinearLayout
/// MessageRecord Attribute Parsers
private void setBubbleState(MessageRecord messageRecord) {
if (messageRecord.isOutgoing()) {
bodyBubble.getBackground().setColorFilter(getResources().getColor(R.color.sent_message_background), PorterDuff.Mode.MULTIPLY);
} else {
bodyBubble.getBackground().setColorFilter(getResources().getColor(R.color.received_message_background), PorterDuff.Mode.MULTIPLY);
}
int bubbleColor = ThemeUtil.getThemedColor(getContext(), messageRecord.isOutgoing() ?
R.attr.message_sent_background_color :
R.attr.message_received_background_color);
bodyBubble.getBackground().setColorFilter(bubbleColor, PorterDuff.Mode.MULTIPLY);
if (audioViewStub.resolved()) {
setAudioViewTint(messageRecord, this.conversationRecipient);
@@ -364,7 +363,7 @@ public class ConversationItem extends LinearLayout
}
private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) {
audioViewStub.get().setTint(Color.WHITE, getResources().getColor(R.color.action_bar_background));
// audioViewStub.get().setTint(Color.WHITE, getResources().getColor(R.color.action_bar_background));
}
private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {

View File

@@ -1,35 +1,35 @@
package org.thoughtcrime.securesms.giph.ui;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.tabs.TabLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import org.thoughtcrime.securesms.logging.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import network.loki.messenger.R;
public class GiphyActivity extends PassphraseRequiredActionBarActivity
implements GiphyActivityToolbar.OnLayoutChangedListener,
GiphyActivityToolbar.OnFilterChangedListener,
@@ -42,7 +42,6 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
public static final String EXTRA_WIDTH = "extra_width";
public static final String EXTRA_HEIGHT = "extra_height";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private GiphyGifFragment gifFragment;
@@ -53,7 +52,6 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
@Override
public void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}

View File

@@ -834,6 +834,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
throws MmsException
{
if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; }
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipients = getSyncMessageMasterDestination(message);
Optional<QuoteModel> quote = getValidatedQuote(message.getMessage().getQuote());
@@ -1002,6 +1004,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message)
throws MmsException
{
if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; }
Recipient recipient = getSyncMessageMasterDestination(message);
String body = message.getMessage().getBody().or("");
@@ -1459,7 +1462,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return true;
}
if (SessionMetaProtocol.shouldIgnoreMessage(content)) {
if (SessionMetaProtocol.shouldIgnoreMessage(content.getTimestamp())) {
Log.d("Loki", "Ignoring duplicate message.");
return true;
}

View File

@@ -45,7 +45,7 @@ public class RegistrationLockDialog {
if (!RegistrationLockReminders.needsReminder(context)) return;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
AlertDialog dialog = new AlertDialog.Builder(context, R.style.RationaleDialog)
AlertDialog dialog = new AlertDialog.Builder(context, R.style.Theme_TextSecure_Dialog_Rationale)
.setView(R.layout.registration_lock_reminder_view)
.setCancelable(true)
.setOnCancelListener(d -> RegistrationLockReminders.scheduleReminder(context, false))

View File

@@ -110,8 +110,8 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
})
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_apply, menu)
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
return members.isNotEmpty()
}
// endregion
@@ -163,7 +163,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
// region Interaction
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId) {
R.id.applyButton -> commitChanges()
R.id.action_apply -> commitChanges()
}
return super.onOptionsItemSelected(item)
}

View File

@@ -13,6 +13,7 @@ class EditClosedGroupMembersAdapter(
private val glide: GlideRequests,
private val memberClickListener: ((String) -> Unit)? = null
) : RecyclerView.Adapter<EditClosedGroupMembersAdapter.ViewHolder>() {
private val members = ArrayList<String>()
private val lockedMembers = HashSet<String>()

View File

@@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
@@ -200,8 +201,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
if (hasViewedSeed || !isMasterDevice) {
seedReminderView.visibility = View.GONE
}
val hasSeenMultiDeviceRemovalSheet = TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)
if (!hasSeenMultiDeviceRemovalSheet) {
// Multiple device removal notification
if (!TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)) {
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey)
@@ -217,8 +219,18 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
startActivity(intent)
}
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
return
}
}
// Light theme introduction
if (!TextSecurePreferences.hasSeenLightThemeIntroSheet(this) &&
UiModeUtilities.isDayUiMode(this)) {
TextSecurePreferences.setHasSeenLightThemeIntroSheet(this)
val bottomSheet = LightThemeFeatureIntroBottomSheet()
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
return
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -259,12 +271,16 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val thread = view.thread ?: return
val bottomSheet = ConversationOptionsBottomSheet()
bottomSheet.recipient = thread.recipient
bottomSheet.onBlockOrUnblockTapped = {
bottomSheet.onBlockTapped = {
bottomSheet.dismiss()
if (!thread.recipient.isBlocked) {
blockConversation(thread)
}
}
bottomSheet.onUnblockTapped = {
bottomSheet.dismiss()
if (thread.recipient.isBlocked) {
unblockConversation(thread)
} else {
blockConversation(thread)
}
}
bottomSheet.onDeleteTapped = {

View File

@@ -116,6 +116,7 @@ class PNModeActivity : BaseActionBarActivity() {
TextSecurePreferences.setPromptedPushRegistration(this, true)
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this)
TextSecurePreferences.setHasSeenLightThemeIntroSheet(this)
val application = ApplicationContext.getInstance(this)
application.setUpStorageAPIIfNeeded()
application.setUpP2PAPIIfNeeded()

View File

@@ -10,7 +10,6 @@ class PrivacySettingsActivity : PassphraseRequiredActionBarActivity() {
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_fragment_wrapper)
supportActionBar!!.title = resources.getString(R.string.activity_privacy_settings_title)
val fragment = AppProtectionPreferenceFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, fragment)

View File

@@ -10,9 +10,11 @@ import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.ActionMode
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.LinearLayout
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_settings.*
import network.loki.messenger.BuildConfig
@@ -27,12 +29,13 @@ import org.thoughtcrime.securesms.avatar.AvatarSelection
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.dialogs.ChangeUiModeDialog
import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog
import org.thoughtcrime.securesms.loki.dialogs.SeedDialog
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
import org.thoughtcrime.securesms.loki.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.profiles.AvatarHelper
@@ -49,9 +52,11 @@ import java.security.SecureRandom
import java.util.*
class SettingsActivity : PassphraseRequiredActionBarActivity() {
private var displayNameEditActionMode: ActionMode? = null
set(value) { field = value; handleDisplayNameEditActionModeChanged() }
private lateinit var glide: GlideRequests
private var isEditingDisplayName = false
set(value) { field = value; handleIsEditingDisplayNameChanged() }
private var displayNameToBeUploaded: String? = null
private var profilePictureToBeUploaded: ByteArray? = null
private var tempFile: File? = null
@@ -66,18 +71,16 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_settings)
setSupportActionBar(toolbar)
cancelButton.setOnClickListener { cancelEditingDisplayName() }
saveButton.setOnClickListener { saveDisplayName() }
showQRCodeButton.setOnClickListener { showQRCode() }
glide = GlideApp.with(this)
profilePictureView.glide = glide
profilePictureView.publicKey = hexEncodedPublicKey
profilePictureView.isLarge = true
profilePictureView.update()
profilePictureView.setOnClickListener { showEditProfilePictureUI() }
ctnGroupNameSection.setOnClickListener { showEditDisplayNameUI() }
ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) }
btnGroupNameDisplay.text = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey)
publicKeyTextView.text = hexEncodedPublicKey
copyButton.setOnClickListener { copyPublicKey() }
@@ -98,7 +101,32 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.settings_general, menu)
// Update UI mode menu icon.
// It uses three-level selector where each level corresponds to the related UiMode ordinal value.
val uiMode = UiModeUtilities.getUserSelectedUiMode(this)
menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_qr_code -> {
showQRCode()
true
}
R.id.action_change_theme -> {
ChangeUiModeDialog().show(supportFragmentManager, ChangeUiModeDialog.TAG)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
AvatarSelection.REQUEST_CODE_AVATAR -> {
@@ -128,17 +156,16 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
// endregion
// region Updating
private fun handleIsEditingDisplayNameChanged() {
cancelButton.visibility = if (isEditingDisplayName) View.VISIBLE else View.GONE
showQRCodeButton.visibility = if (isEditingDisplayName) View.GONE else View.VISIBLE
saveButton.visibility = if (isEditingDisplayName) View.VISIBLE else View.GONE
private fun handleDisplayNameEditActionModeChanged() {
val isEditingDisplayName = this.displayNameEditActionMode !== null
btnGroupNameDisplay.visibility = if (isEditingDisplayName) View.INVISIBLE else View.VISIBLE
displayNameEditText.visibility = if (isEditingDisplayName) View.VISIBLE else View.INVISIBLE
val titleTextViewLayoutParams = titleTextView.layoutParams as LinearLayout.LayoutParams
titleTextViewLayoutParams.leftMargin = if (isEditingDisplayName) toPx(16, resources) else 0
titleTextView.layoutParams = titleTextViewLayoutParams
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (isEditingDisplayName) {
displayNameEditText.setText(btnGroupNameDisplay.text)
displayNameEditText.selectAll()
displayNameEditText.requestFocus()
inputMethodManager.showSoftInput(displayNameEditText, 0)
} else {
@@ -193,21 +220,24 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
// endregion
// region Interaction
private fun cancelEditingDisplayName() {
isEditingDisplayName = false
}
private fun saveDisplayName() {
/**
* @return true if the update was successful.
*/
private fun saveDisplayName(): Boolean {
val displayName = displayNameEditText.text.toString().trim()
if (displayName.isEmpty()) {
return Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show()
return false
}
if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) {
return Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
return false
}
isEditingDisplayName = false
// isEditingDisplayName = false
displayNameToBeUploaded = displayName
updateProfile(false)
return true
}
private fun showQRCode() {
@@ -219,10 +249,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
tempFile = AvatarSelection.startAvatarSelection(this, false, true)
}
private fun showEditDisplayNameUI() {
isEditingDisplayName = true
}
private fun copyPublicKey() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey)
@@ -266,4 +292,34 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog")
}
// endregion
private inner class DisplayNameEditActionModeCallback: ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.title = getString(R.string.activity_settings_display_name_edit_text_hint)
mode.menuInflater.inflate(R.menu.menu_apply, menu)
this@SettingsActivity.displayNameEditActionMode = mode
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode) {
this@SettingsActivity.displayNameEditActionMode = null
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
when (item.itemId) {
R.id.applyButton -> {
if (this@SettingsActivity.saveDisplayName()) {
mode.finish()
}
return true
}
}
return false;
}
}
}

View File

@@ -5,7 +5,6 @@ import android.os.Handler
import android.util.Log
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.then
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address
@@ -34,6 +33,7 @@ import java.util.*
class PublicChatPoller(private val context: Context, private val group: PublicChat) {
private val handler = Handler()
private var hasStarted = false
private var isPollOngoing = false
public var isCaughtUp = false
// region Convenience
@@ -186,12 +186,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
}
fun processOutgoingMessage(message: PublicChatMessage) {
val messageServerID = message.serverID ?: return
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
if (isDuplicate) { return }
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val dataMessage = getDataMessage(message)
SessionMetaProtocol.dropFromTimestampCacheIfNeeded(dataMessage.timestamp)
SessionMetaProtocol.dropFromTimestampCacheIfNeeded(message.serverTimestamp)
val transcript = SentTranscriptMessage(userHexEncodedPublicKey, message.serverTimestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false))
transcript.messageServerID = messageServerID
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
@@ -212,6 +210,8 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
}
}
}
if (isPollOngoing) { return }
isPollOngoing = true
val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey)
var uniqueDevices = setOf<String>()
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
@@ -249,8 +249,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
}
}
isCaughtUp = true
isPollOngoing = false
}.fail {
Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.")
isPollOngoing = false
}
}

View File

@@ -0,0 +1,34 @@
package org.thoughtcrime.securesms.loki.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.UiMode
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
class ChangeUiModeDialog : DialogFragment() {
companion object {
const val TAG = "ChangeUiModeDialog"
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context = requireContext()
val displayNameList = UiMode.values().map { getString(it.displayNameRes) }.toTypedArray()
val activeUiMode = UiModeUtilities.getUserSelectedUiMode(context)
return AlertDialog.Builder(context)
.setSingleChoiceItems(displayNameList, activeUiMode.ordinal) { _, selectedItemIdx: Int ->
val uiMode = UiMode.values()[selectedItemIdx]
UiModeUtilities.setUserSelectedUiMode(context, uiMode)
dismiss()
requireActivity().recreate()
}
.setTitle(R.string.dialog_ui_mode_title)
.setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
.create()
}
}

View File

@@ -5,14 +5,23 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper
import kotlinx.android.synthetic.main.fragment_closed_group_edit_bottom_sheet.*
import kotlinx.android.synthetic.main.fragment_conversation_bottom_sheet.*
import kotlinx.android.synthetic.main.fragment_device_list_bottom_sheet.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.recipients.Recipient
public class ConversationOptionsBottomSheet : BottomSheetDialogFragment() {
//FIXME AC: Supplying a recipient directly into the field from an activity
// is not the best idea. It doesn't survive configuration change.
// We should be dealing with IDs and all sorts of serializable data instead
// if we want to use dialog fragments properly.
lateinit var recipient: Recipient
var onBlockOrUnblockTapped: (() -> Unit)? = null
var onBlockTapped: (() -> Unit)? = null
var onUnblockTapped: (() -> Unit)? = null
var onDeleteTapped: (() -> Unit)? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -21,15 +30,18 @@ public class ConversationOptionsBottomSheet : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (!this::recipient.isInitialized) {
dismiss()
return
}
if (!recipient.isGroupRecipient && !recipient.isLocalNumber) {
val textID = if (recipient.isBlocked) R.string.RecipientPreferenceActivity_unblock else R.string.RecipientPreferenceActivity_block
blockOrUnblockTextView.setText(textID)
val iconID = if (recipient.isBlocked) R.drawable.ic_check_white_24dp else R.drawable.ic_block_white_24dp
val icon = context!!.resources.getDrawable(iconID, context!!.theme)
blockOrUnblockTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
blockOrUnblockTextView.setOnClickListener { onBlockOrUnblockTapped?.invoke() }
} else {
blockOrUnblockTextView.visibility = View.GONE
unblockTextView.visibility = if (recipient.isBlocked) View.VISIBLE else View.GONE
blockTextView.visibility = if (recipient.isBlocked) View.GONE else View.VISIBLE
blockTextView.setOnClickListener { onBlockTapped?.invoke() }
unblockTextView.setOnClickListener { onUnblockTapped?.invoke() }
}
deleteTextView.setOnClickListener { onDeleteTapped?.invoke() }
}

View File

@@ -0,0 +1,41 @@
package org.thoughtcrime.securesms.loki.dialogs
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_light_theme_feature_intro_bottom_sheet.*
import network.loki.messenger.R
class LightThemeFeatureIntroBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(
R.layout.fragment_light_theme_feature_intro_bottom_sheet,
container,
false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
okButton.setOnClickListener { dismiss() }
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
// Make the bottom sheet be expanded by default.
dialog.setOnShowListener {
val d = dialog as BottomSheetDialog
val bottomSheet = d.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
BottomSheetBehavior.from(bottomSheet!!).setState(BottomSheetBehavior.STATE_EXPANDED);
}
return dialog
}
}

View File

@@ -37,7 +37,7 @@ class MultiDeviceRemovalBottomSheet : BottomSheetDialogFragment() {
}
private val explanation by lazy {
if (TextSecurePreferences.getMasterHexEncodedPublicKey(context!!) != null) {
if (TextSecurePreferences.getMasterHexEncodedPublicKey(requireContext()) != null) {
"Youre seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, weve decided to temporarily disable Sessions multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on $removalDateDescription.\n\nTo read more about this change, visit the Session FAQ at getsession.org/faq."
} else {
"Youre seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, weve decided to temporarily disable Sessions multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on $removalDateDescription.\n\nTo read more about this change, visit the Session FAQ at getsession.org/faq"
@@ -49,7 +49,7 @@ class MultiDeviceRemovalBottomSheet : BottomSheetDialogFragment() {
val removalDateStartIndex = explanation.indexOf(removalDateDescription)
val removalDateEndIndex = removalDateStartIndex + removalDateDescription.count()
result.setSpan(StyleSpan(Typeface.BOLD), removalDateStartIndex, removalDateEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, context!!.theme)), removalDateStartIndex, removalDateEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, requireContext().theme)), removalDateStartIndex, removalDateEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
val link = "getsession.org/faq"
val linkStartIndex = explanation.indexOf(link)
val linkEndIndex = linkStartIndex + link.count()
@@ -59,20 +59,15 @@ class MultiDeviceRemovalBottomSheet : BottomSheetDialogFragment() {
try {
onLinkTapped?.invoke()
} catch (e: Exception) {
Toast.makeText(context!!, R.string.invalid_url, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.invalid_url, Toast.LENGTH_SHORT).show()
}
}
}, linkStartIndex, linkEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result.setSpan(StyleSpan(Typeface.BOLD), linkStartIndex, linkEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, context!!.theme)), linkStartIndex, linkEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, requireContext().theme)), linkStartIndex, linkEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
result
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.SessionBottomSheetDialogTheme)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_multi_device_removal_bottom_sheet, container, false)
}

View File

@@ -12,11 +12,6 @@ class OpenGroupSuggestionBottomSheet : BottomSheetDialogFragment() {
var onJoinTapped: (() -> Unit)? = null
var onDismissTapped: (() -> Unit)? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.SessionBottomSheetDialogTheme)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_open_group_suggestion_bottom_sheet, container, false)
}

View File

@@ -7,7 +7,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.fragment_scan_qr_code_v2.*
import kotlinx.android.synthetic.main.fragment_scan_qr_code.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.qr.ScanListener
import org.thoughtcrime.securesms.qr.ScanningThread
@@ -19,7 +19,7 @@ class ScanQRCodeFragment : Fragment() {
var message: CharSequence = ""
override fun onCreateView(layoutInflater: LayoutInflater, viewGroup: ViewGroup?, bundle: Bundle?): View? {
return layoutInflater.inflate(R.layout.fragment_scan_qr_code_v2, viewGroup, false)
return layoutInflater.inflate(R.layout.fragment_scan_qr_code, viewGroup, false)
}
override fun onViewCreated(view: View, bundle: Bundle?) {

View File

@@ -27,7 +27,7 @@ class ScanQRCodeWrapperFragment : Fragment(), ScanQRCodePlaceholderFragmentDeleg
private fun update() {
val fragment: Fragment
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(requireActivity(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
val scanQRCodeFragment = ScanQRCodeFragment()
scanQRCodeFragment.scanListener = this
scanQRCodeFragment.message = message

View File

@@ -23,8 +23,7 @@ object SessionMetaProtocol {
}
@JvmStatic
fun shouldIgnoreMessage(content: SignalServiceContent): Boolean {
val timestamp = content.timestamp
fun shouldIgnoreMessage(timestamp: Long): Boolean {
val shouldIgnoreMessage = timestamps.contains(timestamp)
timestamps.add(timestamp)
return shouldIgnoreMessage

View File

@@ -0,0 +1,65 @@
package org.thoughtcrime.securesms.loki.utilities
import android.content.Context
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import network.loki.messenger.R
/**
* Day/night UI mode related utilities.
* @see <a href="https://developer.android.com/guide/topics/ui/look-and-feel/darktheme">Official Documentation</a>
*/
object UiModeUtilities {
private const val PREF_KEY_SELECTED_UI_MODE = "SELECTED_UI_MODE"
@JvmStatic
fun setUserSelectedUiMode(context: Context, uiMode: UiMode) {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
prefs.edit()
.putString(PREF_KEY_SELECTED_UI_MODE, uiMode.name)
.apply()
AppCompatDelegate.setDefaultNightMode(uiMode.nightModeValue)
}
@JvmStatic
fun getUserSelectedUiMode(context: Context): UiMode {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val selectedUiModeName = prefs.getString(PREF_KEY_SELECTED_UI_MODE, UiMode.SYSTEM_DEFAULT.name)!!
var selectedUiMode: UiMode
try {
selectedUiMode = UiMode.valueOf(selectedUiModeName)
} catch (e: IllegalArgumentException) {
// Cannot recognize UiMode constant from the given string.
selectedUiMode = UiMode.SYSTEM_DEFAULT
}
return selectedUiMode
}
@JvmStatic
fun setupUiModeToUserSelected(context: Context) {
val selectedUiMode = getUserSelectedUiMode(context)
setUserSelectedUiMode(context, selectedUiMode)
}
/**
* Whether the application UI is in the light mode
* (do not confuse with the user selected UiMode).
*/
@JvmStatic
fun isDayUiMode(context: Context): Boolean {
val uiModeNightBit = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return uiModeNightBit == Configuration.UI_MODE_NIGHT_NO
}
}
enum class UiMode(
@StringRes
val displayNameRes: Int,
val nightModeValue: Int) {
DAY(R.string.dialog_ui_mode_option_day, AppCompatDelegate.MODE_NIGHT_NO),
NIGHT(R.string.dialog_ui_mode_option_night, AppCompatDelegate.MODE_NIGHT_YES),
SYSTEM_DEFAULT(R.string.dialog_ui_mode_option_system_default, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}

View File

@@ -38,7 +38,7 @@ class ConversationView : LinearLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_conversation, null)
addView(contentView)
}

View File

@@ -30,7 +30,7 @@ class DeviceView : LinearLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_device, null)
addView(contentView)
}

View File

@@ -37,7 +37,7 @@ class FakeChatView : ScrollView {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_fake_chat, null)
addView(contentView)
isVerticalScrollBarEnabled = false

View File

@@ -9,21 +9,21 @@ import android.view.LayoutInflater
import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.view_separator.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.util.ThemeUtil
class LabeledSeparatorView : RelativeLayout {
private val path = Path()
private val paint: Paint = {
private val paint: Paint by lazy{
val result = Paint()
result.style = Paint.Style.STROKE
result.color = resources.getColorWithID(R.color.separator, context.theme)
result.color = ThemeUtil.getThemedColor(context, R.attr.dividerHorizontal)
result.strokeWidth = toPx(1, resources).toFloat()
result.isAntiAlias = true
result
}()
}
// region Lifecycle
constructor(context: Context) : super(context) {
@@ -43,7 +43,7 @@ class LabeledSeparatorView : RelativeLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_separator, null)
val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
addView(contentView, layoutParams)
@@ -56,7 +56,7 @@ class LabeledSeparatorView : RelativeLayout {
super.onDraw(c)
val w = width.toFloat()
val h = height.toFloat()
val hMargin = toPx(10, resources).toFloat()
val hMargin = toPx(16, resources).toFloat()
path.reset()
path.moveTo(0.0f, h / 2)
path.lineTo(titleTextView.left - hMargin, h / 2)

View File

@@ -7,19 +7,20 @@ import android.animation.ValueAnimator
import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PointF
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.os.VibrationEffect
import android.os.VibrationEffect.DEFAULT_AMPLITUDE
import android.os.Vibrator
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import android.util.AttributeSet
import android.view.Gravity
import android.view.MotionEvent
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.*
@@ -70,10 +71,19 @@ class NewConversationButtonSetView : RelativeLayout {
result.layoutParams = LayoutParams(size, size)
result.setBackgroundResource(R.drawable.new_conversation_button_background)
val background = result.background as GradientDrawable
val colorID = if (isMain) R.color.accent else R.color.new_conversation_button_collapsed_background
background.color = ColorStateList.valueOf(resources.getColorWithID(colorID, context.theme))
@ColorRes val backgroundColorID = if (isMain)
R.color.accent else
R.color.new_conversation_button_collapsed_background
background.color = ColorStateList.valueOf(resources.getColorWithID(backgroundColorID, context.theme))
result.scaleType = ImageView.ScaleType.CENTER
result.setImageResource(iconID)
result.imageTintList = if (isMain)
// Always use white icon for the main button.
ColorStateList.valueOf(resources.getColorWithID(android.R.color.white, context.theme))
else
ColorStateList.valueOf(resources.getColorWithID(R.color.text, context.theme))
result
}

View File

@@ -44,7 +44,7 @@ class ProfilePictureView : RelativeLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_profile_picture, null)
addView(contentView)
}

View File

@@ -35,7 +35,7 @@ class SeedReminderView : FrameLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_seed_reminder, null)
addView(contentView)
button.setOnClickListener { delegate?.handleSeedReminderViewContinueButtonTapped() }

View File

@@ -39,7 +39,7 @@ class UserView : LinearLayout {
}
private fun setUpViewHierarchy() {
val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_user, null)
addView(contentView)
}
@@ -66,7 +66,7 @@ class UserView : LinearLayout {
profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false
}
actionIndicatorImageView.setImageResource(R.drawable.ic_edit_white_24dp)
actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24)
profilePictureView.glide = glide
profilePictureView.update()
nameTextView.text = user.name ?: "Unknown Contact"

View File

@@ -376,7 +376,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Permissions.with(this)
.request(Manifest.permission.CAMERA)
.ifNecessary()
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_photo_camera_white_48dp)
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
.onAllGranted(() -> {
Camera1Fragment fragment = getOrCreateCameraFragment();

View File

@@ -49,7 +49,6 @@ import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
@@ -129,8 +128,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
@Override
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return ThemeUtil.getThemedInflater(requireActivity(), inflater, R.style.TextSecure_DarkTheme)
.inflate(R.layout.mediasend_fragment, container, false);
return inflater.inflate(R.layout.mediasend_fragment, container, false);
}
@Override
@@ -449,7 +447,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
protected void onPreExecute() {
renderTimer = new Stopwatch("ProcessMedia");
progressTimer = () -> {
dialog = new AlertDialog.Builder(new ContextThemeWrapper(requireContext(), R.style.TextSecure_MediaSendProgressDialog))
dialog = new AlertDialog.Builder(new ContextThemeWrapper(requireContext(), R.style.Theme_TextSecure_Dialog_MediaSendProgress))
.setView(R.layout.progress_dialog)
.setCancelable(false)
.create();

View File

@@ -47,7 +47,7 @@ public class RationaleDialog {
text.setText(message);
return new AlertDialog.Builder(context, R.style.RationaleDialog).setView(view);
return new AlertDialog.Builder(context, R.style.Theme_TextSecure_Dialog_Rationale).setView(view);
}
}

View File

@@ -56,7 +56,7 @@ public class WelcomeActivity extends BaseActionBarActivity {
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
.ifNecessary()
.withRationaleDialog(getString(R.string.activity_landing_permission_dialog_message), R.drawable.ic_folder_white_48dp)
.withRationaleDialog(getString(R.string.activity_landing_permission_dialog_message), R.drawable.ic_baseline_folder_48)
.onAnyResult(() -> {
Intent nextIntent = getIntent().getParcelableExtra("next_intent");

View File

@@ -1,16 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.app.Activity;
import network.loki.messenger.R;
public class DynamicIntroTheme extends DynamicTheme {
@Override
protected int getSelectedTheme(Activity activity) {
String theme = TextSecurePreferences.getTheme(activity);
if (theme.equals("dark")) return R.style.TextSecure_DarkIntroTheme;
return R.style.TextSecure_LightIntroTheme;
}
}

View File

@@ -7,6 +7,6 @@ import network.loki.messenger.R;
public class DynamicNoActionBarTheme extends DynamicTheme {
@Override
protected int getSelectedTheme(Activity activity) {
return R.style.TextSecure_DarkNoActionBar;
return R.style.Theme_TextSecure_DayNight_NoActionBar;
}
}

View File

@@ -1,12 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.app.Activity;
import network.loki.messenger.R;
public class DynamicRegistrationTheme extends DynamicTheme {
@Override
protected int getSelectedTheme(Activity activity) {
return R.style.TextSecure_DarkRegistrationTheme;
}
}

View File

@@ -5,6 +5,12 @@ import android.content.Intent;
import network.loki.messenger.R;
/**
* @deprecated Use one of the Theme.Session.DayNight.*
* (or Theme.TextSecure.DayNight.* for old Signal activities)
* app themes to support dark/light modes.
*/
@Deprecated
public class DynamicTheme {
public static final String DARK = "dark";
@@ -28,7 +34,8 @@ public class DynamicTheme {
}
protected int getSelectedTheme(Activity activity) {
return R.style.TextSecure_DarkTheme;
// For all legacy (Signal) activities we lock on to the Signal dedicated theme.
return R.style.Theme_TextSecure_DayNight;
}
private static final class OverridePendingTransition {

View File

@@ -1316,5 +1316,13 @@ public class TextSecurePreferences {
public static void setHasSeenMultiDeviceRemovalSheet(Context context) {
setBooleanPreference(context, "has_seen_multi_device_removal_sheet", true);
}
public static boolean hasSeenLightThemeIntroSheet(Context context) {
return getBooleanPreference(context, "has_seen_light_theme_intro_sheet", false);
}
public static void setHasSeenLightThemeIntroSheet(Context context) {
setBooleanPreference(context, "has_seen_light_theme_intro_sheet", true);
}
// endregion
}

View File

@@ -4,28 +4,49 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.appcompat.view.ContextThemeWrapper;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import network.loki.messenger.R;
public class ThemeUtil {
private static final String TAG = ThemeUtil.class.getSimpleName();
public static boolean isDarkTheme(@NonNull Context context) {
return getAttribute(context, R.attr.theme_type, "light").equals("dark");
return getAttributeText(context, R.attr.theme_type, "light").equals("dark");
}
@ColorInt
public static int getThemedColor(@NonNull Context context, @AttrRes int attr) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
if (theme.resolveAttribute(attr, typedValue, true)) {
return typedValue.data;
} else {
Log.e(TAG, "Couldn't find a color attribute with id: " + attr);
return Color.RED;
}
}
@DrawableRes
public static int getThemedDrawableResId(@NonNull Context context, @AttrRes int attr) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
if (theme.resolveAttribute(attr, typedValue, true)) {
return typedValue.resourceId;
} else {
Log.e(TAG, "Couldn't find a drawable attribute with id: " + attr);
return 0;
}
return Color.RED;
}
public static LayoutInflater getThemedInflater(@NonNull Context context, @NonNull LayoutInflater inflater, @StyleRes int theme) {
@@ -33,7 +54,7 @@ public class ThemeUtil {
return inflater.cloneInContext(contextThemeWrapper);
}
private static String getAttribute(Context context, int attribute, String defaultValue) {
private static String getAttributeText(Context context, int attribute, String defaultValue) {
TypedValue outValue = new TypedValue();
if (context.getTheme().resolveAttribute(attribute, outValue, true)) {