Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
970cde737a | ||
|
|
eb7f61fbc8 | ||
|
|
4069ddc200 | ||
|
|
88dcc844c8 | ||
|
|
f8575da2a9 | ||
|
|
ab737511a7 | ||
|
|
48c4e04a28 | ||
|
|
713fd3540f | ||
|
|
2e10cc5d11 | ||
|
|
f06b6d442f | ||
|
|
4fd8832d90 | ||
|
|
61e3d49670 | ||
|
|
8610ad9a03 | ||
|
|
83f9bcdf88 | ||
|
|
0d7ea6174d | ||
|
|
5aab372920 | ||
|
|
32e5bfa36d | ||
|
|
3a90b17301 | ||
|
|
b706f679a9 | ||
|
|
6f4cf5674e | ||
|
|
3ffba02332 | ||
|
|
937791eb5c | ||
|
|
568c0044a4 | ||
|
|
070fe0a620 | ||
|
|
38d78a8e6a |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
11
.github/ISSUE_TEMPLATE/give-me-some-idea-for-this-app.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/give-me-some-idea-for-this-app.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Give Me Some Idea For This App
|
||||
about: If your idea is unique i will definitely implement the Idea and mention you
|
||||
in my repo and add the feature to the app
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
6
.idea/AndroidProjectSystem.xml
generated
Normal file
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
170
README.md
170
README.md
@@ -1,15 +1,14 @@
|
||||
<div align="center">
|
||||
<img src="app/src/main/assets/logo.png" alt="Calculator Hide File App Logo" width="200" />
|
||||
|
||||
|
||||
# Calculator Hide File App
|
||||
# 📂 Calculator Hide File App for Android 📂
|
||||
|
||||
<a href="https://github.com/Binondi/Calculator-Hide-Files/releases/latest">
|
||||
<img alt="Latest release" src="https://img.shields.io/badge/Releases-v1.0-blue?logo=github&style=for-the-badge">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/Binondi/Calculator-Hide-Files/releases/latest">
|
||||
<img alt="Downloads" src="https://img.shields.io/badge/Downloads-1.2k-blue?logo=github&style=for-the-badge">
|
||||
<img alt="Downloads" src="https://img.shields.io/badge/Downloads-1.3k-blue?logo=github&style=for-the-badge">
|
||||
</a>
|
||||
|
||||
<a href="LICENSE">
|
||||
@@ -19,132 +18,137 @@
|
||||
</div>
|
||||
|
||||
---
|
||||
Welcome to the **Calculator Hide File App**! This app is a unique and secure way to hide your sensitive files under the disguise of a fully functional calculator.
|
||||
|
||||
## 😍 Why Choose This App?
|
||||
The **Calculator Hide File App** is an **open-source** application, allowing you to inspect the code yourself. This ensures **complete transparency** and guarantees that your **privacy remains uncompromised**. 🔒✅
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
## 🔥 About Calculator Hide File App
|
||||
|
||||
- **Dual Functionality**: Operates as a regular calculator and a file hiding app.
|
||||
- **User-Friendly Interface**: Simple and intuitive design for easy use.
|
||||
- **Secure File Storage**: Protects your hidden files with a passcode.
|
||||
- **Passcode Protection**: Access the hidden files by entering a secret passcode in the calculator.
|
||||
- **File Management**: Easily hide, unhide, and manage files within the app.
|
||||
The **Calculator Hide File App** is an innovative **Android file-hiding app** that disguises itself as a **fully functional calculator**. It helps you **securely store** private files and protect them with a **hidden passcode**.
|
||||
|
||||
> **⭐ Why Choose This App?**
|
||||
> - Hide images, videos, documents & other files securely.
|
||||
> - Works like a **real calculator** with hidden storage mode.
|
||||
> - No one will suspect it’s a file vault!
|
||||
|
||||
---
|
||||
|
||||
## Screenshots
|
||||
<div>
|
||||
|
||||
<img src="app/src/main/assets/Screenshot_1.jpg" alt="Screenshot 1" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_2.jpg" alt="Screenshot 2" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_3.jpg" alt="Screenshot 3" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_4.jpg" alt="Screenshot 4" width="24%">
|
||||
## 🚀 Features
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<img src="app/src/main/assets/Screenshot_5.jpg" alt="Screenshot 5" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_6.jpg" alt="Screenshot 6" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_7.jpg" alt="Screenshot 7" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_8.jpg" alt="Screenshot 8" width="24%">
|
||||
✅ **Dual Functionality** – A working **calculator** & a **file vault** in one app.
|
||||
✅ **Secret Passcode** – Unlock hidden files by entering a secret code.
|
||||
✅ **Secure File Manager** – Hide/unhide files easily.
|
||||
✅ **Fast & Lightweight** – Smooth performance on all Android devices.
|
||||
✅ **No Root Required** – Works without rooting your phone.
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Screenshots
|
||||
|
||||
<div align="center">
|
||||
<img src="app/src/main/assets/Screenshot_1.jpg" alt="Calculator Hide File App - Home Screen" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_2.jpg" alt="Calculator Hide File App - Secure File Storage" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_3.jpg" alt="Calculator Hide File App - Passcode Protection" width="24%">
|
||||
<img src="app/src/main/assets/Screenshot_4.jpg" alt="Calculator Hide File App - Hidden Files Manager" width="24%">
|
||||
</div>
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Calculator Mode**:
|
||||
- Perform basic arithmetic operations just like any regular calculator.
|
||||
2. **Setup Password**:
|
||||
- Enter `123456=` to setup your password.
|
||||
3. **Hidden Mode**:
|
||||
- Enter your secret passcode and hit the `=` button to unlock the hidden file manager.
|
||||
5. **File Management**:
|
||||
- Add files to hide them securely.
|
||||
- Retrieve or unhide files as needed.
|
||||
|
||||
---
|
||||
|
||||
## ☕ Support
|
||||
## 🔑 How It Works
|
||||
|
||||
Support My development by donating money. Thank you very much for your help! ❤️
|
||||
1. **Use as a Regular Calculator**
|
||||
- Perform standard arithmetic operations like a normal calculator.
|
||||
|
||||
[<img src="https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA"
|
||||
alt="Sponsor the project on GitHub"
|
||||
height="40">](https://github.com/sponsors/Binondi/Calculator-Hide-Files) [<img src="https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white"
|
||||
alt="Donate with PayPal"
|
||||
height="40">](https://www.paypal.me/BinondiBorthakur56) [<img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black"
|
||||
alt="Donate with buymeacoffee"
|
||||
height="40">](https://buymeacoffee.com/binondi)
|
||||
2. **Enter Secret Passcode**
|
||||
- Type `123456` and press `=` to set up your password.
|
||||
- Enter your **custom passcode** and hit `=` to unlock the hidden file manager.
|
||||
|
||||
|
||||
---
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
- Android Studio (for development)
|
||||
- A device or emulator running Android 6.0 or higher
|
||||
|
||||
### Steps
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/YourUsername/CalculatorHideFileApp.git
|
||||
```
|
||||
2. Open the project in Android Studio.
|
||||
3. Build and run the app on your device or emulator.
|
||||
3. **Manage Hidden Files**
|
||||
- Add, remove, and restore hidden files.
|
||||
- Files stay protected even after closing the app.
|
||||
|
||||
---
|
||||
|
||||
## Technologies Used
|
||||
## 📥 Download & Installation
|
||||
|
||||
- **Programming Language**: Java/Kotlin
|
||||
- **Development Platform**: Android Studio
|
||||
- **UI Framework**: Android XML layouts
|
||||
- **File Storage**: Secure internal storage and MediaStore API
|
||||
### 🔗 **[Download the Latest Version Here](https://github.com/Binondi/Calculator-Hide-Files/releases/latest)**
|
||||
|
||||
### 🔹 Prerequisites
|
||||
- **Android 6.0 or higher**
|
||||
- **Storage permissions enabled**
|
||||
|
||||
### 🔹 Installation Steps
|
||||
```bash
|
||||
git clone https://github.com/Binondi/Calculator-Hide-Files.git
|
||||
```
|
||||
- Open **Android Studio** and build the project.
|
||||
- Install the APK on your **Android device or emulator**.
|
||||
|
||||
---
|
||||
|
||||
## Usage Instructions
|
||||
## 🛠️ Technologies Used
|
||||
|
||||
1. Open the app.
|
||||
2. Use the calculator as normal.
|
||||
3. Enter the secret passcode and press `=` to switch to the hidden file manager.
|
||||
4. Add or manage your hidden files.
|
||||
- **Programming Language**: Kotlin
|
||||
- **UI Framework**: XML (For UI)
|
||||
- **File Storage**: Secure internal storage & MediaStore API
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
## 🎯 Why Use Calculator Hide File App?
|
||||
|
||||
- **No One Can Detect Your Files** – Works like a real calculator.
|
||||
- **100% Secure** – Your private files stay hidden, even if someone opens the app.
|
||||
- **Completely Free & Open Source** – Modify or contribute to the project.
|
||||
|
||||
---
|
||||
|
||||
## ❤️ Support the Project
|
||||
|
||||
If you find this app useful, please consider supporting the development. 🙏
|
||||
|
||||
[](https://github.com/sponsors/Binondi)
|
||||
[](https://paypal.me/BinondiBorthakur56)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Contributing
|
||||
|
||||
We welcome contributions! To contribute:
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a new branch for your feature or bugfix:
|
||||
1. **Fork the repository**
|
||||
2. **Create a new branch**
|
||||
```bash
|
||||
git checkout -b feature-name
|
||||
```
|
||||
3. Commit your changes:
|
||||
3. **Commit your changes**
|
||||
```bash
|
||||
git commit -m "Add a new feature"
|
||||
```
|
||||
4. Push to the branch:
|
||||
4. **Push to GitHub**
|
||||
```bash
|
||||
git push origin feature-name
|
||||
```
|
||||
5. Open a Pull Request.
|
||||
5. **Create a Pull Request**
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
## 📜 License
|
||||
|
||||
This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.
|
||||
This project is licensed under the **Apache License 2.0**.
|
||||
See the full license [here](LICENSE).
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
## 📧 Contact
|
||||
|
||||
For any inquiries or feedback, reach out to:
|
||||
- **Email**: binondiborthakur56@gmail.com
|
||||
- **GitHub**: [Binondi](https://github.com/Binondi)
|
||||
For any questions or feedback:
|
||||
📩 **Email**: binondiborthakur56@gmail.com
|
||||
🐙 **GitHub**: [Binondi](https://github.com/Binondi)
|
||||
|
||||
---
|
||||
|
||||
Thank you for using the **Calculator Hide File App**! We hope you find it secure and easy to use.
|
||||
### 🎉 Thank You for Using Calculator Hide File App! 🎉
|
||||
🚀 **Keep your files secure and hidden!** 🚀
|
||||
|
||||
@@ -11,21 +11,28 @@ android {
|
||||
applicationId = "devs.org.calculator"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
versionCode = 4
|
||||
versionName = "1.3"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
debug {
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
@@ -36,6 +43,21 @@ android {
|
||||
buildFeatures{
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += listOf(
|
||||
"META-INF/DEPENDENCIES",
|
||||
"META-INF/LICENSE",
|
||||
"META-INF/LICENSE.txt",
|
||||
"META-INF/license.txt",
|
||||
"META-INF/NOTICE",
|
||||
"META-INF/NOTICE.txt",
|
||||
"META-INF/notice.txt",
|
||||
"META-INF/ASL2.0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -45,15 +67,17 @@ dependencies {
|
||||
implementation(libs.material)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.gridlayout)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
//custom dependencies
|
||||
implementation(libs.exp4j)
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||
implementation("com.github.chrisbanes:PhotoView:2.3.0")
|
||||
implementation("androidx.viewpager:viewpager:1.0.0")
|
||||
implementation("com.jsibbold:zoomage:1.3.1")
|
||||
implementation(libs.glide)
|
||||
implementation(libs.androidx.documentfile)
|
||||
implementation(libs.photoview)
|
||||
implementation(libs.androidx.viewpager)
|
||||
implementation(libs.zoomage)
|
||||
implementation(libs.lottie)
|
||||
}
|
||||
85
app/proguard-rules.pro
vendored
85
app/proguard-rules.pro
vendored
@@ -1,21 +1,70 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
# Keep your MainActivity and Application class
|
||||
-keep public class devs.org.calculator.activities.MainActivity
|
||||
-keep public class devs.org.calculator.activities.SetupPasswordActivity
|
||||
-keep public class devs.org.calculator.activities.HiddenVaultActivity
|
||||
-keep public class devs.org.calculator.** { *; }
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
# Keep exp4j library since it's used for expression evaluation
|
||||
-keep class net.objecthunter.exp4j.** { *; }
|
||||
-dontwarn net.objecthunter.exp4j.**
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
# Keep Google Material components
|
||||
-keep class com.google.android.material.** { *; }
|
||||
-dontwarn com.google.android.material.**
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
# Keep Android X components
|
||||
-keep class androidx.** { *; }
|
||||
-keep interface androidx.** { *; }
|
||||
|
||||
# Keep any classes with ViewBinding
|
||||
-keep class devs.org.calculator.databinding.** { *; }
|
||||
|
||||
# Keep any callback interfaces
|
||||
-keep class devs.org.calculator.callbacks.** { *; }
|
||||
-keep interface devs.org.calculator.callbacks.** { *; }
|
||||
|
||||
# Keep classes used for regex pattern matching
|
||||
-keep class java.util.regex.** { *; }
|
||||
|
||||
# Keep annotation classes
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes Signature
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Keep Parcelable classes (might be needed for Intent extras)
|
||||
-keep class * implements android.os.Parcelable {
|
||||
public static final android.os.Parcelable$Creator *;
|
||||
}
|
||||
|
||||
# Keep FileManager classes since they work with storage permissions
|
||||
-keep class devs.org.calculator.utils.FileManager { *; }
|
||||
|
||||
# Keep DialogUtil since it's used for permission dialogs
|
||||
-keep class devs.org.calculator.utils.DialogUtil { *; }
|
||||
|
||||
# Keep PrefsUtil since it's used for password validation
|
||||
-keep class devs.org.calculator.utils.PrefsUtil { *; }
|
||||
|
||||
# General Android rules
|
||||
-keepclassmembers class * extends android.app.Activity {
|
||||
public void *(android.view.View);
|
||||
}
|
||||
|
||||
# Keep any classes that use reflection
|
||||
-keepattributes InnerClasses
|
||||
|
||||
# Keep R classes and their fields
|
||||
-keepclassmembers class **.R$* {
|
||||
public static <fields>;
|
||||
}
|
||||
|
||||
# Keep enums
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# Keep specific activities with special code in onCreate
|
||||
-keepclassmembers class * extends android.app.Activity {
|
||||
public void onCreate(android.os.Bundle);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="devs.org.calculator"
|
||||
>
|
||||
package="devs.org.calculator">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_IMAGES"
|
||||
tools:ignore="SelectedPhotoAccess" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_VIDEO"
|
||||
tools:ignore="SelectedPhotoAccess" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
|
||||
<application
|
||||
android:name=".CalculatorApp"
|
||||
android:allowBackup="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Calculator"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".activities.DocumentsActivity"
|
||||
android:name=".activities.ViewFolderActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".activities.AudioGalleryActivity"
|
||||
android:name=".activities.SettingsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".activities.VideoGalleryActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".activities.ImageGalleryActivity"
|
||||
android:name=".activities.HiddenActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".activities.SetupPasswordActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.MainActivity"
|
||||
android:exported="true" >
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activities.HiddenVaultActivity"
|
||||
android:exported="true" />
|
||||
<activity
|
||||
android:name=".activities.PreviewActivity"
|
||||
android:configChanges="orientation|screenSize"/>
|
||||
android:configChanges="orientation|screenSize" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
@@ -69,9 +66,6 @@
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
||||
1
app/src/main/assets/hiding_files.json
Normal file
1
app/src/main/assets/hiding_files.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,12 +1,23 @@
|
||||
package devs.org.calculator
|
||||
|
||||
import android.app.Application
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.google.android.material.color.DynamicColors
|
||||
|
||||
class CalculatorApp : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
// Apply dynamic colors to enable Material You theming
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
|
||||
// Initialize theme settings
|
||||
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
|
||||
|
||||
// Apply saved theme mode
|
||||
val themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
AppCompatDelegate.setDefaultNightMode(themeMode)
|
||||
|
||||
// Apply dynamic colors only if dynamic theme is enabled
|
||||
if (prefs.getBoolean("dynamic_theme", true)) {
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class AudioGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.AUDIO
|
||||
private lateinit var pickAudioLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupFabButton()
|
||||
|
||||
pickAudioLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) }
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(
|
||||
this@AudioGalleryActivity,
|
||||
this@AudioGalleryActivity
|
||||
).processMultipleFiles(uriList, fileType, this@AudioGalleryActivity)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(
|
||||
this@AudioGalleryActivity,
|
||||
"${copiedFiles.size} Audios hidden successfully",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@AudioGalleryActivity, "Failed to hide Audios", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
binding.fabAdd.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "audio/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickAudioLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openPreview() {
|
||||
// Implement audio preview
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import devs.org.calculator.adapters.FileAdapter
|
||||
import devs.org.calculator.databinding.ActivityGalleryBinding
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import java.io.File
|
||||
|
||||
abstract class BaseGalleryActivity : AppCompatActivity() {
|
||||
protected lateinit var binding: ActivityGalleryBinding
|
||||
protected lateinit var fileManager: FileManager
|
||||
protected lateinit var adapter: FileAdapter
|
||||
protected lateinit var files: List<File>
|
||||
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
private val storagePermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
val granted = permissions.values.all { it }
|
||||
if (granted || Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) {
|
||||
loadFiles()
|
||||
} else {
|
||||
showPermissionDeniedDialog()
|
||||
}
|
||||
}
|
||||
|
||||
abstract val fileType: FileManager.FileType
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupIntentSenderLauncher()
|
||||
binding = ActivityGalleryBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
fileManager = FileManager(this, this)
|
||||
|
||||
binding.fabAdd.text = when(fileType){
|
||||
FileManager.FileType.IMAGE -> {
|
||||
"Add Image"
|
||||
}
|
||||
FileManager.FileType.AUDIO -> {
|
||||
"Add Audio"
|
||||
}
|
||||
FileManager.FileType.VIDEO -> {
|
||||
"Add Video"
|
||||
}
|
||||
FileManager.FileType.DOCUMENT -> {
|
||||
"Add Files"
|
||||
}
|
||||
}
|
||||
binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
if (scrollY > oldScrollY && binding.fabAdd.isExtended) {
|
||||
|
||||
binding.fabAdd.shrink()
|
||||
} else if (scrollY < oldScrollY && !binding.fabAdd.isExtended) {
|
||||
|
||||
binding.fabAdd.extend()
|
||||
}
|
||||
}
|
||||
setupRecyclerView()
|
||||
checkPermissionsAndLoadFiles()
|
||||
}
|
||||
|
||||
private fun setupIntentSenderLauncher() {
|
||||
intentSenderLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartIntentSenderForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||
adapter = FileAdapter(fileType, this, this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun checkPermissionsAndLoadFiles() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
.addCategory("android.intent.category.DEFAULT")
|
||||
.setData(Uri.parse("package:${applicationContext.packageName}"))
|
||||
startActivityForResult(intent, 2296)
|
||||
} else {
|
||||
loadFiles()
|
||||
}
|
||||
} else {
|
||||
val permissions = arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
if (permissions.any { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED }) {
|
||||
storagePermissionLauncher.launch(permissions)
|
||||
} else {
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun loadFiles() {
|
||||
files = fileManager.getFilesInHiddenDir(fileType)
|
||||
adapter.submitList(files)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
abstract fun openPreview()
|
||||
|
||||
private fun showPermissionDeniedDialog() {
|
||||
// permission denied
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == 2296 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (Environment.isExternalStorageManager()) {
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class DocumentsActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.DOCUMENT
|
||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedUri: Uri? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupFabButton()
|
||||
|
||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@DocumentsActivity, this@DocumentsActivity).processMultipleFiles(uriList, fileType,this@DocumentsActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@DocumentsActivity, "${copiedFiles.size} Documents hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@DocumentsActivity, "Failed to hide Documents", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
binding.fabAdd.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "*/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openPreview() {
|
||||
// Implement document preview
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.adapters.FolderAdapter
|
||||
import devs.org.calculator.adapters.ListFolderAdapter
|
||||
import devs.org.calculator.databinding.ActivityHiddenBinding
|
||||
import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR
|
||||
import devs.org.calculator.utils.FolderManager
|
||||
import devs.org.calculator.utils.PrefsUtil
|
||||
import java.io.File
|
||||
|
||||
class HiddenActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityHiddenBinding
|
||||
private lateinit var fileManager: FileManager
|
||||
private lateinit var folderManager: FolderManager
|
||||
private lateinit var dialogUtil: DialogUtil
|
||||
private var currentFolder: File? = null
|
||||
private var folderAdapter: FolderAdapter? = null
|
||||
private var listFolderAdapter: ListFolderAdapter? = null
|
||||
private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
companion object {
|
||||
private const val TAG = "HiddenActivity"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityHiddenBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
fileManager = FileManager(this, this)
|
||||
folderManager = FolderManager(this)
|
||||
dialogUtil = DialogUtil(this)
|
||||
|
||||
setupInitialUIState()
|
||||
setupClickListeners()
|
||||
setupBackPressedHandler()
|
||||
|
||||
fileManager.askPermission(this)
|
||||
|
||||
refreshCurrentView()
|
||||
}
|
||||
|
||||
private fun setupInitialUIState() {
|
||||
|
||||
binding.addFolder.visibility = View.VISIBLE
|
||||
binding.settings.visibility = View.VISIBLE
|
||||
binding.folderOrientation.visibility = View.VISIBLE
|
||||
binding.deleteSelected.visibility = View.GONE
|
||||
binding.delete.visibility = View.GONE
|
||||
binding.menuButton.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
|
||||
|
||||
binding.settings.setOnClickListener {
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
}
|
||||
|
||||
|
||||
binding.back.setOnClickListener {
|
||||
handleBackPress()
|
||||
}
|
||||
|
||||
binding.addFolder.setOnClickListener {
|
||||
createNewFolder()
|
||||
}
|
||||
|
||||
binding.deleteSelected.setOnClickListener {
|
||||
deleteSelectedItems()
|
||||
}
|
||||
|
||||
binding.delete.setOnClickListener {
|
||||
deleteSelectedItems()
|
||||
}
|
||||
|
||||
binding.edit.setOnClickListener {
|
||||
editSelectedFolder()
|
||||
}
|
||||
|
||||
binding.folderOrientation.setOnClickListener {
|
||||
// Switch between grid mode and list mode
|
||||
val currentIsList = PrefsUtil(this).getBoolean("isList", false)
|
||||
val newIsList = !currentIsList
|
||||
|
||||
if (newIsList) {
|
||||
// Switch to list view
|
||||
showListUI()
|
||||
PrefsUtil(this).setBoolean("isList", true)
|
||||
binding.folderOrientation.setImageResource(R.drawable.ic_grid)
|
||||
} else {
|
||||
// Switch to grid view
|
||||
showGridUI()
|
||||
PrefsUtil(this).setBoolean("isList", false)
|
||||
binding.folderOrientation.setImageResource(R.drawable.ic_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showGridUI() {
|
||||
listFoldersInHiddenDirectory()
|
||||
}
|
||||
|
||||
private fun showListUI() {
|
||||
listFoldersInHiddenDirectoryListStyle()
|
||||
}
|
||||
|
||||
private fun listFoldersInHiddenDirectoryListStyle() {
|
||||
try {
|
||||
if (!hiddenDir.exists()) {
|
||||
fileManager.getHiddenDirectory()
|
||||
}
|
||||
|
||||
if (hiddenDir.exists() && hiddenDir.isDirectory) {
|
||||
val folders = folderManager.getFoldersInDirectory(hiddenDir)
|
||||
|
||||
if (folders.isNotEmpty()) {
|
||||
showFolderListStyle(folders)
|
||||
} else {
|
||||
showEmptyState()
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
|
||||
showEmptyState()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error listing folders: ${e.message}")
|
||||
showEmptyState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupBackPressedHandler() {
|
||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
handleBackPress()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
private fun createNewFolder() {
|
||||
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_input, null)
|
||||
val inputEditText = dialogView.findViewById<EditText>(R.id.editText)
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Enter Folder Name To Create")
|
||||
.setView(dialogView)
|
||||
.setPositiveButton("Create") { dialog, _ ->
|
||||
val newName = inputEditText.text.toString().trim()
|
||||
if (newName.isNotEmpty()) {
|
||||
try {
|
||||
folderManager.createFolder(hiddenDir, newName)
|
||||
refreshCurrentView()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error creating folder: ${e.message}")
|
||||
Toast.makeText(
|
||||
this@HiddenActivity,
|
||||
"Failed to create folder",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupFlagSecure()
|
||||
}
|
||||
|
||||
private fun setupFlagSecure() {
|
||||
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
|
||||
if (prefs.getBoolean("screenshot_restriction", true)) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun listFoldersInHiddenDirectory() {
|
||||
try {
|
||||
if (!hiddenDir.exists()) {
|
||||
fileManager.getHiddenDirectory()
|
||||
}
|
||||
|
||||
if (hiddenDir.exists() && hiddenDir.isDirectory) {
|
||||
val folders = folderManager.getFoldersInDirectory(hiddenDir)
|
||||
|
||||
if (folders.isNotEmpty()) {
|
||||
showFolderList(folders)
|
||||
} else {
|
||||
showEmptyState()
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
|
||||
showEmptyState()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error listing folders: ${e.message}")
|
||||
showEmptyState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFolderList(folders: List<File>) {
|
||||
binding.noItems.visibility = View.GONE
|
||||
binding.recyclerView.visibility = View.VISIBLE
|
||||
|
||||
// Clear the existing adapter to avoid conflicts
|
||||
listFolderAdapter = null
|
||||
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 2)
|
||||
folderAdapter = FolderAdapter(
|
||||
onFolderClick = { clickedFolder ->
|
||||
startActivity(Intent(this,ViewFolderActivity::class.java).putExtra("folder",clickedFolder.toString()))
|
||||
},
|
||||
onFolderLongClick = {
|
||||
enterFolderSelectionMode()
|
||||
},
|
||||
onSelectionModeChanged = { isSelectionMode ->
|
||||
handleFolderSelectionModeChange(isSelectionMode)
|
||||
},
|
||||
onSelectionCountChanged = { selectedCount ->
|
||||
updateEditButtonVisibility()
|
||||
}
|
||||
)
|
||||
binding.recyclerView.adapter = folderAdapter
|
||||
folderAdapter?.submitList(folders)
|
||||
|
||||
// Ensure proper icon state for folder view
|
||||
if (folderAdapter?.isInSelectionMode() != true) {
|
||||
showFolderViewIcons()
|
||||
}
|
||||
}
|
||||
private fun showFolderListStyle(folders: List<File>) {
|
||||
binding.noItems.visibility = View.GONE
|
||||
binding.recyclerView.visibility = View.VISIBLE
|
||||
|
||||
// Clear the existing adapter to avoid conflicts
|
||||
folderAdapter = null
|
||||
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 1)
|
||||
listFolderAdapter = ListFolderAdapter(
|
||||
onFolderClick = { clickedFolder ->
|
||||
startActivity(Intent(this,ViewFolderActivity::class.java).putExtra("folder",clickedFolder.toString()))
|
||||
},
|
||||
onFolderLongClick = {
|
||||
enterFolderSelectionMode()
|
||||
},
|
||||
onSelectionModeChanged = { isSelectionMode ->
|
||||
handleFolderSelectionModeChange(isSelectionMode)
|
||||
},
|
||||
onSelectionCountChanged = { selectedCount ->
|
||||
updateEditButtonVisibility()
|
||||
}
|
||||
)
|
||||
binding.recyclerView.adapter = listFolderAdapter
|
||||
listFolderAdapter?.submitList(folders)
|
||||
|
||||
// Ensure proper icon state for folder view
|
||||
if (listFolderAdapter?.isInSelectionMode() != true) {
|
||||
showFolderViewIcons()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateEditButtonVisibility() {
|
||||
val selectedCount = when {
|
||||
folderAdapter != null -> folderAdapter?.getSelectedItems()?.size ?: 0
|
||||
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems()?.size ?: 0
|
||||
else -> 0
|
||||
}
|
||||
binding.edit.visibility = if (selectedCount == 1) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun showEmptyState() {
|
||||
binding.noItems.visibility = View.VISIBLE
|
||||
binding.recyclerView.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun enterFolderSelectionMode() {
|
||||
showFolderSelectionIcons()
|
||||
}
|
||||
|
||||
|
||||
private fun refreshCurrentView() {
|
||||
val isList = PrefsUtil(this).getBoolean("isList", false)
|
||||
if (isList) {
|
||||
binding.folderOrientation.setImageResource(R.drawable.ic_grid)
|
||||
listFoldersInHiddenDirectoryListStyle()
|
||||
} else {
|
||||
binding.folderOrientation.setImageResource(R.drawable.ic_list)
|
||||
listFoldersInHiddenDirectory()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun deleteSelectedItems() {
|
||||
deleteSelectedFolders()
|
||||
}
|
||||
|
||||
private fun deleteSelectedFolders() {
|
||||
val selectedFolders = when {
|
||||
folderAdapter != null -> folderAdapter?.getSelectedItems() ?: emptyList()
|
||||
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems() ?: emptyList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
if (selectedFolders.isNotEmpty()) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
getString(R.string.delete_items),
|
||||
getString(R.string.are_you_sure_you_want_to_delete_selected_items),
|
||||
getString(R.string.delete),
|
||||
getString(R.string.cancel),
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
performFolderDeletion(selectedFolders)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun performFolderDeletion(selectedFolders: List<File>) {
|
||||
var allDeleted = true
|
||||
selectedFolders.forEach { folder ->
|
||||
if (!folderManager.deleteFolder(folder)) {
|
||||
allDeleted = false
|
||||
Log.e(TAG, "Failed to delete folder: ${folder.name}")
|
||||
}
|
||||
}
|
||||
|
||||
val message = if (allDeleted) {
|
||||
getString(R.string.folder_deleted_successfully)
|
||||
} else {
|
||||
getString(R.string.some_items_could_not_be_deleted)
|
||||
}
|
||||
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Clear selection from both adapters
|
||||
folderAdapter?.clearSelection()
|
||||
listFolderAdapter?.clearSelection()
|
||||
|
||||
// This will trigger the selection mode change callback and show proper icons
|
||||
exitFolderSelectionMode()
|
||||
|
||||
// Refresh the current view based on orientation
|
||||
refreshCurrentView()
|
||||
}
|
||||
|
||||
private fun handleBackPress() {
|
||||
|
||||
|
||||
// Check if folder adapters are in selection mode
|
||||
if (folderAdapter?.onBackPressed() == true || listFolderAdapter?.onBackPressed() == true) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle navigation back
|
||||
if (currentFolder != null) {
|
||||
navigateBackToFolders()
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateBackToFolders() {
|
||||
currentFolder = null
|
||||
|
||||
// Clean up file adapter
|
||||
|
||||
refreshCurrentView()
|
||||
|
||||
binding.folderName.text = getString(R.string.hidden_space)
|
||||
|
||||
// Set proper icons for folder view
|
||||
showFolderViewIcons()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
|
||||
// Remove any pending callbacks
|
||||
mainHandler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
//visibility related code
|
||||
private fun showFolderViewIcons() {
|
||||
binding.folderOrientation.visibility = View.VISIBLE
|
||||
binding.settings.visibility = View.VISIBLE
|
||||
binding.delete.visibility = View.GONE
|
||||
binding.deleteSelected.visibility = View.GONE
|
||||
binding.menuButton.visibility = View.GONE
|
||||
binding.addFolder.visibility = View.VISIBLE
|
||||
binding.edit.visibility = View.GONE
|
||||
// Ensure FABs are properly managed
|
||||
if (currentFolder == null) {
|
||||
|
||||
binding.addFolder.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
private fun showFolderSelectionIcons() {
|
||||
binding.folderOrientation.visibility = View.GONE
|
||||
binding.settings.visibility = View.GONE
|
||||
binding.delete.visibility = View.VISIBLE
|
||||
binding.deleteSelected.visibility = View.VISIBLE
|
||||
binding.menuButton.visibility = View.GONE
|
||||
binding.addFolder.visibility = View.GONE
|
||||
|
||||
// Update edit button visibility based on current selection count
|
||||
updateEditButtonVisibility()
|
||||
}
|
||||
private fun exitFolderSelectionMode() {
|
||||
showFolderViewIcons()
|
||||
}
|
||||
|
||||
private fun handleFolderSelectionModeChange(isSelectionMode: Boolean) {
|
||||
if (!isSelectionMode) {
|
||||
exitFolderSelectionMode()
|
||||
} else {
|
||||
enterFolderSelectionMode()
|
||||
}
|
||||
// Always update edit button visibility when selection mode changes
|
||||
updateEditButtonVisibility()
|
||||
}
|
||||
|
||||
private fun editSelectedFolder() {
|
||||
val selectedFolders = when {
|
||||
folderAdapter != null -> folderAdapter?.getSelectedItems() ?: emptyList()
|
||||
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems() ?: emptyList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
if (selectedFolders.size != 1) {
|
||||
Toast.makeText(this, "Please select exactly one folder to edit", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val folder = selectedFolders[0]
|
||||
showEditFolderDialog(folder)
|
||||
}
|
||||
|
||||
private fun showEditFolderDialog(folder: File) {
|
||||
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_input, null)
|
||||
val inputEditText = dialogView.findViewById<EditText>(R.id.editText)
|
||||
inputEditText.setText(folder.name)
|
||||
inputEditText.selectAll()
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Rename Folder")
|
||||
.setView(dialogView)
|
||||
.setPositiveButton("Rename") { dialog, _ ->
|
||||
val newName = inputEditText.text.toString().trim()
|
||||
if (newName.isNotEmpty() && newName != folder.name) {
|
||||
if (isValidFolderName(newName)) {
|
||||
renameFolder(folder, newName)
|
||||
} else {
|
||||
Toast.makeText(this, "Invalid folder name", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun isValidFolderName(folderName: String): Boolean {
|
||||
val forbiddenChars = charArrayOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
|
||||
return folderName.isNotBlank() &&
|
||||
folderName.none { it in forbiddenChars } &&
|
||||
!folderName.startsWith(".") &&
|
||||
folderName.length <= 255
|
||||
}
|
||||
|
||||
private fun renameFolder(oldFolder: File, newName: String) {
|
||||
val parentDir = oldFolder.parentFile
|
||||
if (parentDir != null) {
|
||||
val newFolder = File(parentDir, newName)
|
||||
if (newFolder.exists()) {
|
||||
Toast.makeText(this, "Folder with this name already exists", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (oldFolder.renameTo(newFolder)) {
|
||||
// Clear selection from both adapters
|
||||
folderAdapter?.clearSelection()
|
||||
listFolderAdapter?.clearSelection()
|
||||
|
||||
// Exit selection mode
|
||||
exitFolderSelectionMode()
|
||||
|
||||
refreshCurrentView()
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to rename folder", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import devs.org.calculator.databinding.ActivityHiddenVaultBinding
|
||||
import devs.org.calculator.utils.FileManager
|
||||
|
||||
class HiddenVaultActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityHiddenVaultBinding
|
||||
private lateinit var fileManager: FileManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityHiddenVaultBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
fileManager = FileManager(this, this)
|
||||
setupNavigation()
|
||||
}
|
||||
|
||||
private fun setupNavigation() {
|
||||
binding.btnImages.setOnClickListener {
|
||||
startActivity(Intent(this, ImageGalleryActivity::class.java))
|
||||
}
|
||||
binding.btnVideos.setOnClickListener {
|
||||
startActivity(Intent(this, VideoGalleryActivity::class.java))
|
||||
}
|
||||
binding.btnAudio.setOnClickListener {
|
||||
startActivity(Intent(this, AudioGalleryActivity::class.java))
|
||||
}
|
||||
binding.btnDocs.setOnClickListener {
|
||||
startActivity(Intent(this, DocumentsActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import android.Manifest
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
|
||||
class ImageGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.IMAGE
|
||||
private val STORAGE_PERMISSION_CODE = 100
|
||||
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupIntentSenderLauncher()
|
||||
setupFabButton()
|
||||
|
||||
intentSenderLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()){
|
||||
if (it.resultCode != RESULT_OK) Toast.makeText(this, "Failed to hide/unhide photo", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) }
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@ImageGalleryActivity, this@ImageGalleryActivity)
|
||||
.processMultipleFiles(uriList, fileType,this@ImageGalleryActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
askPermissiom()
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@ImageGalleryActivity, "${copiedFiles.size} Images hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@ImageGalleryActivity, "Failed to hide images", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupIntentSenderLauncher() {
|
||||
intentSenderLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartIntentSenderForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun askPermissiom() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
|
||||
if (!Environment.isExternalStorageManager()){
|
||||
val intent = Intent().setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
checkAndRequestStoragePermission()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndRequestStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
STORAGE_PERMISSION_CODE
|
||||
)
|
||||
} else {
|
||||
//storage permission granted
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == STORAGE_PERMISSION_CODE) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, "Storage permissions granted", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Storage permissions denied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
binding.fabAdd.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "image/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickImageLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openPreview() {
|
||||
val intent = Intent(this, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileType)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
@@ -20,16 +21,19 @@ import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.PrefsUtil
|
||||
import net.objecthunter.exp4j.ExpressionBuilder
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.DialogCallback {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var currentExpression = "0"
|
||||
private var lastWasOperator = false
|
||||
private var hasDecimal = false
|
||||
private var lastWasPercent = false
|
||||
private lateinit var launcher: ActivityResultLauncher<Intent>
|
||||
private lateinit var baseDocumentTreeUri: Uri
|
||||
private val dialogUtil = DialogUtil(this)
|
||||
private val fileManager = FileManager(this, this)
|
||||
private val sp by lazy { getSharedPreferences("app", MODE_PRIVATE) }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -41,16 +45,36 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
handleActivityResult(result)
|
||||
}
|
||||
|
||||
if (sp.getBoolean("isFirst", true)){
|
||||
binding.display.text = getString(R.string.enter_123456)
|
||||
}
|
||||
|
||||
// Ask permission
|
||||
if(!Environment.isExternalStorageManager()) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
"Storage Permission",
|
||||
"To ensure the app works properly and allows you to easily hide or unhide your private files, please grant storage access permission.\n" +
|
||||
"To ensure the app works properly and allows you to easily hide or un-hide your private files, please grant storage access permission.\n" +
|
||||
"\n" +
|
||||
"For devices running Android 11 or higher, you'll need to grant the 'All Files Access' permission.",
|
||||
"Grant",
|
||||
"Cancel",
|
||||
this
|
||||
"Later",
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
fileManager.askPermission(this@MainActivity)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
Toast.makeText(this@MainActivity,
|
||||
"Storage permission is required for the app to function properly",
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
Toast.makeText(this@MainActivity,
|
||||
"You can grant permission later from Settings",
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
setupNumberButton(binding.btn0, "0")
|
||||
@@ -71,7 +95,7 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
binding.btnClear.setOnClickListener { clearDisplay() }
|
||||
binding.btnDot.setOnClickListener { addDecimal() }
|
||||
binding.btnEquals.setOnClickListener { calculateResult() }
|
||||
binding.btnPercent.setOnClickListener { calculatePercentage() }
|
||||
binding.btnPercent.setOnClickListener { addPercentage() }
|
||||
binding.cut.setOnClickListener { cutNumbers() }
|
||||
}
|
||||
|
||||
@@ -98,41 +122,57 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
currentExpression += number
|
||||
}
|
||||
lastWasOperator = false
|
||||
lastWasPercent = false
|
||||
updateDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupOperatorButton(button: MaterialButton, operator: String) {
|
||||
button.setOnClickListener {
|
||||
if (!lastWasOperator) {
|
||||
if (lastWasOperator) {
|
||||
currentExpression = currentExpression.substring(0, currentExpression.length - 1) +
|
||||
when (operator) {
|
||||
"×" -> "*"
|
||||
else -> operator
|
||||
}
|
||||
} else if (!lastWasPercent) {
|
||||
currentExpression += when (operator) {
|
||||
"×" -> "*"
|
||||
else -> operator
|
||||
}
|
||||
lastWasOperator = true
|
||||
lastWasPercent = false
|
||||
hasDecimal = false
|
||||
}
|
||||
updateDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun clearDisplay() {
|
||||
currentExpression = "0"
|
||||
binding.total.text = ""
|
||||
lastWasOperator = false
|
||||
lastWasPercent = false
|
||||
hasDecimal = false
|
||||
updateDisplay()
|
||||
}
|
||||
|
||||
private fun addDecimal() {
|
||||
if (!hasDecimal && !lastWasOperator) {
|
||||
if (!hasDecimal && !lastWasOperator && !lastWasPercent) {
|
||||
currentExpression += "."
|
||||
hasDecimal = true
|
||||
updateDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPercentage() {
|
||||
if (!lastWasOperator && !lastWasPercent) {
|
||||
currentExpression += "%"
|
||||
lastWasPercent = true
|
||||
updateDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculatePercentage() {
|
||||
try {
|
||||
val value = currentExpression.toDouble()
|
||||
@@ -143,9 +183,130 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
}
|
||||
|
||||
private fun preprocessExpression(expression: String): String {
|
||||
val percentagePattern = Pattern.compile("(\\d+\\.?\\d*)%")
|
||||
val operatorPercentPattern = Pattern.compile("([+\\-*/])(\\d+\\.?\\d*)%")
|
||||
|
||||
var processedExpression = expression
|
||||
|
||||
// Replace standalone percentages (like "50%") with their decimal form (0.5)
|
||||
val matcher = percentagePattern.matcher(processedExpression)
|
||||
while (matcher.find()) {
|
||||
val fullMatch = matcher.group(0)
|
||||
val number = matcher.group(1)
|
||||
|
||||
// Check if it's a standalone percentage or part of an operation
|
||||
val start = matcher.start()
|
||||
if (start == 0 || !isOperator(processedExpression[start-1].toString())) {
|
||||
val percentageValue = number.toDouble() / 100
|
||||
processedExpression = processedExpression.replace(fullMatch, percentageValue.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Handle operator-percentage combinations (like "100-20%")
|
||||
val opMatcher = operatorPercentPattern.matcher(processedExpression)
|
||||
val sb = StringBuilder(processedExpression)
|
||||
|
||||
// We need to process matches from right to left to maintain indices
|
||||
val matches = mutableListOf<Triple<Int, Int, String>>()
|
||||
|
||||
while (opMatcher.find()) {
|
||||
val operator = opMatcher.group(1)
|
||||
val percentValue = opMatcher.group(2)!!.toDouble()
|
||||
val start = opMatcher.start()
|
||||
val end = opMatcher.end()
|
||||
|
||||
matches.add(Triple(start, end, "$operator$percentValue%"))
|
||||
}
|
||||
|
||||
// Process matches from right to left
|
||||
for (match in matches.reversed()) {
|
||||
val (start, end, fullMatch) = match
|
||||
|
||||
// Find the number before this operator
|
||||
var leftNumberEnd = start
|
||||
var leftNumberStart = start - 1
|
||||
|
||||
// Skip parentheses and move to the actual number
|
||||
if (leftNumberStart >= 0 && sb[leftNumberStart] == ')') {
|
||||
var openParens = 1
|
||||
leftNumberStart--
|
||||
|
||||
while (leftNumberStart >= 0 && openParens > 0) {
|
||||
if (sb[leftNumberStart] == ')') openParens++
|
||||
else if (sb[leftNumberStart] == '(') openParens--
|
||||
leftNumberStart--
|
||||
}
|
||||
|
||||
// Now we need to find the start of the expression
|
||||
if (leftNumberStart >= 0) {
|
||||
while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.' || sb[leftNumberStart] == '-')) {
|
||||
leftNumberStart--
|
||||
}
|
||||
leftNumberStart++
|
||||
} else {
|
||||
leftNumberStart = 0
|
||||
}
|
||||
} else {
|
||||
// For simple numbers, just find the start of the number
|
||||
while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.')) {
|
||||
leftNumberStart--
|
||||
}
|
||||
leftNumberStart++
|
||||
}
|
||||
|
||||
if (leftNumberStart < leftNumberEnd) {
|
||||
val leftPart = sb.substring(leftNumberStart, leftNumberEnd)
|
||||
|
||||
try {
|
||||
// Extract the numerical values
|
||||
val baseNumber = evaluateExpression(leftPart)
|
||||
val operator = fullMatch.substring(0, 1)
|
||||
val percentNumber = fullMatch.substring(1, fullMatch.length - 1).toDouble()
|
||||
|
||||
// Calculate the percentage of the base number
|
||||
val percentValue = baseNumber * (percentNumber / 100)
|
||||
|
||||
// Calculate the new value based on the operator
|
||||
val newValue = when (operator) {
|
||||
"+" -> baseNumber + percentValue
|
||||
"-" -> baseNumber - percentValue
|
||||
"*" -> baseNumber * (percentNumber / 100)
|
||||
"/" -> baseNumber / (percentNumber / 100)
|
||||
else -> baseNumber
|
||||
}
|
||||
|
||||
// Replace the entire expression "number operator percent%" with the result
|
||||
sb.replace(leftNumberStart, end, newValue.toString())
|
||||
} catch (e: Exception) {
|
||||
Log.e("Calculator", "Error processing percentage expression: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun isOperator(char: String): Boolean {
|
||||
return char == "+" || char == "-" || char == "*" || char == "/"
|
||||
}
|
||||
|
||||
private fun isDigit(char: String): Boolean {
|
||||
return char.matches(Regex("[0-9]"))
|
||||
}
|
||||
|
||||
private fun evaluateExpression(expression: String): Double {
|
||||
return try {
|
||||
ExpressionBuilder(expression).build().evaluate()
|
||||
} catch (e: Exception) {
|
||||
expression.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateResult() {
|
||||
if (currentExpression == "123456") {
|
||||
val intent = Intent(this, SetupPasswordActivity::class.java)
|
||||
sp.edit().putBoolean("isFirst", false).apply()
|
||||
intent.putExtra("password", currentExpression)
|
||||
startActivity(intent)
|
||||
clearDisplay()
|
||||
@@ -153,7 +314,7 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
|
||||
if (PrefsUtil(this).validatePassword(currentExpression)) {
|
||||
val intent = Intent(this, HiddenVaultActivity::class.java)
|
||||
val intent = Intent(this, HiddenActivity::class.java)
|
||||
intent.putExtra("password", currentExpression)
|
||||
startActivity(intent)
|
||||
clearDisplay()
|
||||
@@ -161,8 +322,13 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
|
||||
try {
|
||||
currentExpression = currentExpression.replace("×", "*")
|
||||
val expression = ExpressionBuilder(currentExpression).build()
|
||||
var processedExpression = currentExpression.replace("×", "*")
|
||||
|
||||
if (processedExpression.contains("%")) {
|
||||
processedExpression = preprocessExpression(processedExpression)
|
||||
}
|
||||
|
||||
val expression = ExpressionBuilder(processedExpression).build()
|
||||
val result = expression.evaluate()
|
||||
|
||||
currentExpression = if (result.toLong().toDouble() == result) {
|
||||
@@ -172,16 +338,17 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
|
||||
lastWasOperator = false
|
||||
lastWasPercent = false
|
||||
hasDecimal = currentExpression.contains(".")
|
||||
|
||||
updateDisplay()
|
||||
binding.total.text = ""
|
||||
} catch (e: Exception) {
|
||||
binding.display.text = getString(R.string.invalid_message)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun updateDisplay() {
|
||||
binding.display.text = currentExpression.replace("*", "×")
|
||||
|
||||
@@ -191,42 +358,71 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
|
||||
try {
|
||||
val expression = ExpressionBuilder(currentExpression).build()
|
||||
if (currentExpression.isEmpty() ||
|
||||
(isOperator(currentExpression.last().toString()) && currentExpression.last() != '%')) {
|
||||
binding.total.text = ""
|
||||
return
|
||||
}
|
||||
|
||||
// Process the expression for preview calculation
|
||||
var processedExpression = currentExpression.replace("×", "*")
|
||||
|
||||
if (processedExpression.contains("%")) {
|
||||
processedExpression = preprocessExpression(processedExpression)
|
||||
}
|
||||
|
||||
val expression = ExpressionBuilder(processedExpression).build()
|
||||
val result = expression.evaluate()
|
||||
|
||||
val formattedResult = if (result.toLong().toDouble() == result) {
|
||||
result.toLong().toString()
|
||||
} else {
|
||||
String.format("%.2f", result)
|
||||
}
|
||||
|
||||
binding.total.text = formattedResult
|
||||
if (sp.getBoolean("isFirst", true) && (currentExpression == "123456" || binding.display.text.toString() == "123456")){
|
||||
binding.total.text = getString(R.string.now_enter_button)
|
||||
}else binding.total.text = formattedResult
|
||||
} catch (e: Exception) {
|
||||
binding.total.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun cutNumbers() {
|
||||
if (currentExpression.isNotEmpty()){
|
||||
if (currentExpression.length == 1){
|
||||
currentExpression = currentExpression.substring(0, currentExpression.length - 1)
|
||||
currentExpression = "0"
|
||||
}else currentExpression = currentExpression.substring(0, currentExpression.length - 1)
|
||||
}else currentExpression = "0"
|
||||
updateDisplay()
|
||||
} else {
|
||||
val lastChar = currentExpression.last()
|
||||
currentExpression = currentExpression.substring(0, currentExpression.length - 1)
|
||||
|
||||
// Update flags based on what was removed
|
||||
if (lastChar == '%') {
|
||||
lastWasPercent = false
|
||||
} else if (isOperator(lastChar.toString())) {
|
||||
lastWasOperator = false
|
||||
} else if (lastChar == '.') {
|
||||
hasDecimal = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentExpression = "0"
|
||||
}
|
||||
updateDisplay()
|
||||
}
|
||||
|
||||
override fun onPositiveButtonClicked() {
|
||||
// Handle positive button click for both DialogUtil and DialogActionsCallback
|
||||
fileManager.askPermission(this)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
|
||||
// Handle negative button click
|
||||
Toast.makeText(this, "Storage permission is required for the app to function properly", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
|
||||
// Handle neutral button click
|
||||
Toast.makeText(this, "You can grant permission later from Settings", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
@@ -239,6 +435,4 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,18 +2,17 @@ package devs.org.calculator.activities
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.adapters.ImagePreviewAdapter
|
||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
||||
import devs.org.calculator.databinding.ActivityPreviewBinding
|
||||
import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import devs.org.calculator.R
|
||||
|
||||
class PreviewActivity : AppCompatActivity() {
|
||||
|
||||
@@ -21,6 +20,7 @@ class PreviewActivity : AppCompatActivity() {
|
||||
private var currentPosition: Int = 0
|
||||
private lateinit var files: List<File>
|
||||
private lateinit var type: String
|
||||
private lateinit var folder: String
|
||||
private lateinit var filetype: FileManager.FileType
|
||||
private lateinit var adapter: ImagePreviewAdapter
|
||||
private lateinit var fileManager: FileManager
|
||||
@@ -31,13 +31,15 @@ class PreviewActivity : AppCompatActivity() {
|
||||
binding = ActivityPreviewBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
|
||||
fileManager = FileManager(this, this)
|
||||
|
||||
currentPosition = intent.getIntExtra("position", 0)
|
||||
type = intent.getStringExtra("type").toString()
|
||||
folder = intent.getStringExtra("folder").toString()
|
||||
|
||||
setupFileType()
|
||||
files = fileManager.getFilesInHiddenDir(filetype)
|
||||
files = fileManager.getFilesInHiddenDirFromFolder(filetype, folder = folder)
|
||||
|
||||
setupImagePreview()
|
||||
clickListeners()
|
||||
@@ -49,31 +51,50 @@ class PreviewActivity : AppCompatActivity() {
|
||||
}
|
||||
})
|
||||
|
||||
binding.back.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupFlagSecure()
|
||||
}
|
||||
|
||||
private fun setupFlagSecure() {
|
||||
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
|
||||
if (prefs.getBoolean("screenshot_restriction", true)) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFileType() {
|
||||
when (type) {
|
||||
"IMAGE" -> {
|
||||
filetype = FileManager.FileType.IMAGE
|
||||
binding.title.text = "Preview Images"
|
||||
binding.title.text = getString(R.string.preview_images)
|
||||
}
|
||||
"VIDEO" -> {
|
||||
filetype = FileManager.FileType.VIDEO
|
||||
binding.title.text = "Preview Videos"
|
||||
binding.title.text = getString(R.string.preview_videos)
|
||||
}
|
||||
"AUDIO" -> {
|
||||
filetype = FileManager.FileType.AUDIO
|
||||
binding.title.text = "Preview Audios"
|
||||
binding.title.text = getString(R.string.preview_audios)
|
||||
}
|
||||
else -> {
|
||||
filetype = FileManager.FileType.DOCUMENT
|
||||
binding.title.text = "Preview Documents"
|
||||
binding.title.text = getString(R.string.preview_documents)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupImagePreview() {
|
||||
adapter = ImagePreviewAdapter(this, filetype)
|
||||
adapter = ImagePreviewAdapter(this, this)
|
||||
adapter.images = files
|
||||
binding.viewPager.adapter = adapter
|
||||
|
||||
@@ -104,14 +125,14 @@ class PreviewActivity : AppCompatActivity() {
|
||||
|
||||
private fun clickListeners() {
|
||||
binding.delete.setOnClickListener {
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem], filetype)
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem])
|
||||
if (fileUri != null) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
"Delete File",
|
||||
"Are you sure to Delete this file permanently?",
|
||||
"Delete Permanently",
|
||||
"Cancel",
|
||||
object : DialogActionsCallback{
|
||||
getString(R.string.delete_file),
|
||||
getString(R.string.are_you_sure_to_delete_this_file_permanently),
|
||||
getString(R.string.delete_permanently),
|
||||
getString(R.string.cancel),
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@PreviewActivity, this@PreviewActivity).deletePhotoFromExternalStorage(fileUri)
|
||||
@@ -120,27 +141,26 @@ class PreviewActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
|
||||
// Handle negative button click
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
|
||||
// Handle neutral button click
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
binding.unHide.setOnClickListener {
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem], filetype)
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem])
|
||||
if (fileUri != null) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
"Unhide File",
|
||||
"Are you sure you want to Unhide this file?",
|
||||
"Unhide",
|
||||
"Cancel",
|
||||
object : DialogActionsCallback{
|
||||
getString(R.string.un_hide_file),
|
||||
getString(R.string.are_you_sure_you_want_to_un_hide_this_file),
|
||||
getString(R.string.un_hide),
|
||||
getString(R.string.cancel),
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@PreviewActivity, this@PreviewActivity).copyFileToNormalDir(fileUri)
|
||||
@@ -149,13 +169,12 @@ class PreviewActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
|
||||
// Handle negative button click
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
|
||||
// Handle neutral button click
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -168,7 +187,7 @@ class PreviewActivity : AppCompatActivity() {
|
||||
adapter.images = updatedFiles // Update adapter with the new list
|
||||
|
||||
// Update the ViewPager's position
|
||||
if (!updatedFiles.isNotEmpty()) finish()
|
||||
if (updatedFiles.isEmpty()) finish()
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.databinding.ActivitySettingsBinding
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private val DEV_GITHUB_URL = "https://github.com/binondi"
|
||||
private val GITHUB_URL = "$DEV_GITHUB_URL/calculator-hide-files"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
|
||||
setupUI()
|
||||
loadSettings()
|
||||
setupListeners()
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
binding.back.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
|
||||
binding.dynamicThemeSwitch.isChecked = prefs.getBoolean("dynamic_theme", true)
|
||||
|
||||
val themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
binding.themeModeSwitch.isChecked = themeMode != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
|
||||
when (themeMode) {
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> binding.darkThemeRadio.isChecked = true
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> binding.lightThemeRadio.isChecked = true
|
||||
else -> binding.systemThemeRadio.isChecked = true
|
||||
}
|
||||
|
||||
binding.screenshotRestrictionSwitch.isChecked = prefs.getBoolean("screenshot_restriction", true)
|
||||
binding.showFileNames.isChecked = prefs.getBoolean("showFileName", true)
|
||||
|
||||
updateThemeModeVisibility()
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
|
||||
binding.githubButton.setOnClickListener {
|
||||
openUrl(GITHUB_URL)
|
||||
}
|
||||
|
||||
binding.devGithubButton.setOnClickListener {
|
||||
openUrl(DEV_GITHUB_URL)
|
||||
}
|
||||
|
||||
binding.dynamicThemeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.edit().putBoolean("dynamic_theme", isChecked).apply()
|
||||
if (!isChecked) {
|
||||
showThemeModeDialog()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
binding.themeModeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.themeRadioGroup.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
if (!isChecked) {
|
||||
binding.systemThemeRadio.isChecked = true
|
||||
applyThemeMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
}
|
||||
|
||||
binding.themeRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||
val themeMode = when (checkedId) {
|
||||
R.id.lightThemeRadio -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
R.id.darkThemeRadio -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
applyThemeMode(themeMode)
|
||||
}
|
||||
|
||||
binding.screenshotRestrictionSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.edit().putBoolean("screenshot_restriction", isChecked).apply()
|
||||
if (isChecked) {
|
||||
enableScreenshotRestriction()
|
||||
} else {
|
||||
disableScreenshotRestriction()
|
||||
}
|
||||
}
|
||||
binding.showFileNames.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.edit().putBoolean("showFileName", isChecked).apply()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateThemeModeVisibility() {
|
||||
binding.themeRadioGroup.visibility = if (binding.themeModeSwitch.isChecked) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun showThemeModeDialog() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Theme Mode")
|
||||
.setMessage("Would you like to set a specific theme mode?")
|
||||
.setPositiveButton("Yes") { _, _ ->
|
||||
binding.themeModeSwitch.isChecked = true
|
||||
}
|
||||
.setNegativeButton("No") { _, _ ->
|
||||
binding.systemThemeRadio.isChecked = true
|
||||
applyThemeMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun applyThemeMode(themeMode: Int) {
|
||||
prefs.edit().putInt("theme_mode", themeMode).apply()
|
||||
AppCompatDelegate.setDefaultNightMode(themeMode)
|
||||
}
|
||||
|
||||
private fun enableScreenshotRestriction() {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
}
|
||||
|
||||
private fun disableScreenshotRestriction() {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Snackbar.make(binding.root, "Could not open URL", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,28 +45,28 @@ class SetupPasswordActivity : AppCompatActivity() {
|
||||
val securityAnswer = binding.etSecurityAnswer.text.toString()
|
||||
|
||||
if (password.isEmpty()){
|
||||
binding.etPassword.error = "Enter password"
|
||||
binding.etPassword.error = getString(R.string.enter_password)
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (confirmPassword.isEmpty()){
|
||||
binding.etConfirmPassword.error = "Confirm password"
|
||||
binding.etConfirmPassword.error = getString(R.string.confirm_password)
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (securityQuestion.isEmpty()){
|
||||
binding.etSecurityQuestion.error = "Enter security question"
|
||||
binding.etSecurityQuestion.error = getString(R.string.enter_security_question)
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (securityAnswer.isEmpty()){
|
||||
binding.etSecurityAnswer.error = "Enter security answer"
|
||||
binding.etSecurityAnswer.error = getString(R.string.enter_security_answer)
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (password != confirmPassword) {
|
||||
binding.etPassword.error = "Passwords don't match"
|
||||
binding.etPassword.error = getString(R.string.passwords_don_t_match)
|
||||
return@setOnClickListener
|
||||
}
|
||||
prefsUtil.savePassword(password)
|
||||
prefsUtil.saveSecurityQA(securityQuestion, securityAnswer)
|
||||
Toast.makeText(this, "Password set successfully", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.password_set_successfully), Toast.LENGTH_SHORT).show()
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
@@ -75,40 +75,43 @@ class SetupPasswordActivity : AppCompatActivity() {
|
||||
// Implement password reset logic
|
||||
// Could use security questions or email verification
|
||||
if (prefsUtil.getSecurityQuestion() != null) showSecurityQuestionDialog(prefsUtil.getSecurityQuestion().toString())
|
||||
else Toast.makeText(this, "Security question not set yet.", Toast.LENGTH_SHORT).show()
|
||||
else Toast.makeText(this,
|
||||
getString(R.string.security_question_not_set_yet), Toast.LENGTH_SHORT).show()
|
||||
|
||||
}
|
||||
binding2.btnChangePassword.setOnClickListener{
|
||||
val oldPassword = binding2.etOldPassword.text.toString()
|
||||
val newPassword = binding2.etNewPassword.text.toString()
|
||||
if (oldPassword.isEmpty()) {
|
||||
binding2.etOldPassword.error = "This field can't be empty"
|
||||
binding2.etOldPassword.error = getString(R.string.this_field_can_t_be_empty)
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (newPassword.isEmpty()) {
|
||||
binding2.etNewPassword.error = "This field can't be empty"
|
||||
binding2.etNewPassword.error = getString(R.string.this_field_can_t_be_empty)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (prefsUtil.validatePassword(oldPassword)){
|
||||
if (oldPassword != newPassword){
|
||||
prefsUtil.savePassword(newPassword)
|
||||
Toast.makeText(this, "Password reset successfully", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this,
|
||||
getString(R.string.password_reset_successfully), Toast.LENGTH_SHORT).show()
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
|
||||
}else {
|
||||
Toast.makeText(this, "Old Password And New Password Not Be Same", Toast.LENGTH_SHORT).show()
|
||||
binding2.etNewPassword.error = "Old Password And New Password Not Be Same"
|
||||
Toast.makeText(this,
|
||||
getString(R.string.old_password_and_new_password_not_be_same), Toast.LENGTH_SHORT).show()
|
||||
binding2.etNewPassword.error = getString(R.string.old_password_and_new_password_not_be_same)
|
||||
}
|
||||
}else {
|
||||
Toast.makeText(this, "Wrong password entered", Toast.LENGTH_SHORT).show()
|
||||
binding2.etOldPassword.error = "Old Password Not Matching"
|
||||
Toast.makeText(this, getString(R.string.wrong_password_entered), Toast.LENGTH_SHORT).show()
|
||||
binding2.etOldPassword.error = getString(R.string.old_password_not_matching)
|
||||
}
|
||||
}
|
||||
binding2.btnResetPassword.setOnClickListener{
|
||||
if (prefsUtil.getSecurityQuestion() != null) showSecurityQuestionDialog(prefsUtil.getSecurityQuestion().toString())
|
||||
else Toast.makeText(this, "Security question not set yet.", Toast.LENGTH_SHORT).show()
|
||||
else Toast.makeText(this, getString(R.string.this_field_can_t_be_empty), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,26 +123,28 @@ class SetupPasswordActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Answer the Security Question!")
|
||||
.setTitle(getString(R.string.answer_the_security_question))
|
||||
.setView(dialogView)
|
||||
.setPositiveButton("Verify") { dialog, _ ->
|
||||
.setPositiveButton(getString(R.string.verify)) { dialog, _ ->
|
||||
val inputEditText: TextInputEditText = dialogView.findViewById(R.id.text_input_edit_text)
|
||||
val userAnswer = inputEditText.text.toString().trim()
|
||||
|
||||
if (userAnswer.isEmpty()) {
|
||||
Toast.makeText(this, "Answer cannot be empty!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this,
|
||||
getString(R.string.answer_cannot_be_empty), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
if (prefsUtil.validateSecurityAnswer(userAnswer)){
|
||||
prefsUtil.resetPassword()
|
||||
Toast.makeText(this, "Password successfully reset.", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this,
|
||||
getString(R.string.password_successfully_reset), Toast.LENGTH_SHORT).show()
|
||||
dialog.dismiss()
|
||||
}else {
|
||||
Toast.makeText(this, "Invalid answer!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.invalid_answer), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class VideoGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.VIDEO
|
||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedUri: Uri? = null
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupFabButton()
|
||||
|
||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@VideoGalleryActivity, this@VideoGalleryActivity)
|
||||
.processMultipleFiles(uriList, fileType,this@VideoGalleryActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@VideoGalleryActivity, "${copiedFiles.size} Videos hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@VideoGalleryActivity, "Failed to hide videos", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
binding.fabAdd.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "video/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openPreview() {
|
||||
val intent = Intent(this, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileType)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.adapters.FileAdapter
|
||||
import devs.org.calculator.adapters.FolderSelectionAdapter
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
import devs.org.calculator.databinding.ActivityViewFolderBinding
|
||||
import devs.org.calculator.databinding.ProccessingDialogBinding
|
||||
import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR
|
||||
import devs.org.calculator.utils.FolderManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class ViewFolderActivity : AppCompatActivity() {
|
||||
|
||||
private var isFabOpen = false
|
||||
private lateinit var fabOpen: Animation
|
||||
private lateinit var fabClose: Animation
|
||||
private lateinit var rotateOpen: Animation
|
||||
private lateinit var rotateClose: Animation
|
||||
private lateinit var binding: ActivityViewFolderBinding
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private lateinit var fileManager: FileManager
|
||||
private lateinit var folderManager: FolderManager
|
||||
private lateinit var dialogUtil: DialogUtil
|
||||
private var fileAdapter: FileAdapter? = null
|
||||
private var currentFolder: File? = null
|
||||
private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
||||
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
private var customDialog: androidx.appcompat.app.AlertDialog? = null
|
||||
|
||||
private var dialogShowTime: Long = 0
|
||||
private val MINIMUM_DIALOG_DURATION = 1200L
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityViewFolderBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setupAnimations()
|
||||
initialize()
|
||||
setupClickListeners()
|
||||
closeFabs()
|
||||
val folder = intent.getStringExtra("folder").toString()
|
||||
currentFolder = File(folder)
|
||||
if (currentFolder != null){
|
||||
openFolder(currentFolder!!)
|
||||
}else {
|
||||
showEmptyState()
|
||||
}
|
||||
|
||||
setupActivityResultLaunchers()
|
||||
}
|
||||
|
||||
private fun initialize() {
|
||||
fileManager = FileManager(this, this)
|
||||
folderManager = FolderManager(this)
|
||||
dialogUtil = DialogUtil(this)
|
||||
prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
|
||||
}
|
||||
|
||||
private fun setupActivityResultLaunchers() {
|
||||
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
handleFilePickerResult(result.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFilePickerResult(data: Intent?) {
|
||||
val clipData = data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
data?.data?.let { uriList.add(it) }
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
processSelectedFiles(uriList)
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun showCustomDialog(count: Int) {
|
||||
val dialogView = ProccessingDialogBinding.inflate(layoutInflater)
|
||||
customDialog = MaterialAlertDialogBuilder(this)
|
||||
.setView(dialogView.root)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialogView.title.text = "Hiding $count files"
|
||||
customDialog?.show()
|
||||
dialogShowTime = System.currentTimeMillis()
|
||||
}
|
||||
private fun dismissCustomDialog() {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val elapsedTime = currentTime - dialogShowTime
|
||||
|
||||
if (elapsedTime < MINIMUM_DIALOG_DURATION) {
|
||||
val remainingTime = MINIMUM_DIALOG_DURATION - elapsedTime
|
||||
mainHandler.postDelayed({
|
||||
customDialog?.dismiss()
|
||||
customDialog = null
|
||||
updateFilesToAdapter()
|
||||
}, remainingTime)
|
||||
} else {
|
||||
customDialog?.dismiss()
|
||||
customDialog = null
|
||||
updateFilesToAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFilesToAdapter() {
|
||||
openFolder(currentFolder!!)
|
||||
}
|
||||
|
||||
|
||||
private fun processSelectedFiles(uriList: List<Uri>) {
|
||||
val targetFolder = currentFolder ?: hiddenDir
|
||||
if (!targetFolder.exists()) {
|
||||
targetFolder.mkdirs()
|
||||
File(targetFolder, ".nomedia").createNewFile()
|
||||
}
|
||||
|
||||
showCustomDialog(uriList.size)
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
fileManager.processMultipleFiles(uriList, targetFolder,
|
||||
object : FileProcessCallback {
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
mainHandler.post {
|
||||
mainHandler.postDelayed({
|
||||
dismissCustomDialog()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
mainHandler.post {
|
||||
Toast.makeText(
|
||||
this@ViewFolderActivity,
|
||||
getString(R.string.failed_to_hide_files),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
dismissCustomDialog()
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
mainHandler.post {
|
||||
Toast.makeText(
|
||||
this@ViewFolderActivity,
|
||||
getString(R.string.there_was_a_problem_in_the_folder),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
dismissCustomDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshCurrentView() {
|
||||
if (currentFolder != null) {
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
private fun openFolder(folder: File) {
|
||||
// Ensure folder exists and has .nomedia file
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs()
|
||||
File(folder, ".nomedia").createNewFile()
|
||||
}
|
||||
|
||||
val files = folderManager.getFilesInFolder(folder)
|
||||
binding.folderName.text = folder.name
|
||||
|
||||
if (files.isNotEmpty()) {
|
||||
showFileList(files, folder)
|
||||
} else {
|
||||
showEmptyState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEmptyState() {
|
||||
binding.noItems.visibility = View.VISIBLE
|
||||
binding.recyclerView.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showFileList(files: List<File>, folder: File) {
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||
|
||||
// Clean up previous adapter
|
||||
fileAdapter?.cleanup()
|
||||
|
||||
fileAdapter = FileAdapter(this, this, folder, prefs.getBoolean("showFileName", true),
|
||||
onFolderLongClick = { isSelected ->
|
||||
handleFileSelectionModeChange(isSelected)
|
||||
}).apply {
|
||||
setFileOperationCallback(object : FileAdapter.FileOperationCallback {
|
||||
override fun onFileDeleted(file: File) {
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
override fun onFileRenamed(oldFile: File, newFile: File) {
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
override fun onRefreshNeeded() {
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
override fun onSelectionModeChanged(isSelectionMode: Boolean, selectedCount: Int) {
|
||||
handleFileSelectionModeChange(isSelectionMode)
|
||||
}
|
||||
|
||||
override fun onSelectionCountChanged(selectedCount: Int) {
|
||||
updateSelectionCountDisplay(selectedCount)
|
||||
}
|
||||
})
|
||||
|
||||
submitList(files)
|
||||
}
|
||||
|
||||
binding.recyclerView.adapter = fileAdapter
|
||||
binding.recyclerView.visibility = View.VISIBLE
|
||||
binding.noItems.visibility = View.GONE
|
||||
|
||||
binding.menuButton.setOnClickListener {
|
||||
fileAdapter?.let { adapter ->
|
||||
showFileOptionsMenu(adapter.getSelectedItems())
|
||||
}
|
||||
}
|
||||
showFileViewIcons()
|
||||
}
|
||||
|
||||
|
||||
@Deprecated("This method has been deprecated in favor of using the\n {@link OnBackPressedDispatcher} via {@link #getOnBackPressedDispatcher()}.\n The OnBackPressedDispatcher controls how back button events are dispatched\n to one or more {@link OnBackPressedCallback} objects.")
|
||||
@SuppressLint("MissingSuperCall")
|
||||
override fun onBackPressed() {
|
||||
handleBackPress()
|
||||
}
|
||||
|
||||
private fun handleBackPress() {
|
||||
if (fileAdapter?.onBackPressed() == true) {
|
||||
return
|
||||
}
|
||||
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
private fun handleFileSelectionModeChange(isSelectionMode: Boolean) {
|
||||
if (isSelectionMode) {
|
||||
showFileSelectionIcons()
|
||||
} else {
|
||||
showFileViewIcons()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelectionCountDisplay(selectedCount: Int) {
|
||||
if (selectedCount > 0) {
|
||||
showFileSelectionIcons()
|
||||
} else {
|
||||
showFileViewIcons()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFileOptionsMenu(selectedFiles: List<File>) {
|
||||
if (selectedFiles.isEmpty()) return
|
||||
|
||||
val options = arrayOf(
|
||||
getString(R.string.un_hide),
|
||||
getString(R.string.delete),
|
||||
getString(R.string.copy_to_another_folder),
|
||||
getString(R.string.move_to_another_folder)
|
||||
)
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(getString(R.string.file_options))
|
||||
.setItems(options) { _, which ->
|
||||
when (which) {
|
||||
0 -> unhideSelectedFiles(selectedFiles)
|
||||
1 -> deleteSelectedFiles(selectedFiles)
|
||||
2 -> copyToAnotherFolder(selectedFiles)
|
||||
3 -> moveToAnotherFolder(selectedFiles)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun moveToAnotherFolder(selectedFiles: List<File>) {
|
||||
showFolderSelectionDialog { destinationFolder ->
|
||||
moveFilesToFolder(selectedFiles, destinationFolder)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun unhideSelectedFiles(selectedFiles: List<File>) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
getString(R.string.un_hide_files),
|
||||
getString(R.string.are_you_sure_you_want_to_un_hide_selected_files),
|
||||
getString(R.string.un_hide),
|
||||
getString(R.string.cancel),
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
performFileUnhiding(selectedFiles)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun deleteSelectedFiles(selectedFiles: List<File>) {
|
||||
dialogUtil.showMaterialDialog(
|
||||
getString(R.string.delete_items),
|
||||
getString(R.string.are_you_sure_you_want_to_delete_selected_items),
|
||||
getString(R.string.delete),
|
||||
getString(R.string.cancel),
|
||||
object : DialogUtil.DialogCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
performFileDeletion(selectedFiles)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun refreshCurrentFolder() {
|
||||
currentFolder?.let { folder ->
|
||||
val files = folderManager.getFilesInFolder(folder)
|
||||
if (files.isNotEmpty()) {
|
||||
binding.recyclerView.visibility = View.VISIBLE
|
||||
binding.noItems.visibility = View.GONE
|
||||
fileAdapter?.submitList(files.toMutableList())
|
||||
fileAdapter?.let { adapter ->
|
||||
if (adapter.isInSelectionMode()) {
|
||||
showFileSelectionIcons()
|
||||
} else {
|
||||
showFileViewIcons()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showEmptyState()
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun setupClickListeners() {
|
||||
binding.fabExpend.setOnClickListener {
|
||||
if (isFabOpen) closeFabs()
|
||||
else openFabs()
|
||||
}
|
||||
binding.back.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
binding.addImage.setOnClickListener { openFilePicker("image/*") }
|
||||
binding.addVideo.setOnClickListener { openFilePicker("video/*") }
|
||||
binding.addAudio.setOnClickListener { openFilePicker("audio/*") }
|
||||
binding.addDocument.setOnClickListener { openFilePicker("*/*") }
|
||||
}
|
||||
|
||||
private fun setupAnimations() {
|
||||
fabOpen = AnimationUtils.loadAnimation(this, R.anim.fab_open)
|
||||
fabClose = AnimationUtils.loadAnimation(this, R.anim.fab_close)
|
||||
rotateOpen = AnimationUtils.loadAnimation(this, R.anim.rotate_open)
|
||||
rotateClose = AnimationUtils.loadAnimation(this, R.anim.rotate_close)
|
||||
}
|
||||
|
||||
private fun openFilePicker(mimeType: String) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = mimeType
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
closeFabs()
|
||||
pickImageLauncher.launch(intent)
|
||||
}
|
||||
|
||||
private fun openFabs() {
|
||||
if (!isFabOpen) {
|
||||
binding.addImage.startAnimation(fabOpen)
|
||||
binding.addVideo.startAnimation(fabOpen)
|
||||
binding.addAudio.startAnimation(fabOpen)
|
||||
binding.addDocument.startAnimation(fabOpen)
|
||||
binding.fabExpend.startAnimation(rotateOpen)
|
||||
|
||||
binding.addImage.visibility = View.VISIBLE
|
||||
binding.addVideo.visibility = View.VISIBLE
|
||||
binding.addAudio.visibility = View.VISIBLE
|
||||
binding.addDocument.visibility = View.VISIBLE
|
||||
|
||||
isFabOpen = true
|
||||
mainHandler.postDelayed({
|
||||
binding.fabExpend.setImageResource(R.drawable.wrong)
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
private fun closeFabs() {
|
||||
if (isFabOpen) {
|
||||
binding.addImage.startAnimation(fabClose)
|
||||
binding.addVideo.startAnimation(fabClose)
|
||||
binding.addAudio.startAnimation(fabClose)
|
||||
binding.addDocument.startAnimation(fabClose)
|
||||
binding.fabExpend.startAnimation(rotateClose)
|
||||
|
||||
binding.addImage.visibility = View.INVISIBLE
|
||||
binding.addVideo.visibility = View.INVISIBLE
|
||||
binding.addAudio.visibility = View.INVISIBLE
|
||||
binding.addDocument.visibility = View.INVISIBLE
|
||||
|
||||
isFabOpen = false
|
||||
binding.fabExpend.setImageResource(R.drawable.ic_add)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFileViewIcons() {
|
||||
binding.menuButton.visibility = View.GONE
|
||||
binding.fabExpend.visibility = View.VISIBLE
|
||||
binding.addImage.visibility = View.INVISIBLE
|
||||
binding.addVideo.visibility = View.INVISIBLE
|
||||
binding.addAudio.visibility = View.INVISIBLE
|
||||
binding.addDocument.visibility = View.INVISIBLE
|
||||
isFabOpen = false
|
||||
binding.fabExpend.setImageResource(R.drawable.ic_add)
|
||||
}
|
||||
|
||||
private fun showFileSelectionIcons() {
|
||||
binding.menuButton.visibility = View.VISIBLE
|
||||
binding.fabExpend.visibility = View.GONE
|
||||
binding.addImage.visibility = View.INVISIBLE
|
||||
binding.addVideo.visibility = View.INVISIBLE
|
||||
binding.addAudio.visibility = View.INVISIBLE
|
||||
binding.addDocument.visibility = View.INVISIBLE
|
||||
isFabOpen = false
|
||||
}
|
||||
|
||||
private fun performFileUnhiding(selectedFiles: List<File>) {
|
||||
lifecycleScope.launch {
|
||||
var allUnhidden = true
|
||||
selectedFiles.forEach { file ->
|
||||
try {
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(this@ViewFolderActivity, file)
|
||||
|
||||
if (fileUri != null) {
|
||||
val result = fileManager.copyFileToNormalDir(fileUri)
|
||||
if (result == null) {
|
||||
allUnhidden = false
|
||||
}
|
||||
} else {
|
||||
allUnhidden = false
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
allUnhidden = false
|
||||
}
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
val message = if (allUnhidden) {
|
||||
getString(R.string.files_unhidden_successfully)
|
||||
} else {
|
||||
getString(R.string.some_files_could_not_be_unhidden)
|
||||
}
|
||||
|
||||
Toast.makeText(this@ViewFolderActivity, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Fixed: Ensure proper order of operations
|
||||
fileAdapter?.exitSelectionMode()
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performFileDeletion(selectedFiles: List<File>) {
|
||||
var allDeleted = true
|
||||
selectedFiles.forEach { file ->
|
||||
if (!file.delete()) {
|
||||
allDeleted = false
|
||||
}
|
||||
}
|
||||
|
||||
val message = if (allDeleted) {
|
||||
getString(R.string.files_deleted_successfully)
|
||||
} else {
|
||||
getString(R.string.some_items_could_not_be_deleted)
|
||||
}
|
||||
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Fixed: Ensure proper order of operations
|
||||
fileAdapter?.exitSelectionMode()
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
private fun copyToAnotherFolder(selectedFiles: List<File>) {
|
||||
showFolderSelectionDialog { destinationFolder ->
|
||||
copyFilesToFolder(selectedFiles, destinationFolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFilesToFolder(selectedFiles: List<File>, destinationFolder: File) {
|
||||
var allCopied = true
|
||||
selectedFiles.forEach { file ->
|
||||
try {
|
||||
val newFile = File(destinationFolder, file.name)
|
||||
file.copyTo(newFile, overwrite = true)
|
||||
} catch (e: Exception) {
|
||||
allCopied = false
|
||||
}
|
||||
}
|
||||
|
||||
val message = if (allCopied) "Files copied successfully" else "Some files could not be copied"
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Fixed: Ensure proper order of operations
|
||||
fileAdapter?.exitSelectionMode()
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
private fun moveFilesToFolder(selectedFiles: List<File>, destinationFolder: File) {
|
||||
var allMoved = true
|
||||
selectedFiles.forEach { file ->
|
||||
try {
|
||||
val newFile = File(destinationFolder, file.name)
|
||||
file.copyTo(newFile, overwrite = true)
|
||||
file.delete()
|
||||
} catch (e: Exception) {
|
||||
allMoved = false
|
||||
}
|
||||
}
|
||||
|
||||
val message = if (allMoved) "Files moved successfully" else "Some files could not be moved"
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Fixed: Ensure proper order of operations
|
||||
fileAdapter?.exitSelectionMode()
|
||||
refreshCurrentFolder()
|
||||
}
|
||||
|
||||
private fun showFolderSelectionDialog(onFolderSelected: (File) -> Unit) {
|
||||
val folders = folderManager.getFoldersInDirectory(hiddenDir)
|
||||
.filter { it != currentFolder } // Exclude current folder
|
||||
|
||||
if (folders.isEmpty()) {
|
||||
Toast.makeText(this, getString(R.string.no_folders_available), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val bottomSheetView = LayoutInflater.from(this).inflate(R.layout.bottom_sheet_folder_selection, null)
|
||||
val recyclerView = bottomSheetView.findViewById<RecyclerView>(R.id.folderRecyclerView)
|
||||
|
||||
val bottomSheetDialog = BottomSheetDialog(this)
|
||||
bottomSheetDialog.setContentView(bottomSheetView)
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.adapter = FolderSelectionAdapter(folders) { selectedFolder ->
|
||||
bottomSheetDialog.dismiss()
|
||||
onFolderSelected(selectedFolder)
|
||||
}
|
||||
|
||||
bottomSheetDialog.show()
|
||||
}
|
||||
}
|
||||
@@ -1,196 +1,737 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.activities.PreviewActivity
|
||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
||||
import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class FileAdapter(
|
||||
private val fileType: FileManager.FileType,
|
||||
var context: Context,
|
||||
private var lifecycleOwner: LifecycleOwner
|
||||
) :
|
||||
ListAdapter<File, FileAdapter.FileViewHolder>(FileDiffCallback()) {
|
||||
private val context: Context,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val currentFolder: File,
|
||||
private val showFileName: Boolean,
|
||||
private val onFolderLongClick: (Boolean) -> Unit
|
||||
) : ListAdapter<File, FileAdapter.FileViewHolder>(FileDiffCallback()) {
|
||||
|
||||
private val selectedItems = mutableSetOf<Int>()
|
||||
private var isSelectionMode = false
|
||||
private var fileName = "Unknown File"
|
||||
private var fileTypes = when (fileType) {
|
||||
|
||||
FileManager.FileType.IMAGE -> {
|
||||
"IMAGE"
|
||||
}
|
||||
// Use WeakReference to prevent memory leaks
|
||||
private var fileOperationCallback: WeakReference<FileOperationCallback>? = null
|
||||
|
||||
FileManager.FileType.VIDEO -> {
|
||||
"VIDEO"
|
||||
}
|
||||
// Background executor for file operations
|
||||
private val fileExecutor = Executors.newSingleThreadExecutor()
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
FileManager.FileType.AUDIO -> {
|
||||
"AUDIO"
|
||||
}
|
||||
companion object {
|
||||
private const val TAG = "FileAdapter"
|
||||
}
|
||||
|
||||
else -> "DOCUMENT"
|
||||
// Callback interface for handling file operations and selection changes
|
||||
interface FileOperationCallback {
|
||||
fun onFileDeleted(file: File)
|
||||
fun onFileRenamed(oldFile: File, newFile: File)
|
||||
fun onRefreshNeeded()
|
||||
fun onSelectionModeChanged(isSelectionMode: Boolean, selectedCount: Int)
|
||||
fun onSelectionCountChanged(selectedCount: Int)
|
||||
}
|
||||
|
||||
fun setFileOperationCallback(callback: FileOperationCallback?) {
|
||||
fileOperationCallback = callback?.let { WeakReference(it) }
|
||||
}
|
||||
|
||||
inner class FileViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
private val imageView: ImageView = view.findViewById(R.id.imageView)
|
||||
val imageView: ImageView = view.findViewById(R.id.fileIconImageView)
|
||||
val fileNameTextView: TextView = view.findViewById(R.id.fileNameTextView)
|
||||
val playIcon: ImageView = view.findViewById(R.id.videoPlay)
|
||||
val selectedLayer: View = view.findViewById(R.id.selectedLayer)
|
||||
val selected: ImageView = view.findViewById(R.id.selected)
|
||||
|
||||
fun bind(file: File) {
|
||||
val fileType = FileManager(context, lifecycleOwner).getFileType(file)
|
||||
setupFileDisplay(file, fileType)
|
||||
setupClickListeners(file, fileType)
|
||||
fileNameTextView.visibility = if (showFileName) View.VISIBLE else View.GONE
|
||||
|
||||
// Update selection state
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
val isSelected = selectedItems.contains(position)
|
||||
updateSelectionUI(isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(file: File, payloads: List<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
bind(file)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle partial updates based on payload
|
||||
val changes = payloads.firstOrNull() as? List<String>
|
||||
changes?.forEach { change ->
|
||||
when (change) {
|
||||
"NAME_CHANGED" -> {
|
||||
fileNameTextView.text = file.name
|
||||
}
|
||||
"SIZE_CHANGED", "MODIFIED_DATE_CHANGED" -> {
|
||||
// Could update file info if displayed
|
||||
}
|
||||
"SELECTION_CHANGED" -> {
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
val isSelected = selectedItems.contains(position)
|
||||
updateSelectionUI(isSelected)
|
||||
// Notify activity about selection change
|
||||
notifySelectionModeChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelectionUI(isSelected: Boolean) {
|
||||
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||
selected.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun setupFileDisplay(file: File, fileType: FileManager.FileType) {
|
||||
fileNameTextView.text = file.name
|
||||
|
||||
when (fileType) {
|
||||
FileManager.FileType.IMAGE -> {
|
||||
Glide.with(imageView)
|
||||
playIcon.visibility = View.GONE
|
||||
Glide.with(context)
|
||||
.load(file)
|
||||
.centerCrop()
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
.error(R.drawable.ic_document)
|
||||
.placeholder(R.drawable.ic_document)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
FileManager.FileType.VIDEO -> {
|
||||
Glide.with(imageView)
|
||||
.asBitmap()
|
||||
playIcon.visibility = View.VISIBLE
|
||||
Glide.with(context)
|
||||
.load(file)
|
||||
.centerCrop()
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
.error(R.drawable.ic_document)
|
||||
.placeholder(R.drawable.ic_document)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
FileManager.FileType.AUDIO -> {
|
||||
playIcon.visibility = View.GONE
|
||||
imageView.setImageResource(R.drawable.ic_audio)
|
||||
}
|
||||
else -> {
|
||||
val resourceId = when (fileType) {
|
||||
FileManager.FileType.AUDIO -> R.drawable.ic_audio
|
||||
FileManager.FileType.DOCUMENT -> R.drawable.ic_document
|
||||
else -> R.drawable.ic_file
|
||||
}
|
||||
imageView.setImageResource(resourceId)
|
||||
playIcon.visibility = View.GONE
|
||||
imageView.setImageResource(R.drawable.ic_document)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupClickListeners(file: File, fileType: FileManager.FileType) {
|
||||
itemView.setOnClickListener {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) return@setOnClickListener
|
||||
|
||||
|
||||
when(fileType){
|
||||
FileManager.FileType.AUDIO -> {
|
||||
// Create an intent to play audio using available audio players
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "audio/*")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, "No audio player found!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
FileManager.FileType.DOCUMENT -> {
|
||||
// Create an intent to open the document using available viewers or file managers
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "*/*")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, "No suitable app found to open this document!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val intent = Intent(context, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileTypes)
|
||||
putExtra("position", position)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
if (isSelectionMode) {
|
||||
toggleSelection(position)
|
||||
} else {
|
||||
openFile(file, fileType)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
itemView.setOnLongClickListener {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) return@setOnLongClickListener false
|
||||
|
||||
val fileUri = FileManager.FileManager().getContentUriImage(context, file, fileType)
|
||||
if (fileUri == null) {
|
||||
Toast.makeText(context, "Unable to access file: $file", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
|
||||
return@setOnLongClickListener true
|
||||
|
||||
if (!isSelectionMode) {
|
||||
enterSelectionMode()
|
||||
toggleSelection(position)
|
||||
}
|
||||
fileName = FileManager.FileName(context).getFileNameFromUri(fileUri)?.toString()
|
||||
?: "Unknown File"
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
DialogUtil(context).showMaterialDialogWithNaturalButton(
|
||||
"$fileTypes DETAILS",
|
||||
"File Name: $fileName\n\nFile Path: $file\n\nYou can permanently delete or unhide this file.",
|
||||
"Delete Permanently",
|
||||
"Unhide",
|
||||
"Cancel",
|
||||
object : DialogActionsCallback {
|
||||
override fun onPositiveButtonClicked() {
|
||||
lifecycleOwner.lifecycleScope.launch {
|
||||
FileManager(context, lifecycleOwner).deletePhotoFromExternalStorage(
|
||||
fileUri
|
||||
)
|
||||
}
|
||||
val currentList = currentList.toMutableList()
|
||||
currentList.remove(file)
|
||||
submitList(currentList)
|
||||
}
|
||||
|
||||
override fun onNegativeButtonClicked() {
|
||||
FileManager(context, lifecycleOwner).copyFileToNormalDir(fileUri)
|
||||
val currentList = currentList.toMutableList()
|
||||
currentList.remove(file)
|
||||
submitList(currentList)
|
||||
}
|
||||
|
||||
override fun onNaturalButtonClicked() {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return@setOnLongClickListener true
|
||||
private fun openFile(file: File, fileType: FileManager.FileType) {
|
||||
if (!file.exists()) {
|
||||
Toast.makeText(context, "File no longer exists", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
when (fileType) {
|
||||
FileManager.FileType.AUDIO -> openAudioFile(file)
|
||||
FileManager.FileType.IMAGE, FileManager.FileType.VIDEO -> openInPreview(fileType)
|
||||
FileManager.FileType.DOCUMENT -> openDocumentFile(file)
|
||||
else -> openDocumentFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openAudioFile(file: File) {
|
||||
try {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(uri, "audio/*")
|
||||
putExtra("folder", currentFolder.toString())
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open audio file: ${e.message}")
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.no_audio_player_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDocumentFile(file: File) {
|
||||
try {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(uri, "*/*")
|
||||
putExtra("folder", currentFolder.toString())
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open document file: ${e.message}")
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.no_suitable_app_found_to_open_this_document),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openInPreview(fileType: FileManager.FileType) {
|
||||
val fileTypeString = when (fileType) {
|
||||
FileManager.FileType.IMAGE -> context.getString(R.string.image)
|
||||
FileManager.FileType.VIDEO -> context.getString(R.string.video)
|
||||
else -> "unknown"
|
||||
}
|
||||
|
||||
val intent = Intent(context, PreviewActivity::class.java).apply {
|
||||
putExtra("type", fileTypeString)
|
||||
putExtra("folder", currentFolder.toString())
|
||||
putExtra("position", adapterPosition)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun showFileOptionsDialog(file: File) {
|
||||
val options = if (isSelectionMode) {
|
||||
arrayOf(
|
||||
context.getString(R.string.un_hide),
|
||||
context.getString(R.string.delete),
|
||||
context.getString(R.string.copy_to_another_folder),
|
||||
context.getString(R.string.move_to_another_folder)
|
||||
)
|
||||
} else {
|
||||
arrayOf(
|
||||
context.getString(R.string.un_hide),
|
||||
context.getString(R.string.select_multiple),
|
||||
context.getString(R.string.rename),
|
||||
context.getString(R.string.delete),
|
||||
context.getString(R.string.share)
|
||||
)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(context.getString(R.string.file_options))
|
||||
.setItems(options) { dialog, which ->
|
||||
if (isSelectionMode) {
|
||||
when (which) {
|
||||
0 -> unHideFile(file)
|
||||
1 -> deleteFile(file)
|
||||
2 -> copyToAnotherFolder(file)
|
||||
3 -> moveToAnotherFolder(file)
|
||||
}
|
||||
} else {
|
||||
when (which) {
|
||||
0 -> unHideFile(file)
|
||||
1 -> enableSelectMultipleFiles()
|
||||
2 -> renameFile(file)
|
||||
3 -> deleteFile(file)
|
||||
4 -> shareFile(file)
|
||||
}
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun enableSelectMultipleFiles() {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) return
|
||||
|
||||
enterSelectionMode()
|
||||
selectedItems.add(position)
|
||||
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
|
||||
}
|
||||
|
||||
private fun unHideFile(file: File) {
|
||||
FileManager(context, lifecycleOwner).unHideFile(
|
||||
file = file,
|
||||
onSuccess = {
|
||||
fileOperationCallback?.get()?.onFileDeleted(file)
|
||||
},
|
||||
onError = { errorMessage ->
|
||||
Toast.makeText(context, "Failed to unhide: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun deleteFile(file: File) {
|
||||
// Show confirmation dialog first
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("Delete File")
|
||||
.setMessage("Are you sure you want to delete ${file.name}?")
|
||||
.setPositiveButton("Delete") { _, _ ->
|
||||
deleteFileAsync(file)
|
||||
}
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun deleteFileAsync(file: File) {
|
||||
fileExecutor.execute {
|
||||
val success = try {
|
||||
file.delete()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to delete file: ${e.message}")
|
||||
false
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
if (success) {
|
||||
fileOperationCallback?.get()?.onFileDeleted(file)
|
||||
Toast.makeText(context, "File deleted", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingInflatedId")
|
||||
private fun renameFile(file: File) {
|
||||
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_input, null)
|
||||
val inputEditText = dialogView.findViewById<EditText>(R.id.editText)
|
||||
inputEditText.setText(file.name)
|
||||
inputEditText.selectAll()
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(context.getString(R.string.rename_file))
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(context.getString(R.string.rename)) { dialog, _ ->
|
||||
val newName = inputEditText.text.toString().trim()
|
||||
if (newName.isNotEmpty() && newName != file.name) {
|
||||
if (isValidFileName(newName)) {
|
||||
renameFileAsync(file, newName)
|
||||
} else {
|
||||
Toast.makeText(context, "Invalid file name", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton(context.getString(R.string.cancel)) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun isValidFileName(fileName: String): Boolean {
|
||||
val forbiddenChars = charArrayOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
|
||||
return fileName.isNotBlank() &&
|
||||
fileName.none { it in forbiddenChars } &&
|
||||
!fileName.startsWith(".") &&
|
||||
fileName.length <= 255
|
||||
}
|
||||
|
||||
private fun renameFileAsync(file: File, newName: String) {
|
||||
fileExecutor.execute {
|
||||
val parentDir = file.parentFile
|
||||
if (parentDir != null) {
|
||||
val newFile = File(parentDir, newName)
|
||||
if (newFile.exists()) {
|
||||
mainHandler.post {
|
||||
Toast.makeText(context, "File with this name already exists", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return@execute
|
||||
}
|
||||
|
||||
val success = try {
|
||||
file.renameTo(newFile)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to rename file: ${e.message}")
|
||||
false
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
if (success) {
|
||||
fileOperationCallback?.get()?.onFileRenamed(file, newFile)
|
||||
Toast.makeText(context, "File renamed", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to rename file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareFile(file: File) {
|
||||
try {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = context.contentResolver.getType(uri) ?: "*/*"
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
context.startActivity(
|
||||
Intent.createChooser(shareIntent, context.getString(R.string.share_file))
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to share file: ${e.message}")
|
||||
Toast.makeText(context, "Failed to share file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyToAnotherFolder(file: File) {
|
||||
// This will be handled by the activity
|
||||
fileOperationCallback?.get()?.onRefreshNeeded()
|
||||
}
|
||||
|
||||
private fun moveToAnotherFolder(file: File) {
|
||||
// This will be handled by the activity
|
||||
fileOperationCallback?.get()?.onRefreshNeeded()
|
||||
}
|
||||
|
||||
private fun toggleSelection(position: Int) {
|
||||
if (selectedItems.contains(position)) {
|
||||
selectedItems.remove(position)
|
||||
if (selectedItems.isEmpty()) {
|
||||
exitSelectionMode()
|
||||
}
|
||||
} else {
|
||||
selectedItems.add(position)
|
||||
}
|
||||
onSelectionCountChanged(selectedItems.size)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_file, parent, false)
|
||||
.inflate(R.layout.list_item_file, parent, false)
|
||||
return FileViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
if (position < itemCount) {
|
||||
val file = getItem(position)
|
||||
holder.bind(file)
|
||||
}
|
||||
}
|
||||
|
||||
class FileDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem.path == newItem.path
|
||||
override fun onBindViewHolder(holder: FileViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
onBindViewHolder(holder, position)
|
||||
} else {
|
||||
if (position < itemCount) {
|
||||
val file = getItem(position)
|
||||
holder.bind(file, payloads)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun submitList(list: List<File>?) {
|
||||
val currentList = currentList.toMutableList()
|
||||
if (list == null) {
|
||||
currentList.clear()
|
||||
super.submitList(null)
|
||||
} else {
|
||||
// Create a new list to force update
|
||||
val newList = list.toMutableList()
|
||||
super.submitList(newList)
|
||||
}
|
||||
}
|
||||
|
||||
fun enterSelectionMode() {
|
||||
if (!isSelectionMode) {
|
||||
isSelectionMode = true
|
||||
notifySelectionModeChange()
|
||||
}
|
||||
}
|
||||
|
||||
fun exitSelectionMode() {
|
||||
if (isSelectionMode) {
|
||||
isSelectionMode = false
|
||||
selectedItems.clear()
|
||||
notifySelectionModeChange()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSelection() {
|
||||
if (selectedItems.isNotEmpty()) {
|
||||
val previouslySelected = selectedItems.toSet()
|
||||
selectedItems.clear()
|
||||
fileOperationCallback?.get()?.onSelectionCountChanged(0)
|
||||
previouslySelected.forEach { position ->
|
||||
if (position < itemCount) {
|
||||
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectAll() {
|
||||
if (!isSelectionMode) {
|
||||
enterSelectionMode()
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem == newItem
|
||||
val previouslySelected = selectedItems.toSet()
|
||||
selectedItems.clear()
|
||||
|
||||
// Add all positions to selection
|
||||
for (i in 0 until itemCount) {
|
||||
selectedItems.add(i)
|
||||
}
|
||||
|
||||
// Notify callback about selection change
|
||||
fileOperationCallback?.get()?.onSelectionCountChanged(selectedItems.size)
|
||||
|
||||
// Update UI for changed items efficiently
|
||||
updateSelectionItems(selectedItems.toSet(), previouslySelected)
|
||||
}
|
||||
|
||||
private fun updateSelectionItems(newSelections: Set<Int>, oldSelections: Set<Int>) {
|
||||
val changedItems = (oldSelections - newSelections) + (newSelections - oldSelections)
|
||||
changedItems.forEach { position ->
|
||||
if (position < itemCount) {
|
||||
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifySelectionModeChange() {
|
||||
fileOperationCallback?.get()?.onSelectionModeChanged(isSelectionMode, selectedItems.size)
|
||||
onFolderLongClick(isSelectionMode)
|
||||
}
|
||||
|
||||
fun getSelectedItems(): List<File> {
|
||||
return selectedItems.mapNotNull { position ->
|
||||
if (position < itemCount) getItem(position) else null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
fun getSelectedCount(): Int = selectedItems.size
|
||||
|
||||
|
||||
fun isInSelectionMode(): Boolean = isSelectionMode
|
||||
|
||||
|
||||
fun deleteSelectedFiles() {
|
||||
val selectedFiles = getSelectedItems()
|
||||
if (selectedFiles.isEmpty()) return
|
||||
|
||||
// Show confirmation dialog
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("Delete Files")
|
||||
.setMessage("Are you sure you want to delete ${selectedFiles.size} file(s)?")
|
||||
.setPositiveButton("Delete") { _, _ ->
|
||||
deleteFilesAsync(selectedFiles)
|
||||
}
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun deleteFilesAsync(selectedFiles: List<File>) {
|
||||
fileExecutor.execute {
|
||||
var deletedCount = 0
|
||||
var failedCount = 0
|
||||
val failedFiles = mutableListOf<String>()
|
||||
|
||||
selectedFiles.forEach { file ->
|
||||
try {
|
||||
if (file.delete()) {
|
||||
deletedCount++
|
||||
mainHandler.post {
|
||||
fileOperationCallback?.get()?.onFileDeleted(file)
|
||||
}
|
||||
} else {
|
||||
failedCount++
|
||||
failedFiles.add(file.name)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
failedCount++
|
||||
failedFiles.add(file.name)
|
||||
Log.e(TAG, "Failed to delete ${file.name}: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
// Exit selection mode first
|
||||
exitSelectionMode()
|
||||
|
||||
// Show detailed result message
|
||||
when {
|
||||
deletedCount > 0 && failedCount == 0 -> {
|
||||
Toast.makeText(context, "Deleted $deletedCount file(s)", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
deletedCount > 0 && failedCount > 0 -> {
|
||||
Toast.makeText(context,
|
||||
"Deleted $deletedCount file(s), failed to delete $failedCount",
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
failedCount > 0 -> {
|
||||
Toast.makeText(context,
|
||||
"Failed to delete $failedCount file(s)",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun shareSelectedFiles() {
|
||||
val selectedFiles = getSelectedItems()
|
||||
if (selectedFiles.isEmpty()) return
|
||||
|
||||
try {
|
||||
if (selectedFiles.size == 1) {
|
||||
// Share single file
|
||||
val file = selectedFiles.first()
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = context.contentResolver.getType(uri) ?: "*/*"
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
context.startActivity(
|
||||
Intent.createChooser(shareIntent, context.getString(R.string.share_file))
|
||||
)
|
||||
} else {
|
||||
// Share multiple files
|
||||
val uris = selectedFiles.mapNotNull { file ->
|
||||
try {
|
||||
FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to get URI for file ${file.name}: ${e.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (uris.isNotEmpty()) {
|
||||
val shareIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||
type = "*/*"
|
||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
context.startActivity(
|
||||
Intent.createChooser(shareIntent, "Share ${selectedFiles.size} files")
|
||||
)
|
||||
} else {
|
||||
Toast.makeText(context, "No files could be shared", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to share files: ${e.message}")
|
||||
Toast.makeText(context, "Failed to share files", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
exitSelectionMode()
|
||||
}
|
||||
|
||||
|
||||
fun onBackPressed(): Boolean {
|
||||
return if (isSelectionMode) {
|
||||
exitSelectionMode()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshSelectionStates() {
|
||||
if (isSelectionMode) {
|
||||
selectedItems.forEach { position ->
|
||||
if (position < itemCount) {
|
||||
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
|
||||
}
|
||||
}
|
||||
// Ensure callback is notified
|
||||
notifySelectionModeChange()
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanup() {
|
||||
try {
|
||||
if (!fileExecutor.isShutdown) {
|
||||
fileExecutor.shutdown()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error shutting down executor: ${e.message}")
|
||||
}
|
||||
|
||||
fileOperationCallback?.clear()
|
||||
fileOperationCallback = null
|
||||
}
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
cleanup()
|
||||
}
|
||||
|
||||
private fun onSelectionCountChanged(count: Int) {
|
||||
fileOperationCallback?.get()?.onSelectionCountChanged(count)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import java.io.File
|
||||
|
||||
class FileDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
// Compare by absolute path since File objects might be different instances
|
||||
// but represent the same file
|
||||
return oldItem.absolutePath == newItem.absolutePath
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
// Compare all relevant properties that might change and affect the UI
|
||||
return oldItem.name == newItem.name &&
|
||||
oldItem.length() == newItem.length() &&
|
||||
oldItem.lastModified() == newItem.lastModified() &&
|
||||
oldItem.canRead() == newItem.canRead() &&
|
||||
oldItem.canWrite() == newItem.canWrite() &&
|
||||
oldItem.exists() == newItem.exists()
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: File, newItem: File): Any? {
|
||||
// Return a payload if only specific properties changed
|
||||
// This allows for partial updates instead of full rebinding
|
||||
val changes = mutableListOf<String>()
|
||||
|
||||
if (oldItem.name != newItem.name) {
|
||||
changes.add("NAME_CHANGED")
|
||||
}
|
||||
|
||||
if (oldItem.length() != newItem.length()) {
|
||||
changes.add("SIZE_CHANGED")
|
||||
}
|
||||
|
||||
if (oldItem.lastModified() != newItem.lastModified()) {
|
||||
changes.add("MODIFIED_DATE_CHANGED")
|
||||
}
|
||||
|
||||
if (oldItem.exists() != newItem.exists()) {
|
||||
changes.add("EXISTENCE_CHANGED")
|
||||
}
|
||||
|
||||
return if (changes.isNotEmpty()) changes else null
|
||||
}
|
||||
}
|
||||
125
app/src/main/java/devs/org/calculator/adapters/FolderAdapter.kt
Normal file
125
app/src/main/java/devs/org/calculator/adapters/FolderAdapter.kt
Normal file
@@ -0,0 +1,125 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import devs.org.calculator.R
|
||||
import java.io.File
|
||||
|
||||
class FolderAdapter(
|
||||
private val onFolderClick: (File) -> Unit,
|
||||
private val onFolderLongClick: (File) -> Unit,
|
||||
private val onSelectionModeChanged: (Boolean) -> Unit,
|
||||
private val onSelectionCountChanged: (Int) -> Unit
|
||||
) : ListAdapter<File, FolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
|
||||
|
||||
private val selectedItems = mutableSetOf<Int>()
|
||||
private var isSelectionMode = false
|
||||
|
||||
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
|
||||
val selectedView: ImageView = itemView.findViewById(R.id.selected)
|
||||
val selectedLayer: View = itemView.findViewById(R.id.selectedLayer)
|
||||
|
||||
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) {
|
||||
folderNameTextView.text = folder.name
|
||||
|
||||
selectedView.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||
|
||||
itemView.setOnClickListener {
|
||||
if (isSelectionMode) {
|
||||
toggleSelection(adapterPosition)
|
||||
} else {
|
||||
onFolderClick(folder)
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnLongClickListener {
|
||||
if (!isSelectionMode) {
|
||||
enterSelectionMode()
|
||||
onFolderLongClick(folder)
|
||||
toggleSelection(adapterPosition)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleSelection(position: Int) {
|
||||
if (selectedItems.contains(position)) {
|
||||
selectedItems.remove(position)
|
||||
if (selectedItems.isEmpty()) {
|
||||
exitSelectionMode()
|
||||
}
|
||||
} else {
|
||||
selectedItems.add(position)
|
||||
}
|
||||
onSelectionCountChanged(selectedItems.size)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_folder, parent, false)
|
||||
return FolderViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
|
||||
val folder = getItem(position)
|
||||
holder.bind(folder, onFolderClick, onFolderLongClick, selectedItems.contains(position))
|
||||
}
|
||||
|
||||
fun getSelectedItems(): List<File> {
|
||||
return selectedItems.mapNotNull { position ->
|
||||
if (position < itemCount) getItem(position) else null
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSelection() {
|
||||
val wasInSelectionMode = isSelectionMode
|
||||
selectedItems.clear()
|
||||
if (wasInSelectionMode) {
|
||||
exitSelectionMode()
|
||||
}
|
||||
onSelectionCountChanged(0)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun onBackPressed(): Boolean {
|
||||
return if (isInSelectionMode()) {
|
||||
clearSelection()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun isInSelectionMode(): Boolean {
|
||||
return isSelectionMode
|
||||
}
|
||||
|
||||
private fun enterSelectionMode() {
|
||||
isSelectionMode = true
|
||||
onSelectionModeChanged(true)
|
||||
}
|
||||
|
||||
private fun exitSelectionMode() {
|
||||
isSelectionMode = false
|
||||
onSelectionModeChanged(false)
|
||||
}
|
||||
|
||||
private class FolderDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem.absolutePath == newItem.absolutePath
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem.name == newItem.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import devs.org.calculator.R
|
||||
import java.io.File
|
||||
|
||||
class FolderSelectionAdapter(
|
||||
private val folders: List<File>,
|
||||
private val onFolderSelected: (File) -> Unit
|
||||
) : RecyclerView.Adapter<FolderSelectionAdapter.FolderViewHolder>() {
|
||||
|
||||
inner class FolderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val folderName: TextView = view.findViewById(R.id.folderName)
|
||||
|
||||
init {
|
||||
view.setOnClickListener {
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
onFolderSelected(folders[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_folder_selection, parent, false)
|
||||
return FolderViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
|
||||
val folder = folders[position]
|
||||
holder.folderName.text = folder.name
|
||||
}
|
||||
|
||||
override fun getItemCount() = folders.size
|
||||
}
|
||||
@@ -10,10 +10,10 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import android.widget.SeekBar
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import devs.org.calculator.adapters.FileAdapter.FileDiffCallback
|
||||
import devs.org.calculator.databinding.ViewpagerItemsBinding
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import java.io.File
|
||||
@@ -21,7 +21,7 @@ import devs.org.calculator.R
|
||||
|
||||
class ImagePreviewAdapter(
|
||||
private val context: Context,
|
||||
private var fileType: FileManager.FileType
|
||||
private var lifecycleOwner: LifecycleOwner
|
||||
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
|
||||
|
||||
private val differ = AsyncListDiffer(this, FileDiffCallback())
|
||||
@@ -42,7 +42,8 @@ class ImagePreviewAdapter(
|
||||
|
||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||
val imageUrl = images[position]
|
||||
holder.bind(imageUrl)
|
||||
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
|
||||
holder.bind(imageUrl,fileType)
|
||||
currentViewHolder = holder
|
||||
|
||||
currentMediaPlayer?.let {
|
||||
@@ -67,7 +68,7 @@ class ImagePreviewAdapter(
|
||||
private var seekHandler = Handler(Looper.getMainLooper())
|
||||
private var seekRunnable: Runnable? = null
|
||||
|
||||
fun bind(file: File) {
|
||||
fun bind(file: File, fileType: FileManager.FileType) {
|
||||
when (fileType) {
|
||||
FileManager.FileType.VIDEO -> {
|
||||
binding.imageView.visibility = View.GONE
|
||||
@@ -128,13 +129,15 @@ class ImagePreviewAdapter(
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setDataSource(file.absolutePath)
|
||||
setOnPreparedListener { mp ->
|
||||
binding.audioSeekBar.max = mp.duration
|
||||
binding.audioSeekBar.valueTo = mp.duration.toFloat()
|
||||
|
||||
isMediaPlayerPrepared = true
|
||||
}
|
||||
setOnCompletionListener {
|
||||
// isPlaying = false
|
||||
binding.playPause.setImageResource(R.drawable.play)
|
||||
binding.audioSeekBar.progress = 0
|
||||
binding.audioSeekBar.value = 0f
|
||||
|
||||
seekHandler.removeCallbacks(seekRunnable!!)
|
||||
}
|
||||
prepareAsync()
|
||||
@@ -142,27 +145,23 @@ class ImagePreviewAdapter(
|
||||
}
|
||||
|
||||
private fun setupSeekBar() {
|
||||
binding.audioSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
mediaPlayer?.seekTo(progress)
|
||||
}
|
||||
binding.audioSeekBar.addOnChangeListener { slider, value, fromUser ->
|
||||
if (fromUser && mediaPlayer != null && isMediaPlayerPrepared) {
|
||||
mediaPlayer?.seekTo(value.toInt())
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
})
|
||||
}
|
||||
|
||||
seekRunnable = Runnable {
|
||||
mediaPlayer?.let { mp ->
|
||||
if (mp.isPlaying) {
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
binding.audioSeekBar.value = mp.currentPosition.toFloat()
|
||||
seekHandler.postDelayed(seekRunnable!!, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun setupPlaybackControls() {
|
||||
binding.playPause.setOnClickListener {
|
||||
if (isPlaying) {
|
||||
@@ -176,7 +175,7 @@ class ImagePreviewAdapter(
|
||||
mediaPlayer?.let { mp ->
|
||||
val newPosition = mp.currentPosition - 10000
|
||||
mp.seekTo(maxOf(0, newPosition))
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
binding.audioSeekBar.value = mp.currentPosition.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +183,7 @@ class ImagePreviewAdapter(
|
||||
mediaPlayer?.let { mp ->
|
||||
val newPosition = mp.currentPosition + 10000
|
||||
mp.seekTo(minOf(mp.duration, newPosition))
|
||||
binding.audioSeekBar.progress = mp.currentPosition
|
||||
binding.audioSeekBar.value = mp.currentPosition.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,6 +227,7 @@ class ImagePreviewAdapter(
|
||||
|
||||
private fun playVideoAtPosition(position: Int) {
|
||||
val nextFile = images[position]
|
||||
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
|
||||
if (fileType == FileManager.FileType.VIDEO) {
|
||||
val videoUri = Uri.fromFile(nextFile)
|
||||
binding.videoView.setVideoURI(videoUri)
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package devs.org.calculator.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import devs.org.calculator.R
|
||||
import java.io.File
|
||||
|
||||
class ListFolderAdapter(
|
||||
private val onFolderClick: (File) -> Unit,
|
||||
private val onFolderLongClick: (File) -> Unit,
|
||||
private val onSelectionModeChanged: (Boolean) -> Unit,
|
||||
private val onSelectionCountChanged: (Int) -> Unit
|
||||
) : ListAdapter<File, ListFolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
|
||||
|
||||
private val selectedItems = mutableSetOf<Int>()
|
||||
private var isSelectionMode = false
|
||||
|
||||
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
|
||||
|
||||
val selectedLayer: View = itemView.findViewById(R.id.selectedLayer)
|
||||
|
||||
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) {
|
||||
folderNameTextView.text = folder.name
|
||||
|
||||
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||
|
||||
itemView.setOnClickListener {
|
||||
if (isSelectionMode) {
|
||||
toggleSelection(adapterPosition)
|
||||
} else {
|
||||
onFolderClick(folder)
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnLongClickListener {
|
||||
if (!isSelectionMode) {
|
||||
enterSelectionMode()
|
||||
onFolderLongClick(folder)
|
||||
toggleSelection(adapterPosition)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleSelection(position: Int) {
|
||||
if (selectedItems.contains(position)) {
|
||||
selectedItems.remove(position)
|
||||
if (selectedItems.isEmpty()) {
|
||||
exitSelectionMode()
|
||||
}
|
||||
} else {
|
||||
selectedItems.add(position)
|
||||
}
|
||||
onSelectionCountChanged(selectedItems.size)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterSelectionMode() {
|
||||
isSelectionMode = true
|
||||
onSelectionModeChanged(true)
|
||||
}
|
||||
|
||||
private fun exitSelectionMode() {
|
||||
isSelectionMode = false
|
||||
onSelectionModeChanged(false)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_folder_list_style, parent, false)
|
||||
return FolderViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
|
||||
val folder = getItem(position)
|
||||
holder.bind(folder, onFolderClick, onFolderLongClick, selectedItems.contains(position))
|
||||
}
|
||||
|
||||
fun getSelectedItems(): List<File> {
|
||||
return selectedItems.mapNotNull { position ->
|
||||
if (position < itemCount) getItem(position) else null
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSelection() {
|
||||
val wasInSelectionMode = isSelectionMode
|
||||
selectedItems.clear()
|
||||
if (wasInSelectionMode) {
|
||||
exitSelectionMode()
|
||||
}
|
||||
onSelectionCountChanged(0)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun onBackPressed(): Boolean {
|
||||
return if (isInSelectionMode()) {
|
||||
clearSelection()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun isInSelectionMode(): Boolean {
|
||||
return isSelectionMode
|
||||
}
|
||||
|
||||
private class FolderDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem.absolutePath == newItem.absolutePath
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||
return oldItem.name == newItem.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,57 @@
|
||||
package devs.org.calculator.utils
|
||||
|
||||
import android.app.RecoverableSecurityException
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.EditText
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import devs.org.calculator.R
|
||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class DialogUtil(private val context: Context) {
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
fun showMaterialDialogWithNaturalButton(
|
||||
title: String,
|
||||
message: String,
|
||||
positiveButton: String,
|
||||
negativeButton: String,
|
||||
neutralButton: String,
|
||||
callback: DialogActionsCallback
|
||||
) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(positiveButton) { dialog, _ ->
|
||||
// Handle positive button click
|
||||
callback.onPositiveButtonClicked()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton(negativeButton) { dialog, _ ->
|
||||
// Handle negative button click
|
||||
callback.onNegativeButtonClicked()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNeutralButton(neutralButton) { dialog, _ ->
|
||||
callback.onNaturalButtonClicked()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showMaterialDialog(
|
||||
title: String,
|
||||
message: String,
|
||||
positiveButton: String,
|
||||
negativeButton: String,
|
||||
callback: DialogActionsCallback
|
||||
positiveButtonText: String,
|
||||
neutralButtonText: String,
|
||||
callback: DialogCallback
|
||||
) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(positiveButton) { dialog, _ ->
|
||||
// Handle positive button click
|
||||
callback.onPositiveButtonClicked()
|
||||
}
|
||||
.setNegativeButton(negativeButton) { dialog, _ ->
|
||||
// Handle negative button click
|
||||
callback.onNegativeButtonClicked()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setPositiveButton(positiveButtonText) { _, _ -> callback.onPositiveButtonClicked() }
|
||||
.setNegativeButton(neutralButtonText) { _, _ -> callback.onNegativeButtonClicked() }
|
||||
.show()
|
||||
}
|
||||
|
||||
fun createInputDialog(
|
||||
title: String,
|
||||
hint: String,
|
||||
callback: InputDialogCallback
|
||||
) {
|
||||
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_input, null)
|
||||
val editText = dialogView.findViewById<EditText>(R.id.editText)
|
||||
editText.hint = hint
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(R.string.create) { _, _ ->
|
||||
callback.onPositiveButtonClicked(editText.text.toString())
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
interface DialogCallback {
|
||||
fun onPositiveButtonClicked()
|
||||
fun onNegativeButtonClicked()
|
||||
fun onNaturalButtonClicked()
|
||||
}
|
||||
|
||||
interface InputDialogCallback {
|
||||
fun onPositiveButtonClicked(input: String)
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,13 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.callbacks.FileProcessCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import android.Manifest
|
||||
import androidx.core.content.FileProvider
|
||||
import devs.org.calculator.R
|
||||
|
||||
class FileManager(private val context: Context, private val lifecycleOwner: LifecycleOwner) {
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
@@ -41,33 +43,49 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
fun getHiddenDirectory(): File {
|
||||
val dir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
val created = dir.mkdirs()
|
||||
if (!created) {
|
||||
throw RuntimeException("Failed to create hidden directory: ${dir.absolutePath}")
|
||||
}
|
||||
// Create .nomedia file to hide from media scanners
|
||||
File(dir, ".nomedia").createNewFile()
|
||||
val nomediaFile = File(dir, ".nomedia")
|
||||
if (!nomediaFile.exists()) {
|
||||
nomediaFile.createNewFile()
|
||||
}
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun getFilesInHiddenDir(type: FileType): List<File> {
|
||||
val hiddenDir = getHiddenDirectory()
|
||||
val typeDir = File(hiddenDir, type.dirName)
|
||||
return if (typeDir.exists()) {
|
||||
typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
||||
} else {
|
||||
emptyList()
|
||||
if (!typeDir.exists()) {
|
||||
typeDir.mkdirs()
|
||||
File(typeDir, ".nomedia").createNewFile()
|
||||
}
|
||||
return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
||||
}
|
||||
fun getFilesInHiddenDirFromFolder(type: FileType, folder: String): List<File> {
|
||||
val typeDir = File(folder)
|
||||
if (!typeDir.exists()) {
|
||||
typeDir.mkdirs()
|
||||
File(typeDir, ".nomedia").createNewFile()
|
||||
}
|
||||
return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
||||
}
|
||||
|
||||
fun copyFileToHiddenDir(uri: Uri, type: FileType): File? {
|
||||
private fun copyFileToHiddenDir(uri: Uri, folderName: File, currentDir: File? = null): File? {
|
||||
return try {
|
||||
val contentResolver = context.contentResolver
|
||||
|
||||
// Get the target directory
|
||||
val targetDir = File(Environment.getExternalStorageDirectory(), "$HIDDEN_DIR/${type.dirName}")
|
||||
targetDir.mkdirs()
|
||||
File(targetDir, ".nomedia").createNewFile()
|
||||
// Get the target directory (i am using the current opened folder as target folder)
|
||||
val targetDir = folderName
|
||||
|
||||
// Ensure target directory exists and has .nomedia file
|
||||
if (!targetDir.exists()) {
|
||||
targetDir.mkdirs()
|
||||
File(targetDir, ".nomedia").createNewFile()
|
||||
}
|
||||
|
||||
// Create target file
|
||||
val mimeType = contentResolver.getType(uri)
|
||||
@@ -141,6 +159,64 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun unHideFile(file: File, onSuccess: (() -> Unit)? = null, onError: ((String) -> Unit)? = null) {
|
||||
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
// Create target directory (Downloads)
|
||||
val targetDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
targetDir.mkdirs()
|
||||
|
||||
// Create target file with same name or timestamp
|
||||
val targetFile = File(targetDir, file.name)
|
||||
|
||||
// If file with same name exists, add timestamp
|
||||
val finalTargetFile = if (targetFile.exists()) {
|
||||
val nameWithoutExt = file.nameWithoutExtension
|
||||
val extension = file.extension
|
||||
File(targetDir, "${nameWithoutExt}_${System.currentTimeMillis()}.${extension}")
|
||||
} else {
|
||||
targetFile
|
||||
}
|
||||
|
||||
// Copy file content
|
||||
file.copyTo(finalTargetFile, overwrite = false)
|
||||
|
||||
// Verify copy success
|
||||
if (finalTargetFile.exists() && finalTargetFile.length() > 0) {
|
||||
// Delete original hidden file
|
||||
if (file.delete()) {
|
||||
// Trigger media scan for the new file
|
||||
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
|
||||
mediaScanIntent.data = Uri.fromFile(finalTargetFile)
|
||||
context.sendBroadcast(mediaScanIntent)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, context.getString(R.string.file_unhidden_successfully), Toast.LENGTH_SHORT).show()
|
||||
onSuccess?.invoke() // Call success callback
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "File copied but failed to remove from hidden folder", Toast.LENGTH_SHORT).show()
|
||||
onError?.invoke("Failed to remove from hidden folder")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Failed to copy file", Toast.LENGTH_SHORT).show()
|
||||
onError?.invoke("Failed to copy file")
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Error unhiding file: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
onError?.invoke(e.message ?: "Unknown error")
|
||||
}
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deletePhotoFromExternalStorage(photoUri: Uri) {
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -150,11 +226,7 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
if (documentFile?.exists() == true && documentFile.canWrite()) {
|
||||
val deleted = documentFile.delete()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (deleted) {
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to hide/unhide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
return@withContext
|
||||
}
|
||||
@@ -163,7 +235,7 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
try {
|
||||
context.contentResolver.delete(photoUri, null, null)
|
||||
withContext(Dispatchers.Main) {
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
// Handle security exception for Android 10 and above
|
||||
@@ -188,7 +260,7 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Error hiding/unhiding file: ${e.message}",
|
||||
"Error hiding/un-hiding file: ${e.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
@@ -221,8 +293,8 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
}
|
||||
|
||||
}
|
||||
class FileManager(){
|
||||
fun getContentUriImage(context: Context, file: File, fileType: FileType): Uri? {
|
||||
class FileManager{
|
||||
fun getContentUriImage(context: Context, file: File): Uri? {
|
||||
|
||||
// Query MediaStore for the file
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID)
|
||||
@@ -258,8 +330,6 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(activity, "Unable to open settings. Please grant permission manually.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(activity, "Permission already granted", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
// For Android 10 and below
|
||||
@@ -274,19 +344,22 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
|
||||
suspend fun processMultipleFiles(
|
||||
uriList: List<Uri>,
|
||||
fileType: FileType,
|
||||
callback: FileProcessCallback
|
||||
targetFolder: File,
|
||||
callback: FileProcessCallback,
|
||||
currentDir: File? = null
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val copiedFiles = mutableListOf<File>()
|
||||
for (uri in uriList) {
|
||||
try {
|
||||
val file = copyFileToHiddenDir(uri, fileType)
|
||||
val file = copyFileToHiddenDir(uri, targetFolder, currentDir)
|
||||
file?.let { copiedFiles.add(it) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
delay(500)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (copiedFiles.isNotEmpty()) {
|
||||
callback.onFilesProcessedSuccessfully(copiedFiles)
|
||||
@@ -297,12 +370,22 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
}
|
||||
}
|
||||
|
||||
fun getFileType(file: File): FileType {
|
||||
val extension = file.extension.lowercase()
|
||||
return when (extension) {
|
||||
"jpg", "jpeg", "png", "gif", "bmp", "webp" -> FileType.IMAGE
|
||||
"mp4", "avi", "mkv", "mov", "wmv", "flv", "webm", "3gp" -> FileType.VIDEO
|
||||
"mp3", "wav", "flac", "aac", "ogg", "m4a" -> FileType.AUDIO
|
||||
else -> FileType.DOCUMENT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class FileType(val dirName: String) {
|
||||
IMAGE(IMAGES_DIR),
|
||||
VIDEO(VIDEOS_DIR),
|
||||
AUDIO(AUDIO_DIR),
|
||||
DOCUMENT(DOCS_DIR)
|
||||
DOCUMENT(DOCS_DIR),
|
||||
ALL("all")
|
||||
}
|
||||
}
|
||||
73
app/src/main/java/devs/org/calculator/utils/FolderManager.kt
Normal file
73
app/src/main/java/devs/org/calculator/utils/FolderManager.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package devs.org.calculator.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
|
||||
class FolderManager(private val context: Context) {
|
||||
companion object {
|
||||
const val HIDDEN_DIR = ".CalculatorHide"
|
||||
}
|
||||
|
||||
fun createFolder(parentDir: File, folderName: String): Boolean {
|
||||
val newFolder = File(parentDir, folderName)
|
||||
return if (!newFolder.exists()) {
|
||||
newFolder.mkdirs()
|
||||
// Create .nomedia file to hide from media scanners
|
||||
File(newFolder, ".nomedia").createNewFile()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteFolder(folder: File): Boolean {
|
||||
return try {
|
||||
if (folder.exists() && folder.isDirectory) {
|
||||
folder.listFiles()?.forEach { file ->
|
||||
if (file.isFile) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
folder.delete()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun getFoldersInDirectory(directory: File): List<File> {
|
||||
return if (directory.exists() && directory.isDirectory) {
|
||||
directory.listFiles()?.filter { it.isDirectory && it.name != ".nomedia" } ?: emptyList()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun getFilesInFolder(folder: File): List<File> {
|
||||
return if (folder.exists() && folder.isDirectory) {
|
||||
folder.listFiles()?.filter { it.isFile && it.name != ".nomedia" } ?: emptyList()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun moveFileToFolder(file: File, targetFolder: File): Boolean {
|
||||
return try {
|
||||
if (!targetFolder.exists()) {
|
||||
targetFolder.mkdirs()
|
||||
File(targetFolder, ".nomedia").createNewFile()
|
||||
}
|
||||
val newFile = File(targetFolder, file.name)
|
||||
file.copyTo(newFile, overwrite = true)
|
||||
file.delete()
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,15 @@ class PrefsUtil(context: Context) {
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun setBoolean(key:String, value: Boolean){
|
||||
return prefs.edit().putBoolean(key,value).apply()
|
||||
|
||||
}
|
||||
|
||||
fun getBoolean(key: String, defValue: Boolean = false): Boolean{
|
||||
return prefs.getBoolean(key,defValue)
|
||||
}
|
||||
|
||||
fun resetPassword(){
|
||||
prefs.edit()
|
||||
.remove("password")
|
||||
|
||||
9
app/src/main/res/anim/fab_close.xml
Normal file
9
app/src/main/res/anim/fab_close.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<scale xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="200"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:toXScale="0.0"
|
||||
android:toYScale="0.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
9
app/src/main/res/anim/fab_open.xml
Normal file
9
app/src/main/res/anim/fab_open.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<scale xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="200"
|
||||
android:fromXScale="0.0"
|
||||
android:fromYScale="0.0"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
7
app/src/main/res/anim/rotate_close.xml
Normal file
7
app/src/main/res/anim/rotate_close.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="200"
|
||||
android:fromDegrees="45"
|
||||
android:toDegrees="0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
7
app/src/main/res/anim/rotate_open.xml
Normal file
7
app/src/main/res/anim/rotate_open.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="200"
|
||||
android:fromDegrees="0"
|
||||
android:toDegrees="45"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
9
app/src/main/res/drawable/add_image.xml
Normal file
9
app/src/main/res/drawable/add_image.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M23,4v2h-3v3h-2L18,6h-3L15,4h3L18,1h2v3h3zM14.5,11c0.828,0 1.5,-0.672 1.5,-1.5S15.328,8 14.5,8 13,8.672 13,9.5s0.672,1.5 1.5,1.5zM18,14.234l-0.513,-0.57c-0.794,-0.885 -2.18,-0.885 -2.976,0l-0.655,0.73L9,9l-3,3.333L6,6h7L13,4L6,4c-1.105,0 -2,0.895 -2,2v12c0,1.105 0.895,2 2,2h12c1.105,0 2,-0.895 2,-2v-7h-2v3.234z"/>
|
||||
</vector>
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M7.7,6.36L3.533,11.36C3.224,11.731 3.224,12.269 3.533,12.64L7.7,17.64C7.89,17.868 8.172,18 8.468,18H18C19.657,18 21,16.657 21,15V9C21,7.343 19.657,6 18,6H8.468C8.172,6 7.89,6.132 7.7,6.36Z"
|
||||
android:strokeColor="@color/textColor" android:strokeLineCap="round"
|
||||
android:strokeColor="@color/white" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="2"/>
|
||||
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M15,10L13,12M13,12L11,14M13,12L11,10M13,12L15,14"
|
||||
android:strokeColor="@color/textColor"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"/>
|
||||
|
||||
6
app/src/main/res/drawable/bottom_corner.xml
Normal file
6
app/src/main/res/drawable/bottom_corner.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:bottomLeftRadius="15dp" android:bottomRightRadius="15dp"/>
|
||||
<solid android:color="?attr/cardForegroundColor"/>
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/document_add.xml
Normal file
9
app/src/main/res/drawable/document_add.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="205dp"
|
||||
android:height="205dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M15,12h-2v-2c0,-0.553 -0.447,-1 -1,-1s-1,0.447 -1,1v2h-2c-0.553,0 -1,0.447 -1,1s0.447,1 1,1h2v2c0,0.553 0.447,1 1,1s1,-0.447 1,-1v-2h2c0.553,0 1,-0.447 1,-1s-0.447,-1 -1,-1zM19.707,7.293l-4,-4c-0.187,-0.188 -0.441,-0.293 -0.707,-0.293h-8c-1.654,0 -3,1.346 -3,3v12c0,1.654 1.346,3 3,3h10c1.654,0 3,-1.346 3,-3v-10c0,-0.266 -0.105,-0.52 -0.293,-0.707zM17.586,8h-1.086c-0.827,0 -1.5,-0.673 -1.5,-1.5v-1.086l2.586,2.586zM17,19h-10c-0.552,0 -1,-0.448 -1,-1v-12c0,-0.552 0.448,-1 1,-1h7v1.5c0,1.379 1.121,2.5 2.5,2.5h1.5v9c0,0.552 -0.448,1 -1,1z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/gradient_bg.xml
Normal file
5
app/src/main/res/drawable/gradient_bg.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="15dp"/>
|
||||
<gradient android:startColor="?attr/colorPrimary" android:endColor="?attr/colorPrimary" android:angle="75"/>
|
||||
</shape>
|
||||
10
app/src/main/res/drawable/ic_add.xml
Normal file
10
app/src/main/res/drawable/ic_add.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
||||
@@ -1,9 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/textColor"
|
||||
android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M13.75,2C13.75,1.586 13.414,1.25 13,1.25C12.586,1.25 12.25,1.586 12.25,2V14.536C11.4,13.738 10.257,13.25 9,13.25C6.377,13.25 4.25,15.377 4.25,18C4.25,20.623 6.377,22.75 9,22.75C11.623,22.75 13.75,20.623 13.75,18V6.243C14.988,7.772 16.879,8.75 19,8.75C19.414,8.75 19.75,8.414 19.75,8C19.75,7.586 19.414,7.25 19,7.25C16.101,7.25 13.75,4.899 13.75,2Z"/>
|
||||
|
||||
</vector>
|
||||
|
||||
12
app/src/main/res/drawable/ic_back.xml
Normal file
12
app/src/main/res/drawable/ic_back.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M224,480h640a32,32 0,1 1,0 64H224a32,32 0,0 1,0 -64z"
|
||||
android:fillColor="@color/svgTintColor"/>
|
||||
<path
|
||||
android:pathData="m237.2,512 l265.4,265.3a32,32 0,0 1,-45.3 45.3l-288,-288a32,32 0,0 1,0 -45.3l288,-288a32,32 0,1 1,45.3 45.3L237.2,512z"
|
||||
android:fillColor="@color/svgTintColor"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_close.xml
Normal file
10
app/src/main/res/drawable/ic_close.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
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"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_create_new_folder.xml
Normal file
10
app/src/main/res/drawable/ic_create_new_folder.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18L4,8h16v10zM14,12h-2v-2h-2v2L8,12v2h2v2h2v-2h2z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_delete.xml
Normal file
10
app/src/main/res/drawable/ic_delete.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/textColor"
|
||||
android:pathData="M14,2H6C4.9,2 4,2.9 4,4v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V8L14,2zM16,18H8v-2h8V18zM16,14H8v-2h8V14zM13,9V3.5L18.5,9H13z"/>
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_edit.xml
Normal file
9
app/src/main/res/drawable/ic_edit.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_folder.xml
Normal file
10
app/src/main/res/drawable/ic_folder.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||
</vector>
|
||||
13
app/src/main/res/drawable/ic_folder_add.xml
Normal file
13
app/src/main/res/drawable/ic_folder_add.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M15,13h-2v2h-2v-2H9v-2h2V9h2v2h2v2z"/>
|
||||
</vector>
|
||||
37
app/src/main/res/drawable/ic_folder_yellow.xml
Normal file
37
app/src/main/res/drawable/ic_folder_yellow.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="32">
|
||||
<path
|
||||
android:pathData="M2.909,3.5L11.283,3.5C12.441,3.5 13.551,3.96 14.369,4.778L14.722,5.131C15.54,5.949 16.65,6.409 17.807,6.409L23.273,6.409C24.076,6.409 24.762,6.693 25.33,7.261C25.898,7.829 26.182,8.515 26.182,9.318L26.182,25.318C26.182,26.122 25.898,26.807 25.33,27.375C24.762,27.943 24.076,28.227 23.273,28.227L2.909,28.227C2.106,28.227 1.42,27.943 0.852,27.375C0.284,26.807 0,26.122 0,25.318L0,6.409C0,5.606 0.284,4.92 0.852,4.352C1.42,3.784 2.106,3.5 2.909,3.5Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="16"
|
||||
android:startY="3.5"
|
||||
android:endX="16"
|
||||
android:endY="19.854"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFBA200"/>
|
||||
<item android:offset="1" android:color="#FFFF7300"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M5.818,25.318L5.818,12.227C5.818,11.424 6.102,10.738 6.67,10.17C7.238,9.602 7.924,9.318 8.727,9.318L29.091,9.318C29.894,9.318 30.58,9.602 31.148,10.17C31.716,10.738 32,11.424 32,12.227L32,25.318C32,26.122 31.716,26.807 31.148,27.375C30.58,27.943 29.894,28.227 29.091,28.227L2.909,28.227C3.712,28.227 4.398,27.943 4.966,27.375C5.534,26.807 5.818,26.122 5.818,25.318Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="17.454"
|
||||
android:startY="9.318"
|
||||
android:endX="17.454"
|
||||
android:endY="28.227"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFAC227"/>
|
||||
<item android:offset="1" android:color="#FFFAA627"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_github.xml
Normal file
10
app/src/main/res/drawable/ic_github.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="20"
|
||||
android:viewportWidth="20"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="@color/black" android:fillType="evenOdd" android:pathData="M10,0C15.523,0 20,4.59 20,10.253C20,14.782 17.138,18.624 13.167,19.981C12.66,20.082 12.48,19.762 12.48,19.489C12.48,19.151 12.492,18.047 12.492,16.675C12.492,15.719 12.172,15.095 11.813,14.777C14.04,14.523 16.38,13.656 16.38,9.718C16.38,8.598 15.992,7.684 15.35,6.966C15.454,6.707 15.797,5.664 15.252,4.252C15.252,4.252 14.414,3.977 12.505,5.303C11.706,5.076 10.85,4.962 10,4.958C9.15,4.962 8.295,5.076 7.497,5.303C5.586,3.977 4.746,4.252 4.746,4.252C4.203,5.664 4.546,6.707 4.649,6.966C4.01,7.684 3.619,8.598 3.619,9.718C3.619,13.646 5.954,14.526 8.175,14.785C7.889,15.041 7.63,15.493 7.54,16.156C6.97,16.418 5.522,16.871 4.63,15.304C4.63,15.304 4.101,14.319 3.097,14.247C3.097,14.247 2.122,14.234 3.029,14.87C3.029,14.87 3.684,15.185 4.139,16.37C4.139,16.37 4.726,18.2 7.508,17.58C7.513,18.437 7.522,19.245 7.522,19.489C7.522,19.76 7.338,20.077 6.839,19.982C2.865,18.627 0,14.783 0,10.253C0,4.59 4.478,0 10,0" android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_grid.xml
Normal file
12
app/src/main/res/drawable/ic_grid.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="169dp"
|
||||
android:height="169dp"
|
||||
android:viewportWidth="35.28"
|
||||
android:viewportHeight="35.28">
|
||||
<path
|
||||
android:pathData="M27.64,3.64L23.64,3.64C21.431,3.64 19.64,5.431 19.64,7.64L19.64,11.64C19.64,13.849 21.431,15.64 23.64,15.64L27.64,15.64C29.849,15.64 31.64,13.849 31.64,11.64L31.64,7.64C31.64,5.431 29.849,3.64 27.64,3.64L27.64,3.64ZM27.64,19.64L23.64,19.64C21.431,19.64 19.64,21.431 19.64,23.64L19.64,27.64C19.64,29.849 21.431,31.64 23.64,31.64L27.64,31.64C29.849,31.64 31.64,29.849 31.64,27.64L31.64,23.64C31.64,21.431 29.849,19.64 27.64,19.64L27.64,19.64ZM11.64,19.64L7.64,19.64C5.431,19.64 3.64,21.431 3.64,23.64L3.64,27.64C3.64,29.849 5.431,31.64 7.64,31.64L11.64,31.64C13.849,31.64 15.64,29.849 15.64,27.64L15.64,23.64C15.64,21.431 13.849,19.64 11.64,19.64L11.64,19.64ZM11.64,3.64L7.64,3.64C5.431,3.64 3.64,5.431 3.64,7.64L3.64,11.64C3.64,13.849 5.431,15.64 7.64,15.64L11.64,15.64C13.849,15.64 15.64,13.849 15.64,11.64L15.64,7.64C15.64,5.431 13.849,3.64 11.64,3.64L11.64,3.64Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/textColor"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_list.xml
Normal file
9
app/src/main/res/drawable/ic_list.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M4,13L2,13L2,11L4,11ZM14,11L6,11v2h8ZM4,7L2,7L2,9L4,9ZM14,7L6,7L6,9h8ZM4,3L2,3L2,5L4,5ZM14,3L6,3L6,5h8Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
8
app/src/main/res/drawable/ic_more.xml
Normal file
8
app/src/main/res/drawable/ic_more.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="#fff"
|
||||
android:pathData="M12,12H12.01M12,6H12.01M12,18H12.01M13,12C13,12.552 12.552,13 12,13C11.448,13 11,12.552 11,12C11,11.448 11.448,11 12,11C12.552,11 13,11.448 13,12ZM13,18C13,18.552 12.552,19 12,19C11.448,19 11,18.552 11,18C11,17.448 11.448,17 12,17C12.552,17 13,17.448 13,18ZM13,6C13,6.552 12.552,7 12,7C11.448,7 11,6.552 11,6C11,5.448 11.448,5 12,5C12.552,5 13,5.448 13,6Z" android:strokeColor="#fff" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
|
||||
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_no_items.xml
Normal file
10
app/src/main/res/drawable/ic_no_items.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#757575"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_play_circle.xml
Normal file
12
app/src/main/res/drawable/ic_play_circle.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M34.6,23.3 L18.1,13.2c-0.6,-0.4 -1.1,-0.1 -1.1,0.6V34.2c0,0.7 0.5,1 1.1,0.6L34.6,24.7A0.8,0.8 0,0 0,34.6 23.3Z"/>
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M24,2A22,22 0,1 0,46 24,21.9 21.9,0 0,0 24,2ZM24,42A18,18 0,1 1,42 24,18.1 18.1,0 0,1 24,42Z"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_setting.xml
Normal file
11
app/src/main/res/drawable/ic_setting.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillAlpha="1" android:fillColor="?attr/colorPrimary" android:pathData="M17.88,18.6C17.88,19.926 16.806,21 15.48,21C14.155,21 13.08,19.926 13.08,18.6C13.08,17.274 14.155,16.2 15.48,16.2C16.806,16.2 17.88,17.274 17.88,18.6Z"/>
|
||||
|
||||
<path android:fillAlpha="1" android:fillColor="?attr/colorPrimary" android:pathData="M9.48,12C9.48,13.325 8.405,14.4 7.08,14.4C5.755,14.4 4.68,13.325 4.68,12C4.68,10.675 5.755,9.6 7.08,9.6C8.405,9.6 9.48,10.675 9.48,12Z"/>
|
||||
|
||||
<path android:fillAlpha="1" android:fillColor="?attr/colorPrimary" android:pathData="M17.88,5.4C17.88,6.725 16.806,7.8 15.48,7.8C14.155,7.8 13.08,6.725 13.08,5.4C13.08,4.075 14.155,3 15.48,3C16.806,3 17.88,4.075 17.88,5.4Z"/>
|
||||
|
||||
<path android:fillColor="#00000000" android:pathData="M18.48,18.537H21M4.68,12L3,12.044M4.68,12C4.68,13.325 5.755,14.4 7.08,14.4C8.405,14.4 9.48,13.325 9.48,12C9.48,10.675 8.405,9.6 7.08,9.6C5.755,9.6 4.68,10.675 4.68,12ZM10.169,12.044H21M12.801,5.551L3,5.551M21,5.551H18.48M3,18.537H12.801M17.88,18.6C17.88,19.926 16.806,21 15.48,21C14.155,21 13.08,19.926 13.08,18.6C13.08,17.274 14.155,16.2 15.48,16.2C16.806,16.2 17.88,17.274 17.88,18.6ZM17.88,5.4C17.88,6.725 16.806,7.8 15.48,7.8C14.155,7.8 13.08,6.725 13.08,5.4C13.08,4.075 14.155,3 15.48,3C16.806,3 17.88,4.075 17.88,5.4Z" android:strokeColor="?attr/colorPrimary" android:strokeLineCap="round" android:strokeWidth="1.5"/>
|
||||
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_settings.xml
Normal file
9
app/src/main/res/drawable/ic_settings.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4,13.743l-1,0.579a1,1 0,0 0,-0.366 1.366l1.488,2.578a1,1 0,0 0,1.366 0.366L6.5,18.05a1.987,1.987 0,0 1,1.986 0l0.02,0.011a1.989,1.989 0,0 1,1 1.724V21a1,1 0,0 0,1 1h3a1,1 0,0 0,1 -1V19.782a1.985,1.985 0,0 1,0.995 -1.721l0.021,-0.012a1.987,1.987 0,0 1,1.986 0l1.008,0.582a1,1 0,0 0,1.366 -0.366l1.488,-2.578A1,1 0,0 0,21 14.322l-1,-0.579a1.994,1.994 0,0 1,-1 -1.733v-0.021a1.991,1.991 0,0 1,1 -1.732l1,-0.579a1,1 0,0 0,0.366 -1.366L19.876,5.734a1,1 0,0 0,-1.366 -0.366L17.5,5.95a1.987,1.987 0,0 1,-1.986 0L15.5,5.94a1.989,1.989 0,0 1,-1 -1.724V3a1,1 0,0 0,-1 -1h-3a1,1 0,0 0,-1 1V4.294A1.856,1.856 0,0 1,8.57 5.9l-0.153,0.088a1.855,1.855 0,0 1,-1.853 0L5.49,5.368a1,1 0,0 0,-1.366 0.366L2.636,8.312A1,1 0,0 0,3 9.678l1,0.579A1.994,1.994 0,0 1,5 11.99v0.021A1.991,1.991 0,0 1,4 13.743ZM12,9a3,3 0,1 1,-3 3A3,3 0,0 1,12 9Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_video.xml
Normal file
10
app/src/main/res/drawable/ic_video.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M13.75,2C13.75,1.586 13.414,1.25 13,1.25C12.586,1.25 12.25,1.586 12.25,2V14.536C11.4,13.738 10.257,13.25 9,13.25C6.377,13.25 4.25,15.377 4.25,18C4.25,20.623 6.377,22.75 9,22.75C11.623,22.75 13.75,20.623 13.75,18V6.243C14.988,7.772 16.879,8.75 19,8.75C19.414,8.75 19.75,8.414 19.75,8C19.75,7.586 19.414,7.25 19,7.25C16.101,7.25 13.75,4.899 13.75,2Z"
|
||||
android:fillColor="@color/textColor"/>
|
||||
</vector>
|
||||
41
app/src/main/res/drawable/music_add.xml
Normal file
41
app/src/main/res/drawable/music_add.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17,6.5H20M23,6.5H20M20,6.5V3.5M20,6.5V9.5"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M6,16V5L14,4"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M15,14V10"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,19H13C14.105,19 15,18.105 15,17V14H12C10.895,14 10,14.895 10,16V17C10,18.105 10.895,19 12,19Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M3,21H4C5.105,21 6,20.105 6,19V16H3C1.895,16 1,16.895 1,18V19C1,20.105 1.895,21 3,21Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
@@ -5,6 +5,6 @@
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M5,7.766c0,-1.554 1.696,-2.515 3.029,-1.715l7.056,4.234c1.295,0.777 1.295,2.653 0,3.43l-7.056,4.234c-1.333,0.8 -3.029,-0.16 -3.029,-1.715V7.766zM14.056,12L7,7.766v8.468L14.056,12zM18,6a1,1 0,0 0,-1 1v10a1,1 0,1 0,2 0V7a1,1 0,0 0,-1 -1z"
|
||||
android:fillColor="@color/textColor"
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2,6C2,4.114 2,3.172 2.586,2.586C3.172,2 4.114,2 6,2C7.886,2 8.828,2 9.414,2.586C10,3.172 10,4.114 10,6V18C10,19.886 10,20.828 9.414,21.414C8.828,22 7.886,22 6,22C4.114,22 3.172,22 2.586,21.414C2,20.828 2,19.886 2,18V6Z"
|
||||
android:fillColor="@color/textColor"/>
|
||||
android:fillColor="?attr/colorPrimary"/>
|
||||
<path
|
||||
android:pathData="M14,6C14,4.114 14,3.172 14.586,2.586C15.172,2 16.114,2 18,2C19.886,2 20.828,2 21.414,2.586C22,3.172 22,4.114 22,6V18C22,19.886 22,20.828 21.414,21.414C20.828,22 19.886,22 18,22C16.114,22 15.172,22 14.586,21.414C14,20.828 14,19.886 14,18V6Z"
|
||||
android:fillColor="@color/textColor"/>
|
||||
android:fillColor="?attr/colorPrimary"/>
|
||||
</vector>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="512" android:viewportWidth="512" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@color/textColor"
|
||||
android:pathData="M21.409,9.353C23.531,10.507 23.531,13.493 21.409,14.647L8.597,21.615C6.534,22.736 4,21.276 4,18.967L4,5.033C4,2.724 6.534,1.264 8.597,2.385L21.409,9.353Z"/>
|
||||
<path android:fillColor="?attr/colorPrimary" android:pathData="M464.7,221.5L86.1,7.3C52.5,-11.7 25,7.5 25,50v412c0,42.5 27.5,61.7 61.1,42.7l378.6,-214.1C498.2,271.5 498.2,240.5 464.7,221.5z"/>
|
||||
|
||||
</vector>
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,7.766c0,-1.554 -1.696,-2.515 -3.029,-1.715l-7.056,4.234c-1.295,0.777 -1.295,2.653 0,3.43l7.056,4.234c1.333,0.8 3.029,-0.16 3.029,-1.715V7.766zM9.944,12L17,7.766v8.468L9.944,12zM6,6a1,1 0,0 1,1 1v10a1,1 0,1 1,-2 0V7a1,1 0,0 1,1 -1z"
|
||||
android:fillColor="@color/textColor"
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
||||
12
app/src/main/res/drawable/selected.xml
Normal file
12
app/src/main/res/drawable/selected.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M512,512m-448,0a448,448 0,1 0,896 0,448 448,0 1,0 -896,0Z"
|
||||
android:fillColor="#00ffffff"/>
|
||||
<path
|
||||
android:pathData="M738.1,311.5L448,601.6l-119.5,-119.5 -59.7,59.7 179.2,179.2 349.9,-349.9z"
|
||||
android:fillColor="@color/black"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/video_add.xml
Normal file
9
app/src/main/res/drawable/video_add.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16,4c0.552,0 1,0.448 1,1v4.2l5.213,-3.65c0.226,-0.158 0.538,-0.103 0.697,0.124 0.058,0.084 0.09,0.184 0.09,0.286v12.08c0,0.276 -0.224,0.5 -0.5,0.5 -0.103,0 -0.203,-0.032 -0.287,-0.09L17,14.8V19c0,0.552 -0.448,1 -1,1H2c-0.552,0 -1,-0.448 -1,-1V5c0,-0.552 0.448,-1 1,-1h14zM8,8v3H5v2h2.999L8,16h2l-0.001,-3H13v-2h-3V8H8z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/wrong.xml
Normal file
9
app/src/main/res/drawable/wrong.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="256"
|
||||
android:viewportHeight="256">
|
||||
<path
|
||||
android:pathData="M142,128l49,-49a9.9,9.9 0,0 0,-14 -14L128,114 79,65A9.9,9.9 0,0 0,65 79l49,49L65,177a9.9,9.9 0,0 0,14 14l49,-49 49,49a9.9,9.9 0,0 0,14 -14Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.AudioGalleryActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -12,7 +12,7 @@
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Change Password"
|
||||
android:text="@string/change_password"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -24,7 +24,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="Enter Old Password"
|
||||
android:hint="@string/enter_old_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvTitle">
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="Enter New Password"
|
||||
android:hint="@string/enter_new_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilOldPassword">
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:padding="12dp"
|
||||
android:text="Change Password"
|
||||
android:text="@string/change_password"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilNewPassword" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
@@ -70,7 +70,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Forgot Password?"
|
||||
android:text="@string/forgot_password"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnChangePassword" />
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.DocumentsActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp" />
|
||||
|
||||
<!-- <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton-->
|
||||
<!-- android:id="@+id/fabAdd"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_gravity="bottom|end"-->
|
||||
<!-- android:layout_margin="16dp"-->
|
||||
<!-- android:contentDescription="Compose"-->
|
||||
<!-- android:icon="@drawable/plus"-->
|
||||
<!-- android:text="Add File"-->
|
||||
<!-- app:elevation="6dp"/>-->
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/fabAdd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_marginBottom="35dp"
|
||||
android:contentDescription="Compose"
|
||||
app:iconPadding="8dp"
|
||||
app:icon="@android:drawable/ic_input_add"
|
||||
android:text="Add file"/>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAdd"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_marginBottom="35dp"
|
||||
android:src="@android:drawable/ic_input_add" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
164
app/src/main/res/layout/activity_hidden.xml
Normal file
164
app/src/main/res/layout/activity_hidden.xml
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.HiddenActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/toolBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_back"
|
||||
app:tint="@color/svgTintColor"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/back"/>
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hidden_space"
|
||||
android:textSize="22sp"
|
||||
android:singleLine="true"
|
||||
android:padding="4dp"
|
||||
android:textStyle="bold"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/folderName"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_edit"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:scaleType="fitCenter"
|
||||
android:padding="9dp"
|
||||
android:visibility="gone"
|
||||
android:background="#00000000"
|
||||
android:id="@+id/edit"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/delete"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="#00000000"
|
||||
android:padding="9dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_delete"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/menuButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_more"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:scaleType="fitCenter"
|
||||
android:padding="9dp"
|
||||
android:visibility="gone"
|
||||
android:background="#00000000"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_list"
|
||||
android:scaleType="fitCenter"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/folderOrientation"/>
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_settings"
|
||||
android:scaleType="fitCenter"
|
||||
app:tint="?attr/colorPrimary"
|
||||
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/settings"/>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolBar">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noItems"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_no_items" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noItemsTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/addFolder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_folder_add"
|
||||
android:text="@string/add_image"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:tint="@color/white"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:fabCustomSize="57dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/deleteSelected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:visibility="gone"
|
||||
app:tint="@color/white"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:fabCustomSize="57dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:minHeight="300dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal|bottom"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/btnImages"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btnAudio"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnVideos"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="Images"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/btnVideos"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btnDocs"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnImages"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="Videos"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal|top"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/btnAudio"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnDocs"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnImages">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="Audio"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/btnDocs"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnAudio"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnVideos">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="Documents"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.ImageGalleryActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,319 +1,347 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.MainActivity">
|
||||
|
||||
<!-- Calculator Display -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="160dp"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_weight="3"
|
||||
android:layout_marginTop="0dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="right|bottom">
|
||||
<TextView
|
||||
android:id="@+id/display"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/displayContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="0dp"
|
||||
android:background="@drawable/bottom_corner"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_percent="0.3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:scrollbars="none"
|
||||
android:gravity="end|bottom"
|
||||
android:padding="10dp"
|
||||
android:text="0"
|
||||
android:textSize="48sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:autoSizeMinTextSize="20sp"
|
||||
android:autoSizeMaxTextSize="48sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
tools:ignore="Suspicious0dp" />
|
||||
app:layout_constraintBottom_toTopOf="@+id/total"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="end|bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoSizeMaxTextSize="70sp"
|
||||
android:autoSizeMinTextSize="16sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:gravity="end|bottom"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:padding="10dp"
|
||||
android:text=""
|
||||
android:textSize="70sp"
|
||||
tools:ignore="Suspicious0dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoSizeMaxTextSize="40sp"
|
||||
android:autoSizeMinTextSize="24sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:gravity="end|bottom"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text=""
|
||||
android:textSize="26sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:autoSizeMinTextSize="20sp"
|
||||
android:autoSizeMaxTextSize="48sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:textSize="40sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scrollView"
|
||||
tools:ignore="Suspicious0dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="8dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayContainer">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnClear"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="C"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPercent"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="%"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDivide"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="÷"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/cut"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:background="@drawable/gradient_bg"
|
||||
android:src="@drawable/backspace"
|
||||
android:scaleType="centerInside"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn7"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="7"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn8"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="8"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn9"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="9"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnMultiply"
|
||||
style="@style/CustomMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/white"
|
||||
android:text="×"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn4"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="4"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn5"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="5"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn6"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="6"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnMinus"
|
||||
style="@style/CustomMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/white"
|
||||
android:text="-"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="1"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn2"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="2"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn3"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="3"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPlus"
|
||||
style="@style/CustomMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/white"
|
||||
android:text="+"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn0"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="2"
|
||||
android:text="0"
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDot"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="."
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEquals"
|
||||
style="@style/CustomMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/white"
|
||||
android:text="="
|
||||
android:textSize="30sp"
|
||||
app:cornerRadius="15dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Calculator Buttons -->
|
||||
<GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:columnCount="4"
|
||||
android:layout_weight="4"
|
||||
android:rowCount="5">
|
||||
|
||||
<!-- Row 1 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnClear"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:textSize="30sp"
|
||||
android:text="C"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPercent"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="%"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDivide"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="÷"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/cut"
|
||||
android:layout_height="70dp"
|
||||
android:layout_width="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:gravity="center"
|
||||
android:layout_margin="4dp"
|
||||
app:icon="@drawable/backspace"
|
||||
android:textAlignment="center"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:iconSize="32dp"
|
||||
app:cornerRadius="15dp"/>
|
||||
<!-- Row 2 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn7"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="7"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn8"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="8"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn9"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:textSize="30sp"
|
||||
android:text="9"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnMultiply"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:textSize="30sp"
|
||||
android:text="×"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button"/>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn4"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
app:cornerRadius="15dp"
|
||||
android:text="4"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn5"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="5"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn6"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="6"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnMinus"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="-"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button"/>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn1"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="1"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn2"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:text="2"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn3"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:text="3"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPlus"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="+"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button"/>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn0"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="2"
|
||||
android:textSize="30sp"
|
||||
android:layout_columnWeight="2"
|
||||
android:layout_margin="4dp"
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
android:text="0" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDot"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="."
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEquals"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:text="="
|
||||
app:cornerRadius="15dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
style="@style/Widget.MaterialComponents.Button"/>
|
||||
|
||||
</GridLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -15,13 +15,21 @@
|
||||
android:paddingLeft="15dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" >
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_back"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/back"/>
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:textSize="22sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:text="Preview File"/>
|
||||
android:text="@string/preview_file"/>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
@@ -46,19 +54,22 @@
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/unHide"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:minWidth="120dp"
|
||||
android:text="Unhide" />
|
||||
android:layout_weight="1"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:text="@string/un_hide" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="6dp"
|
||||
android:minWidth="120dp"
|
||||
android:text="Delete" />
|
||||
android:text="@string/delete" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
291
app/src/main/res/layout/activity_settings.xml
Normal file
291
app/src/main/res/layout/activity_settings.xml
Normal file
@@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.SettingsActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/toolBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_back"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/back"/>
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings"
|
||||
android:textSize="22sp"
|
||||
android:singleLine="true"
|
||||
android:padding="4dp"
|
||||
android:textStyle="bold"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/folderName"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- App Details Section -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_details"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="5dp">
|
||||
<ImageView
|
||||
android:id="@+id/appLogo"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/full_app_name"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/version"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/githubButton"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_github" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Developer Details Section -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/developer_details"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/binondi_borthakur"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/devGithubButton"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_github" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Theme Settings Section -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/theme_settings"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/dynamicThemeSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/dynamic_theme"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/themeModeSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/theme_mode"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/themeRadioGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/lightThemeRadio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/light" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/darkThemeRadio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dark" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/systemThemeRadio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/system_default" />
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Security Settings Section -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/security_settings"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/screenshotRestrictionSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/restrict_screenshots_in_hidden_section"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="10dp"
|
||||
app:cardElevation="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_settings"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/showFileNames"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/show_file_names"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -12,7 +12,7 @@
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Setup Password"
|
||||
android:text="@string/setup_password"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -24,7 +24,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="Enter Password"
|
||||
android:hint="@string/enter_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvTitle">
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="Confirm Password"
|
||||
android:hint="@string/confirm_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilPassword">
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="Security Question (For Password Reset)"
|
||||
android:hint="@string/security_question_for_password_reset"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilConfirmPassword">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
@@ -75,7 +75,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="Security Answer"
|
||||
android:hint="@string/security_answer"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilSecurityQuestion">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
@@ -91,7 +91,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:padding="12dp"
|
||||
android:text="Save Password"
|
||||
android:text="@string/save_password"
|
||||
app:layout_constraintTop_toBottomOf="@id/tilSecurityAnswer" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
@@ -100,7 +100,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Forgot Password?"
|
||||
android:text="@string/forgot_password"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnSavePassword" />
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.VideoGalleryActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
166
app/src/main/res/layout/activity_view_folder.xml
Normal file
166
app/src/main/res/layout/activity_view_folder.xml
Normal file
@@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.ViewFolderActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/toolBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_back"
|
||||
app:tint="@color/svgTintColor"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/back"/>
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hidden_space"
|
||||
android:textSize="22sp"
|
||||
android:singleLine="true"
|
||||
android:padding="4dp"
|
||||
android:textStyle="bold"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/folderName"/>
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/menuButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_more"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:scaleType="fitCenter"
|
||||
android:padding="7dp"
|
||||
android:visibility="gone"
|
||||
android:background="#00000000"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolBar">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noItems"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_no_items" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noItemsTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/addImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/add_image"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/add_image"
|
||||
app:tint="@color/white"
|
||||
android:visibility="gone"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/addVideo"
|
||||
app:layout_constraintEnd_toEndOf="@+id/addVideo"
|
||||
app:layout_constraintStart_toStartOf="@+id/addVideo" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/addVideo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:src="@drawable/video_add"
|
||||
android:text="@string/add_image"
|
||||
android:visibility="gone"
|
||||
app:tint="@color/white"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/addAudio"
|
||||
app:layout_constraintEnd_toEndOf="@+id/addAudio"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/addAudio" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/addAudio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:src="@drawable/music_add"
|
||||
android:text="@string/add_image"
|
||||
android:visibility="gone"
|
||||
app:tint="@color/white"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/addDocument"
|
||||
app:layout_constraintEnd_toEndOf="@+id/addDocument"
|
||||
app:layout_constraintStart_toStartOf="@+id/addDocument" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/addDocument"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/document_add"
|
||||
android:text="@string/add_image"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:tint="@color/white"
|
||||
android:visibility="gone"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fabExpend"
|
||||
app:layout_constraintEnd_toEndOf="@+id/fabExpend"
|
||||
app:layout_constraintStart_toStartOf="@+id/fabExpend" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabExpend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:src="@drawable/ic_add"
|
||||
android:backgroundTint="?attr/colorPrimary"
|
||||
android:text="@string/add_image"
|
||||
app:tint="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
33
app/src/main/res/layout/bottom_sheet_folder_selection.xml
Normal file
33
app/src/main/res/layout/bottom_sheet_folder_selection.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_gravity="center"
|
||||
app:cardCornerRadius="100dp"
|
||||
android:backgroundTint="#808080"
|
||||
app:cardElevation="0dp"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_destination_folder"
|
||||
android:textSize="20sp"
|
||||
android:padding="8dp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/folderRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="400dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
22
app/src/main/res/layout/dialog_input.xml
Normal file
22
app/src/main/res/layout/dialog_input.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/enter_folder_name">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
<devs.org.calculator.views.SquareImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"/>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
77
app/src/main/res/layout/item_folder.xml
Normal file
77
app/src/main/res/layout/item_folder.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/selectedLayer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.3"
|
||||
android:background="?attr/colorControlNormal"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/folderIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:src="@drawable/ic_folder" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/folderName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:maxLines="1"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
<ImageView
|
||||
android:id="@+id/selected"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:background="@drawable/gradient_bg"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/selected"
|
||||
android:layout_gravity="end"
|
||||
android:visibility="visible"
|
||||
app:tint="#fff" />
|
||||
</FrameLayout>
|
||||
99
app/src/main/res/layout/item_folder_list_style.xml
Normal file
99
app/src/main/res/layout/item_folder_list_style.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/selectedLayer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.3"
|
||||
android:background="?attr/colorControlNormal"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/folderIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="8dp"
|
||||
android:layout_gravity="center"
|
||||
app:tint="?attr/colorPrimary"
|
||||
android:src="@drawable/ic_folder" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/folderName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timeModified"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:text=""
|
||||
android:visibility="gone"
|
||||
android:textColor="#767676"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/selected"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:background="@drawable/gradient_bg"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/selected"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="#fff" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</FrameLayout>
|
||||
24
app/src/main/res/layout/item_folder_selection.xml
Normal file
24
app/src/main/res/layout/item_folder_selection.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/folderIcon"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"
|
||||
android:src="@drawable/ic_folder"
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/folderName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textSize="18sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
85
app/src/main/res/layout/list_item_file.xml
Normal file
85
app/src/main/res/layout/list_item_file.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:cardCornerRadius="10dp"
|
||||
android:layout_margin="5dp"
|
||||
app:cardElevation="3dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/fileIconImageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/add_image" />
|
||||
<LinearLayout
|
||||
android:id="@+id/selectedLayer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0.3"
|
||||
android:background="?attr/colorControlNormal"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<ImageView
|
||||
android:id="@+id/videoPlay"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:visibility="gone"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_play_circle"
|
||||
android:layout_gravity="center"/>
|
||||
<ImageView
|
||||
android:id="@+id/selected"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:padding="3dp"
|
||||
app:tint="#fff"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/selected"
|
||||
android:background="@drawable/gradient_bg"
|
||||
android:layout_gravity="end"/>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fileNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="5dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
</LinearLayout>
|
||||
35
app/src/main/res/layout/proccessing_dialog.xml
Normal file
35
app/src/main/res/layout/proccessing_dialog.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hiding_files"
|
||||
android:padding="8dp"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
app:lottie_fileName="hiding_files.json"
|
||||
app:lottie_autoPlay="true"
|
||||
android:scaleX="1.2"
|
||||
android:scaleY="1.2"
|
||||
android:scaleType="centerCrop"
|
||||
app:lottie_loop="true"/>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="25dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -8,35 +8,42 @@
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
|
||||
<com.jsibbold.zoomage.ZoomageView
|
||||
android:id="@+id/imageView"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:zoomage_restrictBounds="false"
|
||||
app:zoomage_animateOnReset="true"
|
||||
app:zoomage_autoResetMode="UNDER"
|
||||
app:zoomage_autoCenter="true"
|
||||
android:visibility="gone"
|
||||
app:zoomage_zoomable="true"
|
||||
app:zoomage_translatable="true"
|
||||
app:zoomage_minScale="0.6"
|
||||
app:zoomage_maxScale="8"
|
||||
/>
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<com.jsibbold.zoomage.ZoomageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:zoomage_restrictBounds="false"
|
||||
app:zoomage_animateOnReset="true"
|
||||
app:zoomage_autoResetMode="UNDER"
|
||||
app:zoomage_autoCenter="true"
|
||||
android:visibility="gone"
|
||||
app:zoomage_zoomable="true"
|
||||
app:zoomage_translatable="true"
|
||||
app:zoomage_minScale="0.6"
|
||||
app:zoomage_maxScale="8"
|
||||
/>
|
||||
|
||||
<VideoView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/videoView"/>
|
||||
<VideoView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/videoView"/>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/audioBg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="20dp"
|
||||
style="@style/Widget.Material3.CardView.Outlined">
|
||||
app:cardCornerRadius="15dp"
|
||||
app:cardElevation="10dp"
|
||||
android:layout_margin="20dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -47,7 +54,7 @@
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_margin="5dp"
|
||||
android:src="@drawable/music"
|
||||
android:src="@drawable/ic_audio"
|
||||
android:padding="3dp"/>
|
||||
|
||||
<TextView
|
||||
@@ -55,11 +62,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="audio.mp3"
|
||||
android:layout_margin="5dp"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:id="@+id/audioTitle"/>
|
||||
<SeekBar
|
||||
<com.google.android.material.slider.Slider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:scaleY="0.6"
|
||||
android:id="@+id/audioSeekBar"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<color name="white">#FF000000</color>
|
||||
<color name="black">#FFFFFFFF</color>
|
||||
|
||||
<!-- Material You dynamic colors -->
|
||||
<color name="primary">#00B43B</color>
|
||||
<color name="colorPrimaryVariant">#00C15C</color>
|
||||
<color name="colorSecondary">#39FF97</color>
|
||||
<color name="textColor">#ffffff</color>
|
||||
<color name="svgTintColor" tools:ignore="MissingDefaultResource">#ffffff</color>
|
||||
</resources>
|
||||
@@ -1,21 +1,21 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color -->
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color -->
|
||||
<item name="colorAccent">@color/primary</item>
|
||||
<item name="colorSecondary">@color/colorSecondary</item>
|
||||
<item name="colorSecondaryVariant">@color/colorSecondary</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="fontFamily">@font/ubuntu_regular</item>
|
||||
<!-- Status bar color -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
|
||||
<!-- Enable window decor fitting -->
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
|
||||
</style>
|
||||
|
||||
<style name="CustomMaterialButton" parent="Widget.MaterialComponents.Button">
|
||||
<item name="android:background">@drawable/gradient_bg</item>
|
||||
<item name="backgroundTint">@null</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
@@ -8,4 +8,5 @@
|
||||
<color name="colorPrimaryVariant">#00C15C</color>
|
||||
<color name="colorSecondary">#39FF97</color>
|
||||
<color name="textColor">#000000</color>
|
||||
<color name="svgTintColor" tools:ignore="MissingDefaultResource">#000000</color>
|
||||
</resources>
|
||||
@@ -1,4 +1,129 @@
|
||||
<resources>
|
||||
<string name="app_name">Calculator</string>
|
||||
<string name="invalid_message">Invalid Value Entered</string>
|
||||
<string name="add_image">Add Image</string>
|
||||
<string name="add_audio">Add Audio</string>
|
||||
<string name="add_video">Add Video</string>
|
||||
<string name="add_files">Add Files</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="file_options">File Options</string>
|
||||
<string name="rename_file">Rename File</string>
|
||||
<string name="share_file">Share File</string>
|
||||
<string name="add_document">Add Document</string>
|
||||
<string name="failed_to_hide_audio">Failed to hide Audios</string>
|
||||
<string name="failed_to_hide_documents">Failed to hide Documents</string>
|
||||
<string name="no_files_selected">No files selected</string>
|
||||
<string name="documents_hidden_successfully"> Files hidden successfully</string>
|
||||
<string name="failed_to_hide_unhide_photo">Failed to hide/unhide photo</string>
|
||||
<string name="images_hidden_successfully"> Images hidden successfully</string>
|
||||
<string name="failed_to_hide_images">Failed to hide images</string>
|
||||
<string name="failed_to_hide_files">Failed to hide files</string>
|
||||
<string name="storage_permissions_granted">Storage permissions granted</string>
|
||||
<string name="storage_permissions_denied">Storage permissions denied</string>
|
||||
<string name="preview_images">Preview Images</string>
|
||||
<string name="preview_videos">Preview Videos</string>
|
||||
<string name="preview_audios">Preview Audios</string>
|
||||
<string name="preview_documents">Preview Documents</string>
|
||||
<string name="delete_file">Delete File</string>
|
||||
<string name="are_you_sure_to_delete_this_file_permanently">Are you sure to Delete this file permanently?</string>
|
||||
<string name="delete_permanently">Delete Permanently</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="un_hide_file">Un-hide File</string>
|
||||
<string name="are_you_sure_you_want_to_un_hide_this_file">Are you sure you want to Un-hide this file?</string>
|
||||
<string name="un_hide">Un-hide</string>
|
||||
<string name="enter_password">Enter password</string>
|
||||
<string name="confirm_password">Confirm password</string>
|
||||
<string name="enter_security_question">Enter security question</string>
|
||||
<string name="enter_security_answer">Enter security answer</string>
|
||||
<string name="passwords_don_t_match">Passwords don\'t match</string>
|
||||
<string name="password_set_successfully">Password set successfully</string>
|
||||
<string name="security_question_not_set_yet">Security question not set yet.</string>
|
||||
<string name="this_field_can_t_be_empty">This field can\'t be empty</string>
|
||||
<string name="password_reset_successfully">Password reset successfully</string>
|
||||
<string name="old_password_and_new_password_not_be_same">Old Password And New Password Not Be Same</string>
|
||||
<string name="wrong_password_entered">Wrong password entered</string>
|
||||
<string name="old_password_not_matching">Old Password Not Matching</string>
|
||||
<string name="answer_the_security_question">Answer the Security Question!</string>
|
||||
<string name="verify">Verify</string>
|
||||
<string name="answer_cannot_be_empty">Answer cannot be empty!</string>
|
||||
<string name="password_successfully_reset">Password successfully reset.</string>
|
||||
<string name="invalid_answer">Invalid answer!</string>
|
||||
<string name="videos_hidden_successfully"> Videos hidden successfully</string>
|
||||
<string name="failed_to_hide_videos">Failed to hide videos</string>
|
||||
<string name="image">IMAGE</string>
|
||||
<string name="video">VIDEO</string>
|
||||
<string name="audio">AUDIO</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="create">Create</string>
|
||||
<string name="delete_folder">Delete Folder</string>
|
||||
<string name="rename">Rename</string>
|
||||
<string name="cannot_delete_folder">Cannot Delete Folder</string>
|
||||
<string name="document">DOCUMENT</string>
|
||||
<string name="no_audio_player_found">No audio player found!</string>
|
||||
<string name="no_suitable_app_found_to_open_this_document">No suitable app found to open this document!</string>
|
||||
<string name="unknown_file">Unknown File</string>
|
||||
<string name="details"> DETAILS</string>
|
||||
<string name="audio_hidded_successfully">Audios hidden successfully</string>
|
||||
<string name="no_items_available_add_one_by_clicking_on_the_plus_button">No Items Available, Add one by clicking on the</string>
|
||||
<string name="now_enter_button">Now Enter \'=\' button</string>
|
||||
<string name="enter_123456">Enter 123456</string>
|
||||
<string name="create_folder">Create Folder</string>
|
||||
<string name="enter_folder_name">Enter folder name</string>
|
||||
<string name="folder_already_exists">Folder already exists</string>
|
||||
<string name="folder_options">Folder Options</string>
|
||||
<string name="rename_folder">Rename Folder</string>
|
||||
<string name="enter_new_folder_name">Enter new folder name</string>
|
||||
<string name="failed_to_create_folder">Failed to create folder</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_folder">Are you sure you want to delete this folder?</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="error_loading_files">Error loading files</string>
|
||||
<string name="delete_items">Delete Folder</string>
|
||||
<string name="are_you_sure_you_want_to_delete_selected_items">Are you sure you want to delete selected Folders?</string>
|
||||
<string name="items_deleted_successfully">Items deleted successfully</string>
|
||||
<string name="folder_deleted_successfully">Folder deleted successfully</string>
|
||||
<string name="some_items_could_not_be_deleted">Some items could not be deleted</string>
|
||||
<string name="hidden_space">Hidden Space</string>
|
||||
<string name="there_was_a_problem_in_the_folder">There was a problem in the Folder</string>
|
||||
<string name="hiding_files">Hiding Files</string>
|
||||
<string name="file_unhidden_successfully">File Will Now Show In Gallery</string>
|
||||
<string name="file_unhidden_but_failed_to_remove_from_hidden_folder">File unhidden but failed to remove from hidden folder</string>
|
||||
<string name="failed_to_unhidden_file">Failed to unhidden file</string>
|
||||
<string name="failed_to_create_uri_for_file">Failed to create URI for file</string>
|
||||
<string name="error_unhiding_file">Error unhiding file: %1$s</string>
|
||||
<string name="select_multiple">Select Multiple</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="binondi_borthakur">Binondi Borthakur</string>
|
||||
<string name="dynamic_theme">Dynamic Theme</string>
|
||||
<string name="theme_mode">Theme Mode</string>
|
||||
<string name="light">Light</string>
|
||||
<string name="dark">Dark</string>
|
||||
<string name="system_default">System Default</string>
|
||||
<string name="full_app_name">Calculator Hide Files</string>
|
||||
<string name="developer_details">Developer Details</string>
|
||||
<string name="theme_settings">Theme Settings</string>
|
||||
<string name="files_deleted_successfully">Files deleted successfully</string>
|
||||
<string name="some_files_could_not_be_deleted">Some files could not be deleted</string>
|
||||
<string name="un_hide_files">Unhide Files</string>
|
||||
<string name="are_you_sure_you_want_to_un_hide_selected_files">Are you sure you want to unhide the selected files?</string>
|
||||
<string name="files_unhidden_successfully">Files unhidden successfully</string>
|
||||
<string name="some_files_could_not_be_unhidden">Some files could not be unhidden</string>
|
||||
<string name="copy_to_another_folder">Copy to Another Folder</string>
|
||||
<string name="move_to_another_folder">Move to Another Folder</string>
|
||||
<string name="select_destination_folder">Select Destination Folder</string>
|
||||
<string name="no_folders_available">No other folders available</string>
|
||||
<string name="change_password">Change Password</string>
|
||||
<string name="enter_old_password">Enter Old Password</string>
|
||||
<string name="enter_new_password">Enter New Password</string>
|
||||
<string name="forgot_password">Forgot Password?</string>
|
||||
<string name="preview_file">Preview File</string>
|
||||
<string name="show_file_names">Show File Names</string>
|
||||
<string name="app_settings">App Settings</string>
|
||||
<string name="restrict_screenshots_in_hidden_section">Restrict Screenshots in Hidden Section</string>
|
||||
<string name="security_settings">Security Settings</string>
|
||||
<string name="version">Version 1.3</string>
|
||||
<string name="app_details">App Details</string>
|
||||
<string name="setup_password">Setup Password</string>
|
||||
<string name="security_question_for_password_reset">Security Question (For Password Reset)</string>
|
||||
<string name="security_answer">Security Answer</string>
|
||||
<string name="save_password">Save Password</string>
|
||||
</resources>
|
||||
@@ -1,23 +1,26 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
|
||||
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color -->
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||
<item name="colorAccent">@color/primary</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color -->
|
||||
<item name="colorSecondary">@color/colorSecondary</item>
|
||||
<item name="colorSecondaryVariant">@color/colorSecondary</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
<item name="fontFamily">@font/ubuntu_regular</item>
|
||||
<!-- Status bar color -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="colorControlNormal">#483CFF61</item>
|
||||
|
||||
<!-- Enable window decor fitting -->
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Calculator" parent="Base.Theme.Calculator" />
|
||||
|
||||
<style name="CustomMaterialButton" parent="Widget.MaterialComponents.Button">
|
||||
<item name="android:background">@drawable/gradient_bg</item>
|
||||
<item name="backgroundTint">@null</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,28 +1,42 @@
|
||||
[versions]
|
||||
agp = "8.7.2"
|
||||
agp = "8.9.1"
|
||||
documentfile = "1.0.1"
|
||||
exp4j = "0.4.8"
|
||||
glide = "4.16.0"
|
||||
kotlin = "1.9.24"
|
||||
coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
appcompat = "1.7.0"
|
||||
lottie = "6.2.0"
|
||||
material = "1.12.0"
|
||||
activity = "1.9.3"
|
||||
constraintlayout = "2.2.0"
|
||||
materialColorUtilities = "1.3.0"
|
||||
gridlayout = "1.0.0"
|
||||
photoview = "2.3.0"
|
||||
viewpager = "1.1.0"
|
||||
zoomage = "1.3.1"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "documentfile" }
|
||||
androidx-viewpager = { module = "androidx.viewpager:viewpager", version.ref = "viewpager" }
|
||||
exp4j = { module = "net.objecthunter:exp4j", version.ref = "exp4j" }
|
||||
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
material-color-utilities = { module = "com.google.android.material:material-color-utilities", version.ref = "materialColorUtilities" }
|
||||
androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", version.ref = "gridlayout" }
|
||||
photoview = { module = "com.github.chrisbanes:PhotoView", version.ref = "photoview" }
|
||||
zoomage = { module = "com.jsibbold:zoomage", version.ref = "zoomage" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user