mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-18 21:38:30 +00:00
New app theming (#913)
* feat: start new app theming feature * feat: add some theming colours * refactor: start refactoring themes and colours to use dynamic attributes * feat: adding more colours and switching over default colours to be theme based instead of hard-coded or day/night specific * refactor: take a look at ocean light and logo colour * feat: global search colours for light and dark ocean * feat: more styling * feat: adding themes to conversation activity and refactoring the base theme to apply over the top of the activity's theme so it retains noActionBar etc * feat: add dynamic accent color * docs: add todo for changing how accent colour is applied * feat: update new theming to use override primary style so that the regular colorAccent attribute can be used in existing layouts * feat: coordinating styles across layouts, fixing up pinned icons and naming for conversation list items * refactor: re-styling layouts to match new themes and attributes. Need to figure out action mode close button * refactor: remove @color/text and replace with ?android:textColorPrimary to override in themes * refactor: add context theme wrapper to bottom sheet dialog that references accent color * fix: input bar bug fix and preference activity themes * refactor: new settings menu options * fix: crash for PNModeActivity.kt refactor: move ordering in seed dialog to match designs, copy changes to match new settings menu * feat: add new appearance settings activity * refactor: title and VM changes * fix: correct override * feat: add theme appearance screen UI features and start VM implementation. re-add legacy theme utils to get default for migration * fix: compile errors and missing themes from emoji features * refactor: remove background shape alteration and old bottom sheet styles, re-add the theme mode attr * feat: appearance screen wired up, just need to refresh theme * feat: add theme state recreation and fix match system settings option * refactor: add bottom margin * feat: explore custom preference category * feat: add the customized session theme for CorrectedPreferenceFragment * feat: replace AppProtectionPreferenceFragment to extend ListSummaryPreferenceFragment * refactor: change drawable style and remove explicit dividers * refactor: remove divider in CorrectedPreferenceFragment * feat: add theme state check on resume, might be jarring currently * feat: add preference divider elements for settings menu * refactor: settings menu redesigns * refactor: change led preference to integer and refactor TextSecurePreferences.kt * feat: add scroll parcel to save/restore hierarchy on restart with appearance changes * feat: add the conversations blocked contacts and refactor preference order and copy * feat: add blocked contacts activity, basic layout and vm * feat: add unblock DB functions and storage protocol, start working on the DB query state flow, might have to just implement recipient on modified listener * feat: add blocked contacts and notif recipient listeners * feat: add recipient db reader * feat: add blocked contact interactions and fix a theming crash for notifications * feat: introduce better equals and hashcode implementations to recipient, replace home diff util content check with hashcode-based comparison * feat: add settings menu vectors * fix: preview compile error * refactor: migrating settings menu to new designs * feat: help menu * refactor: simplify link opening * refactor: remove space * feat: refactor preferences and start theming for light mode options * refactor: fixing dark and light modes with dialogs * refactor: popup dialogs use proper themes now * refactor: alert dialogs and media edit fragments use attribute references * refactor: use input bar button attribute instead color control normal in vector tint * refactor: transparency, dialog fixes, notification fix * refactor: attrs and styles for buttons * fix: use prominent button color on the outline button's border * fix: fix the trash * refactor: remove the appearance * refactor: avatar placeholder generation, chips and element border styles * refactor: use colors instead of style references * refactor: theming changes to match designs and feedback * refactor: the titles are bold and the categories are tertiary coloured now * fix: appearance settings match preferences, search bottom bar uses themed attributes * refactor: increase setting button height * Update clear all data dialog * Update seed dialog * refactor: more qa feedback changes * feat: add new TLs and fa-rIR TLs * Update notification content dialog * Fix message requests clear all button text color * feat: re-add screenshot observer * refactor: make send tint accent color * feat: add unread background differences * fix: change unread count indicator * build: upgrade build numbers * Fix message requests popupmenu background color * fix: crash from attr reference in color attribute * build: upgrade build number * fix: message bubbles, thumbnail backgrounds, search bar visibility with input bar, attachment buttons * fix: tertiary text for keyboard page search view * fix: emoji overflow colour differences * fix: reaction pill dialog background is now correct colour * Add style to reactions tab layout * fix: appearance activity reverting primary color at correct time * fix: show call privacy warning every time instead of just once * fix: gradient background(?) and audio autoplay disable * fix: crash in all media containing documents * fix: reaction dialog heading fixes * Add style to reactions tab layout * fix: remove gradient backgrounds * fix: adding new reaction normal text attribute to try correct the tab layout * fix: ocean dark unread/read colours * build; update build number * build: update build number Co-authored-by: charles <charles@oxen.io>
This commit is contained in:
parent
92075aed32
commit
7a773016da
@ -27,7 +27,7 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
implementation 'com.google.android:flexbox:2.0.1'
|
implementation 'com.google.android:flexbox:2.0.1'
|
||||||
@ -157,8 +157,8 @@ dependencies {
|
|||||||
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 303
|
def canonicalVersionCode = 307
|
||||||
def canonicalVersionName = "1.15.4"
|
def canonicalVersionName = "1.16.0"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
|
@ -140,6 +140,12 @@
|
|||||||
android:name="org.thoughtcrime.securesms.preferences.QRCodeActivity"
|
android:name="org.thoughtcrime.securesms.preferences.QRCodeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
|
<activity
|
||||||
|
android:name="org.thoughtcrime.securesms.preferences.BlockedContactsActivity"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
||||||
|
android:label="@string/blocked_contacts_title"
|
||||||
|
/>
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
||||||
android:label="@string/activity_edit_closed_group_title"
|
android:label="@string/activity_edit_closed_group_title"
|
||||||
@ -160,6 +166,12 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.preferences.ChatSettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.ChatSettingsActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name="org.thoughtcrime.securesms.preferences.HelpSettingsActivity"
|
||||||
|
android:label="@string/activity_help_settings_title"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity android:name="org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity"
|
||||||
|
android:screenOrientation="portrait"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.ShareActivity"
|
android:name="org.thoughtcrime.securesms.ShareActivity"
|
||||||
@ -216,12 +228,12 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity"
|
android:name="org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.TextSecure.DayNight">
|
android:theme="@style/Theme.Session.DayNight">
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.longmessage.LongMessageActivity"
|
android:name="org.thoughtcrime.securesms.longmessage.LongMessageActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.TextSecure.DayNight" />
|
android:theme="@style/Theme.Session.DayNight" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.DatabaseUpgradeActivity"
|
android:name="org.thoughtcrime.securesms.DatabaseUpgradeActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
@ -235,14 +247,14 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.giph.ui.GiphyActivity"
|
android:name="org.thoughtcrime.securesms.giph.ui.GiphyActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:theme="@style/Theme.TextSecure.DayNight.NoActionBar"
|
android:theme="@style/Theme.Session.DayNight.NoActionBar"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.mediasend.MediaSendActivity"
|
android:name="org.thoughtcrime.securesms.mediasend.MediaSendActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.TextSecure.DayNight.NoActionBar"
|
android:theme="@style/Theme.Session.DayNight.NoActionBar"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.MediaPreviewActivity"
|
android:name="org.thoughtcrime.securesms.MediaPreviewActivity"
|
||||||
@ -381,6 +393,10 @@
|
|||||||
android:name="org.thoughtcrime.securesms.database.DatabaseContentProviders$StickerPack"
|
android:name="org.thoughtcrime.securesms.database.DatabaseContentProviders$StickerPack"
|
||||||
android:authorities="network.loki.securesms.database.stickerpack"
|
android:authorities="network.loki.securesms.database.stickerpack"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<provider
|
||||||
|
android:name="org.thoughtcrime.securesms.database.DatabaseContentProviders$Recipient"
|
||||||
|
android:authorities="network.loki.securesms.database.recipient"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver android:name="org.thoughtcrime.securesms.service.BootReceiver">
|
<receiver android:name="org.thoughtcrime.securesms.service.BootReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -85,7 +85,6 @@ import org.thoughtcrime.securesms.sskenvironment.ProfileManager;
|
|||||||
import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager;
|
import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager;
|
||||||
import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository;
|
import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository;
|
||||||
import org.thoughtcrime.securesms.util.Broadcaster;
|
import org.thoughtcrime.securesms.util.Broadcaster;
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
|
||||||
import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper;
|
import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper;
|
||||||
import org.thoughtcrime.securesms.webrtc.CallMessageProcessor;
|
import org.thoughtcrime.securesms.webrtc.CallMessageProcessor;
|
||||||
import org.webrtc.PeerConnectionFactory;
|
import org.webrtc.PeerConnectionFactory;
|
||||||
@ -165,6 +164,10 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
return (ApplicationContext) context.getApplicationContext();
|
return (ApplicationContext) context.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextSecurePreferences getPrefs() {
|
||||||
|
return textSecurePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
public DatabaseComponent getDatabaseComponent() {
|
public DatabaseComponent getDatabaseComponent() {
|
||||||
return EntryPoints.get(getApplicationContext(), DatabaseComponent.class);
|
return EntryPoints.get(getApplicationContext(), DatabaseComponent.class);
|
||||||
}
|
}
|
||||||
@ -220,7 +223,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
if (userPublicKey != null) {
|
if (userPublicKey != null) {
|
||||||
registerForFCMIfNeeded(false);
|
registerForFCMIfNeeded(false);
|
||||||
}
|
}
|
||||||
UiModeUtilities.setupUiModeToUserSelected(this);
|
|
||||||
initializeExpiringMessageManager();
|
initializeExpiringMessageManager();
|
||||||
initializeTypingStatusRepository();
|
initializeTypingStatusRepository();
|
||||||
initializeTypingStatusSender();
|
initializeTypingStatusSender();
|
||||||
|
@ -1,44 +1,103 @@
|
|||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.TextSecurePreferences.SELECTED_ACCENT_COLOR;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StyleRes;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||||
|
import org.thoughtcrime.securesms.util.ActivityUtilitiesKt;
|
||||||
|
import org.thoughtcrime.securesms.util.ThemeState;
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public abstract class BaseActionBarActivity extends AppCompatActivity {
|
public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||||
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
|
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
|
||||||
|
private ThemeState currentThemeState;
|
||||||
|
|
||||||
|
private TextSecurePreferences getPreferences() {
|
||||||
|
ApplicationContext appContext = (ApplicationContext) getApplicationContext();
|
||||||
|
return appContext.textSecurePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
public int getDesiredTheme() {
|
||||||
|
ThemeState themeState = ActivityUtilitiesKt.themeState(getPreferences());
|
||||||
|
int userSelectedTheme = themeState.getTheme();
|
||||||
|
if (themeState.getFollowSystem()) {
|
||||||
|
// do light or dark based on the selected theme
|
||||||
|
boolean isDayUi = UiModeUtilities.isDayUiMode(this);
|
||||||
|
if (userSelectedTheme == R.style.Ocean_Dark || userSelectedTheme == R.style.Ocean_Light) {
|
||||||
|
return isDayUi ? R.style.Ocean_Light : R.style.Ocean_Dark;
|
||||||
|
} else {
|
||||||
|
return isDayUi ? R.style.Classic_Light : R.style.Classic_Dark;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return userSelectedTheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@StyleRes @Nullable
|
||||||
|
public Integer getAccentTheme() {
|
||||||
|
if (!getPreferences().hasPreference(SELECTED_ACCENT_COLOR)) return null;
|
||||||
|
ThemeState themeState = ActivityUtilitiesKt.themeState(getPreferences());
|
||||||
|
return themeState.getAccentStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources.Theme getTheme() {
|
||||||
|
// New themes
|
||||||
|
Resources.Theme modifiedTheme = super.getTheme();
|
||||||
|
modifiedTheme.applyStyle(getDesiredTheme(), true);
|
||||||
|
Integer accentTheme = getAccentTheme();
|
||||||
|
if (accentTheme != null) {
|
||||||
|
modifiedTheme.applyStyle(accentTheme, true);
|
||||||
|
}
|
||||||
|
currentThemeState = ActivityUtilitiesKt.themeState(getPreferences());
|
||||||
|
return modifiedTheme;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setHomeButtonEnabled(true);
|
actionBar.setHomeButtonEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
initializeScreenshotSecurity();
|
initializeScreenshotSecurity(true);
|
||||||
DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this));
|
DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this));
|
||||||
String name = getResources().getString(R.string.app_name);
|
String name = getResources().getString(R.string.app_name);
|
||||||
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
|
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
|
||||||
int color = getResources().getColor(R.color.app_icon_background);
|
int color = getResources().getColor(R.color.app_icon_background);
|
||||||
setTaskDescription(new ActivityManager.TaskDescription(name, icon, color));
|
setTaskDescription(new ActivityManager.TaskDescription(name, icon, color));
|
||||||
|
if (!currentThemeState.equals(ActivityUtilitiesKt.themeState(getPreferences()))) {
|
||||||
|
recreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
initializeScreenshotSecurity(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,11 +108,15 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeScreenshotSecurity() {
|
private void initializeScreenshotSecurity(boolean isResume) {
|
||||||
if (TextSecurePreferences.isScreenSecurityEnabled(this)) {
|
if (!isResume) {
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
} else {
|
} else {
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
if (TextSecurePreferences.isScreenSecurityEnabled(this)) {
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
} else {
|
||||||
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.thoughtcrime.securesms.attachments
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.ContentObserver
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
|
||||||
|
class ScreenshotObserver(private val context: Context, handler: Handler, private val screenshotTriggered: ()->Unit): ContentObserver(handler) {
|
||||||
|
|
||||||
|
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||||
|
super.onChange(selfChange, uri)
|
||||||
|
uri ?: return
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
queryRelativeDataColumn(uri)
|
||||||
|
} else {
|
||||||
|
queryDataColumn(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cache = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
private fun queryDataColumn(uri: Uri) {
|
||||||
|
val projection = arrayOf(
|
||||||
|
MediaStore.Images.Media.DATA
|
||||||
|
)
|
||||||
|
context.contentResolver.query(
|
||||||
|
uri,
|
||||||
|
projection,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)?.use { cursor ->
|
||||||
|
val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
val path = cursor.getString(dataColumn)
|
||||||
|
if (path.contains("screenshot", true)) {
|
||||||
|
if (cache.add(uri.hashCode())) {
|
||||||
|
screenshotTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
|
private fun queryRelativeDataColumn(uri: Uri) {
|
||||||
|
val projection = arrayOf(
|
||||||
|
MediaStore.Images.Media.DISPLAY_NAME,
|
||||||
|
MediaStore.Images.Media.RELATIVE_PATH
|
||||||
|
)
|
||||||
|
context.contentResolver.query(
|
||||||
|
uri,
|
||||||
|
projection,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)?.use { cursor ->
|
||||||
|
val relativePathColumn =
|
||||||
|
cursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH)
|
||||||
|
val displayNameColumn =
|
||||||
|
cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
val name = cursor.getString(displayNameColumn)
|
||||||
|
val relativePath = cursor.getString(relativePathColumn)
|
||||||
|
if (name.contains("screenshot", true) or
|
||||||
|
relativePath.contains("screenshot", true)) {
|
||||||
|
if (cache.add(uri.hashCode())) {
|
||||||
|
screenshotTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -107,7 +107,7 @@ class ProfilePictureView @JvmOverloads constructor(
|
|||||||
if (profilePicturesCache.containsKey(publicKey) && profilePicturesCache[publicKey] == recipient.profileAvatar) return
|
if (profilePicturesCache.containsKey(publicKey) && profilePicturesCache[publicKey] == recipient.profileAvatar) return
|
||||||
val signalProfilePicture = recipient.contactPhoto
|
val signalProfilePicture = recipient.contactPhoto
|
||||||
val avatar = (signalProfilePicture as? ProfileContactPhoto)?.avatarObject
|
val avatar = (signalProfilePicture as? ProfileContactPhoto)?.avatarObject
|
||||||
val placeholder = PlaceholderAvatarPhoto(publicKey, displayName ?: "${publicKey.take(4)}...${publicKey.takeLast(4)}")
|
val placeholder = PlaceholderAvatarPhoto(context, publicKey, displayName ?: "${publicKey.take(4)}...${publicKey.takeLast(4)}")
|
||||||
if (signalProfilePicture != null && avatar != "0" && avatar != "") {
|
if (signalProfilePicture != null && avatar != "0" && avatar != "") {
|
||||||
glide.clear(imageView)
|
glide.clear(imageView)
|
||||||
glide.load(signalProfilePicture)
|
glide.load(signalProfilePicture)
|
||||||
|
@ -1,304 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.contacts.Contact;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
import org.session.libsession.utilities.ThemeUtil;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
|
||||||
import org.session.libsession.utilities.recipients.RecipientModifiedListener;
|
|
||||||
import org.thoughtcrime.securesms.database.SessionContactDatabase;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
public class QuoteView extends FrameLayout implements RecipientModifiedListener {
|
|
||||||
|
|
||||||
private static final String TAG = QuoteView.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final int MESSAGE_TYPE_PREVIEW = 0;
|
|
||||||
private static final int MESSAGE_TYPE_OUTGOING = 1;
|
|
||||||
private static final int MESSAGE_TYPE_INCOMING = 2;
|
|
||||||
|
|
||||||
private ViewGroup mainView;
|
|
||||||
private ViewGroup footerView;
|
|
||||||
private TextView authorView;
|
|
||||||
private TextView bodyView;
|
|
||||||
private ImageView quoteBarView;
|
|
||||||
private ImageView thumbnailView;
|
|
||||||
private View attachmentVideoOverlayView;
|
|
||||||
private ViewGroup attachmentContainerView;
|
|
||||||
private TextView attachmentNameView;
|
|
||||||
private ImageView dismissView;
|
|
||||||
|
|
||||||
private long id;
|
|
||||||
private Recipient author;
|
|
||||||
private String body;
|
|
||||||
private Recipient conversationRecipient;
|
|
||||||
private TextView mediaDescriptionText;
|
|
||||||
private TextView missingLinkText;
|
|
||||||
private SlideDeck attachments;
|
|
||||||
private int messageType;
|
|
||||||
private int largeCornerRadius;
|
|
||||||
private int smallCornerRadius;
|
|
||||||
private CornerMask cornerMask;
|
|
||||||
|
|
||||||
|
|
||||||
public QuoteView(Context context) {
|
|
||||||
super(context);
|
|
||||||
initialize(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public QuoteView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
initialize(attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public QuoteView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
initialize(attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
initialize(attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize(@Nullable AttributeSet attrs) {
|
|
||||||
inflate(getContext(), R.layout.quote_view, this);
|
|
||||||
|
|
||||||
this.mainView = findViewById(R.id.quote_main);
|
|
||||||
this.footerView = findViewById(R.id.quote_missing_footer);
|
|
||||||
this.authorView = findViewById(R.id.quote_author);
|
|
||||||
this.bodyView = findViewById(R.id.quote_text);
|
|
||||||
this.quoteBarView = findViewById(R.id.quote_bar);
|
|
||||||
this.thumbnailView = findViewById(R.id.quote_thumbnail);
|
|
||||||
this.attachmentVideoOverlayView = findViewById(R.id.quote_video_overlay);
|
|
||||||
this.attachmentContainerView = findViewById(R.id.quote_attachment_container);
|
|
||||||
this.attachmentNameView = findViewById(R.id.quote_attachment_name);
|
|
||||||
this.dismissView = findViewById(R.id.quote_dismiss);
|
|
||||||
this.mediaDescriptionText = findViewById(R.id.media_type);
|
|
||||||
this.missingLinkText = findViewById(R.id.quote_missing_text);
|
|
||||||
this.largeCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom);
|
|
||||||
this.smallCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom);
|
|
||||||
|
|
||||||
cornerMask = new CornerMask(this);
|
|
||||||
cornerMask.setRadii(largeCornerRadius, largeCornerRadius, smallCornerRadius, smallCornerRadius);
|
|
||||||
|
|
||||||
if (attrs != null) {
|
|
||||||
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.QuoteView, 0, 0);
|
|
||||||
int primaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorPrimary, Color.BLACK);
|
|
||||||
int secondaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorSecondary, Color.BLACK);
|
|
||||||
messageType = typedArray.getInt(R.styleable.QuoteView_message_type, 0);
|
|
||||||
typedArray.recycle();
|
|
||||||
|
|
||||||
dismissView.setVisibility(messageType == MESSAGE_TYPE_PREVIEW ? VISIBLE : GONE);
|
|
||||||
|
|
||||||
authorView.setTextColor(primaryColor);
|
|
||||||
bodyView.setTextColor(primaryColor);
|
|
||||||
attachmentNameView.setTextColor(primaryColor);
|
|
||||||
mediaDescriptionText.setTextColor(secondaryColor);
|
|
||||||
missingLinkText.setTextColor(primaryColor);
|
|
||||||
|
|
||||||
if (messageType == MESSAGE_TYPE_PREVIEW) {
|
|
||||||
int radius = getResources().getDimensionPixelOffset(R.dimen.quote_corner_radius_preview);
|
|
||||||
cornerMask.setTopLeftRadius(radius);
|
|
||||||
cornerMask.setTopRightRadius(radius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dismissView.setOnClickListener(view -> setVisibility(GONE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void dispatchDraw(Canvas canvas) {
|
|
||||||
super.dispatchDraw(canvas);
|
|
||||||
cornerMask.mask(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuote(GlideRequests glideRequests,
|
|
||||||
long id,
|
|
||||||
@NonNull Recipient author,
|
|
||||||
@Nullable String body,
|
|
||||||
boolean originalMissing,
|
|
||||||
@NonNull SlideDeck attachments,
|
|
||||||
@NonNull Recipient conversationRecipient)
|
|
||||||
{
|
|
||||||
if (this.author != null) this.author.removeListener(this);
|
|
||||||
|
|
||||||
this.id = id;
|
|
||||||
this.author = author;
|
|
||||||
this.body = body;
|
|
||||||
this.attachments = attachments;
|
|
||||||
this.conversationRecipient = conversationRecipient;
|
|
||||||
|
|
||||||
author.addListener(this);
|
|
||||||
setQuoteAuthor(author);
|
|
||||||
setQuoteText(body, attachments);
|
|
||||||
setQuoteAttachment(glideRequests, attachments);
|
|
||||||
setQuoteMissingFooter(originalMissing);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTopCornerSizes(boolean topLeftLarge, boolean topRightLarge) {
|
|
||||||
cornerMask.setTopLeftRadius(topLeftLarge ? largeCornerRadius : smallCornerRadius);
|
|
||||||
cornerMask.setTopRightRadius(topRightLarge ? largeCornerRadius : smallCornerRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dismiss() {
|
|
||||||
if (this.author != null) this.author.removeListener(this);
|
|
||||||
|
|
||||||
this.id = 0;
|
|
||||||
this.author = null;
|
|
||||||
this.body = null;
|
|
||||||
|
|
||||||
setVisibility(GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModified(Recipient recipient) {
|
|
||||||
Util.runOnMain(() -> {
|
|
||||||
if (recipient == author) {
|
|
||||||
setQuoteAuthor(recipient);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setQuoteAuthor(@NonNull Recipient author) {
|
|
||||||
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
|
|
||||||
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress().serialize());
|
|
||||||
|
|
||||||
String quoteeDisplayName;
|
|
||||||
|
|
||||||
String senderHexEncodedPublicKey = author.getAddress().serialize();
|
|
||||||
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
|
|
||||||
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
|
|
||||||
} else {
|
|
||||||
SessionContactDatabase contactDB = DatabaseComponent.get(getContext()).sessionContactDatabase();
|
|
||||||
Contact contact = contactDB.getContactWithSessionID(senderHexEncodedPublicKey);
|
|
||||||
if (contact != null) {
|
|
||||||
Contact.ContactContext context = (this.conversationRecipient.isOpenGroupRecipient()) ? Contact.ContactContext.OPEN_GROUP : Contact.ContactContext.REGULAR;
|
|
||||||
quoteeDisplayName = contact.displayName(context);
|
|
||||||
} else {
|
|
||||||
quoteeDisplayName = senderHexEncodedPublicKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName);
|
|
||||||
|
|
||||||
// We use the raw color resource because Android 4.x was struggling with tints here
|
|
||||||
int colorID = UiModeUtilities.isDayUiMode(getContext()) ? R.color.black : R.color.accent;
|
|
||||||
quoteBarView.setImageResource(colorID);
|
|
||||||
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) {
|
|
||||||
if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) {
|
|
||||||
bodyView.setVisibility(VISIBLE);
|
|
||||||
bodyView.setText(body == null ? "" : body);
|
|
||||||
mediaDescriptionText.setVisibility(GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyView.setVisibility(GONE);
|
|
||||||
mediaDescriptionText.setVisibility(VISIBLE);
|
|
||||||
|
|
||||||
List<Slide> audioSlides = Stream.of(attachments.getSlides()).filter(Slide::hasAudio).limit(1).toList();
|
|
||||||
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
|
|
||||||
List<Slide> imageSlides = Stream.of(attachments.getSlides()).filter(Slide::hasImage).limit(1).toList();
|
|
||||||
List<Slide> videoSlides = Stream.of(attachments.getSlides()).filter(Slide::hasVideo).limit(1).toList();
|
|
||||||
|
|
||||||
// Given that most types have images, we specifically check images last
|
|
||||||
if (!audioSlides.isEmpty()) {
|
|
||||||
mediaDescriptionText.setText(R.string.QuoteView_audio);
|
|
||||||
} else if (!documentSlides.isEmpty()) {
|
|
||||||
mediaDescriptionText.setVisibility(GONE);
|
|
||||||
} else if (!videoSlides.isEmpty()) {
|
|
||||||
mediaDescriptionText.setText(R.string.QuoteView_video);
|
|
||||||
} else if (!imageSlides.isEmpty()) {
|
|
||||||
mediaDescriptionText.setText(R.string.QuoteView_photo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) {
|
|
||||||
List<Slide> imageVideoSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo()).limit(1).toList();
|
|
||||||
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
|
|
||||||
|
|
||||||
attachmentVideoOverlayView.setVisibility(GONE);
|
|
||||||
|
|
||||||
if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) {
|
|
||||||
thumbnailView.setVisibility(VISIBLE);
|
|
||||||
attachmentContainerView.setVisibility(GONE);
|
|
||||||
dismissView.setBackgroundResource(R.drawable.dismiss_background);
|
|
||||||
if (imageVideoSlides.get(0).hasVideo()) {
|
|
||||||
attachmentVideoOverlayView.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri()))
|
|
||||||
.centerCrop()
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
|
||||||
.into(thumbnailView);
|
|
||||||
} else if (!documentSlides.isEmpty()){
|
|
||||||
thumbnailView.setVisibility(GONE);
|
|
||||||
attachmentContainerView.setVisibility(VISIBLE);
|
|
||||||
attachmentNameView.setText(documentSlides.get(0).getFileName().or(""));
|
|
||||||
} else {
|
|
||||||
thumbnailView.setVisibility(GONE);
|
|
||||||
attachmentContainerView.setVisibility(GONE);
|
|
||||||
dismissView.setBackgroundDrawable(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ThemeUtil.isDarkTheme(getContext())) {
|
|
||||||
dismissView.setBackgroundResource(R.drawable.circle_alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setQuoteMissingFooter(boolean missing) {
|
|
||||||
footerView.setVisibility(missing ? VISIBLE : GONE);
|
|
||||||
footerView.setBackgroundColor(getResources().getColor(R.color.quote_not_found_background));
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getQuoteId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Recipient getAuthor() {
|
|
||||||
return author;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBody() {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Attachment> getAttachments() {
|
|
||||||
return attachments.asAttachments();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import androidx.preference.CheckBoxPreference;
|
import androidx.preference.CheckBoxPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ public class SwitchPreferenceCompat extends CheckBoxPreference {
|
|||||||
setLayoutRes();
|
setLayoutRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
setLayoutRes();
|
setLayoutRes();
|
||||||
|
@ -68,18 +68,22 @@ class UserView : LinearLayout {
|
|||||||
}
|
}
|
||||||
ActionIndicator.Tick -> {
|
ActionIndicator.Tick -> {
|
||||||
binding.actionIndicatorImageView.visibility = View.VISIBLE
|
binding.actionIndicatorImageView.visibility = View.VISIBLE
|
||||||
binding.actionIndicatorImageView.setImageResource(
|
if (isSelected) {
|
||||||
if (isSelected) R.drawable.ic_circle_check else R.drawable.ic_circle
|
binding.actionIndicatorImageView.setImageResource(R.drawable.padded_circle_accent)
|
||||||
)
|
} else {
|
||||||
|
binding.actionIndicatorImageView.setImageDrawable(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleCheckbox(isSelected: Boolean = false) {
|
fun toggleCheckbox(isSelected: Boolean = false) {
|
||||||
binding.actionIndicatorImageView.visibility = View.VISIBLE
|
binding.actionIndicatorImageView.visibility = View.VISIBLE
|
||||||
binding.actionIndicatorImageView.setImageResource(
|
if (isSelected) {
|
||||||
if (isSelected) R.drawable.ic_circle_check else R.drawable.ic_circle
|
binding.actionIndicatorImageView.setImageResource(R.drawable.padded_circle_accent)
|
||||||
)
|
} else {
|
||||||
|
binding.actionIndicatorImageView.setImageDrawable(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind() {
|
fun unbind() {
|
||||||
|
@ -41,7 +41,7 @@ class NewConversationFragment : BottomSheetDialogFragment(), NewConversationDele
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val dialog = BottomSheetDialog(requireContext(), theme)
|
val dialog = BottomSheetDialog(requireContext(), R.style.Theme_Session_BottomSheet)
|
||||||
dialog.setOnShowListener {
|
dialog.setOnShowListener {
|
||||||
val bottomSheetDialog = it as BottomSheetDialog
|
val bottomSheetDialog = it as BottomSheetDialog
|
||||||
val parentLayout =
|
val parentLayout =
|
||||||
|
@ -4,8 +4,11 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.FragmentNewConversationHomeBinding
|
import network.loki.messenger.databinding.FragmentNewConversationHomeBinding
|
||||||
@ -57,5 +60,11 @@ class NewConversationHomeFragment : Fragment() {
|
|||||||
contactGroups.remove(unknownSectionTitle)?.let { contactGroups.put(unknownSectionTitle, it) }
|
contactGroups.remove(unknownSectionTitle)?.let { contactGroups.put(unknownSectionTitle, it) }
|
||||||
adapter.items = contactGroups.flatMap { entry -> listOf(ContactListItem.Header(entry.key)) + entry.value }
|
adapter.items = contactGroups.flatMap { entry -> listOf(ContactListItem.Header(entry.key)) + entry.value }
|
||||||
binding.contactsRecyclerView.adapter = adapter
|
binding.contactsRecyclerView.adapter = adapter
|
||||||
|
val divider = ContextCompat.getDrawable(requireActivity(), R.drawable.conversation_menu_divider)!!.let {
|
||||||
|
DividerItemDecoration(requireActivity(), RecyclerView.VERTICAL).apply {
|
||||||
|
setDrawable(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.contactsRecyclerView.addItemDecoration(divider)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
@ -85,6 +86,7 @@ import org.session.libsignal.utilities.hexEncodedPrivateKey
|
|||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.ExpirationDialog
|
import org.thoughtcrime.securesms.ExpirationDialog
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.attachments.ScreenshotObserver
|
||||||
import org.thoughtcrime.securesms.audio.AudioRecorder
|
import org.thoughtcrime.securesms.audio.AudioRecorder
|
||||||
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
|
||||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
|
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
|
||||||
@ -191,6 +193,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
@Inject lateinit var reactionDb: ReactionDatabase
|
@Inject lateinit var reactionDb: ReactionDatabase
|
||||||
@Inject lateinit var viewModelFactory: ConversationViewModel.AssistedFactory
|
@Inject lateinit var viewModelFactory: ConversationViewModel.AssistedFactory
|
||||||
|
|
||||||
|
private val screenshotObserver by lazy {
|
||||||
|
ScreenshotObserver(this, Handler(Looper.getMainLooper())) {
|
||||||
|
// post screenshot message
|
||||||
|
sendScreenshotNotification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||||
private val linkPreviewViewModel: LinkPreviewViewModel by lazy {
|
private val linkPreviewViewModel: LinkPreviewViewModel by lazy {
|
||||||
ViewModelProvider(this, LinkPreviewViewModel.Factory(LinkPreviewRepository()))
|
ViewModelProvider(this, LinkPreviewViewModel.Factory(LinkPreviewRepository()))
|
||||||
@ -380,11 +389,17 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(viewModel.threadId)
|
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(viewModel.threadId)
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
threadDb.markAllAsRead(viewModel.threadId, recipient.isOpenGroupRecipient)
|
threadDb.markAllAsRead(viewModel.threadId, recipient.isOpenGroupRecipient)
|
||||||
|
contentResolver.registerContentObserver(
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
true,
|
||||||
|
screenshotObserver
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1)
|
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1)
|
||||||
|
contentResolver.unregisterContentObserver(screenshotObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSystemService(name: String): Any? {
|
override fun getSystemService(name: String): Any? {
|
||||||
@ -923,10 +938,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
} else if (recipient.isGroupRecipient) {
|
} else if (recipient.isGroupRecipient) {
|
||||||
viewModel.openGroup?.let { openGroup ->
|
viewModel.openGroup?.let { openGroup ->
|
||||||
val userCount = lokiApiDb.getUserCount(openGroup.room, openGroup.server) ?: 0
|
val userCount = lokiApiDb.getUserCount(openGroup.room, openGroup.server) ?: 0
|
||||||
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_member_count, userCount)
|
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_active_member_count, userCount)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
actionBarBinding.conversationSubtitleView.isVisible = false
|
val userCount = groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size
|
||||||
|
actionBarBinding.conversationSubtitleView.text = getString(R.string.ConversationActivity_member_count, userCount)
|
||||||
}
|
}
|
||||||
|
viewModel
|
||||||
} else {
|
} else {
|
||||||
actionBarBinding.conversationSubtitleView.isVisible = false
|
actionBarBinding.conversationSubtitleView.isVisible = false
|
||||||
}
|
}
|
||||||
@ -1317,6 +1334,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun playVoiceMessageAtIndexIfPossible(indexInAdapter: Int) {
|
override fun playVoiceMessageAtIndexIfPossible(indexInAdapter: Int) {
|
||||||
|
if (!textSecurePreferences.autoplayAudioMessages()) return
|
||||||
|
|
||||||
if (indexInAdapter < 0 || indexInAdapter >= adapter.itemCount) { return }
|
if (indexInAdapter < 0 || indexInAdapter >= adapter.itemCount) { return }
|
||||||
val viewHolder = binding?.conversationRecyclerView?.findViewHolderForAdapterPosition(indexInAdapter) as? ConversationAdapter.VisibleMessageViewHolder ?: return
|
val viewHolder = binding?.conversationRecyclerView?.findViewHolderForAdapterPosition(indexInAdapter) as? ConversationAdapter.VisibleMessageViewHolder ?: return
|
||||||
val visibleMessageView = ViewVisibleMessageBinding.bind(viewHolder.view).visibleMessageView
|
val visibleMessageView = ViewVisibleMessageBinding.bind(viewHolder.view).visibleMessageView
|
||||||
@ -1770,6 +1789,14 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
endActionMode()
|
endActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendScreenshotNotification() {
|
||||||
|
val recipient = viewModel.recipient ?: return
|
||||||
|
if (recipient.isGroupRecipient) return
|
||||||
|
val kind = DataExtractionNotification.Kind.Screenshot()
|
||||||
|
val message = DataExtractionNotification(kind)
|
||||||
|
MessageSender.send(message, recipient.address)
|
||||||
|
}
|
||||||
|
|
||||||
private fun sendMediaSavedNotification() {
|
private fun sendMediaSavedNotification() {
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
if (recipient.isGroupRecipient) { return }
|
if (recipient.isGroupRecipient) { return }
|
||||||
@ -1819,7 +1846,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
searchViewModel.onSearchOpened()
|
searchViewModel.onSearchOpened()
|
||||||
binding?.searchBottomBar?.visibility = View.VISIBLE
|
binding?.searchBottomBar?.visibility = View.VISIBLE
|
||||||
binding?.searchBottomBar?.setData(0, 0)
|
binding?.searchBottomBar?.setData(0, 0)
|
||||||
binding?.inputBar?.visibility = View.GONE
|
binding?.inputBar?.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSearchClosed() {
|
fun onSearchClosed() {
|
||||||
@ -1873,6 +1900,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
ConversationReactionOverlay.Action.DELETE -> deleteMessages(selectedItems)
|
ConversationReactionOverlay.Action.DELETE -> deleteMessages(selectedItems)
|
||||||
ConversationReactionOverlay.Action.BAN_AND_DELETE_ALL -> banAndDeleteAll(selectedItems)
|
ConversationReactionOverlay.Action.BAN_AND_DELETE_ALL -> banAndDeleteAll(selectedItems)
|
||||||
ConversationReactionOverlay.Action.BAN_USER -> banUser(selectedItems)
|
ConversationReactionOverlay.Action.BAN_USER -> banUser(selectedItems)
|
||||||
|
ConversationReactionOverlay.Action.COPY_SESSION_ID -> TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,12 @@ import android.net.Uri
|
|||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewInputBarBinding
|
import network.loki.messenger.databinding.ViewInputBarBinding
|
||||||
@ -27,7 +30,8 @@ import org.thoughtcrime.securesms.util.contains
|
|||||||
import org.thoughtcrime.securesms.util.toDp
|
import org.thoughtcrime.securesms.util.toDp
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
|
||||||
class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, LinkPreviewDraftViewDelegate {
|
class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, LinkPreviewDraftViewDelegate,
|
||||||
|
TextView.OnEditorActionListener {
|
||||||
private lateinit var binding: ViewInputBarBinding
|
private lateinit var binding: ViewInputBarBinding
|
||||||
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||||
private val vMargin by lazy { toDp(4, resources) }
|
private val vMargin by lazy { toDp(4, resources) }
|
||||||
@ -85,11 +89,31 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Edit text
|
// Edit text
|
||||||
|
binding.inputBarEditText.setOnEditorActionListener(this)
|
||||||
|
if (TextSecurePreferences.isEnterSendsEnabled(context)) {
|
||||||
|
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_SEND
|
||||||
|
binding.inputBarEditText.inputType =
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||||
|
} else {
|
||||||
|
binding.inputBarEditText.imeOptions = EditorInfo.IME_ACTION_NONE
|
||||||
|
binding.inputBarEditText.inputType =
|
||||||
|
binding.inputBarEditText.inputType or
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||||
|
}
|
||||||
val incognitoFlag = if (TextSecurePreferences.isIncognitoKeyboardEnabled(context)) 16777216 else 0
|
val incognitoFlag = if (TextSecurePreferences.isIncognitoKeyboardEnabled(context)) 16777216 else 0
|
||||||
binding.inputBarEditText.imeOptions = binding.inputBarEditText.imeOptions or incognitoFlag // Always use incognito keyboard if setting enabled
|
binding.inputBarEditText.imeOptions = binding.inputBarEditText.imeOptions or incognitoFlag // Always use incognito keyboard if setting enabled
|
||||||
binding.inputBarEditText.inputType = binding.inputBarEditText.inputType or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
||||||
binding.inputBarEditText.delegate = this
|
binding.inputBarEditText.delegate = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
|
||||||
|
if (v === binding.inputBarEditText && actionId == EditorInfo.IME_ACTION_SEND) {
|
||||||
|
// same as pressing send button
|
||||||
|
delegate?.sendMessage()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Updating
|
// region Updating
|
||||||
|
@ -16,8 +16,13 @@ import android.widget.ImageView
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.util.*
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import java.util.*
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
|
import org.thoughtcrime.securesms.util.InputBarButtonImageViewContainer
|
||||||
|
import org.thoughtcrime.securesms.util.animateSizeChange
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class InputBarButton : RelativeLayout {
|
class InputBarButton : RelativeLayout {
|
||||||
private val gestureHandler = Handler(Looper.getMainLooper())
|
private val gestureHandler = Handler(Looper.getMainLooper())
|
||||||
@ -43,11 +48,11 @@ class InputBarButton : RelativeLayout {
|
|||||||
private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) }
|
private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) }
|
||||||
private val colorID by lazy {
|
private val colorID by lazy {
|
||||||
if (hasOpaqueBackground) {
|
if (hasOpaqueBackground) {
|
||||||
R.color.input_bar_button_background_opaque
|
R.attr.input_bar_button_background_opaque
|
||||||
} else if (isSendButton) {
|
} else if (isSendButton) {
|
||||||
R.color.accent
|
R.attr.colorAccent
|
||||||
} else {
|
} else {
|
||||||
R.color.input_bar_button_background
|
R.attr.input_bar_button_background
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +64,9 @@ class InputBarButton : RelativeLayout {
|
|||||||
val size = collapsedSize.toInt()
|
val size = collapsedSize.toInt()
|
||||||
result.layoutParams = LayoutParams(size, size)
|
result.layoutParams = LayoutParams(size, size)
|
||||||
result.setBackgroundResource(R.drawable.input_bar_button_background)
|
result.setBackgroundResource(R.drawable.input_bar_button_background)
|
||||||
result.mainColor = resources.getColorWithID(colorID, context.theme)
|
result.mainColor = context.getColorFromAttr(colorID)
|
||||||
if (hasOpaqueBackground) {
|
if (hasOpaqueBackground) {
|
||||||
result.strokeColor = resources.getColorWithID(R.color.input_bar_button_background_opaque_border, context.theme)
|
result.strokeColor = context.getColorFromAttr(R.attr.input_bar_button_background_opaque_border)
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -72,8 +77,7 @@ class InputBarButton : RelativeLayout {
|
|||||||
result.layoutParams = LayoutParams(size, size)
|
result.layoutParams = LayoutParams(size, size)
|
||||||
result.scaleType = ImageView.ScaleType.CENTER_INSIDE
|
result.scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||||
result.setImageResource(iconID)
|
result.setImageResource(iconID)
|
||||||
val colorID = if (isSendButton) R.color.black else R.color.text
|
result.imageTintList = ColorStateList.valueOf(context.getColorFromAttr(R.attr.input_bar_button_text_color))
|
||||||
result.imageTintList = ColorStateList.valueOf(resources.getColorWithID(colorID, context.theme))
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +108,18 @@ class InputBarButton : RelativeLayout {
|
|||||||
fun getIconID() = iconID
|
fun getIconID() = iconID
|
||||||
|
|
||||||
fun expand() {
|
fun expand() {
|
||||||
GlowViewUtilities.animateColorChange(context, imageViewContainer, colorID, R.color.accent)
|
val fromColor = context.getColorFromAttr(colorID)
|
||||||
|
val toColor = context.getAccentColor()
|
||||||
|
GlowViewUtilities.animateColorChange(imageViewContainer, fromColor, toColor)
|
||||||
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_collapsed_size, R.dimen.input_bar_button_expanded_size, animationDuration)
|
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_collapsed_size, R.dimen.input_bar_button_expanded_size, animationDuration)
|
||||||
animateImageViewContainerPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
|
animateImageViewContainerPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collapse() {
|
fun collapse() {
|
||||||
GlowViewUtilities.animateColorChange(context, imageViewContainer, R.color.accent, colorID)
|
val fromColor = context.getAccentColor()
|
||||||
|
val toColor = context.getColorFromAttr(colorID)
|
||||||
|
|
||||||
|
GlowViewUtilities.animateColorChange(imageViewContainer, fromColor, toColor)
|
||||||
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_expanded_size, R.dimen.input_bar_button_collapsed_size, animationDuration)
|
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_expanded_size, R.dimen.input_bar_button_collapsed_size, animationDuration)
|
||||||
animateImageViewContainerPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
|
animateImageViewContainerPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||||
import androidx.core.content.pm.ShortcutInfoCompat
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
@ -27,6 +28,7 @@ import org.session.libsession.messaging.sending_receiving.leave
|
|||||||
import org.session.libsession.utilities.ExpirationUtil
|
import org.session.libsession.utilities.ExpirationUtil
|
||||||
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
|
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.session.libsignal.utilities.toHexString
|
||||||
@ -43,7 +45,6 @@ import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.group
|
|||||||
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
object ConversationMenuHelper {
|
object ConversationMenuHelper {
|
||||||
@ -69,7 +70,7 @@ object ConversationMenuHelper {
|
|||||||
val actionView = item.actionView
|
val actionView = item.actionView
|
||||||
val iconView = actionView.findViewById<ImageView>(R.id.menu_badge_icon)
|
val iconView = actionView.findViewById<ImageView>(R.id.menu_badge_icon)
|
||||||
val badgeView = actionView.findViewById<TextView>(R.id.expiration_badge)
|
val badgeView = actionView.findViewById<TextView>(R.id.expiration_badge)
|
||||||
@ColorInt val color = context.resources.getColorWithID(R.color.text, context.theme)
|
@ColorInt val color = context.getColorFromAttr(android.R.attr.textColorPrimary)
|
||||||
iconView.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)
|
iconView.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)
|
||||||
badgeView.text = ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, thread.expireMessages)
|
badgeView.text = ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, thread.expireMessages)
|
||||||
actionView.setOnClickListener { onOptionsItemSelected(item) }
|
actionView.setOnClickListener { onOptionsItemSelected(item) }
|
||||||
@ -328,7 +329,7 @@ object ConversationMenuHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun mute(context: Context, thread: Recipient) {
|
private fun mute(context: Context, thread: Recipient) {
|
||||||
MuteDialog.show(context) { until: Long ->
|
MuteDialog.show(ContextThemeWrapper(context, context.theme)) { until: Long ->
|
||||||
DatabaseComponent.get(context).recipientDatabase().setMuted(thread, until)
|
DatabaseComponent.get(context).recipientDatabase().setMuted(thread, until)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import com.google.android.flexbox.FlexboxLayout;
|
|||||||
import com.google.android.flexbox.JustifyContent;
|
import com.google.android.flexbox.JustifyContent;
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
|
import org.session.libsession.utilities.ThemeUtil;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
|
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ViewUtil;
|
import org.thoughtcrime.securesms.conversation.v2.ViewUtil;
|
||||||
@ -126,14 +127,17 @@ public class EmojiReactionsView extends LinearLayout implements View.OnTouchList
|
|||||||
int innerPadding = ViewUtil.dpToPx(4);
|
int innerPadding = ViewUtil.dpToPx(4);
|
||||||
overflowContainer.setPaddingRelative(innerPadding,innerPadding,innerPadding,innerPadding);
|
overflowContainer.setPaddingRelative(innerPadding,innerPadding,innerPadding,innerPadding);
|
||||||
|
|
||||||
|
int pixelSize = ViewUtil.dpToPx(1);
|
||||||
|
|
||||||
for (Reaction reaction : reactions) {
|
for (Reaction reaction : reactions) {
|
||||||
if (container.getChildCount() + 1 >= DEFAULT_THRESHOLD && threshold != Integer.MAX_VALUE && reactions.size() > threshold) {
|
if (container.getChildCount() + 1 >= DEFAULT_THRESHOLD && threshold != Integer.MAX_VALUE && reactions.size() > threshold) {
|
||||||
if (overflowContainer.getParent() == null) {
|
if (overflowContainer.getParent() == null) {
|
||||||
container.addView(overflowContainer);
|
container.addView(overflowContainer);
|
||||||
ViewGroup.LayoutParams overflowParams = overflowContainer.getLayoutParams();
|
MarginLayoutParams overflowParams = (MarginLayoutParams) overflowContainer.getLayoutParams();
|
||||||
overflowParams.height = ViewUtil.dpToPx(26);
|
overflowParams.height = ViewUtil.dpToPx(26);
|
||||||
|
overflowParams.setMargins(pixelSize, pixelSize, pixelSize, pixelSize);
|
||||||
overflowContainer.setLayoutParams(overflowParams);
|
overflowContainer.setLayoutParams(overflowParams);
|
||||||
overflowContainer.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.reaction_pill_dialog_background));
|
overflowContainer.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.reaction_pill_background));
|
||||||
}
|
}
|
||||||
View pill = buildPill(getContext(), this, reaction, true);
|
View pill = buildPill(getContext(), this, reaction, true);
|
||||||
pill.setOnClickListener(v -> {
|
pill.setOnClickListener(v -> {
|
||||||
@ -147,11 +151,10 @@ public class EmojiReactionsView extends LinearLayout implements View.OnTouchList
|
|||||||
View pill = buildPill(getContext(), this, reaction, false);
|
View pill = buildPill(getContext(), this, reaction, false);
|
||||||
pill.setTag(reaction);
|
pill.setTag(reaction);
|
||||||
pill.setOnTouchListener(this);
|
pill.setOnTouchListener(this);
|
||||||
container.addView(pill);
|
|
||||||
int pixelSize = ViewUtil.dpToPx(1);
|
|
||||||
MarginLayoutParams params = (MarginLayoutParams) pill.getLayoutParams();
|
MarginLayoutParams params = (MarginLayoutParams) pill.getLayoutParams();
|
||||||
params.setMargins(pixelSize, 0, pixelSize, 0);
|
params.setMargins(pixelSize, pixelSize, pixelSize, pixelSize);
|
||||||
pill.setLayoutParams(params);
|
pill.setLayoutParams(params);
|
||||||
|
container.addView(pill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +249,7 @@ public class EmojiReactionsView extends LinearLayout implements View.OnTouchList
|
|||||||
|
|
||||||
if (reaction.userWasSender && !isCompact) {
|
if (reaction.userWasSender && !isCompact) {
|
||||||
root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected));
|
root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected));
|
||||||
countView.setTextColor(ContextCompat.getColor(context, R.color.reactions_pill_selected_text_color));
|
countView.setTextColor(ThemeUtil.getThemedColor(context, R.attr.reactionsPillSelectedTextColor));
|
||||||
} else {
|
} else {
|
||||||
if (!isCompact) {
|
if (!isCompact) {
|
||||||
root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background));
|
root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background));
|
||||||
|
@ -12,6 +12,7 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewLinkPreviewBinding
|
import network.loki.messenger.databinding.ViewLinkPreviewBinding
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.thoughtcrime.securesms.components.CornerMask
|
import org.thoughtcrime.securesms.components.CornerMask
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
||||||
@ -52,14 +53,13 @@ class LinkPreviewView : LinearLayout {
|
|||||||
}
|
}
|
||||||
// Title
|
// Title
|
||||||
binding.titleTextView.text = linkPreview.title
|
binding.titleTextView.text = linkPreview.title
|
||||||
val textColorID = if (message.isOutgoing && UiModeUtilities.isDayUiMode(context)) {
|
val textColorID = if (message.isOutgoing) {
|
||||||
R.color.white
|
R.attr.message_sent_text_color
|
||||||
} else {
|
} else {
|
||||||
if (UiModeUtilities.isDayUiMode(context)) R.color.black else R.color.white
|
R.attr.message_received_text_color
|
||||||
}
|
}
|
||||||
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
binding.titleTextView.setTextColor(context.getColorFromAttr(textColorID))
|
||||||
// Body
|
// Body
|
||||||
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
|
||||||
// Corner radii
|
// Corner radii
|
||||||
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
||||||
cornerMask.setTopLeftRadius(cornerRadii[0])
|
cornerMask.setTopLeftRadius(cornerRadii[0])
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.messages
|
package org.thoughtcrime.securesms.conversation.v2.messages
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewOpenGroupInvitationBinding
|
import network.loki.messenger.databinding.ViewOpenGroupInvitationBinding
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageData
|
import org.session.libsession.messaging.utilities.UpdateMessageData
|
||||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
import org.session.libsession.utilities.OpenGroupUrlParser
|
||||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.JoinOpenGroupDialog
|
import org.thoughtcrime.securesms.conversation.v2.dialogs.JoinOpenGroupDialog
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
|
|
||||||
class OpenGroupInvitationView : LinearLayout {
|
class OpenGroupInvitationView : LinearLayout {
|
||||||
private val binding: ViewOpenGroupInvitationBinding by lazy { ViewOpenGroupInvitationBinding.bind(this) }
|
private val binding: ViewOpenGroupInvitationBinding by lazy { ViewOpenGroupInvitationBinding.bind(this) }
|
||||||
@ -26,8 +29,11 @@ class OpenGroupInvitationView : LinearLayout {
|
|||||||
val data = umd.kind as UpdateMessageData.Kind.OpenGroupInvitation
|
val data = umd.kind as UpdateMessageData.Kind.OpenGroupInvitation
|
||||||
this.data = data
|
this.data = data
|
||||||
val iconID = if (message.isOutgoing) R.drawable.ic_globe else R.drawable.ic_plus
|
val iconID = if (message.isOutgoing) R.drawable.ic_globe else R.drawable.ic_plus
|
||||||
|
val backgroundColor = if (!message.isOutgoing) context.getAccentColor()
|
||||||
|
else ContextCompat.getColor(context, R.color.transparent_black_6)
|
||||||
with(binding){
|
with(binding){
|
||||||
openGroupInvitationIconImageView.setImageResource(iconID)
|
openGroupInvitationIconImageView.setImageResource(iconID)
|
||||||
|
openGroupInvitationIconBackground.backgroundTintList = ColorStateList.valueOf(backgroundColor)
|
||||||
openGroupTitleTextView.text = data.groupName
|
openGroupTitleTextView.text = data.groupName
|
||||||
openGroupURLTextView.text = OpenGroupUrlParser.trimQueryParameter(data.groupUrl)
|
openGroupURLTextView.text = OpenGroupUrlParser.trimQueryParameter(data.groupUrl)
|
||||||
openGroupTitleTextView.setTextColor(textColor)
|
openGroupTitleTextView.setTextColor(textColor)
|
||||||
|
@ -14,13 +14,14 @@ import network.loki.messenger.R
|
|||||||
import network.loki.messenger.databinding.ViewQuoteBinding
|
import network.loki.messenger.databinding.ViewQuoteBinding
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck
|
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -89,8 +90,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
binding.quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
|
binding.quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
|
||||||
} else if (attachments != null) {
|
} else if (attachments != null) {
|
||||||
binding.quoteViewAttachmentPreviewImageView.imageTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources, R.color.white, context.theme))
|
binding.quoteViewAttachmentPreviewImageView.imageTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources, R.color.white, context.theme))
|
||||||
val backgroundColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.black else R.color.accent
|
val backgroundColor = context.getAccentColor()
|
||||||
val backgroundColor = ResourcesCompat.getColor(resources, backgroundColorID, context.theme)
|
|
||||||
binding.quoteViewAttachmentPreviewContainer.backgroundTintList = ColorStateList.valueOf(backgroundColor)
|
binding.quoteViewAttachmentPreviewContainer.backgroundTintList = ColorStateList.valueOf(backgroundColor)
|
||||||
binding.quoteViewAttachmentPreviewImageView.isVisible = false
|
binding.quoteViewAttachmentPreviewImageView.isVisible = false
|
||||||
binding.quoteViewAttachmentThumbnailImageView.isVisible = false
|
binding.quoteViewAttachmentThumbnailImageView.isVisible = false
|
||||||
@ -120,31 +120,19 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
|
|
||||||
// region Convenience
|
// region Convenience
|
||||||
@ColorInt private fun getLineColor(isOutgoingMessage: Boolean): Int {
|
@ColorInt private fun getLineColor(isOutgoingMessage: Boolean): Int {
|
||||||
val isLightMode = UiModeUtilities.isDayUiMode(context)
|
|
||||||
return when {
|
return when {
|
||||||
mode == Mode.Regular && isLightMode || mode == Mode.Draft && isLightMode -> {
|
mode == Mode.Regular && !isOutgoingMessage -> context.getColorFromAttr(R.attr.colorAccent)
|
||||||
ResourcesCompat.getColor(resources, R.color.black, context.theme)
|
mode == Mode.Regular -> context.getColorFromAttr(R.attr.message_sent_text_color)
|
||||||
}
|
else -> context.getColorFromAttr(R.attr.colorAccent)
|
||||||
mode == Mode.Regular && !isLightMode -> {
|
|
||||||
if (isOutgoingMessage) {
|
|
||||||
ResourcesCompat.getColor(resources, R.color.black, context.theme)
|
|
||||||
} else {
|
|
||||||
ResourcesCompat.getColor(resources, R.color.accent, context.theme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> { // Draft & dark mode
|
|
||||||
ResourcesCompat.getColor(resources, R.color.accent, context.theme)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt private fun getTextColor(isOutgoingMessage: Boolean): Int {
|
@ColorInt private fun getTextColor(isOutgoingMessage: Boolean): Int {
|
||||||
if (mode == Mode.Draft) { return ResourcesCompat.getColor(resources, R.color.text, context.theme) }
|
if (mode == Mode.Draft) { return context.getColorFromAttr(android.R.attr.textColorPrimary) }
|
||||||
val isLightMode = UiModeUtilities.isDayUiMode(context)
|
return if (!isOutgoingMessage) {
|
||||||
return if (!isOutgoingMessage && !isLightMode) {
|
context.getColorFromAttr(R.attr.message_received_text_color)
|
||||||
ResourcesCompat.getColor(resources, R.color.white, context.theme)
|
|
||||||
} else {
|
} else {
|
||||||
ResourcesCompat.getColor(resources, R.color.black, context.theme)
|
context.getColorFromAttr(R.attr.message_sent_text_color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import android.view.MotionEvent
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
@ -32,7 +31,7 @@ import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
|||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
||||||
@ -44,8 +43,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
|||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.SearchUtil
|
import org.thoughtcrime.securesms.util.SearchUtil
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -70,9 +68,9 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
|
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
|
||||||
glide: GlideRequests, thread: Recipient, searchQuery: String?, contactIsTrusted: Boolean) {
|
glide: GlideRequests, thread: Recipient, searchQuery: String?, contactIsTrusted: Boolean) {
|
||||||
// Background
|
// Background
|
||||||
val background = getBackground(message.isOutgoing, isStartOfMessageCluster, isEndOfMessageCluster)
|
val background = getBackground(message.isOutgoing)
|
||||||
val colorID = if (message.isOutgoing) R.attr.message_sent_background_color else R.attr.message_received_background_color
|
val color = if (message.isOutgoing) context.getAccentColor()
|
||||||
val color = ThemeUtil.getThemedColor(context, colorID)
|
else context.getColorFromAttr(R.attr.message_received_background_color)
|
||||||
val filter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_IN)
|
val filter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_IN)
|
||||||
background.colorFilter = filter
|
background.colorFilter = filter
|
||||||
binding.contentParent.background = background
|
binding.contentParent.background = background
|
||||||
@ -237,22 +235,8 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
private fun ViewVisibleMessageContentBinding.barrierViewsGone(): Boolean =
|
private fun ViewVisibleMessageContentBinding.barrierViewsGone(): Boolean =
|
||||||
listOf<View>(albumThumbnailView, linkPreviewView, voiceMessageView.root, quoteView.root).none { it.isVisible }
|
listOf<View>(albumThumbnailView, linkPreviewView, voiceMessageView.root, quoteView.root).none { it.isVisible }
|
||||||
|
|
||||||
private fun getBackground(isOutgoing: Boolean, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean): Drawable {
|
private fun getBackground(isOutgoing: Boolean): Drawable {
|
||||||
val isSingleMessage = (isStartOfMessageCluster && isEndOfMessageCluster)
|
val backgroundID = if (isOutgoing) R.drawable.message_bubble_background_sent_alone else R.drawable.message_bubble_background_received_alone
|
||||||
@DrawableRes val backgroundID = when {
|
|
||||||
isSingleMessage -> {
|
|
||||||
if (isOutgoing) R.drawable.message_bubble_background_sent_alone else R.drawable.message_bubble_background_received_alone
|
|
||||||
}
|
|
||||||
isStartOfMessageCluster -> {
|
|
||||||
if (isOutgoing) R.drawable.message_bubble_background_sent_start else R.drawable.message_bubble_background_received_start
|
|
||||||
}
|
|
||||||
isEndOfMessageCluster -> {
|
|
||||||
if (isOutgoing) R.drawable.message_bubble_background_sent_end else R.drawable.message_bubble_background_received_end
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
if (isOutgoing) R.drawable.message_bubble_background_sent_middle else R.drawable.message_bubble_background_received_middle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ResourcesCompat.getDrawable(resources, backgroundID, context.theme)!!
|
return ResourcesCompat.getDrawable(resources, backgroundID, context.theme)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,13 +291,14 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getTextColor(context: Context, message: MessageRecord): Int {
|
fun getTextColor(context: Context, message: MessageRecord): Int {
|
||||||
val isDayUiMode = UiModeUtilities.isDayUiMode(context)
|
val colorAttribute = if (message.isOutgoing) {
|
||||||
val colorID = if (message.isOutgoing) {
|
// sent
|
||||||
R.color.black
|
R.attr.message_sent_text_color
|
||||||
} else {
|
} else {
|
||||||
if (isDayUiMode) R.color.black else R.color.white
|
// received
|
||||||
|
R.attr.message_received_text_color
|
||||||
}
|
}
|
||||||
return context.resources.getColorWithID(colorID, context.theme)
|
return context.getColorFromAttr(colorAttribute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
@ -16,7 +16,6 @@ import android.widget.LinearLayout
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
@ -29,6 +28,7 @@ import org.session.libsession.messaging.contacts.Contact.ContactContext
|
|||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.ViewUtil
|
import org.session.libsession.utilities.ViewUtil
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
@ -45,7 +45,6 @@ import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
|
|||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
import org.thoughtcrime.securesms.util.toDp
|
import org.thoughtcrime.securesms.util.toDp
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@ -280,7 +279,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
containerParams.horizontalBias = if (message.isOutgoing) 1f else 0f
|
containerParams.horizontalBias = if (message.isOutgoing) 1f else 0f
|
||||||
container.layoutParams = containerParams
|
container.layoutParams = containerParams
|
||||||
if (message.expiresIn > 0 && !message.isPending) {
|
if (message.expiresIn > 0 && !message.isPending) {
|
||||||
binding.expirationTimerView.setColorFilter(ResourcesCompat.getColor(resources, R.color.text, context.theme))
|
binding.expirationTimerView.setColorFilter(context.getColorFromAttr(android.R.attr.textColorPrimary))
|
||||||
binding.expirationTimerView.isInvisible = false
|
binding.expirationTimerView.isInvisible = false
|
||||||
binding.expirationTimerView.setPercentComplete(0.0f)
|
binding.expirationTimerView.setPercentComplete(0.0f)
|
||||||
if (message.expireStarted > 0) {
|
if (message.expireStarted > 0) {
|
||||||
@ -311,7 +310,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
|
|
||||||
private fun handleIsSelectedChanged() {
|
private fun handleIsSelectedChanged() {
|
||||||
background = if (snIsSelected) {
|
background = if (snIsSelected) {
|
||||||
ColorDrawable(context.resources.getColorWithID(R.color.message_selected, context.theme))
|
ColorDrawable(context.getColorFromAttr(R.attr.message_selected))
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ package org.thoughtcrime.securesms.conversation.v2.utilities
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||||
@ -64,6 +66,8 @@ open class KThumbnailView: FrameLayout {
|
|||||||
|
|
||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
}
|
}
|
||||||
|
val background = ContextCompat.getColor(context, R.color.transparent_black_6)
|
||||||
|
binding.root.background = ColorDrawable(background)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
@ -15,6 +15,7 @@ import org.session.libsession.messaging.utilities.SodiumUtilities
|
|||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
object MentionUtilities {
|
object MentionUtilities {
|
||||||
@ -58,13 +59,12 @@ object MentionUtilities {
|
|||||||
}
|
}
|
||||||
val result = SpannableString(text)
|
val result = SpannableString(text)
|
||||||
val isLightMode = UiModeUtilities.isDayUiMode(context)
|
val isLightMode = UiModeUtilities.isDayUiMode(context)
|
||||||
|
val color = if (isOutgoingMessage) {
|
||||||
|
ResourcesCompat.getColor(context.resources, if (isLightMode) R.color.white else R.color.black, context.theme)
|
||||||
|
} else {
|
||||||
|
context.getAccentColor()
|
||||||
|
}
|
||||||
for (mention in mentions) {
|
for (mention in mentions) {
|
||||||
val colorID = if (isOutgoingMessage) {
|
|
||||||
if (isLightMode) R.color.white else R.color.black
|
|
||||||
} else {
|
|
||||||
R.color.accent
|
|
||||||
}
|
|
||||||
val color = ResourcesCompat.getColor(context.resources, colorID, context.theme)
|
|
||||||
result.setSpan(ForegroundColorSpan(color), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
result.setSpan(ForegroundColorSpan(color), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
result.setSpan(StyleSpan(Typeface.BOLD), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
result.setSpan(StyleSpan(Typeface.BOLD), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ import android.graphics.Rect
|
|||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import network.loki.messenger.R
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
class ThumbnailProgressBar: View {
|
class ThumbnailProgressBar: View {
|
||||||
@ -25,7 +24,7 @@ class ThumbnailProgressBar: View {
|
|||||||
|
|
||||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
color = ResourcesCompat.getColor(resources, R.color.accent, null)
|
color = context.getAccentColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val objectRect = Rect()
|
private val objectRect = Rect()
|
||||||
|
@ -72,6 +72,11 @@ public abstract class Database {
|
|||||||
context.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
|
context.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void notifyRecipientListeners() {
|
||||||
|
context.getContentResolver().notifyChange(DatabaseContentProviders.Recipient.CONTENT_URI, null);
|
||||||
|
notifyConversationListListeners();
|
||||||
|
}
|
||||||
|
|
||||||
protected void setNotifyConverationListeners(Cursor cursor, long threadId) {
|
protected void setNotifyConverationListeners(Cursor cursor, long threadId) {
|
||||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getUriForThread(threadId));
|
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getUriForThread(threadId));
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,10 @@ public class DatabaseContentProviders {
|
|||||||
public static final Uri CONTENT_URI = Uri.parse("content://network.loki.securesms.database.stickerpack");
|
public static final Uri CONTENT_URI = Uri.parse("content://network.loki.securesms.database.stickerpack");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Recipient extends NoopContentProvider {
|
||||||
|
public static final Uri CONTENT_URI = Uri.parse("content://network.loki.securesms.database.recipient");
|
||||||
|
}
|
||||||
|
|
||||||
private static abstract class NoopContentProvider extends ContentProvider {
|
private static abstract class NoopContentProvider extends ContentProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class RecipientDatabase extends Database {
|
public class RecipientDatabase extends Database {
|
||||||
@ -232,6 +233,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(COLOR, color.serialize());
|
values.put(COLOR, color.serialize());
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setColor(color);
|
recipient.resolve().setColor(color);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
|
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
|
||||||
@ -239,6 +241,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
|
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId));
|
recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId));
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) {
|
public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) {
|
||||||
@ -246,6 +249,7 @@ public class RecipientDatabase extends Database {
|
|||||||
contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0);
|
contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0);
|
||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.resolve().setForceSmsSelection(forceSmsSelection);
|
recipient.resolve().setForceSmsSelection(forceSmsSelection);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApproved(@NonNull Recipient recipient, boolean approved) {
|
public void setApproved(@NonNull Recipient recipient, boolean approved) {
|
||||||
@ -253,6 +257,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(APPROVED, approved ? 1 : 0);
|
values.put(APPROVED, approved ? 1 : 0);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setApproved(approved);
|
recipient.resolve().setApproved(approved);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApprovedMe(@NonNull Recipient recipient, boolean approvedMe) {
|
public void setApprovedMe(@NonNull Recipient recipient, boolean approvedMe) {
|
||||||
@ -260,6 +265,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(APPROVED_ME, approvedMe ? 1 : 0);
|
values.put(APPROVED_ME, approvedMe ? 1 : 0);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setHasApprovedMe(approvedMe);
|
recipient.resolve().setHasApprovedMe(approvedMe);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlocked(@NonNull Recipient recipient, boolean blocked) {
|
public void setBlocked(@NonNull Recipient recipient, boolean blocked) {
|
||||||
@ -267,6 +273,24 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(BLOCK, blocked ? 1 : 0);
|
values.put(BLOCK, blocked ? 1 : 0);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setBlocked(blocked);
|
recipient.resolve().setBlocked(blocked);
|
||||||
|
notifyRecipientListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlocked(@NonNull List<Recipient> recipients, boolean blocked) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(BLOCK, blocked ? 1 : 0);
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
db.update(TABLE_NAME, values, ADDRESS + " = ?", new String[]{recipient.getAddress().serialize()});
|
||||||
|
recipient.resolve().setBlocked(blocked);
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMuted(@NonNull Recipient recipient, long until) {
|
public void setMuted(@NonNull Recipient recipient, long until) {
|
||||||
@ -274,6 +298,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(MUTE_UNTIL, until);
|
values.put(MUTE_UNTIL, until);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setMuted(until);
|
recipient.resolve().setMuted(until);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -287,6 +312,7 @@ public class RecipientDatabase extends Database {
|
|||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setNotifyType(notifyType);
|
recipient.resolve().setNotifyType(notifyType);
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpireMessages(@NonNull Recipient recipient, int expiration) {
|
public void setExpireMessages(@NonNull Recipient recipient, int expiration) {
|
||||||
@ -296,6 +322,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(EXPIRE_MESSAGES, expiration);
|
values.put(EXPIRE_MESSAGES, expiration);
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setExpireMessages(expiration);
|
recipient.resolve().setExpireMessages(expiration);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
|
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
|
||||||
@ -303,6 +330,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
|
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
|
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
|
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
|
||||||
@ -310,6 +338,7 @@ public class RecipientDatabase extends Database {
|
|||||||
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
||||||
updateOrInsert(recipient.getAddress(), values);
|
updateOrInsert(recipient.getAddress(), values);
|
||||||
recipient.resolve().setProfileKey(profileKey);
|
recipient.resolve().setProfileKey(profileKey);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
|
public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
|
||||||
@ -317,6 +346,7 @@ public class RecipientDatabase extends Database {
|
|||||||
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
|
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
|
||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.resolve().setProfileAvatar(profileAvatar);
|
recipient.resolve().setProfileAvatar(profileAvatar);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
|
public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
|
||||||
@ -325,6 +355,7 @@ public class RecipientDatabase extends Database {
|
|||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.resolve().setName(profileName);
|
recipient.resolve().setName(profileName);
|
||||||
recipient.resolve().setProfileName(profileName);
|
recipient.resolve().setProfileName(profileName);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfileSharing(@NonNull Recipient recipient, boolean enabled) {
|
public void setProfileSharing(@NonNull Recipient recipient, boolean enabled) {
|
||||||
@ -332,6 +363,7 @@ public class RecipientDatabase extends Database {
|
|||||||
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
|
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
|
||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.setProfileSharing(enabled);
|
recipient.setProfileSharing(enabled);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) {
|
public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) {
|
||||||
@ -339,6 +371,7 @@ public class RecipientDatabase extends Database {
|
|||||||
contentValues.put(NOTIFICATION_CHANNEL, notificationChannel);
|
contentValues.put(NOTIFICATION_CHANNEL, notificationChannel);
|
||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.setNotificationChannel(notificationChannel);
|
recipient.setNotificationChannel(notificationChannel);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) {
|
public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) {
|
||||||
@ -346,6 +379,7 @@ public class RecipientDatabase extends Database {
|
|||||||
contentValues.put(REGISTERED, registeredState.getId());
|
contentValues.put(REGISTERED, registeredState.getId());
|
||||||
updateOrInsert(recipient.getAddress(), contentValues);
|
updateOrInsert(recipient.getAddress(), contentValues);
|
||||||
recipient.setRegistered(registeredState);
|
recipient.setRegistered(registeredState);
|
||||||
|
notifyRecipientListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOrInsert(Address address, ContentValues contentValues) {
|
private void updateOrInsert(Address address, ContentValues contentValues) {
|
||||||
@ -365,6 +399,22 @@ public class RecipientDatabase extends Database {
|
|||||||
database.endTransaction();
|
database.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Recipient> getBlockedContacts() {
|
||||||
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
|
|
||||||
|
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1",
|
||||||
|
null, null, null, null, null);
|
||||||
|
|
||||||
|
RecipientReader reader = new RecipientReader(context, cursor);
|
||||||
|
List<Recipient> returnList = new ArrayList<>();
|
||||||
|
Recipient current;
|
||||||
|
while ((current = reader.getNext()) != null) {
|
||||||
|
returnList.add(current);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
public static class RecipientReader implements Closeable {
|
public static class RecipientReader implements Closeable {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -952,4 +952,14 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions(MessageId(messageId, mms))
|
DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions(MessageId(messageId, mms))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun unblock(toUnblock: List<Recipient>) {
|
||||||
|
val recipientDb = DatabaseComponent.get(context).recipientDatabase()
|
||||||
|
recipientDb.setBlocked(toUnblock, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockedContacts(): List<Recipient> {
|
||||||
|
val recipientDb = DatabaseComponent.get(context).recipientDatabase()
|
||||||
|
return recipientDb.blockedContacts
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -50,6 +50,7 @@ public class ThreadRecord extends DisplayRecord {
|
|||||||
private final long expiresIn;
|
private final long expiresIn;
|
||||||
private final long lastSeen;
|
private final long lastSeen;
|
||||||
private final boolean pinned;
|
private final boolean pinned;
|
||||||
|
private final int recipientHash;
|
||||||
|
|
||||||
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
||||||
@NonNull Recipient recipient, long date, long count, int unreadCount,
|
@NonNull Recipient recipient, long date, long count, int unreadCount,
|
||||||
@ -65,13 +66,18 @@ public class ThreadRecord extends DisplayRecord {
|
|||||||
this.archived = archived;
|
this.archived = archived;
|
||||||
this.expiresIn = expiresIn;
|
this.expiresIn = expiresIn;
|
||||||
this.lastSeen = lastSeen;
|
this.lastSeen = lastSeen;
|
||||||
this.pinned = pinned;
|
this.pinned = pinned;
|
||||||
|
this.recipientHash = recipient.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable Uri getSnippetUri() {
|
public @Nullable Uri getSnippetUri() {
|
||||||
return snippetUri;
|
return snippetUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRecipientHash() {
|
||||||
|
return recipientHash;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
if (isGroupUpdateMessage()) {
|
if (isGroupUpdateMessage()) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.thoughtcrime.securesms.glide
|
package org.thoughtcrime.securesms.glide
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import com.bumptech.glide.load.Options
|
import com.bumptech.glide.load.Options
|
||||||
import com.bumptech.glide.load.model.ModelLoader
|
import com.bumptech.glide.load.model.ModelLoader
|
||||||
@ -9,7 +8,7 @@ import com.bumptech.glide.load.model.ModelLoaderFactory
|
|||||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||||
import org.session.libsession.avatars.PlaceholderAvatarPhoto
|
import org.session.libsession.avatars.PlaceholderAvatarPhoto
|
||||||
|
|
||||||
class PlaceholderAvatarLoader(private val context: Context): ModelLoader<PlaceholderAvatarPhoto, BitmapDrawable> {
|
class PlaceholderAvatarLoader(): ModelLoader<PlaceholderAvatarPhoto, BitmapDrawable> {
|
||||||
|
|
||||||
override fun buildLoadData(
|
override fun buildLoadData(
|
||||||
model: PlaceholderAvatarPhoto,
|
model: PlaceholderAvatarPhoto,
|
||||||
@ -17,14 +16,14 @@ class PlaceholderAvatarLoader(private val context: Context): ModelLoader<Placeho
|
|||||||
height: Int,
|
height: Int,
|
||||||
options: Options
|
options: Options
|
||||||
): LoadData<BitmapDrawable> {
|
): LoadData<BitmapDrawable> {
|
||||||
return LoadData(model, PlaceholderAvatarFetcher(context, model))
|
return LoadData(model, PlaceholderAvatarFetcher(model.context, model))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handles(model: PlaceholderAvatarPhoto): Boolean = true
|
override fun handles(model: PlaceholderAvatarPhoto): Boolean = true
|
||||||
|
|
||||||
class Factory(private val context: Context) : ModelLoaderFactory<PlaceholderAvatarPhoto, BitmapDrawable> {
|
class Factory() : ModelLoaderFactory<PlaceholderAvatarPhoto, BitmapDrawable> {
|
||||||
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<PlaceholderAvatarPhoto, BitmapDrawable> {
|
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<PlaceholderAvatarPhoto, BitmapDrawable> {
|
||||||
return PlaceholderAvatarLoader(context)
|
return PlaceholderAvatarLoader()
|
||||||
}
|
}
|
||||||
override fun teardown() {}
|
override fun teardown() {}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,12 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.FragmentCreateGroupBinding
|
import network.loki.messenger.databinding.FragmentCreateGroupBinding
|
||||||
@ -57,6 +60,12 @@ class CreateGroupFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
binding.createNewPrivateChatButton.setOnClickListener { delegate.onNewMessageSelected() }
|
binding.createNewPrivateChatButton.setOnClickListener { delegate.onNewMessageSelected() }
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
val divider = ContextCompat.getDrawable(requireActivity(), R.drawable.conversation_menu_divider)!!.let {
|
||||||
|
DividerItemDecoration(requireActivity(), RecyclerView.VERTICAL).apply {
|
||||||
|
setDrawable(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recyclerView.addItemDecoration(divider)
|
||||||
var isLoading = false
|
var isLoading = false
|
||||||
binding.createClosedGroupButton.setOnClickListener {
|
binding.createClosedGroupButton.setOnClickListener {
|
||||||
if (isLoading) return@setOnClickListener
|
if (isLoading) return@setOnClickListener
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.home
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -10,7 +11,7 @@ import network.loki.messenger.databinding.FragmentConversationBottomSheetBinding
|
|||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
|
|
||||||
class ConversationOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListener {
|
class ConversationOptionsBottomSheet(private val parentContext: Context) : BottomSheetDialogFragment(), View.OnClickListener {
|
||||||
private lateinit var binding: FragmentConversationBottomSheetBinding
|
private lateinit var binding: FragmentConversationBottomSheetBinding
|
||||||
//FIXME AC: Supplying a threadRecord directly into the field from an activity
|
//FIXME AC: Supplying a threadRecord directly into the field from an activity
|
||||||
// is not the best idea. It doesn't survive configuration change.
|
// is not the best idea. It doesn't survive configuration change.
|
||||||
@ -28,8 +29,8 @@ class ConversationOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClick
|
|||||||
var onNotificationTapped: (() -> Unit)? = null
|
var onNotificationTapped: (() -> Unit)? = null
|
||||||
var onSetMuteTapped: ((Boolean) -> Unit)? = null
|
var onSetMuteTapped: ((Boolean) -> Unit)? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
binding = FragmentConversationBottomSheetBinding.inflate(inflater, container, false)
|
binding = FragmentConversationBottomSheetBinding.inflate(LayoutInflater.from(parentContext), container, false)
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.home
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -20,6 +21,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_NONE
|
|||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class ConversationView : LinearLayout {
|
class ConversationView : LinearLayout {
|
||||||
@ -41,11 +43,19 @@ class ConversationView : LinearLayout {
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
|
fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
|
||||||
this.thread = thread
|
this.thread = thread
|
||||||
background = if (thread.isPinned) {
|
if (thread.isPinned) {
|
||||||
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_pin, 0)
|
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
ContextCompat.getDrawable(context, R.drawable.conversation_pinned_background)
|
0,
|
||||||
|
0,
|
||||||
|
R.drawable.ic_pin,
|
||||||
|
0
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
binding.conversationViewDisplayNameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
background = if (thread.unreadCount > 0) {
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.conversation_unread_background)
|
||||||
|
} else {
|
||||||
ContextCompat.getDrawable(context, R.drawable.conversation_view_background)
|
ContextCompat.getDrawable(context, R.drawable.conversation_view_background)
|
||||||
}
|
}
|
||||||
binding.profilePictureView.root.glide = glide
|
binding.profilePictureView.root.glide = glide
|
||||||
@ -54,7 +64,9 @@ class ConversationView : LinearLayout {
|
|||||||
binding.accentView.setBackgroundResource(R.color.destructive)
|
binding.accentView.setBackgroundResource(R.color.destructive)
|
||||||
binding.accentView.visibility = View.VISIBLE
|
binding.accentView.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.accentView.setBackgroundResource(R.color.accent)
|
val accentColor = context.getAccentColor()
|
||||||
|
val background = ColorDrawable(accentColor)
|
||||||
|
binding.accentView.background = background
|
||||||
// Using thread.isRead we can determine if the last message was our own, and display it as 'read' even though previous messages may not be
|
// Using thread.isRead we can determine if the last message was our own, and display it as 'read' even though previous messages may not be
|
||||||
// This would also not trigger the disappearing message timer which may or may not be desirable
|
// This would also not trigger the disappearing message timer which may or may not be desirable
|
||||||
binding.accentView.visibility = if (unreadCount > 0 && !thread.isRead) View.VISIBLE else View.INVISIBLE
|
binding.accentView.visibility = if (unreadCount > 0 && !thread.isRead) View.VISIBLE else View.INVISIBLE
|
||||||
@ -65,9 +77,9 @@ class ConversationView : LinearLayout {
|
|||||||
if (unreadCount < 10000) unreadCount.toString() else "9999+"
|
if (unreadCount < 10000) unreadCount.toString() else "9999+"
|
||||||
}
|
}
|
||||||
binding.unreadCountTextView.text = formattedUnreadCount
|
binding.unreadCountTextView.text = formattedUnreadCount
|
||||||
val textSize = if (unreadCount < 10000) 12.0f else 9.0f
|
val textSize = if (unreadCount < 1000) 12.0f else 10.0f
|
||||||
binding.unreadCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
|
binding.unreadCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
|
||||||
binding.unreadCountTextView.setTypeface(Typeface.DEFAULT, if (unreadCount < 100) Typeface.BOLD else Typeface.NORMAL)
|
binding.unreadCountIndicator.background.setTint(context.getAccentColor())
|
||||||
binding.unreadCountIndicator.isVisible = (unreadCount != 0 && !thread.isRead)
|
binding.unreadCountIndicator.isVisible = (unreadCount != 0 && !thread.isRead)
|
||||||
val senderDisplayName = getUserDisplayName(thread.recipient)
|
val senderDisplayName = getUserDisplayName(thread.recipient)
|
||||||
?: thread.recipient.address.toString()
|
?: thread.recipient.address.toString()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.thoughtcrime.securesms.home
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -9,6 +8,7 @@ import android.os.Bundle
|
|||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.preferences.SettingsActivity
|
|||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.IP2Country
|
import org.thoughtcrime.securesms.util.IP2Country
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.push
|
import org.thoughtcrime.securesms.util.push
|
||||||
import org.thoughtcrime.securesms.util.show
|
import org.thoughtcrime.securesms.util.show
|
||||||
@ -167,7 +166,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
binding.seedReminderView.isVisible = false
|
binding.seedReminderView.isVisible = false
|
||||||
}
|
}
|
||||||
setupMessageRequestsBanner()
|
setupMessageRequestsBanner()
|
||||||
setupHeaderImage()
|
|
||||||
// Set up recycler view
|
// Set up recycler view
|
||||||
binding.globalSearchInputLayout.listener = this
|
binding.globalSearchInputLayout.listener = this
|
||||||
homeAdapter.setHasStableIds(true)
|
homeAdapter.setHasStableIds(true)
|
||||||
@ -276,12 +274,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
EventBus.getDefault().register(this@HomeActivity)
|
EventBus.getDefault().register(this@HomeActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupHeaderImage() {
|
|
||||||
val isDayUiMode = UiModeUtilities.isDayUiMode(this)
|
|
||||||
val headerTint = if (isDayUiMode) R.color.black else R.color.white
|
|
||||||
binding.sessionHeaderImage.setColorFilter(getColor(headerTint))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onInputFocusChanged(hasFocus: Boolean) {
|
override fun onInputFocusChanged(hasFocus: Boolean) {
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
setSearchShown(true)
|
setSearchShown(true)
|
||||||
@ -296,7 +288,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
binding.recyclerView.isVisible = !isShown
|
binding.recyclerView.isVisible = !isShown
|
||||||
binding.emptyStateContainer.isVisible = (binding.recyclerView.adapter as NewHomeAdapter).itemCount == 0 && binding.recyclerView.isVisible
|
binding.emptyStateContainer.isVisible = (binding.recyclerView.adapter as NewHomeAdapter).itemCount == 0 && binding.recyclerView.isVisible
|
||||||
binding.seedReminderView.isVisible = !TextSecurePreferences.getHasViewedSeed(this) && !isShown
|
binding.seedReminderView.isVisible = !TextSecurePreferences.getHasViewedSeed(this) && !isShown
|
||||||
binding.gradientView.isVisible = !isShown
|
|
||||||
binding.globalSearchRecycler.isVisible = isShown
|
binding.globalSearchRecycler.isVisible = isShown
|
||||||
binding.newConversationButton.isVisible = !isShown
|
binding.newConversationButton.isVisible = !isShown
|
||||||
}
|
}
|
||||||
@ -405,7 +396,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongConversationClick(thread: ThreadRecord) {
|
override fun onLongConversationClick(thread: ThreadRecord) {
|
||||||
val bottomSheet = ConversationOptionsBottomSheet()
|
val bottomSheet = ConversationOptionsBottomSheet(this)
|
||||||
bottomSheet.thread = thread
|
bottomSheet.thread = thread
|
||||||
bottomSheet.onViewDetailsTapped = {
|
bottomSheet.onViewDetailsTapped = {
|
||||||
bottomSheet.dismiss()
|
bottomSheet.dismiss()
|
||||||
|
@ -28,10 +28,8 @@ class HomeDiffUtil(
|
|||||||
if (!sameUnreads) return false
|
if (!sameUnreads) return false
|
||||||
val samePinned = oldItem.isPinned == newItem.isPinned
|
val samePinned = oldItem.isPinned == newItem.isPinned
|
||||||
if (!samePinned) return false
|
if (!samePinned) return false
|
||||||
val sameAvatar = oldItem.recipient.profileAvatar == newItem.recipient.profileAvatar
|
val sameRecipientHash = oldItem.recipientHash == newItem.recipientHash
|
||||||
if (!sameAvatar) return false
|
if (!sameRecipientHash) return false
|
||||||
val sameUsername = oldItem.recipient.name == newItem.recipient.name
|
|
||||||
if (!sameUsername) return false
|
|
||||||
val sameSnippet = oldItem.getDisplayBody(context) == newItem.getDisplayBody(context)
|
val sameSnippet = oldItem.getDisplayBody(context) == newItem.getDisplayBody(context)
|
||||||
if (!sameSnippet) return false
|
if (!sameSnippet) return false
|
||||||
val sameSendStatus = oldItem.isFailed == newItem.isFailed && oldItem.isDelivered == newItem.isDelivered
|
val sameSendStatus = oldItem.isFailed == newItem.isFailed && oldItem.isDelivered == newItem.isDelivered
|
||||||
|
@ -20,6 +20,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ActivityPathBinding
|
import network.loki.messenger.databinding.ActivityPathBinding
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsignal.utilities.Snode
|
import org.session.libsignal.utilities.Snode
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
@ -30,6 +31,7 @@ import org.thoughtcrime.securesms.util.animateSizeChange
|
|||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.fadeIn
|
import org.thoughtcrime.securesms.util.fadeIn
|
||||||
import org.thoughtcrime.securesms.util.fadeOut
|
import org.thoughtcrime.securesms.util.fadeOut
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
|
|
||||||
class PathActivity : PassphraseRequiredActionBarActivity() {
|
class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||||
@ -131,7 +133,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
lineView.layoutParams = lineViewLayoutParams
|
lineView.layoutParams = lineViewLayoutParams
|
||||||
mainContainer.addView(lineView)
|
mainContainer.addView(lineView)
|
||||||
val titleTextView = TextView(this)
|
val titleTextView = TextView(this)
|
||||||
titleTextView.setTextColor(resources.getColorWithID(R.color.text, theme))
|
titleTextView.setTextColor(getColorFromAttr(android.R.attr.textColorPrimary))
|
||||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.medium_font_size))
|
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.medium_font_size))
|
||||||
titleTextView.text = title
|
titleTextView.text = title
|
||||||
titleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
titleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
||||||
@ -144,7 +146,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
mainContainer.addView(titleContainer)
|
mainContainer.addView(titleContainer)
|
||||||
if (subtitle != null) {
|
if (subtitle != null) {
|
||||||
val subtitleTextView = TextView(this)
|
val subtitleTextView = TextView(this)
|
||||||
subtitleTextView.setTextColor(resources.getColorWithID(R.color.text, theme))
|
subtitleTextView.setTextColor(getColorFromAttr(android.R.attr.textColorPrimary))
|
||||||
subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size))
|
subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size))
|
||||||
subtitleTextView.text = subtitle
|
subtitleTextView.text = subtitle
|
||||||
subtitleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
subtitleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
||||||
@ -185,7 +187,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private val dotView by lazy {
|
private val dotView by lazy {
|
||||||
val result = PathDotView(context)
|
val result = PathDotView(context)
|
||||||
result.setBackgroundResource(R.drawable.accent_dot)
|
result.setBackgroundResource(R.drawable.accent_dot)
|
||||||
result.mainColor = resources.getColorWithID(R.color.accent, context.theme)
|
result.mainColor = context.getAccentColor()
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +221,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private fun setUpViewHierarchy() {
|
private fun setUpViewHierarchy() {
|
||||||
disableClipping()
|
disableClipping()
|
||||||
val lineView = View(context)
|
val lineView = View(context)
|
||||||
lineView.setBackgroundColor(resources.getColorWithID(R.color.text, context.theme))
|
lineView.setBackgroundColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
|
||||||
val lineViewHeight = when (location) {
|
val lineViewHeight = when (location) {
|
||||||
Location.Top, Location.Bottom -> resources.getDimensionPixelSize(R.dimen.path_row_height) / 2
|
Location.Top, Location.Bottom -> resources.getDimensionPixelSize(R.dimen.path_row_height) / 2
|
||||||
Location.Middle -> resources.getDimensionPixelSize(R.dimen.path_row_height)
|
Location.Middle -> resources.getDimensionPixelSize(R.dimen.path_row_height)
|
||||||
@ -255,13 +257,17 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private fun expand() {
|
private fun expand() {
|
||||||
dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size)
|
dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size)
|
||||||
@ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
@ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||||
GlowViewUtilities.animateShadowColorChange(context, dotView, startColorID, R.color.accent)
|
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
||||||
|
val endColor = context.getAccentColor()
|
||||||
|
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collapse() {
|
private fun collapse() {
|
||||||
dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size)
|
dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size)
|
||||||
@ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
@ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||||
GlowViewUtilities.animateShadowColorChange(context, dotView, R.color.accent, endColorID)
|
val startColor = context.getAccentColor()
|
||||||
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
||||||
|
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
@ -6,10 +6,10 @@ import android.content.Intent
|
|||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
@ -46,7 +46,9 @@ class PathStatusView : View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
update()
|
if (!isInEditMode) {
|
||||||
|
update()
|
||||||
|
}
|
||||||
setWillNotDraw(false)
|
setWillNotDraw(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +89,14 @@ class PathStatusView : View {
|
|||||||
private fun update() {
|
private fun update() {
|
||||||
if (OnionRequestAPI.paths.isNotEmpty()) {
|
if (OnionRequestAPI.paths.isNotEmpty()) {
|
||||||
setBackgroundResource(R.drawable.accent_dot)
|
setBackgroundResource(R.drawable.accent_dot)
|
||||||
mainColor = resources.getColorWithID(R.color.accent, context.theme)
|
val hasPathsColor = context.getColor(R.color.accent_green)
|
||||||
sessionShadowColor = resources.getColorWithID(R.color.accent, context.theme)
|
mainColor = hasPathsColor
|
||||||
|
sessionShadowColor = hasPathsColor
|
||||||
} else {
|
} else {
|
||||||
setBackgroundResource(R.drawable.paths_building_dot)
|
setBackgroundResource(R.drawable.paths_building_dot)
|
||||||
mainColor = resources.getColorWithID(R.color.paths_building, context.theme)
|
val pathsBuildingColor = resources.getColorWithID(R.color.paths_building, context.theme)
|
||||||
sessionShadowColor = resources.getColorWithID(R.color.paths_building, context.theme)
|
mainColor = pathsBuildingColor
|
||||||
|
sessionShadowColor = pathsBuildingColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import android.content.ClipboardManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -30,7 +31,7 @@ import org.thoughtcrime.securesms.util.UiModeUtilities
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class UserDetailsBottomSheet : BottomSheetDialogFragment() {
|
class UserDetailsBottomSheet: BottomSheetDialogFragment() {
|
||||||
|
|
||||||
@Inject lateinit var threadDb: ThreadDatabase
|
@Inject lateinit var threadDb: ThreadDatabase
|
||||||
|
|
||||||
@ -41,7 +42,9 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
binding = FragmentUserDetailsBottomSheetBinding.inflate(inflater, container, false)
|
val wrappedContext = ContextThemeWrapper(requireActivity(), requireActivity().theme)
|
||||||
|
val themedInflater = inflater.cloneInContext(wrappedContext)
|
||||||
|
binding = FragmentUserDetailsBottomSheetBinding.inflate(themedInflater, container, false)
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,6 @@ class KeyboardPageSearchView @JvmOverloads constructor(
|
|||||||
val iconTint = typedArray.getColorStateList(R.styleable.KeyboardPageSearchView_search_icon_tint) ?: ContextCompat.getColorStateList(context, R.color.signal_icon_tint_tab_selected)
|
val iconTint = typedArray.getColorStateList(R.styleable.KeyboardPageSearchView_search_icon_tint) ?: ContextCompat.getColorStateList(context, R.color.signal_icon_tint_tab_selected)
|
||||||
ImageViewCompat.setImageTintList(navButton, iconTint)
|
ImageViewCompat.setImageTintList(navButton, iconTint)
|
||||||
ImageViewCompat.setImageTintList(clearButton, iconTint)
|
ImageViewCompat.setImageTintList(clearButton, iconTint)
|
||||||
input.setHintTextColor(iconTint)
|
|
||||||
|
|
||||||
val clickOnly: Boolean = typedArray.getBoolean(R.styleable.KeyboardPageSearchView_click_only, false)
|
val clickOnly: Boolean = typedArray.getBoolean(R.styleable.KeyboardPageSearchView_click_only, false)
|
||||||
if (clickOnly) {
|
if (clickOnly) {
|
||||||
|
@ -5,11 +5,11 @@ import android.content.res.ColorStateList
|
|||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
@ -47,7 +47,7 @@ class MessageRequestsAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showPopupMenu(view: MessageRequestView) {
|
private fun showPopupMenu(view: MessageRequestView) {
|
||||||
val popupMenu = PopupMenu(context, view)
|
val popupMenu = PopupMenu(ContextThemeWrapper(context, R.style.PopupMenu_MessageRequests), view)
|
||||||
popupMenu.menuInflater.inflate(R.menu.menu_message_request, popupMenu.menu)
|
popupMenu.menuInflater.inflate(R.menu.menu_message_request, popupMenu.menu)
|
||||||
popupMenu.setOnMenuItemClickListener { menuItem ->
|
popupMenu.setOnMenuItemClickListener { menuItem ->
|
||||||
if (menuItem.itemId == R.id.menu_delete_message_request) {
|
if (menuItem.itemId == R.id.menu_delete_message_request) {
|
||||||
|
@ -73,7 +73,7 @@ public class SignalGlideModule extends AppGlideModule {
|
|||||||
registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
|
registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
|
||||||
registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
|
registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
|
||||||
registry.append(ChunkedImageUrl.class, InputStream.class, new ChunkedImageUrlLoader.Factory());
|
registry.append(ChunkedImageUrl.class, InputStream.class, new ChunkedImageUrlLoader.Factory());
|
||||||
registry.append(PlaceholderAvatarPhoto.class, BitmapDrawable.class, new PlaceholderAvatarLoader.Factory(context));
|
registry.append(PlaceholderAvatarPhoto.class, BitmapDrawable.class, new PlaceholderAvatarLoader.Factory());
|
||||||
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
|
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.notifications;
|
|||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
@ -65,17 +64,8 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setLed() {
|
private void setLed() {
|
||||||
String ledColor = TextSecurePreferences.getNotificationLedColor(context);
|
int ledColor = TextSecurePreferences.getNotificationLedColor(context);
|
||||||
String ledBlinkPattern = TextSecurePreferences.getNotificationLedPattern(context);
|
setLights(ledColor, 500,2000);
|
||||||
String ledBlinkPatternCustom = TextSecurePreferences.getNotificationLedPatternCustom(context);
|
|
||||||
|
|
||||||
if (!ledColor.equals("none")) {
|
|
||||||
String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom);
|
|
||||||
|
|
||||||
setLights(Color.parseColor(ledColor),
|
|
||||||
Integer.parseInt(blinkPatternArray[0]),
|
|
||||||
Integer.parseInt(blinkPatternArray[1]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTicker(@NonNull Recipient recipient, @Nullable CharSequence message) {
|
public void setTicker(@NonNull Recipient recipient, @Nullable CharSequence message) {
|
||||||
@ -88,13 +78,6 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) {
|
|
||||||
if (blinkPattern.equals("custom"))
|
|
||||||
blinkPattern = blinkPatternCustom;
|
|
||||||
|
|
||||||
return blinkPattern.split(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @NonNull CharSequence trimToDisplayLength(@Nullable CharSequence text) {
|
protected @NonNull CharSequence trimToDisplayLength(@Nullable CharSequence text) {
|
||||||
text = text == null ? "" : text;
|
text = text == null ? "" : text;
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package org.thoughtcrime.securesms.notifications;
|
package org.thoughtcrime.securesms.notifications;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationChannelGroup;
|
import android.app.NotificationChannelGroup;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
@ -221,7 +219,7 @@ public class NotificationChannels {
|
|||||||
* channels. Performs database operations and should therefore be invoked on a background thread.
|
* channels. Performs database operations and should therefore be invoked on a background thread.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull String color) {
|
public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull Integer color) {
|
||||||
if (!supported()) {
|
if (!supported()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -472,12 +470,12 @@ public class NotificationChannels {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(26)
|
@TargetApi(26)
|
||||||
private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull String ledColor) {
|
private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull Integer ledColor) {
|
||||||
if ("none".equals(ledColor)) {
|
if ("none".equals(ledColor)) {
|
||||||
channel.enableLights(false);
|
channel.enableLights(false);
|
||||||
} else {
|
} else {
|
||||||
channel.enableLights(true);
|
channel.enableLights(true);
|
||||||
channel.setLightColor(Color.parseColor(ledColor));
|
channel.setLightColor(ledColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +507,7 @@ public class NotificationChannels {
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
@TargetApi(26)
|
@TargetApi(26)
|
||||||
private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull String color) {
|
private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Integer color) {
|
||||||
RecipientDatabase database = DatabaseComponent.get(context).recipientDatabase();
|
RecipientDatabase database = DatabaseComponent.get(context).recipientDatabase();
|
||||||
|
|
||||||
try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) {
|
try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) {
|
||||||
|
@ -22,6 +22,7 @@ import androidx.annotation.StringRes;
|
|||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationCompat.Action;
|
import androidx.core.app.NotificationCompat.Action;
|
||||||
import androidx.core.app.RemoteInput;
|
import androidx.core.app.RemoteInput;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
@ -63,9 +64,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||||||
{
|
{
|
||||||
super(context, privacy);
|
super(context, privacy);
|
||||||
|
|
||||||
|
|
||||||
setSmallIcon(R.drawable.ic_notification);
|
setSmallIcon(R.drawable.ic_notification);
|
||||||
setColor(context.getResources().getColor(R.color.textsecure_primary));
|
setColor(ContextCompat.getColor(context, R.color.accent_green));
|
||||||
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
||||||
|
|
||||||
if (!NotificationChannels.supported()) {
|
if (!NotificationChannels.supported()) {
|
||||||
|
@ -11,20 +11,22 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ActivityPnModeBinding
|
import network.loki.messenger.databinding.ActivityPnModeBinding
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.home.HomeActivity
|
import org.thoughtcrime.securesms.home.HomeActivity
|
||||||
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
|
import org.thoughtcrime.securesms.util.PNModeView
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
|
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
|
||||||
import org.thoughtcrime.securesms.util.show
|
import org.thoughtcrime.securesms.util.show
|
||||||
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
|
||||||
import org.thoughtcrime.securesms.util.PNModeView
|
|
||||||
|
|
||||||
class PNModeActivity : BaseActionBarActivity() {
|
class PNModeActivity : BaseActionBarActivity() {
|
||||||
private lateinit var binding: ActivityPnModeBinding
|
private lateinit var binding: ActivityPnModeBinding
|
||||||
@ -40,10 +42,10 @@ class PNModeActivity : BaseActionBarActivity() {
|
|||||||
with(binding) {
|
with(binding) {
|
||||||
contentView.disableClipping()
|
contentView.disableClipping()
|
||||||
fcmOptionView.setOnClickListener { toggleFCM() }
|
fcmOptionView.setOnClickListener { toggleFCM() }
|
||||||
fcmOptionView.mainColor = resources.getColorWithID(R.color.pn_option_background, theme)
|
fcmOptionView.mainColor = ThemeUtil.getThemedColor(root.context, R.attr.colorPrimary)
|
||||||
fcmOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
|
fcmOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
|
||||||
backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
|
backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
|
||||||
backgroundPollingOptionView.mainColor = resources.getColorWithID(R.color.pn_option_background, theme)
|
backgroundPollingOptionView.mainColor = ThemeUtil.getThemedColor(root.context, R.attr.colorPrimary)
|
||||||
backgroundPollingOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
|
backgroundPollingOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
|
||||||
registerButton.setOnClickListener { register() }
|
registerButton.setOnClickListener { register() }
|
||||||
}
|
}
|
||||||
@ -84,60 +86,60 @@ class PNModeActivity : BaseActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleFCM() = with(binding) {
|
private fun toggleFCM() = with(binding) {
|
||||||
|
val accentColor = getAccentColor()
|
||||||
when (selectedOptionView) {
|
when (selectedOptionView) {
|
||||||
null -> {
|
null -> {
|
||||||
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
|
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, fcmOptionView, R.color.transparent, R.color.accent)
|
GlowViewUtilities.animateShadowColorChange(fcmOptionView, resources.getColorWithID(R.color.transparent, theme), accentColor)
|
||||||
animateStrokeColorChange(fcmOptionView, R.color.pn_option_border, R.color.accent)
|
animateStrokeColorChange(fcmOptionView, resources.getColorWithID(R.color.pn_option_border, theme), accentColor)
|
||||||
selectedOptionView = fcmOptionView
|
selectedOptionView = fcmOptionView
|
||||||
}
|
}
|
||||||
fcmOptionView -> {
|
fcmOptionView -> {
|
||||||
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
|
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, fcmOptionView, R.color.accent, R.color.transparent)
|
GlowViewUtilities.animateShadowColorChange(fcmOptionView, accentColor, resources.getColorWithID(R.color.transparent, theme))
|
||||||
animateStrokeColorChange(fcmOptionView, R.color.accent, R.color.pn_option_border)
|
animateStrokeColorChange(fcmOptionView, accentColor, resources.getColorWithID(R.color.pn_option_border, theme))
|
||||||
selectedOptionView = null
|
selectedOptionView = null
|
||||||
}
|
}
|
||||||
backgroundPollingOptionView -> {
|
backgroundPollingOptionView -> {
|
||||||
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
|
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, fcmOptionView, R.color.transparent, R.color.accent)
|
GlowViewUtilities.animateShadowColorChange(fcmOptionView, resources.getColorWithID(R.color.transparent, theme), accentColor)
|
||||||
animateStrokeColorChange(fcmOptionView, R.color.pn_option_border, R.color.accent)
|
animateStrokeColorChange(fcmOptionView, resources.getColorWithID(R.color.pn_option_border, theme), accentColor)
|
||||||
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
|
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, backgroundPollingOptionView, R.color.accent, R.color.transparent)
|
GlowViewUtilities.animateShadowColorChange(backgroundPollingOptionView, accentColor, resources.getColorWithID(R.color.transparent, theme))
|
||||||
animateStrokeColorChange(backgroundPollingOptionView, R.color.accent, R.color.pn_option_border)
|
animateStrokeColorChange(backgroundPollingOptionView, accentColor, resources.getColorWithID(R.color.pn_option_border, theme))
|
||||||
selectedOptionView = fcmOptionView
|
selectedOptionView = fcmOptionView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleBackgroundPolling() = with(binding) {
|
private fun toggleBackgroundPolling() = with(binding) {
|
||||||
|
val accentColor = getAccentColor()
|
||||||
when (selectedOptionView) {
|
when (selectedOptionView) {
|
||||||
null -> {
|
null -> {
|
||||||
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
|
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, backgroundPollingOptionView, R.color.transparent, R.color.accent)
|
GlowViewUtilities.animateShadowColorChange(backgroundPollingOptionView, resources.getColorWithID(R.color.transparent, theme), accentColor)
|
||||||
animateStrokeColorChange(backgroundPollingOptionView, R.color.pn_option_border, R.color.accent)
|
animateStrokeColorChange(backgroundPollingOptionView, resources.getColorWithID(R.color.pn_option_border, theme), accentColor)
|
||||||
selectedOptionView = backgroundPollingOptionView
|
selectedOptionView = backgroundPollingOptionView
|
||||||
}
|
}
|
||||||
backgroundPollingOptionView -> {
|
backgroundPollingOptionView -> {
|
||||||
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
|
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, backgroundPollingOptionView, R.color.accent, R.color.transparent)
|
GlowViewUtilities.animateShadowColorChange(backgroundPollingOptionView, accentColor, resources.getColorWithID(R.color.transparent, theme))
|
||||||
animateStrokeColorChange(backgroundPollingOptionView, R.color.accent, R.color.pn_option_border)
|
animateStrokeColorChange(backgroundPollingOptionView, accentColor, resources.getColorWithID(R.color.pn_option_border, theme))
|
||||||
selectedOptionView = null
|
selectedOptionView = null
|
||||||
}
|
}
|
||||||
fcmOptionView -> {
|
fcmOptionView -> {
|
||||||
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
|
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, backgroundPollingOptionView, R.color.transparent, R.color.accent)
|
GlowViewUtilities.animateShadowColorChange(backgroundPollingOptionView, resources.getColorWithID(R.color.transparent, theme), accentColor)
|
||||||
animateStrokeColorChange(backgroundPollingOptionView, R.color.pn_option_border, R.color.accent)
|
animateStrokeColorChange(backgroundPollingOptionView, resources.getColorWithID(R.color.pn_option_border, theme), accentColor)
|
||||||
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
|
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
|
||||||
GlowViewUtilities.animateShadowColorChange(this@PNModeActivity, fcmOptionView, R.color.accent, R.color.transparent)
|
GlowViewUtilities.animateShadowColorChange(fcmOptionView, accentColor, resources.getColorWithID(R.color.transparent, theme))
|
||||||
animateStrokeColorChange(fcmOptionView, R.color.accent, R.color.pn_option_border)
|
animateStrokeColorChange(fcmOptionView, accentColor, resources.getColorWithID(R.color.pn_option_border, theme))
|
||||||
selectedOptionView = backgroundPollingOptionView
|
selectedOptionView = backgroundPollingOptionView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateStrokeColorChange(bubble: PNModeView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
private fun animateStrokeColorChange(bubble: PNModeView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
||||||
val startColor = resources.getColorWithID(startColorID, theme)
|
|
||||||
val endColor = resources.getColorWithID(endColorID, theme)
|
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
animation.duration = 250
|
animation.duration = 250
|
||||||
animation.addUpdateListener { animator ->
|
animation.addUpdateListener { animator ->
|
||||||
|
@ -12,12 +12,13 @@ import android.widget.Toast
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ActivitySeedBinding
|
import network.loki.messenger.databinding.ActivitySeedBinding
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsignal.crypto.MnemonicCodec
|
import org.session.libsignal.crypto.MnemonicCodec
|
||||||
import org.session.libsignal.utilities.hexEncodedPrivateKey
|
import org.session.libsignal.utilities.hexEncodedPrivateKey
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
|
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
|
|
||||||
class SeedActivity : BaseActionBarActivity() {
|
class SeedActivity : BaseActionBarActivity() {
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
supportActionBar!!.title = resources.getString(R.string.activity_seed_title)
|
supportActionBar!!.title = resources.getString(R.string.activity_seed_title)
|
||||||
val seedReminderViewTitle = SpannableString("You're almost finished! 90%") // Intentionally not yet translated
|
val seedReminderViewTitle = SpannableString("You're almost finished! 90%") // Intentionally not yet translated
|
||||||
seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
seedReminderViewTitle.setSpan(ForegroundColorSpan(getAccentColor()), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
with(binding) {
|
with(binding) {
|
||||||
seedReminderView.title = seedReminderViewTitle
|
seedReminderView.title = seedReminderViewTitle
|
||||||
seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_2)
|
seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_2)
|
||||||
@ -55,7 +56,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
seedTextView.setTextColor(resources.getColorWithID(R.color.accent, theme))
|
seedTextView.setTextColor(getAccentColor())
|
||||||
seedTextView.text = redactedSeed
|
seedTextView.text = redactedSeed
|
||||||
seedTextView.setOnLongClickListener { revealSeed(); true }
|
seedTextView.setOnLongClickListener { revealSeed(); true }
|
||||||
revealButton.setOnLongClickListener { revealSeed(); true }
|
revealButton.setOnLongClickListener { revealSeed(); true }
|
||||||
@ -67,7 +68,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
// region Updating
|
// region Updating
|
||||||
private fun revealSeed() {
|
private fun revealSeed() {
|
||||||
val seedReminderViewTitle = SpannableString("Account secured! 100%") // Intentionally not yet translated
|
val seedReminderViewTitle = SpannableString("Account secured! 100%") // Intentionally not yet translated
|
||||||
seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 17, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
seedReminderViewTitle.setSpan(ForegroundColorSpan(getAccentColor()), 17, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
with(binding) {
|
with(binding) {
|
||||||
seedReminderView.title = seedReminderViewTitle
|
seedReminderView.title = seedReminderViewTitle
|
||||||
seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_3)
|
seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_3)
|
||||||
@ -75,7 +76,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
val seedTextViewLayoutParams = seedTextView.layoutParams as LinearLayout.LayoutParams
|
val seedTextViewLayoutParams = seedTextView.layoutParams as LinearLayout.LayoutParams
|
||||||
seedTextViewLayoutParams.height = seedTextView.height
|
seedTextViewLayoutParams.height = seedTextView.height
|
||||||
seedTextView.layoutParams = seedTextViewLayoutParams
|
seedTextView.layoutParams = seedTextViewLayoutParams
|
||||||
seedTextView.setTextColor(resources.getColorWithID(R.color.text, theme))
|
seedTextView.setTextColor(getColorFromAttr(android.R.attr.textColorPrimary))
|
||||||
seedTextView.text = seed
|
seedTextView.text = seed
|
||||||
}
|
}
|
||||||
TextSecurePreferences.setHasViewedSeed(this, true)
|
TextSecurePreferences.setHasViewedSeed(this, true)
|
||||||
|
@ -22,8 +22,8 @@ import androidx.fragment.app.Fragment;
|
|||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.annimon.stream.function.Consumer;
|
import com.annimon.stream.function.Consumer;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.LRUCache;
|
|
||||||
import org.session.libsession.utilities.ServiceUtil;
|
import org.session.libsession.utilities.ServiceUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.LRUCache;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
@ -353,7 +353,7 @@ public class Permissions {
|
|||||||
Context context = this.context.get();
|
Context context = this.context.get();
|
||||||
|
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog)
|
||||||
.setTitle(R.string.Permissions_permission_required)
|
.setTitle(R.string.Permissions_permission_required)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(R.string.Permissions_continue, (dialog, which) -> context.startActivity(getApplicationSettingsIntent(context)))
|
.setPositiveButton(R.string.Permissions_continue, (dialog, which) -> context.startActivity(getApplicationSettingsIntent(context)))
|
||||||
|
@ -4,8 +4,6 @@ package org.thoughtcrime.securesms.permissions;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import androidx.annotation.DrawableRes;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -14,13 +12,18 @@ import android.widget.ImageView;
|
|||||||
import android.widget.LinearLayout.LayoutParams;
|
import android.widget.LinearLayout.LayoutParams;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.session.libsession.utilities.ViewUtil;
|
import org.session.libsession.utilities.ViewUtil;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class RationaleDialog {
|
public class RationaleDialog {
|
||||||
|
|
||||||
public static AlertDialog.Builder createFor(@NonNull Context context, @NonNull String message, @DrawableRes int... drawables) {
|
public static AlertDialog.Builder createFor(@NonNull Context context, @NonNull String message, @DrawableRes int... drawables) {
|
||||||
View view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null);
|
View view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null);
|
||||||
|
view.setClipToOutline(true);
|
||||||
ViewGroup header = view.findViewById(R.id.header_container);
|
ViewGroup header = view.findViewById(R.id.header_container);
|
||||||
TextView text = view.findViewById(R.id.message);
|
TextView text = view.findViewById(R.id.message);
|
||||||
|
|
||||||
@ -47,7 +50,7 @@ public class RationaleDialog {
|
|||||||
|
|
||||||
text.setText(message);
|
text.setText(message);
|
||||||
|
|
||||||
return new AlertDialog.Builder(context, R.style.Theme_TextSecure_Dialog_Rationale).setView(view);
|
return new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog).setView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import network.loki.messenger.databinding.ActivityBlockedContactsBinding
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class BlockedContactsActivity: PassphraseRequiredActionBarActivity(), View.OnClickListener {
|
||||||
|
|
||||||
|
lateinit var binding: ActivityBlockedContactsBinding
|
||||||
|
|
||||||
|
val viewModel: BlockedContactsViewModel by viewModels()
|
||||||
|
|
||||||
|
val adapter = BlockedContactsAdapter()
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
if (v === binding.unblockButton && adapter.getSelectedItems().isNotEmpty()) {
|
||||||
|
val contactsToUnblock = adapter.getSelectedItems()
|
||||||
|
// show dialog
|
||||||
|
val title = if (contactsToUnblock.size == 1) {
|
||||||
|
getString(R.string.Unblock_dialog__title_single, contactsToUnblock.first().name)
|
||||||
|
} else {
|
||||||
|
getString(R.string.Unblock_dialog__title_multiple)
|
||||||
|
}
|
||||||
|
|
||||||
|
val message = if (contactsToUnblock.size == 1) {
|
||||||
|
getString(R.string.Unblock_dialog__message, contactsToUnblock.first().name)
|
||||||
|
} else {
|
||||||
|
val stringBuilder = StringBuilder()
|
||||||
|
val iterator = contactsToUnblock.iterator()
|
||||||
|
var numberAdded = 0
|
||||||
|
while (iterator.hasNext() && numberAdded < 3) {
|
||||||
|
val nextRecipient = iterator.next()
|
||||||
|
if (numberAdded > 0) stringBuilder.append(", ")
|
||||||
|
|
||||||
|
stringBuilder.append(nextRecipient.name)
|
||||||
|
numberAdded++
|
||||||
|
}
|
||||||
|
val overflow = contactsToUnblock.size - numberAdded
|
||||||
|
if (overflow > 0) {
|
||||||
|
stringBuilder.append(" ")
|
||||||
|
val string = resources.getQuantityString(R.plurals.Unblock_dialog__message_multiple_overflow, overflow)
|
||||||
|
stringBuilder.append(string.format(overflow))
|
||||||
|
}
|
||||||
|
getString(R.string.Unblock_dialog__message, stringBuilder.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setPositiveButton(R.string.continue_2) { d, _ ->
|
||||||
|
viewModel.unblock(contactsToUnblock)
|
||||||
|
d.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel) { d, _ ->
|
||||||
|
d.dismiss()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
super.onCreate(savedInstanceState, ready)
|
||||||
|
binding = ActivityBlockedContactsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
binding.recyclerView.adapter = adapter
|
||||||
|
|
||||||
|
viewModel.subscribe(this)
|
||||||
|
.observe(this) { newState ->
|
||||||
|
adapter.submitList(newState.blockedContacts)
|
||||||
|
val isEmpty = newState.blockedContacts.isEmpty()
|
||||||
|
binding.emptyStateMessageTextView.isVisible = isEmpty
|
||||||
|
binding.nonEmptyStateGroup.isVisible = !isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.unblockButton.setOnClickListener(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import network.loki.messenger.databinding.BlockedContactLayoutBinding
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
|
||||||
|
class BlockedContactsAdapter: ListAdapter<Recipient,BlockedContactsAdapter.ViewHolder>(RecipientDiffer()) {
|
||||||
|
|
||||||
|
class RecipientDiffer: DiffUtil.ItemCallback<Recipient>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Recipient, newItem: Recipient) = oldItem === newItem
|
||||||
|
override fun areContentsTheSame(oldItem: Recipient, newItem: Recipient) = oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
private val selectedItems = mutableListOf<Recipient>()
|
||||||
|
|
||||||
|
fun getSelectedItems() = selectedItems
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.blocked_contact_layout, parent, false)
|
||||||
|
return ViewHolder(itemView)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleSelection(recipient: Recipient, isSelected: Boolean, position: Int) {
|
||||||
|
if (isSelected) {
|
||||||
|
selectedItems -= recipient
|
||||||
|
} else {
|
||||||
|
selectedItems += recipient
|
||||||
|
}
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val recipient = getItem(position)
|
||||||
|
val isSelected = recipient in selectedItems
|
||||||
|
holder.bind(recipient, isSelected) {
|
||||||
|
toggleSelection(recipient, isSelected, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewRecycled(holder: ViewHolder) {
|
||||||
|
super.onViewRecycled(holder)
|
||||||
|
holder.binding.profilePictureView.root.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
val glide = GlideApp.with(itemView)
|
||||||
|
val binding = BlockedContactLayoutBinding.bind(itemView)
|
||||||
|
|
||||||
|
fun bind(recipient: Recipient, isSelected: Boolean, toggleSelection: () -> Unit) {
|
||||||
|
binding.recipientName.text = recipient.name
|
||||||
|
with (binding.profilePictureView.root) {
|
||||||
|
glide = this@ViewHolder.glide
|
||||||
|
update(recipient)
|
||||||
|
}
|
||||||
|
binding.root.setOnClickListener { toggleSelection() }
|
||||||
|
binding.selectButton.isSelected = isSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
class BlockedContactsLayout @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null
|
||||||
|
) : FrameLayout(context, attrs)
|
@ -0,0 +1,28 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import androidx.preference.PreferenceViewHolder
|
||||||
|
|
||||||
|
class BlockedContactsPreference @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attributeSet: AttributeSet? = null) : PreferenceCategory(context, attributeSet), View.OnClickListener {
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
if (v is BlockedContactsLayout) {
|
||||||
|
val intent = Intent(context, BlockedContactsActivity::class.java)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||||
|
super.onBindViewHolder(holder)
|
||||||
|
|
||||||
|
val itemView = holder.itemView
|
||||||
|
itemView.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.cash.copper.flow.observeQuery
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.plus
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||||
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class BlockedContactsViewModel @Inject constructor(private val storage: Storage): ViewModel() {
|
||||||
|
|
||||||
|
private val executor = viewModelScope + SupervisorJob()
|
||||||
|
|
||||||
|
private val listUpdateChannel = Channel<Unit>(capacity = Channel.CONFLATED)
|
||||||
|
|
||||||
|
private val _contacts = MutableLiveData(BlockedContactsViewState(emptyList()))
|
||||||
|
|
||||||
|
fun subscribe(context: Context): LiveData<BlockedContactsViewState> {
|
||||||
|
executor.launch(IO) {
|
||||||
|
context.contentResolver
|
||||||
|
.observeQuery(DatabaseContentProviders.Recipient.CONTENT_URI)
|
||||||
|
.onStart {
|
||||||
|
listUpdateChannel.trySend(Unit)
|
||||||
|
}
|
||||||
|
.onEach {
|
||||||
|
listUpdateChannel.trySend(Unit)
|
||||||
|
}
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
executor.launch(IO) {
|
||||||
|
for (update in listUpdateChannel) {
|
||||||
|
val blockedContactState = BlockedContactsViewState(storage.blockedContacts().sortedBy { it.name })
|
||||||
|
withContext(Main) {
|
||||||
|
_contacts.value = blockedContactState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _contacts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unblock(toUnblock: List<Recipient>) {
|
||||||
|
storage.unblock(toUnblock)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class BlockedContactsViewState(
|
||||||
|
val blockedContacts: List<Recipient>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
@ -2,11 +2,7 @@ package org.thoughtcrime.securesms.preferences
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.util.UiMode
|
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
|
||||||
|
|
||||||
class ChangeUiModeDialog : DialogFragment() {
|
class ChangeUiModeDialog : DialogFragment() {
|
||||||
|
|
||||||
@ -16,19 +12,8 @@ class ChangeUiModeDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
|
return android.app.AlertDialog.Builder(context)
|
||||||
val displayNameList = UiMode.values().map { getString(it.displayNameRes) }.toTypedArray()
|
.setTitle("TODO: remove this")
|
||||||
val activeUiMode = UiModeUtilities.getUserSelectedUiMode(context)
|
.show()
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,14 +3,13 @@ package org.thoughtcrime.securesms.preferences
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment
|
|
||||||
|
|
||||||
class ChatSettingsActivity : PassphraseRequiredActionBarActivity() {
|
class ChatSettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||||
super.onCreate(savedInstanceState, isReady)
|
super.onCreate(savedInstanceState, isReady)
|
||||||
setContentView(R.layout.activity_fragment_wrapper)
|
setContentView(R.layout.activity_fragment_wrapper)
|
||||||
supportActionBar!!.title = resources.getString(R.string.activity_chat_settings_title)
|
supportActionBar!!.title = resources.getString(R.string.activity_conversations_settings_title)
|
||||||
val fragment = ChatsPreferenceFragment()
|
val fragment = ChatsPreferenceFragment()
|
||||||
val transaction = supportFragmentManager.beginTransaction()
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
transaction.replace(R.id.fragmentContainer, fragment)
|
transaction.replace(R.id.fragmentContainer, fragment)
|
||||||
|
@ -2,8 +2,10 @@ package org.thoughtcrime.securesms.preferences
|
|||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -13,8 +15,8 @@ import network.loki.messenger.databinding.DialogClearAllDataBinding
|
|||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
|
|
||||||
class ClearAllDataDialog : BaseDialog() {
|
class ClearAllDataDialog : BaseDialog() {
|
||||||
private lateinit var binding: DialogClearAllDataBinding
|
private lateinit var binding: DialogClearAllDataBinding
|
||||||
@ -26,9 +28,6 @@ class ClearAllDataDialog : BaseDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var clearJob: Job? = null
|
var clearJob: Job? = null
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
var step = Steps.INFO_PROMPT
|
var step = Steps.INFO_PROMPT
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -38,19 +37,27 @@ class ClearAllDataDialog : BaseDialog() {
|
|||||||
|
|
||||||
override fun setContentView(builder: AlertDialog.Builder) {
|
override fun setContentView(builder: AlertDialog.Builder) {
|
||||||
binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext()))
|
binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext()))
|
||||||
|
val device = RadioOption("deviceOnly", requireContext().getString(R.string.dialog_clear_all_data_clear_device_only))
|
||||||
|
val network = RadioOption("deviceAndNetwork", requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network))
|
||||||
|
var selectedOption = device
|
||||||
|
val optionAdapter = RadioOptionAdapter { selectedOption = it }
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = optionAdapter
|
||||||
|
addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
|
||||||
|
setHasFixedSize(true)
|
||||||
|
}
|
||||||
|
optionAdapter.submitList(listOf(device, network))
|
||||||
binding.cancelButton.setOnClickListener {
|
binding.cancelButton.setOnClickListener {
|
||||||
if (step == Steps.NETWORK_PROMPT) {
|
dismiss()
|
||||||
clearAllData(false)
|
|
||||||
} else if (step != Steps.DELETING) {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
binding.clearAllDataButton.setOnClickListener {
|
binding.clearAllDataButton.setOnClickListener {
|
||||||
when(step) {
|
when(step) {
|
||||||
Steps.INFO_PROMPT -> step = Steps.NETWORK_PROMPT
|
Steps.INFO_PROMPT -> if (selectedOption == network) {
|
||||||
Steps.NETWORK_PROMPT -> {
|
step = Steps.NETWORK_PROMPT
|
||||||
clearAllData(true)
|
} else {
|
||||||
|
clearAllData(false)
|
||||||
}
|
}
|
||||||
|
Steps.NETWORK_PROMPT -> clearAllData(true)
|
||||||
Steps.DELETING -> { /* do nothing intentionally */ }
|
Steps.DELETING -> { /* do nothing intentionally */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,17 +71,14 @@ class ClearAllDataDialog : BaseDialog() {
|
|||||||
|
|
||||||
when (step) {
|
when (step) {
|
||||||
Steps.INFO_PROMPT -> {
|
Steps.INFO_PROMPT -> {
|
||||||
binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_explanation)
|
binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_message)
|
||||||
binding.cancelButton.setText(R.string.cancel)
|
|
||||||
binding.clearAllDataButton.setText(R.string.delete)
|
|
||||||
}
|
}
|
||||||
else -> {
|
Steps.NETWORK_PROMPT -> {
|
||||||
binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_network_explanation)
|
binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_clear_device_and_network_confirmation)
|
||||||
binding.cancelButton.setText(R.string.dialog_clear_all_data_local_only)
|
|
||||||
binding.clearAllDataButton.setText(R.string.dialog_clear_all_data_clear_network)
|
|
||||||
}
|
}
|
||||||
|
Steps.DELETING -> { /* do nothing intentionally */ }
|
||||||
}
|
}
|
||||||
|
binding.recyclerView.isGone = step == Steps.NETWORK_PROMPT
|
||||||
binding.cancelButton.isVisible = !isLoading
|
binding.cancelButton.isVisible = !isLoading
|
||||||
binding.clearAllDataButton.isVisible = !isLoading
|
binding.clearAllDataButton.isVisible = !isLoading
|
||||||
binding.progressBar.isVisible = isLoading
|
binding.progressBar.isVisible = isLoading
|
||||||
|
@ -2,9 +2,18 @@ package org.thoughtcrime.securesms.preferences;
|
|||||||
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceCategory;
|
import androidx.preference.PreferenceCategory;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
@ -12,19 +21,30 @@ import androidx.preference.PreferenceGroupAdapter;
|
|||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
import org.thoughtcrime.securesms.components.CustomDefaultPreference;
|
import org.thoughtcrime.securesms.components.CustomDefaultPreference;
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.ViewUtil;
|
||||||
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
|
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
|
||||||
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreferenceDialogFragmentCompat;
|
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreferenceDialogFragmentCompat;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public abstract class CorrectedPreferenceFragment extends PreferenceFragmentCompat {
|
public abstract class CorrectedPreferenceFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
public static final int SINGLE_TYPE = 21;
|
||||||
|
public static final int TOP_TYPE = 22;
|
||||||
|
public static final int MIDDLE_TYPE = 23;
|
||||||
|
public static final int BOTTOM_TYPE = 24;
|
||||||
|
public static final int CATEGORY_TYPE = 25;
|
||||||
|
|
||||||
|
public int horizontalPadding;
|
||||||
|
public int verticalPadding;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
|
horizontalPadding = ViewUtil.dpToPx(requireContext(), 36);
|
||||||
|
verticalPadding = ViewUtil.dpToPx(requireContext(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -33,6 +53,7 @@ public abstract class CorrectedPreferenceFragment extends PreferenceFragmentComp
|
|||||||
|
|
||||||
View lv = getView().findViewById(android.R.id.list);
|
View lv = getView().findViewById(android.R.id.list);
|
||||||
if (lv != null) lv.setPadding(0, 0, 0, 0);
|
if (lv != null) lv.setPadding(0, 0, 0, 0);
|
||||||
|
setDivider(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -57,17 +78,100 @@ public abstract class CorrectedPreferenceFragment extends PreferenceFragmentComp
|
|||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
|
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
|
||||||
return new PreferenceGroupAdapter(preferenceScreen) {
|
return new PreferenceGroupAdapter(preferenceScreen) {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public PreferenceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
PreferenceViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);
|
||||||
|
return viewHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPreferenceType(int position) {
|
||||||
|
Preference preference = getItem(position);
|
||||||
|
if (preference instanceof PreferenceCategory) {
|
||||||
|
return CATEGORY_TYPE;
|
||||||
|
}
|
||||||
|
boolean isStart = isTop(position);
|
||||||
|
boolean isEnd = isBottom(position);
|
||||||
|
if (isStart && isEnd) {
|
||||||
|
// always show full
|
||||||
|
return SINGLE_TYPE;
|
||||||
|
} else {
|
||||||
|
if (isStart) {
|
||||||
|
return TOP_TYPE;
|
||||||
|
} else if (isEnd) {
|
||||||
|
return BOTTOM_TYPE;
|
||||||
|
} else {
|
||||||
|
return MIDDLE_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTop(int position) {
|
||||||
|
if (position == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Preference previous = getItem(position - 1);
|
||||||
|
return previous instanceof PreferenceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBottom(int position) {
|
||||||
|
int size = getItemCount();
|
||||||
|
if (position == size - 1) {
|
||||||
|
// last one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Preference next = getItem(position + 1);
|
||||||
|
return next instanceof PreferenceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable getBackground(Context context, int position) {
|
||||||
|
int viewType = getPreferenceType(position);
|
||||||
|
Drawable background;
|
||||||
|
switch (viewType) {
|
||||||
|
case SINGLE_TYPE:
|
||||||
|
background = ContextCompat.getDrawable(context, R.drawable.preference_single);
|
||||||
|
break;
|
||||||
|
case TOP_TYPE:
|
||||||
|
background = ContextCompat.getDrawable(context, R.drawable.preference_top);
|
||||||
|
break;
|
||||||
|
case MIDDLE_TYPE:
|
||||||
|
background = ContextCompat.getDrawable(context, R.drawable.preference_middle);
|
||||||
|
break;
|
||||||
|
case BOTTOM_TYPE:
|
||||||
|
background = ContextCompat.getDrawable(context, R.drawable.preference_bottom);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
background = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
|
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
|
||||||
super.onBindViewHolder(holder, position);
|
super.onBindViewHolder(holder, position);
|
||||||
Preference preference = getItem(position);
|
Preference preference = getItem(position);
|
||||||
if (preference instanceof PreferenceCategory) {
|
if (preference instanceof PreferenceCategory) {
|
||||||
|
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) holder.itemView.getLayoutParams();
|
||||||
|
layoutParams.topMargin = 0;
|
||||||
|
layoutParams.bottomMargin = 0;
|
||||||
|
holder.itemView.setLayoutParams(layoutParams);
|
||||||
setZeroPaddingToLayoutChildren(holder.itemView);
|
setZeroPaddingToLayoutChildren(holder.itemView);
|
||||||
} else {
|
} else {
|
||||||
View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
|
View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
|
||||||
if (iconFrame != null) {
|
if (iconFrame != null) {
|
||||||
iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
|
iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
Drawable background = getBackground(holder.itemView.getContext(), position);
|
||||||
|
holder.itemView.setBackground(background);
|
||||||
|
TextView titleView = holder.itemView.findViewById(android.R.id.title);
|
||||||
|
if (titleView != null) {
|
||||||
|
((TextView) titleView).setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||||
|
}
|
||||||
|
boolean isTop = isTop(position);
|
||||||
|
boolean isBottom = isBottom(position);
|
||||||
|
holder.itemView.setPadding(horizontalPadding, isTop ? verticalPadding : 0, horizontalPadding, isBottom ? verticalPadding : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
|
|
||||||
|
class HelpSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
super.onCreate(savedInstanceState, ready)
|
||||||
|
setContentView(R.layout.activity_fragment_wrapper)
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragmentContainer, HelpSettingsFragment())
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HelpSettingsFragment: CorrectedPreferenceFragment() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EXPORT_LOGS = "export_logs"
|
||||||
|
private const val TRANSLATE = "translate_session"
|
||||||
|
private const val FEEDBACK = "feedback"
|
||||||
|
private const val FAQ = "faq"
|
||||||
|
private const val SUPPORT = "support"
|
||||||
|
|
||||||
|
private const val CROWDIN_URL = "https://crowdin.com/project/session-android"
|
||||||
|
private const val FEEDBACK_URL = "https://getsession.org/survey"
|
||||||
|
private const val FAQ_URL = "https://getsession.org/faq"
|
||||||
|
private const val SUPPORT_URL = "https://sessionapp.zendesk.com/hc/en-us"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
addPreferencesFromResource(R.xml.preferences_help)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||||
|
preference ?: return false
|
||||||
|
return when (preference.key) {
|
||||||
|
EXPORT_LOGS -> {
|
||||||
|
shareLogs()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
TRANSLATE -> {
|
||||||
|
openLink(CROWDIN_URL)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
FEEDBACK -> {
|
||||||
|
openLink(FEEDBACK_URL)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
FAQ -> {
|
||||||
|
openLink(FAQ_URL)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
SUPPORT -> {
|
||||||
|
openLink(SUPPORT_URL)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onPreferenceTreeClick(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareLogs() {
|
||||||
|
Permissions.with(this)
|
||||||
|
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
.maxSdkVersion(Build.VERSION_CODES.P)
|
||||||
|
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
|
||||||
|
.onAnyDenied {
|
||||||
|
Toast.makeText(requireActivity(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
.onAllGranted {
|
||||||
|
ShareLogsDialog().show(parentFragmentManager,"Share Logs Dialog")
|
||||||
|
}
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openLink(url: String) {
|
||||||
|
try {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(requireActivity(), "Can't open URL", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import network.loki.messenger.databinding.DialogListPreferenceBinding
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
|
|
||||||
|
class ListPreferenceDialog(
|
||||||
|
private val listPreference: ListPreference,
|
||||||
|
private val dialogListener: () -> Unit
|
||||||
|
) : BaseDialog() {
|
||||||
|
private lateinit var binding: DialogListPreferenceBinding
|
||||||
|
|
||||||
|
override fun setContentView(builder: AlertDialog.Builder) {
|
||||||
|
binding = DialogListPreferenceBinding.inflate(LayoutInflater.from(requireContext()))
|
||||||
|
binding.titleTextView.text = listPreference.dialogTitle
|
||||||
|
binding.messageTextView.text = listPreference.dialogMessage
|
||||||
|
binding.closeButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
val options = listPreference.entryValues.zip(listPreference.entries) { value, title ->
|
||||||
|
RadioOption(value.toString(), title.toString())
|
||||||
|
}
|
||||||
|
val valueIndex = listPreference.findIndexOfValue(listPreference.value)
|
||||||
|
val optionAdapter = RadioOptionAdapter(valueIndex) {
|
||||||
|
listPreference.value = it.value
|
||||||
|
dismiss()
|
||||||
|
dialogListener.invoke()
|
||||||
|
}
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = optionAdapter
|
||||||
|
addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
|
||||||
|
setHasFixedSize(true)
|
||||||
|
}
|
||||||
|
optionAdapter.submitList(options)
|
||||||
|
builder.setView(binding.root)
|
||||||
|
builder.setCancelable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,10 +4,10 @@ package org.thoughtcrime.securesms.preferences;
|
|||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public abstract class ListSummaryPreferenceFragment extends CorrectedPreferenceFragment {
|
public abstract class ListSummaryPreferenceFragment extends CorrectedPreferenceFragment {
|
||||||
|
|
||||||
protected class ListSummaryListener implements Preference.OnPreferenceChangeListener {
|
protected class ListSummaryListener implements Preference.OnPreferenceChangeListener {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.preferences;
|
package org.thoughtcrime.securesms.preferences;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -9,20 +11,19 @@ import android.net.Uri;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
|
||||||
|
|
||||||
public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragment {
|
public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ -42,28 +43,14 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
Preference ledBlinkPref = this.findPreference(TextSecurePreferences.LED_BLINK_PREF);
|
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
if (NotificationChannels.supported()) {
|
||||||
ledBlinkPref.setVisible(false);
|
|
||||||
TextSecurePreferences.setNotificationRingtone(getContext(), NotificationChannels.getMessageRingtone(getContext()).toString());
|
TextSecurePreferences.setNotificationRingtone(getContext(), NotificationChannels.getMessageRingtone(getContext()).toString());
|
||||||
TextSecurePreferences.setNotificationVibrateEnabled(getContext(), NotificationChannels.getMessageVibrate(getContext()));
|
TextSecurePreferences.setNotificationVibrateEnabled(getContext(), NotificationChannels.getMessageVibrate(getContext()));
|
||||||
|
|
||||||
} else {
|
|
||||||
ledBlinkPref.setOnPreferenceChangeListener(new ListSummaryListener());
|
|
||||||
initializeListSummary((ListPreference) ledBlinkPref);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.findPreference(TextSecurePreferences.LED_COLOR_PREF)
|
|
||||||
.setOnPreferenceChangeListener(new LedColorChangeListener());
|
|
||||||
this.findPreference(TextSecurePreferences.RINGTONE_PREF)
|
this.findPreference(TextSecurePreferences.RINGTONE_PREF)
|
||||||
.setOnPreferenceChangeListener(new RingtoneSummaryListener());
|
.setOnPreferenceChangeListener(new RingtoneSummaryListener());
|
||||||
this.findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF)
|
|
||||||
.setOnPreferenceChangeListener(new ListSummaryListener());
|
|
||||||
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)
|
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)
|
||||||
.setOnPreferenceChangeListener(new NotificationPrivacyListener());
|
.setOnPreferenceChangeListener(new NotificationPrivacyListener());
|
||||||
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)
|
|
||||||
.setOnPreferenceChangeListener(new ListSummaryListener());
|
|
||||||
this.findPreference(TextSecurePreferences.VIBRATE_PREF)
|
this.findPreference(TextSecurePreferences.VIBRATE_PREF)
|
||||||
.setOnPreferenceChangeListener((preference, newValue) -> {
|
.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
NotificationChannels.updateMessageVibrate(getContext(), (boolean) newValue);
|
NotificationChannels.updateMessageVibrate(getContext(), (boolean) newValue);
|
||||||
@ -86,8 +73,17 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_COLOR_PREF));
|
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)
|
||||||
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF));
|
.setOnPreferenceClickListener(preference -> {
|
||||||
|
ListPreference listPreference = (ListPreference) preference;
|
||||||
|
listPreference.setDialogMessage(R.string.preferences_notifications__content_message);
|
||||||
|
new ListPreferenceDialog(listPreference, () -> {
|
||||||
|
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF));
|
||||||
|
return null;
|
||||||
|
}).show(getChildFragmentManager(), "ListPreferenceDialog");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF));
|
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF));
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
if (NotificationChannels.supported()) {
|
||||||
@ -99,8 +95,6 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF));
|
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF));
|
||||||
@ -183,20 +177,4 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
private class LedColorChangeListener extends ListSummaryListener {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
|
||||||
if (NotificationChannels.supported()) {
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
NotificationChannels.updateMessagesLedColor(getActivity(), (String) value);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
|
||||||
return super.onPreferenceChange(preference, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,14 @@ package org.thoughtcrime.securesms.preferences
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment
|
|
||||||
|
|
||||||
class PrivacySettingsActivity : PassphraseRequiredActionBarActivity() {
|
class PrivacySettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||||
super.onCreate(savedInstanceState, isReady)
|
super.onCreate(savedInstanceState, isReady)
|
||||||
setContentView(R.layout.activity_fragment_wrapper)
|
setContentView(R.layout.activity_fragment_wrapper)
|
||||||
val fragment = AppProtectionPreferenceFragment()
|
val fragment =
|
||||||
|
PrivacySettingsPreferenceFragment()
|
||||||
val transaction = supportFragmentManager.beginTransaction()
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
transaction.replace(R.id.fragmentContainer, fragment)
|
transaction.replace(R.id.fragmentContainer, fragment)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
|
@ -12,6 +12,7 @@ import android.provider.Settings;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.view.ContextThemeWrapper;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
@ -23,14 +24,11 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
|
|||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder;
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder;
|
||||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
|
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment {
|
public class PrivacySettingsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Activity activity) {
|
||||||
@ -42,7 +40,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
super.onCreate(paramBundle);
|
super.onCreate(paramBundle);
|
||||||
|
|
||||||
this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener());
|
this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener());
|
||||||
this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener());
|
|
||||||
|
|
||||||
this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener());
|
this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener());
|
||||||
this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener());
|
this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener());
|
||||||
@ -56,7 +53,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
((SwitchPreferenceCompat)findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)).setChecked(isEnabled);
|
((SwitchPreferenceCompat)findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)).setChecked(isEnabled);
|
||||||
if (isEnabled && !CallNotificationBuilder.areNotificationsEnabled(requireActivity())) {
|
if (isEnabled && !CallNotificationBuilder.areNotificationsEnabled(requireActivity())) {
|
||||||
// show a dialog saying that calls won't work properly if you don't have notifications on at a system level
|
// show a dialog saying that calls won't work properly if you don't have notifications on at a system level
|
||||||
new AlertDialog.Builder(requireActivity())
|
new AlertDialog.Builder(new ContextThemeWrapper(requireActivity(), R.style.ThemeOverlay_Session_AlertDialog))
|
||||||
.setTitle(R.string.CallNotificationBuilder_system_notification_title)
|
.setTitle(R.string.CallNotificationBuilder_system_notification_title)
|
||||||
.setMessage(R.string.CallNotificationBuilder_system_notification_message)
|
.setMessage(R.string.CallNotificationBuilder_system_notification_message)
|
||||||
.setPositiveButton(R.string.activity_notification_settings_title, (d, w) -> {
|
.setPositiveButton(R.string.activity_notification_settings_title, (d, w) -> {
|
||||||
@ -100,20 +97,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (TextSecurePreferences.isPasswordDisabled(getContext())) {
|
|
||||||
initializeScreenLockTimeoutSummary();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeScreenLockTimeoutSummary() {
|
|
||||||
long timeoutSeconds = TextSecurePreferences.getScreenLockTimeout(getContext());
|
|
||||||
long hours = TimeUnit.SECONDS.toHours(timeoutSeconds);
|
|
||||||
long minutes = TimeUnit.SECONDS.toMinutes(timeoutSeconds) - (TimeUnit.SECONDS.toHours(timeoutSeconds) * 60 );
|
|
||||||
long seconds = TimeUnit.SECONDS.toSeconds(timeoutSeconds) - (TimeUnit.SECONDS.toMinutes(timeoutSeconds) * 60);
|
|
||||||
|
|
||||||
findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT)
|
|
||||||
.setSummary(timeoutSeconds <= 0 ? getString(R.string.AppProtectionPreferenceFragment_none) :
|
|
||||||
String.format("%02d:%02d:%02d", hours, minutes, seconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeVisibility() {
|
private void initializeVisibility() {
|
||||||
@ -143,25 +126,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ScreenLockTimeoutListener implements Preference.OnPreferenceClickListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
new TimeDurationPickerDialog(getContext(), (view, duration) -> {
|
|
||||||
if (duration == 0) {
|
|
||||||
TextSecurePreferences.setScreenLockTimeout(getContext(), 0);
|
|
||||||
} else {
|
|
||||||
long timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(duration);
|
|
||||||
TextSecurePreferences.setScreenLockTimeout(getContext(), timeoutSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeScreenLockTimeoutSummary();
|
|
||||||
}, 0).show();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener {
|
private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
@ -215,20 +179,16 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
boolean val = (boolean) newValue;
|
boolean val = (boolean) newValue;
|
||||||
if (val) {
|
if (val) {
|
||||||
// check if we've shown the info dialog and check for microphone permissions
|
// check if we've shown the info dialog and check for microphone permissions
|
||||||
if (TextSecurePreferences.setShownCallWarning(context.requireContext())) {
|
new AlertDialog.Builder(new ContextThemeWrapper(context.requireContext(), R.style.ThemeOverlay_Session_AlertDialog))
|
||||||
new AlertDialog.Builder(context.requireContext())
|
.setTitle(R.string.dialog_voice_video_title)
|
||||||
.setTitle(R.string.dialog_voice_video_title)
|
.setMessage(R.string.dialog_voice_video_message)
|
||||||
.setMessage(R.string.dialog_voice_video_message)
|
.setPositiveButton(R.string.dialog_link_preview_enable_button_title, (d, w) -> {
|
||||||
.setPositiveButton(R.string.dialog_link_preview_enable_button_title, (d, w) -> {
|
requestMicrophonePermission();
|
||||||
requestMicrophonePermission();
|
})
|
||||||
})
|
.setNegativeButton(R.string.cancel, (d, w) -> {
|
||||||
.setNegativeButton(R.string.cancel, (d, w) -> {
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
} else {
|
|
||||||
requestMicrophonePermission();
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
@ -0,0 +1,55 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import network.loki.messenger.databinding.ItemSelectableBinding
|
||||||
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
|
||||||
|
class RadioOptionAdapter(
|
||||||
|
var selectedOptionPosition: Int = 0,
|
||||||
|
private val onClickListener: (RadioOption) -> Unit
|
||||||
|
) : ListAdapter<RadioOption, RadioOptionAdapter.ViewHolder>(RadioOptionDiffer()) {
|
||||||
|
|
||||||
|
class RadioOptionDiffer: DiffUtil.ItemCallback<RadioOption>() {
|
||||||
|
override fun areItemsTheSame(oldItem: RadioOption, newItem: RadioOption) = oldItem === newItem
|
||||||
|
override fun areContentsTheSame(oldItem: RadioOption, newItem: RadioOption) = oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_selectable, parent, false)
|
||||||
|
return ViewHolder(itemView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val option = getItem(position)
|
||||||
|
val isSelected = position == selectedOptionPosition
|
||||||
|
holder.bind(option, isSelected) {
|
||||||
|
onClickListener(it)
|
||||||
|
selectedOptionPosition = position
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
val glide = GlideApp.with(itemView)
|
||||||
|
val binding = ItemSelectableBinding.bind(itemView)
|
||||||
|
|
||||||
|
fun bind(option: RadioOption, isSelected: Boolean, toggleSelection: (RadioOption) -> Unit) {
|
||||||
|
binding.titleTextView.text = option.title
|
||||||
|
binding.root.setOnClickListener { toggleSelection(option) }
|
||||||
|
binding.selectButton.isSelected = isSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RadioOption(
|
||||||
|
val value: String,
|
||||||
|
val title: String
|
||||||
|
)
|
@ -30,7 +30,7 @@ class SeedDialog : BaseDialog() {
|
|||||||
override fun setContentView(builder: AlertDialog.Builder) {
|
override fun setContentView(builder: AlertDialog.Builder) {
|
||||||
val binding = DialogSeedBinding.inflate(LayoutInflater.from(requireContext()))
|
val binding = DialogSeedBinding.inflate(LayoutInflater.from(requireContext()))
|
||||||
binding.seedTextView.text = seed
|
binding.seedTextView.text = seed
|
||||||
binding.cancelButton.setOnClickListener { dismiss() }
|
binding.closeButton.setOnClickListener { dismiss() }
|
||||||
binding.copyButton.setOnClickListener { copySeed() }
|
binding.copyButton.setOnClickListener { copySeed() }
|
||||||
builder.setView(binding.root)
|
builder.setView(binding.root)
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,11 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.util.SparseArray
|
||||||
import android.view.ActionMode
|
import android.view.ActionMode
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@ -39,11 +40,11 @@ import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity
|
|||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
|
import org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.push
|
import org.thoughtcrime.securesms.util.push
|
||||||
import org.thoughtcrime.securesms.util.show
|
import org.thoughtcrime.securesms.util.show
|
||||||
@ -67,6 +68,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val updatedProfileResultCode = 1234
|
const val updatedProfileResultCode = 1234
|
||||||
|
private const val SCROLL_STATE = "SCROLL_STATE"
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
@ -94,24 +96,31 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
notificationsButton.setOnClickListener { showNotificationSettings() }
|
notificationsButton.setOnClickListener { showNotificationSettings() }
|
||||||
messageRequestsButton.setOnClickListener { showMessageRequests() }
|
messageRequestsButton.setOnClickListener { showMessageRequests() }
|
||||||
chatsButton.setOnClickListener { showChatSettings() }
|
chatsButton.setOnClickListener { showChatSettings() }
|
||||||
sendInvitationButton.setOnClickListener { sendInvitation() }
|
appearanceButton.setOnClickListener { showAppearanceSettings() }
|
||||||
faqButton.setOnClickListener { showFAQ() }
|
inviteFriendButton.setOnClickListener { sendInvitation() }
|
||||||
surveyButton.setOnClickListener { showSurvey() }
|
helpButton.setOnClickListener { showHelp() }
|
||||||
helpTranslateButton.setOnClickListener { helpTranslate() }
|
|
||||||
seedButton.setOnClickListener { showSeed() }
|
seedButton.setOnClickListener { showSeed() }
|
||||||
clearAllDataButton.setOnClickListener { clearAllData() }
|
clearAllDataButton.setOnClickListener { clearAllData() }
|
||||||
debugLogButton.setOnClickListener { shareLogs() }
|
|
||||||
val isLightMode = UiModeUtilities.isDayUiMode(this@SettingsActivity)
|
|
||||||
oxenLogoImageView.setImageResource(if (isLightMode) R.drawable.oxen_light_mode else R.drawable.oxen_dark_mode)
|
|
||||||
versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
val scrollBundle = SparseArray<Parcelable>()
|
||||||
|
binding.scrollView.saveHierarchyState(scrollBundle)
|
||||||
|
outState.putSparseParcelableArray(SCROLL_STATE, scrollBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
|
savedInstanceState.getSparseParcelableArray<Parcelable>(SCROLL_STATE)?.let { scrollBundle ->
|
||||||
|
binding.scrollView.restoreHierarchyState(scrollBundle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.settings_general, menu)
|
menuInflater.inflate(R.menu.settings_general, menu)
|
||||||
// Update UI mode menu icon
|
|
||||||
val uiMode = UiModeUtilities.getUserSelectedUiMode(this)
|
|
||||||
menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,10 +130,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
showQRCode()
|
showQRCode()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_change_theme -> {
|
|
||||||
ChangeUiModeDialog().show(supportFragmentManager, ChangeUiModeDialog.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,6 +300,11 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
push(intent)
|
push(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showAppearanceSettings() {
|
||||||
|
val intent = Intent(this, AppearanceSettingsActivity::class.java)
|
||||||
|
push(intent)
|
||||||
|
}
|
||||||
|
|
||||||
private fun sendInvitation() {
|
private fun sendInvitation() {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.action = Intent.ACTION_SEND
|
intent.action = Intent.ACTION_SEND
|
||||||
@ -305,14 +315,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
startActivity(chooser)
|
startActivity(chooser)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showFAQ() {
|
private fun showHelp() {
|
||||||
try {
|
val intent = Intent(this, HelpSettingsActivity::class.java)
|
||||||
val url = "https://getsession.org/faq"
|
push(intent)
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPath() {
|
private fun showPath() {
|
||||||
@ -320,26 +325,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
show(intent)
|
show(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSurvey() {
|
|
||||||
try {
|
|
||||||
val url = "https://getsession.org/survey"
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun helpTranslate() {
|
|
||||||
try {
|
|
||||||
val url = "https://crowdin.com/project/session-android"
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showSeed() {
|
private fun showSeed() {
|
||||||
SeedDialog().show(supportFragmentManager, "Recovery Phrase Dialog")
|
SeedDialog().show(supportFragmentManager, "Recovery Phrase Dialog")
|
||||||
}
|
}
|
||||||
@ -348,20 +333,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog")
|
ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareLogs() {
|
|
||||||
Permissions.with(this)
|
|
||||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
|
||||||
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
|
|
||||||
.onAnyDenied {
|
|
||||||
Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
.onAllGranted {
|
|
||||||
ShareLogsDialog().show(supportFragmentManager,"Share Logs Dialog")
|
|
||||||
}
|
|
||||||
.execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
private inner class DisplayNameEditActionModeCallback: ActionMode.Callback {
|
private inner class DisplayNameEditActionModeCallback: ActionMode.Callback {
|
||||||
|
@ -0,0 +1,153 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences.appearance
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.util.SparseArray
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.widget.SwitchCompat
|
||||||
|
import androidx.core.view.children
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import network.loki.messenger.databinding.ActivityAppearanceSettingsBinding
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.util.ThemeState
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.OnClickListener {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SCROLL_PARCEL = "scroll_parcel"
|
||||||
|
}
|
||||||
|
|
||||||
|
val viewModel: AppearanceSettingsViewModel by viewModels()
|
||||||
|
lateinit var binding : ActivityAppearanceSettingsBinding
|
||||||
|
|
||||||
|
var currentTheme: ThemeState? = null
|
||||||
|
|
||||||
|
private val accentColors
|
||||||
|
get() = mapOf(
|
||||||
|
binding.accentGreen to R.style.PrimaryGreen,
|
||||||
|
binding.accentBlue to R.style.PrimaryBlue,
|
||||||
|
binding.accentYellow to R.style.PrimaryYellow,
|
||||||
|
binding.accentPink to R.style.PrimaryPink,
|
||||||
|
binding.accentPurple to R.style.PrimaryPurple,
|
||||||
|
binding.accentOrange to R.style.PrimaryOrange,
|
||||||
|
binding.accentRed to R.style.PrimaryRed
|
||||||
|
)
|
||||||
|
|
||||||
|
private val themeViews
|
||||||
|
get() = listOf(
|
||||||
|
binding.themeOptionClassicDark,
|
||||||
|
binding.themeRadioClassicDark,
|
||||||
|
binding.themeOptionClassicLight,
|
||||||
|
binding.themeRadioClassicLight,
|
||||||
|
binding.themeOptionOceanDark,
|
||||||
|
binding.themeRadioOceanDark,
|
||||||
|
binding.themeOptionOceanLight,
|
||||||
|
binding.themeRadioOceanLight
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
v ?: return
|
||||||
|
val accents = accentColors
|
||||||
|
val themes = themeViews
|
||||||
|
if (v in accents) {
|
||||||
|
val entry = accents[v]
|
||||||
|
entry?.let { viewModel.setNewAccent(it) }
|
||||||
|
} else if (v in themes) {
|
||||||
|
val currentBase = if (currentTheme?.theme == R.style.Classic_Dark || currentTheme?.theme == R.style.Classic_Light) R.style.Classic else R.style.Ocean
|
||||||
|
val (mappedStyle, newBase) = when (v) {
|
||||||
|
binding.themeOptionClassicDark, binding.themeRadioClassicDark -> CLASSIC_DARK to R.style.Classic
|
||||||
|
binding.themeOptionClassicLight, binding.themeRadioClassicLight -> CLASSIC_LIGHT to R.style.Classic
|
||||||
|
binding.themeOptionOceanDark, binding.themeRadioOceanDark -> OCEAN_DARK to R.style.Ocean
|
||||||
|
binding.themeOptionOceanLight, binding.themeRadioOceanLight -> OCEAN_LIGHT to R.style.Ocean
|
||||||
|
else -> throw NullPointerException("Invalid style for view [$v]")
|
||||||
|
}
|
||||||
|
viewModel.setNewStyle(mappedStyle)
|
||||||
|
if (currentBase != newBase) {
|
||||||
|
if (newBase == R.style.Ocean) {
|
||||||
|
viewModel.setNewAccent(R.style.PrimaryBlue)
|
||||||
|
} else if (newBase == R.style.Classic) {
|
||||||
|
viewModel.setNewAccent(R.style.PrimaryGreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (v == binding.systemSettingsSwitch) {
|
||||||
|
viewModel.setNewFollowSystemSettings((v as SwitchCompat).isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
val scrollParcelArray = SparseArray<Parcelable>()
|
||||||
|
binding.scrollView.saveHierarchyState(scrollParcelArray)
|
||||||
|
outState.putSparseParcelableArray(SCROLL_PARCEL, scrollParcelArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSelectedTheme(themeStyle: Int) {
|
||||||
|
mapOf(
|
||||||
|
R.style.Classic_Dark to binding.themeRadioClassicDark,
|
||||||
|
R.style.Classic_Light to binding.themeRadioClassicLight,
|
||||||
|
R.style.Ocean_Dark to binding.themeRadioOceanDark,
|
||||||
|
R.style.Ocean_Light to binding.themeRadioOceanLight
|
||||||
|
).forEach { (style, view) ->
|
||||||
|
view.isChecked = themeStyle == style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSelectedAccent(accentStyle: Int) {
|
||||||
|
accentColors.forEach { (view, style) ->
|
||||||
|
view.isSelected = style == accentStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFollowSystemToggle(followSystemSettings: Boolean) {
|
||||||
|
binding.systemSettingsSwitch.isChecked = followSystemSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
super.onCreate(savedInstanceState, ready)
|
||||||
|
binding = ActivityAppearanceSettingsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
savedInstanceState?.let { bundle ->
|
||||||
|
val scrollStateParcel = bundle.getSparseParcelableArray<Parcelable>(SCROLL_PARCEL)
|
||||||
|
if (scrollStateParcel != null) {
|
||||||
|
binding.scrollView.restoreHierarchyState(scrollStateParcel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
supportActionBar!!.title = getString(R.string.activity_settings_message_appearance_button_title)
|
||||||
|
with (binding) {
|
||||||
|
// accent toggles
|
||||||
|
accentContainer.children.forEach { view ->
|
||||||
|
view.setOnClickListener(this@AppearanceSettingsActivity)
|
||||||
|
}
|
||||||
|
// theme toggles
|
||||||
|
themeViews.forEach {
|
||||||
|
it.setOnClickListener(this@AppearanceSettingsActivity)
|
||||||
|
}
|
||||||
|
// system settings toggle
|
||||||
|
systemSettingsSwitch.setOnClickListener(this@AppearanceSettingsActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launchWhenResumed {
|
||||||
|
viewModel.uiState.collectLatest { themeState ->
|
||||||
|
val (theme, accent, followSystem) = themeState
|
||||||
|
updateSelectedTheme(theme)
|
||||||
|
updateSelectedAccent(accent)
|
||||||
|
updateFollowSystemToggle(followSystem)
|
||||||
|
if (currentTheme != null && currentTheme != themeState) {
|
||||||
|
recreate()
|
||||||
|
} else {
|
||||||
|
currentTheme = themeState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences.appearance
|
||||||
|
|
||||||
|
import androidx.annotation.StyleRes
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.util.ThemeState
|
||||||
|
import org.thoughtcrime.securesms.util.themeState
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSecurePreferences) : ViewModel() {
|
||||||
|
|
||||||
|
private val _uiState = MutableStateFlow(prefs.themeState())
|
||||||
|
val uiState: StateFlow<ThemeState> = _uiState
|
||||||
|
|
||||||
|
fun setNewAccent(@StyleRes newAccentColorStyle: Int) {
|
||||||
|
prefs.setAccentColorStyle(newAccentColorStyle)
|
||||||
|
// update UI state
|
||||||
|
_uiState.value = prefs.themeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNewStyle(newThemeStyle: String) {
|
||||||
|
prefs.setThemeStyle(newThemeStyle)
|
||||||
|
// update UI state
|
||||||
|
_uiState.value = prefs.themeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNewFollowSystemSettings(followSystemSettings: Boolean) {
|
||||||
|
prefs.setFollowSystemSettings(followSystemSettings)
|
||||||
|
_uiState.value = prefs.themeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2017 Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.preferences.widgets;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.GradientDrawable;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.preference.ListPreference;
|
|
||||||
import androidx.preference.PreferenceViewHolder;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List preference that disables dependents when set to "none", similar to a CheckBoxPreference.
|
|
||||||
*
|
|
||||||
* @author Taylor Kline
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class LEDColorListPreference extends ListPreference {
|
|
||||||
|
|
||||||
private static final String TAG = LEDColorListPreference.class.getSimpleName();
|
|
||||||
|
|
||||||
private ImageView colorImageView;
|
|
||||||
|
|
||||||
public LEDColorListPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setWidgetLayoutResource(R.layout.led_color_preference_widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LEDColorListPreference(Context context) {
|
|
||||||
super(context);
|
|
||||||
setWidgetLayoutResource(R.layout.led_color_preference_widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValue(String value) {
|
|
||||||
CharSequence oldEntry = getEntry();
|
|
||||||
super.setValue(value);
|
|
||||||
CharSequence newEntry = getEntry();
|
|
||||||
if (oldEntry != newEntry) {
|
|
||||||
notifyDependencyChange(shouldDisableDependents());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null) setPreviewColor(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldDisableDependents() {
|
|
||||||
CharSequence newEntry = getValue();
|
|
||||||
boolean shouldDisable = newEntry.equals("none");
|
|
||||||
return shouldDisable || super.shouldDisableDependents();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(PreferenceViewHolder view) {
|
|
||||||
super.onBindViewHolder(view);
|
|
||||||
this.colorImageView = (ImageView)view.findViewById(R.id.color_view);
|
|
||||||
setPreviewColor(getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSummary(CharSequence summary) {
|
|
||||||
super.setSummary(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPreviewColor(@NonNull String value) {
|
|
||||||
int color;
|
|
||||||
|
|
||||||
switch (value) {
|
|
||||||
case "green": color = getContext().getResources().getColor(R.color.green_500); break;
|
|
||||||
case "red": color = getContext().getResources().getColor(R.color.red_500); break;
|
|
||||||
case "blue": color = getContext().getResources().getColor(R.color.blue_500); break;
|
|
||||||
case "yellow": color = getContext().getResources().getColor(R.color.yellow_500); break;
|
|
||||||
case "cyan": color = getContext().getResources().getColor(R.color.cyan_500); break;
|
|
||||||
case "magenta": color = getContext().getResources().getColor(R.color.pink_500); break;
|
|
||||||
case "white": color = getContext().getResources().getColor(R.color.white); break;
|
|
||||||
default: color = getContext().getResources().getColor(R.color.transparent); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorImageView != null) {
|
|
||||||
GradientDrawable drawable = new GradientDrawable();
|
|
||||||
drawable.setShape(GradientDrawable.OVAL);
|
|
||||||
drawable.setColor(color);
|
|
||||||
|
|
||||||
colorImageView.setImageDrawable(drawable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.thoughtcrime.securesms.preferences.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
class NotificationSettingsPreference @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null
|
||||||
|
) : FrameLayout(context, attrs) {
|
||||||
|
|
||||||
|
override fun onFinishInflate() {
|
||||||
|
super.onFinishInflate()
|
||||||
|
// TODO: if we want do the spans
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -68,20 +68,14 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
if (ThemeUtil.isDarkTheme(requireContext())) {
|
// setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_Session_BottomSheet);
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_TextSecure_BottomSheetDialog_Fixed_ReactWithAny);
|
|
||||||
} else {
|
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_TextSecure_Light_BottomSheetDialog_Fixed_ReactWithAny);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater,
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState)
|
@Nullable Bundle savedInstanceState) {
|
||||||
{
|
|
||||||
return inflater.inflate(R.layout.reactions_bottom_sheet_dialog_fragment, container, false);
|
return inflater.inflate(R.layout.reactions_bottom_sheet_dialog_fragment, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +120,7 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
|
|||||||
View customView = tab.getCustomView();
|
View customView = tab.getCustomView();
|
||||||
TextView text = customView.findViewById(R.id.reactions_pill_count);
|
TextView text = customView.findViewById(R.id.reactions_pill_count);
|
||||||
customView.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.reaction_pill_background_selected));
|
customView.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.reaction_pill_background_selected));
|
||||||
text.setTextColor(ContextCompat.getColor(requireContext(), R.color.reactions_pill_selected_text_color));
|
text.setTextColor(ThemeUtil.getThemedColor(requireContext(), R.attr.reactionsPillSelectedTextColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -134,7 +128,7 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
|
|||||||
View customView = tab.getCustomView();
|
View customView = tab.getCustomView();
|
||||||
TextView text = customView.findViewById(R.id.reactions_pill_count);
|
TextView text = customView.findViewById(R.id.reactions_pill_count);
|
||||||
customView.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.reaction_pill_dialog_background));
|
customView.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.reaction_pill_dialog_background));
|
||||||
text.setTextColor(ContextCompat.getColor(requireContext(), R.color.reactions_pill_text_color));
|
text.setTextColor(ThemeUtil.getThemedColor(requireContext(), R.attr.reactionsPillNormalTextColor));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onTabReselected(TabLayout.Tab tab) {}
|
public void onTabReselected(TabLayout.Tab tab) {}
|
||||||
|
@ -14,8 +14,6 @@ import android.view.WindowManager;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
@ -24,7 +22,6 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
|||||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
import com.google.android.material.shape.CornerFamily;
|
import com.google.android.material.shape.CornerFamily;
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable;
|
|
||||||
import com.google.android.material.shape.ShapeAppearanceModel;
|
import com.google.android.material.shape.ShapeAppearanceModel;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
|
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
|
||||||
@ -80,7 +77,6 @@ public final class ReactWithAnyEmojiDialogFragment extends BottomSheetDialogFrag
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.Widget_TextSecure_ReactWithAny);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -93,22 +89,6 @@ public final class ReactWithAnyEmojiDialogFragment extends BottomSheetDialogFrag
|
|||||||
.setTopRightCorner(CornerFamily.ROUNDED, ViewUtil.dpToPx(requireContext(), 18))
|
.setTopRightCorner(CornerFamily.ROUNDED, ViewUtil.dpToPx(requireContext(), 18))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
MaterialShapeDrawable dialogBackground = new MaterialShapeDrawable(shapeAppearanceModel);
|
|
||||||
|
|
||||||
dialogBackground.setTint(ContextCompat.getColor(requireContext(), R.color.react_with_any_background));
|
|
||||||
|
|
||||||
dialog.getBehavior().addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
|
||||||
@Override
|
|
||||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
|
||||||
if (bottomSheet.getBackground() != dialogBackground) {
|
|
||||||
ViewCompat.setBackground(bottomSheet, dialogBackground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
boolean shadows = requireArguments().getBoolean(ARG_SHADOWS, true);
|
boolean shadows = requireArguments().getBoolean(ARG_SHADOWS, true);
|
||||||
if (!shadows) {
|
if (!shadows) {
|
||||||
Window window = dialog.getWindow();
|
Window window = dialog.getWindow();
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package org.thoughtcrime.securesms.util
|
package org.thoughtcrime.securesms.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.StyleRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
|
|
||||||
@ -67,4 +67,35 @@ interface ActivityDispatcher {
|
|||||||
}
|
}
|
||||||
fun dispatchIntent(body: (Context)->Intent?)
|
fun dispatchIntent(body: (Context)->Intent?)
|
||||||
fun showDialog(baseDialog: BaseDialog, tag: String? = null)
|
fun showDialog(baseDialog: BaseDialog, tag: String? = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TextSecurePreferences.themeState(): ThemeState {
|
||||||
|
val themeStyle = getThemeStyle().getThemeStyle()
|
||||||
|
val accentStyle = getAccentColorStyle() ?: themeStyle.getDefaultAccentColor()
|
||||||
|
val followSystem = getFollowSystemSettings()
|
||||||
|
return ThemeState(
|
||||||
|
themeStyle,
|
||||||
|
accentStyle,
|
||||||
|
followSystem
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
fun String.getThemeStyle(): Int = when (this) {
|
||||||
|
TextSecurePreferences.CLASSIC_DARK -> R.style.Classic_Dark
|
||||||
|
TextSecurePreferences.CLASSIC_LIGHT -> R.style.Classic_Light
|
||||||
|
TextSecurePreferences.OCEAN_DARK -> R.style.Ocean_Dark
|
||||||
|
TextSecurePreferences.OCEAN_LIGHT -> R.style.Ocean_Light
|
||||||
|
else -> throw NullPointerException("The style [$this] is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
@StyleRes
|
||||||
|
fun Int.getDefaultAccentColor(): Int =
|
||||||
|
if (this == R.style.Ocean_Dark || this == R.style.Ocean_Light) R.style.PrimaryBlue
|
||||||
|
else R.style.PrimaryGreen
|
||||||
|
|
||||||
|
data class ThemeState (
|
||||||
|
@StyleRes val theme: Int,
|
||||||
|
@StyleRes val accentStyle: Int,
|
||||||
|
val followSystem: Boolean
|
||||||
|
)
|
@ -3,7 +3,8 @@ package org.thoughtcrime.securesms.util
|
|||||||
import android.animation.ArgbEvaluator
|
import android.animation.ArgbEvaluator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@ -11,8 +12,6 @@ import android.widget.RelativeLayout
|
|||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
interface GlowView {
|
interface GlowView {
|
||||||
@ -22,7 +21,7 @@ interface GlowView {
|
|||||||
|
|
||||||
object GlowViewUtilities {
|
object GlowViewUtilities {
|
||||||
|
|
||||||
fun animateColorChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
fun animateColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
||||||
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
@ -34,7 +33,17 @@ object GlowViewUtilities {
|
|||||||
animation.start()
|
animation.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun animateShadowColorChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
fun animateColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
||||||
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
|
animation.duration = 250
|
||||||
|
animation.addUpdateListener { animator ->
|
||||||
|
val color = animator.animatedValue as Int
|
||||||
|
view.mainColor = color
|
||||||
|
}
|
||||||
|
animation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun animateShadowColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
||||||
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
@ -45,6 +54,17 @@ object GlowViewUtilities {
|
|||||||
}
|
}
|
||||||
animation.start()
|
animation.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun animateShadowColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
||||||
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
|
animation.duration = 250
|
||||||
|
animation.addUpdateListener { animator ->
|
||||||
|
val color = animator.animatedValue as Int
|
||||||
|
view.sessionShadowColor = color
|
||||||
|
}
|
||||||
|
animation.start()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PNModeView : LinearLayout, GlowView {
|
class PNModeView : LinearLayout, GlowView {
|
||||||
|
@ -2,46 +2,12 @@ package org.thoughtcrime.securesms.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
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.
|
* Day/night UI mode related utilities.
|
||||||
* @see <a href="https://developer.android.com/guide/topics/ui/look-and-feel/darktheme">Official Documentation</a>
|
* @see <a href="https://developer.android.com/guide/topics/ui/look-and-feel/darktheme">Official Documentation</a>
|
||||||
*/
|
*/
|
||||||
object UiModeUtilities {
|
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
|
* Whether the application UI is in the light mode
|
||||||
@ -52,14 +18,5 @@ object UiModeUtilities {
|
|||||||
val uiModeNightBit = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
val uiModeNightBit = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||||
return uiModeNightBit == Configuration.UI_MODE_NIGHT_NO
|
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);
|
|
||||||
}
|
}
|
@ -7,8 +7,11 @@ import android.animation.ValueAnimator
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import androidx.annotation.DimenRes
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DimenRes
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
|
||||||
fun View.contains(point: PointF): Boolean {
|
fun View.contains(point: PointF): Boolean {
|
||||||
@ -22,6 +25,9 @@ val View.hitRect: Rect
|
|||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
fun Context.getAccentColor() = getColorFromAttr(R.attr.colorAccent)
|
||||||
|
|
||||||
fun View.animateSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int, animationDuration: Long = 250) {
|
fun View.animateSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int, animationDuration: Long = 250) {
|
||||||
val startSize = resources.getDimension(startSizeID)
|
val startSize = resources.getDimension(startSizeID)
|
||||||
val endSize = resources.getDimension(endSizeID)
|
val endSize = resources.getDimension(endSizeID)
|
||||||
|
5
app/src/main/res/color/emoji_tab_text_color.xml
Normal file
5
app/src/main/res/color/emoji_tab_text_color.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?android:textColorPrimary" android:state_selected="true"/>
|
||||||
|
<item android:color="?android:textColorTertiary"/>
|
||||||
|
</selector>
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<corners android:radius="1000dp" />
|
|
||||||
<solid android:color="@color/core_grey_05" />
|
|
||||||
<stroke android:color="@color/white" android:width="1dp" />
|
|
||||||
</shape>
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<corners android:radius="1000dp" />
|
|
||||||
<solid android:color="@color/core_grey_05" />
|
|
||||||
<stroke android:color="?colorAccent" android:width="1dp" />
|
|
||||||
</shape>
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<corners android:radius="1000dp" />
|
|
||||||
<solid android:color="@color/core_grey_05" />
|
|
||||||
</shape>
|
|
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:shape="rectangle">
|
|
||||||
|
|
||||||
<solid android:color="@color/unimportant_button_background" />
|
|
||||||
|
|
||||||
<corners android:radius="@dimen/medium_button_corner_radius" />
|
|
||||||
|
|
||||||
<stroke android:width="@dimen/border_thickness" android:color="?android:textColorPrimary" />
|
|
||||||
</shape>
|
|
@ -3,6 +3,6 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
|
|
||||||
<solid android:color="@color/accent" />
|
<solid android:color="?colorAccent" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -4,6 +4,6 @@
|
|||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<corners android:radius="@dimen/context_menu_corner_radius" />
|
<corners android:radius="@dimen/context_menu_corner_radius" />
|
||||||
<solid android:color="@color/background_dialog" />
|
<solid android:color="?colorPrimary" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -4,7 +4,7 @@
|
|||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item>
|
<item>
|
||||||
<selector>
|
<selector>
|
||||||
<item android:drawable="@color/accent_alpha50" android:state_selected="true" />
|
<item android:drawable="?colorAccent" android:state_selected="true" />
|
||||||
</selector>
|
</selector>
|
||||||
</item>
|
</item>
|
||||||
</ripple>
|
</ripple>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item>
|
<item>
|
||||||
<selector>
|
<selector>
|
||||||
<item android:drawable="@color/accent_alpha50" android:state_selected="true" />
|
<item android:drawable="?colorAccent" android:state_selected="true" />
|
||||||
</selector>
|
</selector>
|
||||||
</item>
|
</item>
|
||||||
</ripple>
|
</ripple>
|
||||||
|
6
app/src/main/res/drawable/conversation_menu_divider.xml
Normal file
6
app/src/main/res/drawable/conversation_menu_divider.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size android:height="1dp"/>
|
||||||
|
<solid android:color="?conversation_menu_border_color"/>
|
||||||
|
</shape>
|
8
app/src/main/res/drawable/conversation_menu_gradient.xml
Normal file
8
app/src/main/res/drawable/conversation_menu_gradient.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:type="linear"
|
||||||
|
android:angle="90"
|
||||||
|
android:endColor="@color/transparent"
|
||||||
|
android:startColor="?conversation_menu_background_color"/>
|
||||||
|
</shape>
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ripple
|
<ripple
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="@color/cell_selected">
|
android:color="?colorCellRipple">
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<color android:color="?attr/conversation_pinned_background_color" />
|
<color android:color="?conversation_pinned_background_color" />
|
||||||
</item>
|
</item>
|
||||||
</ripple>
|
</ripple>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?colorCellRipple">
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<color android:color="?conversation_unread_background_color" />
|
||||||
|
</item>
|
||||||
|
</ripple>
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ripple
|
<ripple
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="@color/cell_selected">
|
android:color="?colorCellRipple">
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<color android:color="@color/cell_background" />
|
<color android:color="?colorCellBackground" />
|
||||||
</item>
|
</item>
|
||||||
</ripple>
|
</ripple>
|
@ -3,11 +3,11 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="?attr/dialog_background_color" />
|
<solid android:color="?dialog_background_color" />
|
||||||
|
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="@dimen/dialog_corner_radius"
|
android:topLeftRadius="@dimen/dialog_corner_radius"
|
||||||
android:topRightRadius="@dimen/dialog_corner_radius" />
|
android:topRightRadius="@dimen/dialog_corner_radius" />
|
||||||
|
|
||||||
<!-- <stroke android:width="@dimen/border_thickness" android:color="@color/dialog_border" />-->
|
<stroke android:width="@dimen/border_thickness" android:color="?dialog_border" />
|
||||||
</shape>
|
</shape>
|
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
<solid android:color="?attr/dialog_background_color" />
|
<solid android:color="?attr/dialog_background_color" />
|
||||||
|
|
||||||
<corners android:radius="@dimen/dialog_corner_radius" />
|
<corners android:radius="?dialogCornerRadius" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
<gradient
|
<gradient
|
||||||
android:angle="90"
|
android:angle="90"
|
||||||
android:startColor="@color/default_background_start"
|
android:startColor="?default_background_start"
|
||||||
android:endColor="@color/default_background_end"
|
android:endColor="?default_background_end"
|
||||||
android:type="linear" />
|
android:type="linear" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<solid android:color="@color/transparent" />
|
<solid android:color="@color/transparent" />
|
||||||
|
|
||||||
<corners android:radius="@dimen/medium_button_corner_radius" />
|
<corners android:radius="@dimen/dialog_button_corner_radius" />
|
||||||
|
|
||||||
<stroke android:width="@dimen/border_thickness" android:color="?android:textColorPrimary" />
|
<stroke android:width="@dimen/border_thickness" android:color="@color/transparent" />
|
||||||
</shape>
|
</shape>
|
16
app/src/main/res/drawable/ic_appearance.xml
Normal file
16
app/src/main/res/drawable/ic_appearance.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="26dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="26"
|
||||||
|
android:viewportHeight="26">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0.722,0.471h25v25h-25z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M18.554,17.315C18.379,17.315 18.213,17.245 18.09,17.122L9.084,8.112C8.962,7.989 8.892,7.818 8.892,7.643C8.892,7.468 8.962,7.302 9.089,7.179L9.391,6.881C9.658,6.619 9.938,6.343 10.214,6.067L11.676,4.605C12.876,3.405 14.08,2.201 15.284,1.006C15.428,0.861 15.582,0.765 15.717,0.677C15.761,0.651 15.809,0.62 15.849,0.59C15.958,0.511 16.089,0.471 16.225,0.471H16.597C16.715,0.471 16.829,0.502 16.93,0.563C16.956,0.577 16.983,0.59 17.004,0.603C17.105,0.655 17.245,0.725 17.372,0.848C17.731,1.185 18.016,1.47 18.274,1.741C18.511,1.991 18.642,2.288 18.673,2.604C19.04,2.63 19.369,2.827 19.745,3.203L22.066,5.524C22.596,6.054 22.722,6.658 22.434,7.28C23.046,6.991 23.642,7.109 24.172,7.63L24.408,7.862C24.645,8.094 24.881,8.326 25.104,8.567C25.249,8.72 25.363,8.887 25.463,9.031C25.503,9.088 25.542,9.145 25.586,9.202C25.669,9.316 25.717,9.456 25.717,9.596V9.968C25.717,10.112 25.669,10.248 25.586,10.362C25.546,10.419 25.507,10.476 25.468,10.533C25.363,10.682 25.244,10.857 25.087,11.01C23.655,12.446 22.223,13.882 20.792,15.318L19.128,16.982C19.128,16.982 19.08,17.052 19.071,17.061C18.957,17.21 18.782,17.302 18.594,17.315C18.581,17.315 18.567,17.315 18.554,17.315ZM10.481,7.652L18.541,15.712L19.868,14.386C21.3,12.954 22.731,11.518 24.163,10.082C24.237,10.007 24.312,9.898 24.395,9.78C24.312,9.661 24.229,9.548 24.15,9.46C23.94,9.232 23.716,9.018 23.497,8.799L23.257,8.563C23.143,8.449 23.086,8.436 23.086,8.436C23.086,8.436 23.029,8.436 22.889,8.532L20.77,9.972C20.652,10.051 20.507,10.139 20.297,10.174C20.039,10.222 19.78,10.108 19.636,9.889C19.491,9.67 19.491,9.386 19.636,9.167L19.741,9.005C19.802,8.908 19.868,8.812 19.929,8.716L21.181,6.855C21.326,6.64 21.313,6.614 21.151,6.448L18.83,4.127C18.646,3.943 18.581,3.913 18.567,3.908C18.567,3.908 18.497,3.917 18.261,4.031C18.222,4.048 18.182,4.066 18.13,4.088L18.038,4.127C17.793,4.232 17.508,4.175 17.32,3.987C17.132,3.799 17.075,3.514 17.175,3.269L17.232,3.138C17.272,3.037 17.307,2.954 17.346,2.871C17.412,2.726 17.381,2.691 17.333,2.643C17.096,2.389 16.829,2.126 16.492,1.811C16.483,1.807 16.466,1.798 16.448,1.789H16.44C16.352,1.846 16.269,1.899 16.225,1.938C15.021,3.133 13.821,4.337 12.618,5.537L11.151,6.999C10.932,7.218 10.708,7.442 10.49,7.652H10.481Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M4.618,25.471H3.922C3.852,25.471 3.782,25.458 3.716,25.436C3.708,25.436 3.66,25.419 3.655,25.414C2.425,25.183 1.501,24.451 1.006,23.295C0.914,23.081 0.857,22.866 0.809,22.678C0.792,22.604 0.77,22.529 0.748,22.455C0.73,22.393 0.722,22.332 0.722,22.271V21.579C0.722,21.535 0.722,21.492 0.735,21.448C0.941,20.441 1.374,19.697 2.066,19.162C2.364,18.935 2.718,18.668 3.095,18.418C4.561,17.45 6.028,16.483 7.499,15.524C7.981,15.209 8.033,14.894 8.025,14.626C8.016,14.394 7.911,14.193 7.688,13.97L7.254,13.536C6.86,13.147 6.47,12.757 6.081,12.363C5.437,11.71 5.433,10.866 6.072,10.217C6.475,9.81 6.878,9.403 7.285,8.996L7.758,8.523C7.797,8.484 7.841,8.444 7.88,8.418C8.143,8.208 8.541,8.208 8.778,8.449L17.766,17.437C17.889,17.56 17.959,17.731 17.959,17.906C17.959,18.081 17.885,18.247 17.762,18.37L17.495,18.633C17.272,18.851 17.044,19.07 16.821,19.294L16.488,19.626C16.317,19.797 16.151,19.964 15.98,20.13C15.328,20.76 14.487,20.756 13.839,20.117C13.305,19.591 12.775,19.057 12.25,18.528C12.018,18.295 11.812,18.186 11.58,18.177C11.199,18.16 10.927,18.317 10.678,18.698L10.639,18.76C9.688,20.218 8.703,21.728 7.714,23.199C7.39,23.681 7.022,24.136 6.619,24.561C6.203,24.999 5.638,25.279 4.894,25.419L4.82,25.445C4.758,25.463 4.693,25.476 4.627,25.476L4.618,25.471ZM4.036,24.158H4.513C4.561,24.14 4.605,24.132 4.649,24.123C5.113,24.035 5.446,23.882 5.665,23.65C6.015,23.282 6.339,22.879 6.619,22.464C7.604,20.997 8.585,19.495 9.535,18.037L9.575,17.976C10.205,17.008 11.037,16.833 11.628,16.859C12.193,16.881 12.718,17.131 13.182,17.599C13.708,18.129 14.233,18.659 14.763,19.18C14.899,19.316 14.929,19.316 15.069,19.18C15.236,19.018 15.398,18.856 15.56,18.694L15.901,18.352C16.059,18.199 16.216,18.042 16.374,17.893L8.305,9.832L8.213,9.924C7.81,10.327 7.407,10.725 7.009,11.133C6.886,11.255 6.869,11.286 7.018,11.435C7.403,11.824 7.793,12.214 8.178,12.599L8.611,13.033C9.067,13.488 9.312,14.005 9.334,14.565C9.36,15.152 9.185,15.984 8.213,16.618C6.742,17.577 5.275,18.541 3.813,19.508C3.471,19.736 3.138,19.985 2.858,20.2C2.438,20.524 2.171,20.984 2.026,21.645V22.179C2.044,22.236 2.057,22.293 2.07,22.35C2.11,22.507 2.149,22.656 2.202,22.774C2.526,23.532 3.082,23.974 3.905,24.123C3.948,24.132 3.988,24.14 4.023,24.154L4.036,24.158ZM4.312,23.668H4.303C3.331,23.663 2.534,22.866 2.53,21.89C2.53,21.413 2.714,20.962 3.055,20.62C3.392,20.283 3.839,20.095 4.312,20.095H4.321C5.297,20.099 6.094,20.9 6.098,21.872C6.098,22.345 5.914,22.796 5.573,23.138C5.231,23.475 4.785,23.663 4.312,23.663V23.668ZM4.312,21.413C4.189,21.413 4.075,21.461 3.984,21.553C3.892,21.645 3.843,21.763 3.843,21.886C3.843,22.14 4.058,22.35 4.312,22.354C4.43,22.337 4.557,22.306 4.649,22.214C4.741,22.122 4.789,22.004 4.789,21.881C4.789,21.632 4.57,21.413 4.321,21.413H4.312Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -3,7 +3,7 @@
|
|||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24"
|
android:viewportHeight="24"
|
||||||
android:tint="?attr/colorControlNormal">
|
android:tint="?colorAccent">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="m12,2.4355c-5.2796,0 -9.5645,4.2848 -9.5645,9.5645 0,5.2796 4.2848,9.5645 9.5645,9.5645 5.2796,0 9.5645,-4.2848 9.5645,-9.5645 0,-5.2796 -4.2848,-9.5645 -9.5645,-9.5645zM12.123,7.9375 L15.6777,11.4922 14.9707,12.1992 12.623,9.8515v6.1797h-1v-6.1797l-1.9961,1.9941 -0.3535,0.3535 -0.707,-0.707 0.3535,-0.3535 3.2031,-3.2012z"
|
android:pathData="m12,2.4355c-5.2796,0 -9.5645,4.2848 -9.5645,9.5645 0,5.2796 4.2848,9.5645 9.5645,9.5645 5.2796,0 9.5645,-4.2848 9.5645,-9.5645 0,-5.2796 -4.2848,-9.5645 -9.5645,-9.5645zM12.123,7.9375 L15.6777,11.4922 14.9707,12.1992 12.623,9.8515v6.1797h-1v-6.1797l-1.9961,1.9941 -0.3535,0.3535 -0.707,-0.707 0.3535,-0.3535 3.2031,-3.2012z"
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
|
||||||
|
</vector>
|
@ -2,8 +2,7 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24"
|
android:viewportHeight="24">
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
11
app/src/main/res/drawable/ic_clear_data.xml
Normal file
11
app/src/main/res/drawable/ic_clear_data.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="25dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="25"
|
||||||
|
android:viewportHeight="26">
|
||||||
|
<path
|
||||||
|
android:pathData="M19.907,7.674H19.907H4.54H4.54C4.317,7.674 4.095,7.719 3.888,7.806L3.888,7.806C3.681,7.893 3.491,8.023 3.334,8.189C3.176,8.355 3.054,8.554 2.978,8.775L3.922,9.097L2.978,8.775C2.903,8.996 2.877,9.231 2.904,9.465L2.904,9.465L2.904,9.469L4.555,23.412C4.555,23.413 4.555,23.413 4.555,23.414C4.603,23.823 4.807,24.189 5.111,24.447C5.415,24.705 5.798,24.84 6.187,24.84H6.188H18.26H18.26C18.649,24.84 19.032,24.705 19.336,24.447C19.64,24.189 19.844,23.823 19.892,23.414C19.892,23.413 19.892,23.413 19.892,23.412L21.543,9.469L21.544,9.465C21.57,9.231 21.544,8.996 21.469,8.775L21.469,8.775C21.393,8.554 21.271,8.355 21.113,8.189C20.956,8.023 20.766,7.893 20.559,7.806L20.17,8.728L20.559,7.806C20.352,7.719 20.13,7.674 19.907,7.674ZM21.412,1.84H3.031C2.045,1.84 1.149,2.609 1.149,3.674V5.828C1.149,6.893 2.045,7.662 3.031,7.662H21.412C22.398,7.662 23.294,6.893 23.294,5.828V3.674C23.294,2.609 22.398,1.84 21.412,1.84Z"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#FF3A3A"
|
||||||
|
android:strokeColor="?colorPrimaryDark"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_conversations.xml
Normal file
9
app/src/main/res/drawable/ic_conversations.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="25dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="25"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M1.137,21.463C1.018,21.463 0.899,21.447 0.789,21.406C0.441,21.284 0.222,20.983 0.222,20.649V3.549C0.222,1.856 1.768,0.471 3.68,0.471H20.763C22.666,0.471 24.222,1.848 24.222,3.549V14.68C24.222,16.374 22.675,17.758 20.763,17.758H5.638L1.795,21.227C1.622,21.382 1.384,21.471 1.146,21.471L1.137,21.463ZM3.68,2.1C2.784,2.1 2.052,2.751 2.052,3.549V18.67L4.595,16.374C4.769,16.219 4.998,16.13 5.245,16.13H20.754C21.651,16.13 22.383,15.478 22.383,14.68V3.549C22.383,2.751 21.651,2.1 20.754,2.1H3.671H3.68Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user