diff --git a/README.md b/README.md
index 26310607e..a26db7ccd 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,6 @@
privacy
* Rely on existing, well established protocols (XMPP)
* Do not require a Google Account or specifically Google Cloud Messaging (GCM)
-* Require as few permissions as possible
## Features
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index a7eaad0bc..f3bfdc7eb 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -22,6 +22,7 @@
+
-
-
-
-
-
+ android:label="@string/title_activity_share_location"/>
+
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 89068fab4..370b68057 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -55,6 +55,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.RecordingActivity;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExifHelper;
import eu.siacs.conversations.utils.FileUtils;
@@ -76,11 +77,13 @@ public class FileBackend {
this.mXmppConnectionService = service;
}
- private void createNoMedia() {
- final File nomedia = new File(getConversationsDirectory("Files") + ".nomedia");
- if (!nomedia.exists()) {
+ private void createNoMedia(File diretory) {
+ final File noMedia = new File(diretory,".nomedia");
+ if (!noMedia.exists()) {
try {
- nomedia.createNewFile();
+ if (!noMedia.createNewFile()) {
+ Log.d(Config.LOGTAG,"created nomedia file "+noMedia.getAbsolutePath());
+ }
} catch (Exception e) {
Log.d(Config.LOGTAG, "could not create nomedia file");
}
@@ -88,16 +91,25 @@ public class FileBackend {
}
public void updateMediaScanner(File file) {
- String path = file.getAbsolutePath();
- if (!path.startsWith(getConversationsDirectory("Files"))) {
+ if (!isInDirectoryThatShouldNotBeScanned(mXmppConnectionService, file)) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
mXmppConnectionService.sendBroadcast(intent);
- } else {
- createNoMedia();
+ } else if (file.getAbsolutePath().startsWith(getAppMediaDirectory(mXmppConnectionService))) {
+ createNoMedia(file.getParentFile());
}
}
+ private static boolean isInDirectoryThatShouldNotBeScanned(Context context, File file) {
+ String path = file.getAbsolutePath();
+ for(String type : new String[]{RecordingActivity.STORAGE_DIRECTORY_TYPE_NAME, "Files"}) {
+ if (path.startsWith(getConversationsDirectory(context, type))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public boolean deleteFile(Message message) {
File file = getFile(message);
if (file.delete()) {
@@ -186,13 +198,21 @@ public class FileBackend {
}
public String getConversationsDirectory(final String type) {
+ return getConversationsDirectory(mXmppConnectionService, type);
+ }
+
+ public static String getConversationsDirectory(Context context, final String type) {
if (Config.ONLY_INTERNAL_STORAGE) {
- return mXmppConnectionService.getFilesDir().getAbsolutePath() + "/" + type + "/";
+ return context.getFilesDir().getAbsolutePath() + "/" + type + "/";
} else {
- return Environment.getExternalStorageDirectory() + "/Conversations/Media/Conversations " + type + "/";
+ return getAppMediaDirectory(context)+context.getString(R.string.app_name)+" " + type + "/";
}
}
+ public static String getAppMediaDirectory(Context context) {
+ return Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+context.getString(R.string.app_name)+"/Media/";
+ }
+
public static String getConversationsLogsDirectory() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Conversations/";
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 338d5fdee..f7a0ffb29 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -279,7 +279,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return false;
}
}
- if (hasStoragePermission(REQUEST_ADD_EDITOR_CONTENT)) {
+ if (hasPermissions(REQUEST_ADD_EDITOR_CONTENT, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
attachImageToConversation(inputContentInfo.getContentUri());
} else {
mPendingEditorContent = inputContentInfo.getContentUri();
@@ -1284,12 +1284,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
public void attachFile(final int attachmentChoice) {
- if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO || attachmentChoice == ATTACHMENT_CHOICE_RECORD_VIDEO) {
- if (!hasStorageAndCameraPermission(attachmentChoice)) {
+ if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
+ if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO)) {
+ return;
+ }
+ } else if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO || attachmentChoice == ATTACHMENT_CHOICE_RECORD_VIDEO) {
+ if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)) {
return;
}
} else if (attachmentChoice != ATTACHMENT_CHOICE_LOCATION) {
- if (!Config.ONLY_INTERNAL_STORAGE && !hasStoragePermission(attachmentChoice)) {
+ if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
return;
}
}
@@ -1365,7 +1369,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
} else {
@StringRes int res;
- if (Manifest.permission.CAMERA.equals(getFirstDenied(grantResults, permissions))) {
+ String firstDenied = getFirstDenied(grantResults, permissions);
+ if (Manifest.permission.RECORD_AUDIO.equals(firstDenied)) {
+ res = R.string.no_microphone_permission;
+ } else if (Manifest.permission.CAMERA.equals(firstDenied)) {
res = R.string.no_camera_permission;
} else {
res = R.string.no_storage_permission;
@@ -1375,7 +1382,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
public void startDownloadable(Message message) {
- if (!Config.ONLY_INTERNAL_STORAGE && !hasStoragePermission(REQUEST_START_DOWNLOAD)) {
+ if (!hasPermissions(REQUEST_START_DOWNLOAD, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
this.mPendingDownloadableMessage = message;
return;
}
@@ -1443,27 +1450,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
builder.create().show();
}
- private boolean hasStoragePermission(int requestCode) {
+ private boolean hasPermissions(int requestCode, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
- return false;
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-
- private boolean hasStorageAndCameraPermission(int requestCode) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- List missingPermissions = new ArrayList<>();
- if (!Config.ONLY_INTERNAL_STORAGE && activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- missingPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- }
- if (activity.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- missingPermissions.add(Manifest.permission.CAMERA);
+ final List missingPermissions = new ArrayList<>();
+ for(String permission : permissions) {
+ if (Config.ONLY_INTERNAL_STORAGE && permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ continue;
+ }
+ if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ missingPermissions.add(permission);
+ }
}
if (missingPermissions.size() == 0) {
return true;
@@ -1489,7 +1485,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final PresenceSelector.OnPresenceSelected callback = () -> {
Intent intent = new Intent();
boolean chooser = false;
- String fallbackPackageId = null;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
@@ -1515,12 +1510,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
- intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
- fallbackPackageId = "eu.siacs.conversations.voicerecorder";
+ intent = new Intent(getActivity(), RecordingActivity.class);
break;
case ATTACHMENT_CHOICE_LOCATION:
- intent.setAction("eu.siacs.conversations.location.request");
- fallbackPackageId = "eu.siacs.conversations.sharelocation";
+ intent = new Intent(getActivity(), ShareLocationActivity.class);
break;
}
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
@@ -1531,8 +1524,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
} else {
startActivityForResult(intent, attachmentChoice);
}
- } else if (fallbackPackageId != null) {
- startActivity(getInstallApkIntent(fallbackPackageId));
}
};
if (account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) {
@@ -1543,28 +1534,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
- private Intent getInstallApkIntent(final String packageId) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("market://details?id=" + packageId));
- if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
- return intent;
- } else {
- intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId));
- return intent;
- }
- }
-
@Override
public void onResume() {
- new Handler().post(() -> {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
- final PackageManager packageManager = activity.getPackageManager();
- ConversationMenuConfigurator.updateAttachmentAvailability(packageManager);
- getActivity().invalidateOptionsMenu();
- });
super.onResume();
binding.messagesView.post(this::fireReadEvent);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java
new file mode 100644
index 000000000..2829feb6a
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java
@@ -0,0 +1,192 @@
+package eu.siacs.conversations.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaRecorder;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.utils.ThemeHelper;
+
+public class RecordingActivity extends Activity implements View.OnClickListener {
+
+ public static String STORAGE_DIRECTORY_TYPE_NAME = "Recordings";
+
+ private TextView mTimerTextView;
+ private Button mCancelButton;
+ private Button mStopButton;
+
+ private MediaRecorder mRecorder;
+ private long mStartTime = 0;
+
+ private Handler mHandler = new Handler();
+ private Runnable mTickExecutor = new Runnable() {
+ @Override
+ public void run() {
+ tick();
+ mHandler.postDelayed(mTickExecutor, 100);
+ }
+ };
+
+ private File mOutputFile;
+ private boolean mShouldFinishAfterWrite = false;
+
+ private FileObserver mFileObserver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(ThemeHelper.findDialog(this));
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_recording);
+ this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
+ this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
+ this.mCancelButton.setOnClickListener(this);
+ this.mStopButton = (Button) this.findViewById(R.id.share_button);
+ this.mStopButton.setOnClickListener(this);
+ this.setFinishOnTouchOutside(false);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (!startRecording()) {
+ mStopButton.setEnabled(false);
+ Toast.makeText(this, R.string.unable_to_start_recording, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mRecorder != null) {
+ mHandler.removeCallbacks(mTickExecutor);
+ stopRecording(false);
+ }
+ if (mFileObserver != null) {
+ mFileObserver.stopWatching();
+ }
+ }
+
+ private boolean startRecording() {
+ mRecorder = new MediaRecorder();
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
+ mRecorder.setAudioEncodingBitRate(48000);
+ mRecorder.setAudioSamplingRate(16000);
+ setupOutputFile();
+ mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
+
+ try {
+ mRecorder.prepare();
+ mRecorder.start();
+ mStartTime = SystemClock.elapsedRealtime();
+ mHandler.postDelayed(mTickExecutor, 100);
+ Log.d("Voice Recorder", "started recording to " + mOutputFile.getAbsolutePath());
+ return true;
+ } catch (Exception e) {
+ Log.e("Voice Recorder", "prepare() failed " + e.getMessage());
+ return false;
+ }
+ }
+
+ protected void stopRecording(boolean saveFile) {
+ mShouldFinishAfterWrite = saveFile;
+ mRecorder.stop();
+ mRecorder.release();
+ mRecorder = null;
+ mStartTime = 0;
+ if (!saveFile && mOutputFile != null) {
+ if (mOutputFile.delete()) {
+ Log.d(Config.LOGTAG,"deleted canceled recording");
+ }
+ }
+ }
+
+ private static File generateOutputFilename(Context context) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
+ return new File(FileBackend.getConversationsDirectory(context, STORAGE_DIRECTORY_TYPE_NAME) + "/" + filename);
+ }
+
+ private void setupOutputFile() {
+ mOutputFile = generateOutputFilename(this);
+ File parentDirectory = mOutputFile.getParentFile();
+ if (parentDirectory.mkdirs()) {
+ Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath());
+ }
+ File noMedia = new File(parentDirectory, ".nomedia");
+ if (!noMedia.exists()) {
+ try {
+ if (noMedia.createNewFile()) {
+ Log.d(Config.LOGTAG, "created nomedia file in " + parentDirectory.getAbsolutePath());
+ }
+ } catch (IOException e) {
+ Log.d(Config.LOGTAG, "unable to create nomedia file in " + parentDirectory.getAbsolutePath(), e);
+ }
+ }
+ setupFileObserver(parentDirectory);
+ }
+
+ private void setupFileObserver(File directory) {
+ mFileObserver = new FileObserver(directory.getAbsolutePath()) {
+ @Override
+ public void onEvent(int event, String s) {
+ if (s != null && s.equals(mOutputFile.getName()) && event == FileObserver.CLOSE_WRITE) {
+ if (mShouldFinishAfterWrite) {
+ setResult(Activity.RESULT_OK, new Intent().setData(Uri.fromFile(mOutputFile)));
+ finish();
+ }
+ }
+ }
+ };
+ mFileObserver.startWatching();
+ }
+
+ private void tick() {
+ long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
+ int minutes = (int) (time / 60000);
+ int seconds = (int) (time / 1000) % 60;
+ int milliseconds = (int) (time / 100) % 10;
+ mTimerTextView.setText(minutes + ":" + (seconds < 10 ? "0" + seconds : seconds) + "." + milliseconds);
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.cancel_button:
+ mHandler.removeCallbacks(mTickExecutor);
+ stopRecording(false);
+ setResult(RESULT_CANCELED);
+ finish();
+ break;
+ case R.id.share_button:
+ mStopButton.setEnabled(false);
+ mStopButton.setText(R.string.please_wait);
+ mHandler.removeCallbacks(mTickExecutor);
+ mHandler.postDelayed(() -> stopRecording(true), 500);
+ break;
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/util/AttachmentTool.java b/src/main/java/eu/siacs/conversations/ui/util/AttachmentTool.java
index 86b7bfab3..20780bbac 100644
--- a/src/main/java/eu/siacs/conversations/ui/util/AttachmentTool.java
+++ b/src/main/java/eu/siacs/conversations/ui/util/AttachmentTool.java
@@ -45,8 +45,8 @@ public class AttachmentTool {
if (intent == null) {
return uris;
}
- Uri uri = intent.getData();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) {
+ final Uri uri = intent.getData();
+ if (uri == null) {
final ClipData clipData = intent.getClipData();
if (clipData != null) {
for (int i = 0; i < clipData.getItemCount(); ++i) {
diff --git a/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java b/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java
index 1ba05badc..388d07d77 100644
--- a/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java
+++ b/src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java
@@ -45,13 +45,7 @@ import eu.siacs.conversations.entities.Message;
public class ConversationMenuConfigurator {
- private static boolean showSoundRecorderAttachment = false;
- private static boolean showLocationAttachment = false;
-
-
public static void configureAttachmentMenu(@NonNull Conversation conversation, Menu menu) {
- final MenuItem menuAttachSoundRecorder = menu.findItem(R.id.attach_record_voice);
- final MenuItem menuAttachLocation = menu.findItem(R.id.attach_location);
final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
final boolean visible;
@@ -66,9 +60,6 @@ public class ConversationMenuConfigurator {
if (!visible) {
return;
}
-
- menuAttachLocation.setVisible(showLocationAttachment);
- menuAttachSoundRecorder.setVisible(showSoundRecorderAttachment);
}
public static void configureEncryptionMenu(@NonNull Conversation conversation, Menu menu) {
@@ -118,9 +109,4 @@ public class ConversationMenuConfigurator {
break;
}
}
-
- public static void updateAttachmentAvailability(PackageManager packageManager) {
- showSoundRecorderAttachment = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(packageManager) != null;
- showLocationAttachment = new Intent("eu.siacs.conversations.location.request").resolveActivity(packageManager) != null;
- }
}
diff --git a/src/main/java/eu/siacs/conversations/utils/ThemeHelper.java b/src/main/java/eu/siacs/conversations/utils/ThemeHelper.java
index 0214b7f6e..460ef9b5f 100644
--- a/src/main/java/eu/siacs/conversations/utils/ThemeHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/ThemeHelper.java
@@ -61,6 +61,21 @@ public class ThemeHelper {
}
}
+ public static int findDialog(Context context) {
+ final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ final Resources resources = context.getResources();
+ final boolean dark = sharedPreferences.getString(SettingsActivity.THEME, resources.getString(R.string.theme)).equals("dark");
+ final String fontSize = sharedPreferences.getString("font_size", resources.getString(R.string.default_font_size));
+ switch (fontSize) {
+ case "medium":
+ return dark ? R.style.ConversationsTheme_Dark_Dialog_Medium : R.style.ConversationsTheme_Dialog_Medium;
+ case "large":
+ return dark ? R.style.ConversationsTheme_Dark_Dialog_Large : R.style.ConversationsTheme_Dialog_Large;
+ default:
+ return dark ? R.style.ConversationsTheme_Dark_Dialog : R.style.ConversationsTheme_Dialog;
+ }
+ }
+
public static boolean isDark(@StyleRes int id) {
switch (id) {
case R.style.ConversationsTheme_Dark:
diff --git a/src/main/res/layout/activity_recording.xml b/src/main/res/layout/activity_recording.xml
new file mode 100644
index 000000000..c14338db4
--- /dev/null
+++ b/src/main/res/layout/activity_recording.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index 1e6683c12..871c6a46d 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -6,6 +6,7 @@
+
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index d0453900b..35e1c5440 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -699,4 +699,7 @@
Share location
Show location
Share
+ Unable to start recording
+ Please wait…
+ Conversations needs access to the microphone
diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml
index 282d6c9d7..e236a2943 100644
--- a/src/main/res/values/styles.xml
+++ b/src/main/res/values/styles.xml
@@ -1,5 +1,11 @@
+
+
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index 0a06a23a3..27bbede85 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -239,4 +239,47 @@
- @color/grey300
- @drawable/background
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file