mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-23 05:16:58 +00:00
Merge pull request #170 from ammargitham/task/add-favourites
add favorites and bugfixes
This commit is contained in:
commit
2f0e81d86f
@ -4,6 +4,7 @@
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"badgeTemplate": "[![All Contributors](https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg)](#contributors)",
|
||||
"contributors": [
|
||||
{
|
||||
"login": "austinhuang0131",
|
||||
@ -79,8 +80,7 @@
|
||||
"blog",
|
||||
"bug",
|
||||
"ideas",
|
||||
"question",
|
||||
"userTesting"
|
||||
"question"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -89,8 +89,18 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
|
||||
"profile": "https://airikr.me/",
|
||||
"contributions": [
|
||||
"question",
|
||||
"ideas"
|
||||
"ideas",
|
||||
"question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Akrai",
|
||||
"name": "Akrai",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5624597?v=4",
|
||||
"profile": "https://github.com/Akrai",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -111,6 +121,15 @@
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "faydin",
|
||||
"name": "Fatih Aydın",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/22706676?v=4",
|
||||
"profile": "https://github.com/faydin",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kernoeb",
|
||||
"name": "kernoeb",
|
||||
@ -167,10 +186,17 @@
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 6,
|
||||
"projectName": "instagrabber",
|
||||
"projectName": "barinsta",
|
||||
"projectOwner": "austinhuang0131",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"skipCi": true,
|
||||
"types": {
|
||||
"translation": {
|
||||
"symbol": "🌍",
|
||||
"description": "Translation",
|
||||
"link": "https://crowdin.com/project/instagrabber"
|
||||
}
|
||||
},
|
||||
"commitConvention": "none"
|
||||
}
|
||||
}
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -9,11 +9,11 @@
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
/.idea/git_toolbox_prj.xml
|
||||
/.idea/dbnavigator.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
app/release
|
||||
|
||||
.idea/git_toolbox_prj.xml
|
||||
|
44
README.md
44
README.md
@ -6,7 +6,9 @@
|
||||
[![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/)
|
||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
|
||||
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg)](#contributors)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
We're previously known as InstaGrabber and still in process of rebranding. For documentation, visit [InstaGrabber.AustinHuang.me](https://instagrabber.austinhuang.me).
|
||||
|
||||
@ -34,37 +36,35 @@ Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.insta
|
||||
|
||||
### Contributors
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Prominent contributors are listed here in the [all-contributors](https://allcontributors.org/) specifications. See [emoji key](https://allcontributors.org/docs/en/emoji-key).
|
||||
Prominent contributors are listed here in the [all-contributors](https://allcontributors.org/) specifications, see [emoji key](https://allcontributors.org/docs/en/emoji-key). [Want to contribute to Barinsta?](https://github.com/austinhuang0131/barinsta/blob/master/.github/CONTRIBUTING.md)
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/instagrabber/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/instagrabber/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="#translation-austinhuang0131" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/instagrabber/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/instagrabber/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/instagrabber/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="http://rerolledgeek.blogspot.com/"><img src="https://avatars3.githubusercontent.com/u/5278488?s=100" width="100px;" alt=""/><br /><sub><b>AWAiS</b></sub></a><br /><a href="https://github.com/austinhuang0131/instagrabber/commits?author=AwaisKing" title="Code">💻</a> <a href="#ideas-AwaisKing" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://stefannajdovski.com/"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="#translation-snajdovski" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="http://rerolledgeek.blogspot.com/"><img src="https://avatars3.githubusercontent.com/u/5278488?s=100" width="100px;" alt=""/><br /><sub><b>AWAiS</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=AwaisKing" title="Code">💻</a> <a href="#ideas-AwaisKing" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://stefannajdovski.com/"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/instagrabber/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a> <a href="#userTesting-Shadowspear123" title="User Testing">📓</a></td>
|
||||
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#question-e-edgren" title="Answering Questions">💬</a> <a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="#translation-Galang23" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="#translation-farzadx" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="#translation-kernoeb" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="#translation-Lego8486" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="#translation-MoaufmKlo" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="#translation-peterge1998" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="#translation-RAMAR-RAR" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="#translation-wagnim" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -99,7 +99,7 @@ Logo by [Stefan Najdovski](https://stefannajdovski.com/). Used under license.
|
||||
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/instagrabber)
|
||||
[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/instagrabber)
|
||||
[![LGTM Grade](https://img.shields.io/lgtm/grade/java/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/instagrabber)
|
||||
[![CodeFactor](https://www.codefactor.io/repository/github/austinhuang0131/instagrabber/badge)](https://www.codefactor.io/repository/github/austinhuang0131/instagrabber)
|
||||
[![CodeFactor](https://www.codefactor.io/repository/github/austinhuang0131/barinsta/badge)](https://www.codefactor.io/repository/github/austinhuang0131/barinsta)
|
||||
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e9cfcb7733f8477d92e5c0f30cac137a)](https://www.codacy.com/manual/austinhuang0131/instagrabber)
|
||||
[![Crowdin](https://badges.crowdin.net/instagrabber/localized.svg)](https://crowdin.com/project/instagrabber)
|
||||
|
||||
|
@ -7,11 +7,11 @@ android {
|
||||
defaultConfig {
|
||||
applicationId 'me.austinhuang.instagrabbr'
|
||||
|
||||
minSdkVersion 16
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
|
||||
versionCode 50
|
||||
versionName '19.0-a1'
|
||||
versionCode 51
|
||||
versionName '19.0-a2'
|
||||
|
||||
multiDexEnabled true
|
||||
|
||||
@ -41,7 +41,7 @@ dependencies {
|
||||
def nav_version = "2.3.0"
|
||||
def preference_version = "1.1.1"
|
||||
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha02'
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.11.1'
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
@ -54,6 +54,7 @@ dependencies {
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
||||
implementation "androidx.preference:preference:$preference_version"
|
||||
|
||||
// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
implementation 'com.facebook.fresco:fresco:2.3.0'
|
||||
|
@ -21,7 +21,7 @@
|
||||
android:name=".activities.MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=".Main"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -1,11 +1,10 @@
|
||||
package awais.instagrabber;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
||||
|
||||
@ -27,7 +26,7 @@ import static awais.instagrabber.utils.Utils.datetimeParser;
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class InstaGrabberApplication extends MultiDexApplication {
|
||||
public final class InstaGrabberApplication extends Application {
|
||||
private static final String TAG = "InstaGrabberApplication";
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,5 @@
|
||||
package awais.instagrabber.activities;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
@ -29,6 +28,7 @@ import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.navigation.NavBackStackEntry;
|
||||
import androidx.navigation.NavController;
|
||||
@ -38,6 +38,7 @@ import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -67,7 +68,7 @@ import awais.instagrabber.utils.TextUtils;
|
||||
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class MainActivity extends BaseLanguageActivity {
|
||||
public class MainActivity extends BaseLanguageActivity implements FragmentManager.OnBackStackChangedListener {
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = Arrays.asList(
|
||||
@ -91,7 +92,9 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
R.id.followViewerFragment,
|
||||
R.id.directMessagesSettingsFragment,
|
||||
R.id.notificationsViewer,
|
||||
R.id.themePreferencesFragment);
|
||||
R.id.themePreferencesFragment,
|
||||
R.id.favoritesFragment,
|
||||
R.id.backupPreferencesFragment);
|
||||
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
|
||||
private static final List<Integer> REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
|
||||
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
|
||||
@ -106,6 +109,7 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
private Handler suggestionsFetchHandler;
|
||||
private int firstFragmentGraphIndex;
|
||||
private boolean isActivityCheckerServiceBound = false;
|
||||
private boolean isBackStackEmpty = false;
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
@ -152,6 +156,11 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) {
|
||||
bindActivityCheckerService();
|
||||
}
|
||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||
|
||||
// Log.d("austin_debug", "dir: "+Arrays.toString(StorageUtil.getStorageDirectories(getApplicationContext())));
|
||||
// final File sdcard = new File(StorageUtil.getStorageDirectories(getApplicationContext())[0]);
|
||||
// Log.d("austin_debug", "files: "+Arrays.toString(sdcard.listFiles()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -211,6 +220,21 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
unbindActivityCheckerService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTaskRoot() && isBackStackEmpty) {
|
||||
finishAfterTransition();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
final int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
|
||||
isBackStackEmpty = backStackEntryCount == 0;
|
||||
}
|
||||
|
||||
private void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||
@ -367,32 +391,26 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
if (!isLoggedIn) {
|
||||
main_nav_ids = R.array.logged_out_main_nav_ids;
|
||||
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
|
||||
binding.bottomNavView.getMenu().clear();
|
||||
binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu);
|
||||
if (selectedItemId == R.id.profile_nav_graph
|
||||
|| selectedItemId == R.id.more_nav_graph) {
|
||||
binding.bottomNavView.setSelectedItemId(selectedItemId);
|
||||
} else {
|
||||
setBottomNavSelectedItem(R.navigation.profile_nav_graph);
|
||||
}
|
||||
}
|
||||
final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids);
|
||||
final List<Integer> mainNavList = new ArrayList<>(navIds.length());
|
||||
final int length = navIds.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
final int resourceId = navIds.getResourceId(i, -1);
|
||||
if (resourceId < 0) continue;
|
||||
mainNavList.add(resourceId);
|
||||
}
|
||||
navIds.recycle();
|
||||
if (setDefaultFromSettings || !isLoggedIn) {
|
||||
final List<Integer> mainNavList = getMainNavList(main_nav_ids);
|
||||
if (setDefaultFromSettings) {
|
||||
final String defaultTabIdString = settingsHelper.getString(Constants.DEFAULT_TAB);
|
||||
try {
|
||||
final int defaultNavId = TextUtils.isEmpty(defaultTabIdString) || !isLoggedIn
|
||||
final int defaultNavId = TextUtils.isEmpty(defaultTabIdString)
|
||||
? R.navigation.profile_nav_graph
|
||||
: Integer.parseInt(defaultTabIdString);
|
||||
final int index = mainNavList.indexOf(defaultNavId);
|
||||
if (index >= 0) {
|
||||
firstFragmentGraphIndex = index;
|
||||
final Integer menuId = NAV_TO_MENU_ID_MAP.get(defaultNavId);
|
||||
if (menuId != null) {
|
||||
binding.bottomNavView.setSelectedItemId(menuId);
|
||||
}
|
||||
}
|
||||
if (index >= 0) firstFragmentGraphIndex = index;
|
||||
setBottomNavSelectedItem(defaultNavId);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Error parsing id", e);
|
||||
}
|
||||
@ -408,6 +426,27 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
currentNavControllerLiveData = navControllerLiveData;
|
||||
}
|
||||
|
||||
private void setBottomNavSelectedItem(final int navId) {
|
||||
final Integer menuId = NAV_TO_MENU_ID_MAP.get(navId);
|
||||
if (menuId != null) {
|
||||
binding.bottomNavView.setSelectedItemId(menuId);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Integer> getMainNavList(final int main_nav_ids) {
|
||||
final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids);
|
||||
final List<Integer> mainNavList = new ArrayList<>(navIds.length());
|
||||
final int length = navIds.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
final int resourceId = navIds.getResourceId(i, -1);
|
||||
if (resourceId < 0) continue;
|
||||
mainNavList.add(resourceId);
|
||||
}
|
||||
navIds.recycle();
|
||||
return mainNavList;
|
||||
}
|
||||
|
||||
private void setupNavigation(final NavController navController) {
|
||||
NavigationUI.setupWithNavController(binding.toolbar, navController);
|
||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||
@ -582,4 +621,9 @@ public class MainActivity extends BaseLanguageActivity {
|
||||
unbindService(serviceConnection);
|
||||
isActivityCheckerServiceBound = false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BottomNavigationView getBottomNavView() {
|
||||
return binding.bottomNavView;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.databinding.ItemDirListBinding;
|
||||
|
||||
public final class DirectoryFilesAdapter extends ListAdapter<File, DirectoryFilesAdapter.ViewHolder> {
|
||||
private final OnFileClickListener onFileClickListener;
|
||||
|
||||
private static final DiffUtil.ItemCallback<File> DIFF_CALLBACK = new DiffUtil.ItemCallback<File>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final File oldItem, @NonNull final File newItem) {
|
||||
return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final File oldItem, @NonNull final File newItem) {
|
||||
return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath());
|
||||
}
|
||||
};
|
||||
|
||||
public DirectoryFilesAdapter(final OnFileClickListener onFileClickListener) {
|
||||
super(DIFF_CALLBACK);
|
||||
this.onFileClickListener = onFileClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
final ItemDirListBinding binding = ItemDirListBinding.inflate(inflater, parent, false);
|
||||
return new ViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
|
||||
final File file = getItem(position);
|
||||
holder.bind(file, onFileClickListener);
|
||||
}
|
||||
|
||||
public interface OnFileClickListener {
|
||||
void onFileClick(File file);
|
||||
}
|
||||
|
||||
static final class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemDirListBinding binding;
|
||||
|
||||
private ViewHolder(final ItemDirListBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void bind(final File file, final OnFileClickListener onFileClickListener) {
|
||||
if (file == null) return;
|
||||
if (onFileClickListener != null) {
|
||||
itemView.setOnClickListener(v -> onFileClickListener.onFileClick(file));
|
||||
}
|
||||
binding.text.setText(file.getName());
|
||||
if (file.isDirectory()) {
|
||||
binding.icon.setImageResource(R.drawable.ic_folder_24);
|
||||
return;
|
||||
}
|
||||
binding.icon.setImageResource(R.drawable.ic_file_24);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.ObjectsCompat;
|
||||
import androidx.recyclerview.widget.AdapterListUpdateCallback;
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||
import androidx.recyclerview.widget.AsyncListDiffer;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.viewholder.FavoriteViewHolder;
|
||||
import awais.instagrabber.databinding.ItemFavSectionHeaderBinding;
|
||||
import awais.instagrabber.databinding.ItemSuggestionBinding;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
|
||||
public class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private final OnFavoriteClickListener clickListener;
|
||||
private final OnFavoriteLongClickListener longClickListener;
|
||||
private final AsyncListDiffer<FavoriteModelOrHeader> differ;
|
||||
|
||||
private static final DiffUtil.ItemCallback<FavoriteModelOrHeader> diffCallback = new DiffUtil.ItemCallback<FavoriteModelOrHeader>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final FavoriteModelOrHeader oldItem, @NonNull final FavoriteModelOrHeader newItem) {
|
||||
boolean areSame = oldItem.isHeader() && newItem.isHeader();
|
||||
if (!areSame) {
|
||||
return false;
|
||||
}
|
||||
if (oldItem.isHeader()) {
|
||||
return ObjectsCompat.equals(oldItem.header, newItem.header);
|
||||
}
|
||||
if (oldItem.model != null && newItem.model != null) {
|
||||
return oldItem.model.getId() == newItem.model.getId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final FavoriteModelOrHeader oldItem, @NonNull final FavoriteModelOrHeader newItem) {
|
||||
boolean areSame = oldItem.isHeader() && newItem.isHeader();
|
||||
if (!areSame) {
|
||||
return false;
|
||||
}
|
||||
if (oldItem.isHeader()) {
|
||||
return ObjectsCompat.equals(oldItem.header, newItem.header);
|
||||
}
|
||||
return ObjectsCompat.equals(oldItem.model, newItem.model);
|
||||
}
|
||||
};
|
||||
|
||||
public FavoritesAdapter(final OnFavoriteClickListener clickListener, final OnFavoriteLongClickListener longClickListener) {
|
||||
this.clickListener = clickListener;
|
||||
this.longClickListener = longClickListener;
|
||||
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
|
||||
new AsyncDifferConfig.Builder<>(diffCallback).build());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
if (viewType == 0) {
|
||||
// header
|
||||
return new FavSectionViewHolder(ItemFavSectionHeaderBinding.inflate(inflater, parent, false));
|
||||
}
|
||||
final ItemSuggestionBinding binding = ItemSuggestionBinding.inflate(inflater, parent, false);
|
||||
return new FavoriteViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
|
||||
if (getItemViewType(position) == 0) {
|
||||
final FavoriteModelOrHeader modelOrHeader = getItem(position);
|
||||
if (!modelOrHeader.isHeader()) return;
|
||||
((FavSectionViewHolder) holder).bind(modelOrHeader.header);
|
||||
return;
|
||||
}
|
||||
((FavoriteViewHolder) holder).bind(getItem(position).model, clickListener, longClickListener);
|
||||
}
|
||||
|
||||
protected FavoriteModelOrHeader getItem(int position) {
|
||||
return differ.getCurrentList().get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return differ.getCurrentList().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(final int position) {
|
||||
return getItem(position).isHeader() ? 0 : 1;
|
||||
}
|
||||
|
||||
public void submitList(@Nullable final List<DataBox.FavoriteModel> list) {
|
||||
if (list == null) {
|
||||
differ.submitList(null);
|
||||
return;
|
||||
}
|
||||
differ.submitList(sectionAndSort(list));
|
||||
}
|
||||
|
||||
public void submitList(@Nullable final List<DataBox.FavoriteModel> list, @Nullable final Runnable commitCallback) {
|
||||
if (list == null) {
|
||||
differ.submitList(null, commitCallback);
|
||||
return;
|
||||
}
|
||||
differ.submitList(sectionAndSort(list), commitCallback);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<FavoriteModelOrHeader> sectionAndSort(@NonNull final List<DataBox.FavoriteModel> list) {
|
||||
final List<DataBox.FavoriteModel> listCopy = new ArrayList<>(list);
|
||||
Collections.sort(listCopy, (o1, o2) -> {
|
||||
if (o1.getType() == o2.getType()) return 0;
|
||||
// keep users at top
|
||||
if (o1.getType() == FavoriteType.USER) return -1;
|
||||
if (o2.getType() == FavoriteType.USER) return 1;
|
||||
// keep locations at bottom
|
||||
if (o1.getType() == FavoriteType.LOCATION) return 1;
|
||||
if (o2.getType() == FavoriteType.LOCATION) return -1;
|
||||
return 0;
|
||||
});
|
||||
final List<FavoriteModelOrHeader> modelOrHeaders = new ArrayList<>();
|
||||
for (int i = 0; i < listCopy.size(); i++) {
|
||||
final DataBox.FavoriteModel model = listCopy.get(i);
|
||||
final FavoriteModelOrHeader prev = modelOrHeaders.isEmpty() ? null : modelOrHeaders.get(modelOrHeaders.size() - 1);
|
||||
boolean prevWasSameType = prev != null && prev.model.getType() == model.getType();
|
||||
if (prevWasSameType) {
|
||||
// just add model
|
||||
final FavoriteModelOrHeader modelOrHeader = new FavoriteModelOrHeader();
|
||||
modelOrHeader.model = model;
|
||||
modelOrHeaders.add(modelOrHeader);
|
||||
continue;
|
||||
}
|
||||
// add header and model
|
||||
FavoriteModelOrHeader modelOrHeader = new FavoriteModelOrHeader();
|
||||
modelOrHeader.header = model.getType();
|
||||
modelOrHeaders.add(modelOrHeader);
|
||||
modelOrHeader = new FavoriteModelOrHeader();
|
||||
modelOrHeader.model = model;
|
||||
modelOrHeaders.add(modelOrHeader);
|
||||
}
|
||||
return modelOrHeaders;
|
||||
}
|
||||
|
||||
private static class FavoriteModelOrHeader {
|
||||
FavoriteType header;
|
||||
DataBox.FavoriteModel model;
|
||||
|
||||
boolean isHeader() {
|
||||
return header != null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnFavoriteClickListener {
|
||||
void onClick(final DataBox.FavoriteModel model);
|
||||
}
|
||||
|
||||
public interface OnFavoriteLongClickListener {
|
||||
boolean onLongClick(final DataBox.FavoriteModel model);
|
||||
}
|
||||
|
||||
public static class FavSectionViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemFavSectionHeaderBinding binding;
|
||||
|
||||
public FavSectionViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void bind(final FavoriteType header) {
|
||||
if (header == null) return;
|
||||
final int headerText;
|
||||
switch (header) {
|
||||
case USER:
|
||||
headerText = R.string.accounts;
|
||||
break;
|
||||
case HASHTAG:
|
||||
headerText = R.string.hashtags;
|
||||
break;
|
||||
case LOCATION:
|
||||
headerText = R.string.locations;
|
||||
break;
|
||||
default:
|
||||
headerText = R.string.unknown;
|
||||
break;
|
||||
}
|
||||
binding.getRoot().setText(headerText);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,13 +4,19 @@ import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
|
||||
import awais.instagrabber.databinding.ItemNotificationBinding;
|
||||
import awais.instagrabber.interfaces.MentionClickListener;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
|
||||
public final class NotificationsAdapter extends ListAdapter<NotificationModel, NotificationViewHolder> {
|
||||
private final OnNotificationClickListener notificationClickListener;
|
||||
@ -49,6 +55,36 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
holder.bind(notificationModel, notificationClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable final List<NotificationModel> list, @Nullable final Runnable commitCallback) {
|
||||
if (list == null) {
|
||||
super.submitList(null, commitCallback);
|
||||
return;
|
||||
}
|
||||
super.submitList(sort(list), commitCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable final List<NotificationModel> list) {
|
||||
if (list == null) {
|
||||
super.submitList(null);
|
||||
return;
|
||||
}
|
||||
super.submitList(sort(list));
|
||||
}
|
||||
|
||||
private List<NotificationModel> sort(final List<NotificationModel> list) {
|
||||
final List<NotificationModel> listCopy = new ArrayList<>(list);
|
||||
Collections.sort(listCopy, (o1, o2) -> {
|
||||
if (o1.getType() == o2.getType()) return 0;
|
||||
// keep requests at top
|
||||
if (o1.getType() == NotificationType.REQUEST) return -1;
|
||||
if (o2.getType() == NotificationType.REQUEST) return 1;
|
||||
return 0;
|
||||
});
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
public interface OnNotificationClickListener {
|
||||
void onNotificationClick(final NotificationModel model);
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
|
||||
public final class SimpleAdapter<T> extends RecyclerView.Adapter<SimpleAdapter.SimpleViewHolder> {
|
||||
private List<T> items;
|
||||
private final LayoutInflater layoutInflater;
|
||||
private final View.OnClickListener onClickListener;
|
||||
private final View.OnLongClickListener longClickListener;
|
||||
|
||||
public SimpleAdapter(final Context context, final List<T> items, final View.OnClickListener onClickListener) {
|
||||
this(context, items, onClickListener, null);
|
||||
}
|
||||
|
||||
public SimpleAdapter(final Context context, final List<T> items, final View.OnClickListener onClickListener,
|
||||
final View.OnLongClickListener longClickListener) {
|
||||
this.layoutInflater = LayoutInflater.from(context);
|
||||
this.items = items;
|
||||
this.onClickListener = onClickListener;
|
||||
this.longClickListener = longClickListener;
|
||||
}
|
||||
|
||||
public void setItems(final List<T> items) {
|
||||
this.items = items;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
return new SimpleViewHolder(layoutInflater.
|
||||
inflate(R.layout.item_dir_list, parent, false), onClickListener, longClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final SimpleViewHolder holder, final int position) {
|
||||
final T item = items.get(position);
|
||||
holder.itemView.setTag(item);
|
||||
holder.text.setText(item.toString());
|
||||
if (item instanceof DataBox.CookieModel && ((DataBox.CookieModel) item).isSelected() ||
|
||||
item instanceof String && ((String) item).toLowerCase().endsWith(".zaai"))
|
||||
holder.itemView.setBackgroundColor(0xF0_125687);
|
||||
else
|
||||
holder.itemView.setBackground(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items != null ? items.size() : 0;
|
||||
}
|
||||
|
||||
static final class SimpleViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView text;
|
||||
|
||||
private SimpleViewHolder(@NonNull final View itemView, final View.OnClickListener onClickListener,
|
||||
final View.OnLongClickListener longClickListener) {
|
||||
super(itemView);
|
||||
text = itemView.findViewById(android.R.id.text1);
|
||||
itemView.setOnClickListener(onClickListener);
|
||||
if (longClickListener != null) itemView.setOnLongClickListener(longClickListener);
|
||||
}
|
||||
}
|
||||
}
|
@ -73,7 +73,14 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
|
||||
}
|
||||
}
|
||||
binding.tvUsername.setText(model.getThreadTitle());
|
||||
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1];
|
||||
final int length = itemModels.length;
|
||||
DirectItemModel lastItemModel = null;
|
||||
if (length != 0) {
|
||||
lastItemModel = itemModels[length - 1];
|
||||
}
|
||||
if (lastItemModel == null) {
|
||||
return;
|
||||
}
|
||||
final DirectItemType itemType = lastItemModel.getItemType();
|
||||
// binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE);
|
||||
final Context context = itemView.getContext();
|
||||
|
@ -0,0 +1,61 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import awais.instagrabber.adapters.FavoritesAdapter;
|
||||
import awais.instagrabber.databinding.ItemSuggestionBinding;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
|
||||
public class FavoriteViewHolder extends RecyclerView.ViewHolder {
|
||||
private static final String TAG = "FavoriteViewHolder";
|
||||
|
||||
private final ItemSuggestionBinding binding;
|
||||
|
||||
public FavoriteViewHolder(@NonNull final ItemSuggestionBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
binding.isVerified.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void bind(final DataBox.FavoriteModel model,
|
||||
final FavoritesAdapter.OnFavoriteClickListener clickListener,
|
||||
final FavoritesAdapter.OnFavoriteLongClickListener longClickListener) {
|
||||
// Log.d(TAG, "bind: " + model);
|
||||
if (model == null) return;
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (clickListener == null) return;
|
||||
clickListener.onClick(model);
|
||||
});
|
||||
itemView.setOnLongClickListener(v -> {
|
||||
if (clickListener == null) return false;
|
||||
return longClickListener.onLongClick(model);
|
||||
});
|
||||
if (model.getType() == FavoriteType.HASHTAG) {
|
||||
binding.ivProfilePic.setImageURI(Constants.DEFAULT_HASH_TAG_PIC);
|
||||
} else {
|
||||
binding.ivProfilePic.setImageURI(model.getPicUrl());
|
||||
}
|
||||
binding.tvFullName.setText(model.getDisplayName());
|
||||
binding.tvUsername.setVisibility(View.VISIBLE);
|
||||
String query = model.getQuery();
|
||||
switch (model.getType()) {
|
||||
case HASHTAG:
|
||||
query = "#" + query;
|
||||
break;
|
||||
case USER:
|
||||
query = "@" + query;
|
||||
break;
|
||||
case LOCATION:
|
||||
binding.tvUsername.setVisibility(View.GONE);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
binding.tvUsername.setText(query);
|
||||
}
|
||||
}
|
@ -212,9 +212,10 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
|
||||
feedModelsList.trimToSize();
|
||||
|
||||
final FeedModel[] feedModels = feedModelsList.toArray(new FeedModel[0]);
|
||||
if (feedModels[feedModels.length - 1] != null)
|
||||
feedModels[feedModels.length - 1].setPageCursor(hasNextPage, endCursor);
|
||||
|
||||
final int length = feedModels.length;
|
||||
if (length >= 1 && feedModels[length - 1] != null) {
|
||||
feedModels[length - 1].setPageCursor(hasNextPage, endCursor);
|
||||
}
|
||||
result = feedModels;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,10 @@ import androidx.annotation.Nullable;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
@ -21,6 +25,8 @@ import awaisomereport.LogCollector;
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
|
||||
public final class HashtagFetcher extends AsyncTask<Void, Void, HashtagModel> {
|
||||
private static final String TAG = "HashtagFetcher";
|
||||
|
||||
private final FetchListener<HashtagModel> fetchListener;
|
||||
private final String hashtag;
|
||||
|
||||
@ -35,12 +41,14 @@ public final class HashtagFetcher extends AsyncTask<Void, Void, HashtagModel> {
|
||||
HashtagModel result = null;
|
||||
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/tags/" + hashtag + "/?__a=1").openConnection();
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/tags/" + hashtag + "/?__a=1")
|
||||
.openConnection();
|
||||
conn.setUseCaches(true);
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_HASHTAG);
|
||||
final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
|
||||
.getJSONObject(Constants.EXTRAS_HASHTAG);
|
||||
|
||||
final JSONObject timelineMedia = user.getJSONObject("edge_hashtag_to_media");
|
||||
if (timelineMedia.has("edges")) {
|
||||
@ -53,13 +61,34 @@ public final class HashtagFetcher extends AsyncTask<Void, Void, HashtagModel> {
|
||||
user.getString("profile_pic_url"),
|
||||
timelineMedia.getLong("count"),
|
||||
user.optBoolean("is_following"));
|
||||
} else {
|
||||
BufferedReader bufferedReader = null;
|
||||
try {
|
||||
final InputStream responseInputStream = conn.getErrorStream();
|
||||
bufferedReader = new BufferedReader(new InputStreamReader(responseInputStream));
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
Log.d(TAG, "doInBackground: " + builder.toString());
|
||||
} finally {
|
||||
if (bufferedReader != null) {
|
||||
try {
|
||||
bufferedReader.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_HASHTAG_FETCHER, "doInBackground");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -19,25 +19,23 @@ import awais.instagrabber.utils.NetworkUtils;
|
||||
|
||||
public final class HighlightsFetcher extends AsyncTask<Void, Void, List<HighlightModel>> {
|
||||
private final String id;
|
||||
private final boolean storiesig;
|
||||
private final FetchListener<List<HighlightModel>> fetchListener;
|
||||
|
||||
public HighlightsFetcher(final String id, final boolean storiesig, final FetchListener<List<HighlightModel>> fetchListener) {
|
||||
public HighlightsFetcher(final String id, final FetchListener<List<HighlightModel>> fetchListener) {
|
||||
this.id = id;
|
||||
this.storiesig = storiesig;
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<HighlightModel> doInBackground(final Void... voids) {
|
||||
List<HighlightModel> result = null;
|
||||
String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/highlights/" + id + "/highlights_tray/";
|
||||
String url = "https://i.instagram.com/api/v1/highlights/" + id + "/highlights_tray/";
|
||||
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestProperty("User-Agent", storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT);
|
||||
conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
|
@ -10,6 +10,8 @@ import org.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
@ -28,18 +30,18 @@ import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
|
||||
public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
public final class PostsFetcher extends AsyncTask<Void, Void, List<PostModel>> {
|
||||
private static final String TAG = "PostsFetcher";
|
||||
private final PostItemType type;
|
||||
private final String endCursor;
|
||||
private final String id;
|
||||
private final FetchListener<PostModel[]> fetchListener;
|
||||
private final FetchListener<List<PostModel>> fetchListener;
|
||||
private String username = null;
|
||||
|
||||
public PostsFetcher(final String id,
|
||||
final PostItemType type,
|
||||
final String endCursor,
|
||||
final FetchListener<PostModel[]> fetchListener) {
|
||||
final FetchListener<List<PostModel>> fetchListener) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.endCursor = endCursor == null ? "" : endCursor;
|
||||
@ -52,7 +54,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PostModel[] doInBackground(final Void... voids) {
|
||||
protected List<PostModel> doInBackground(final Void... voids) {
|
||||
// final boolean isHashTag = id.charAt(0) == '#';
|
||||
// final boolean isSaved = id.charAt(0) == '$';
|
||||
// final boolean isTagged = id.charAt(0) == '%';
|
||||
@ -79,7 +81,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
default:
|
||||
url = "https://www.instagram.com/graphql/query/?query_id=17880160963012870&id=" + id + "&first=50&after=" + endCursor;
|
||||
}
|
||||
PostModel[] result = null;
|
||||
List<PostModel> result = new ArrayList<>();
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
@ -126,8 +128,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
}
|
||||
|
||||
final JSONArray edges = mediaPosts.getJSONArray("edges");
|
||||
final PostModel[] models = new PostModel[edges.length()];
|
||||
for (int i = 0; i < models.length; ++i) {
|
||||
for (int i = 0; i < edges.length(); ++i) {
|
||||
final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
|
||||
final JSONArray captions = mediaNode.getJSONObject("edge_media_to_caption").getJSONArray("edges");
|
||||
|
||||
@ -139,34 +140,43 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
|
||||
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
|
||||
|
||||
models[i] = new PostModel(itemType, mediaNode.getString(Constants.EXTRAS_ID),
|
||||
mediaNode.getString("display_url"), mediaNode.getString("thumbnail_src"),
|
||||
mediaNode.getString(Constants.EXTRAS_SHORTCODE),
|
||||
captions.length() > 0 ? captions.getJSONObject(0).getJSONObject("node").getString("text") : null,
|
||||
mediaNode.getLong("taken_at_timestamp"), mediaNode.optBoolean("viewer_has_liked"),
|
||||
mediaNode.optBoolean("viewer_has_saved"), mediaNode.getJSONObject("edge_liked_by").getLong("count"));
|
||||
|
||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, models[i]);
|
||||
final PostModel model = new PostModel(
|
||||
itemType,
|
||||
mediaNode.getString(Constants.EXTRAS_ID),
|
||||
mediaNode.getString("display_url"),
|
||||
mediaNode.getString("thumbnail_src"),
|
||||
mediaNode.getString(Constants.EXTRAS_SHORTCODE),
|
||||
captions.length() > 0 ? captions.getJSONObject(0)
|
||||
.getJSONObject("node")
|
||||
.getString("text")
|
||||
: null,
|
||||
mediaNode.getLong("taken_at_timestamp"),
|
||||
mediaNode.optBoolean("viewer_has_liked"),
|
||||
mediaNode.optBoolean("viewer_has_saved"),
|
||||
mediaNode.getJSONObject("edge_liked_by")
|
||||
.getLong("count")
|
||||
);
|
||||
result.add(model);
|
||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
|
||||
}
|
||||
|
||||
if (models.length != 0 && models[models.length - 1] != null)
|
||||
models[models.length - 1].setPageCursor(hasNextPage, endCursor);
|
||||
|
||||
result = models;
|
||||
if (!result.isEmpty() && result.get(result.size() - 1) != null)
|
||||
result.get(result.size() - 1).setPageCursor(hasNextPage, endCursor);
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
} catch (Exception e) {
|
||||
if (logCollector != null)
|
||||
if (logCollector != null) {
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error fetching posts", e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final PostModel[] postModels) {
|
||||
protected void onPostExecute(final List<PostModel> postModels) {
|
||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ public final class SuggestionsFetcher extends AsyncTask<String, String, Suggesti
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
final String defaultHashTagPic = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
|
||||
final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn));
|
||||
conn.disconnect();
|
||||
|
||||
@ -63,7 +62,7 @@ public final class SuggestionsFetcher extends AsyncTask<String, String, Suggesti
|
||||
suggestionModels.add(new SuggestionModel(false,
|
||||
hashtag.getString(Constants.EXTRAS_NAME),
|
||||
null,
|
||||
hashtag.optString("profile_pic_url", defaultHashTagPic),
|
||||
hashtag.optString("profile_pic_url", Constants.DEFAULT_HASH_TAG_PIC),
|
||||
SuggestionType.TYPE_HASHTAG,
|
||||
hashtagsArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
package awais.instagrabber.asyncs.direct_messages;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
@ -9,7 +9,6 @@ import java.io.DataOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
@ -17,16 +16,16 @@ import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class CommentAction extends AsyncTask<Void, Void, String> {
|
||||
public class CreateThreadAction extends AsyncTask<Void, Void, String> {
|
||||
private static final String TAG = "CommentAction";
|
||||
|
||||
private final String cookie;
|
||||
private final StoryModel storyModel;
|
||||
private final String userId;
|
||||
private final OnTaskCompleteListener onTaskCompleteListener;
|
||||
|
||||
public CommentAction(final String cookie, final StoryModel storyModel, final OnTaskCompleteListener onTaskCompleteListener) {
|
||||
public CreateThreadAction(final String cookie, final String userId, final OnTaskCompleteListener onTaskCompleteListener) {
|
||||
this.cookie = cookie;
|
||||
this.storyModel = storyModel;
|
||||
this.userId = userId;
|
||||
this.onTaskCompleteListener = onTaskCompleteListener;
|
||||
}
|
||||
|
||||
@ -41,7 +40,7 @@ public class CommentAction extends AsyncTask<Void, Void, String> {
|
||||
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
|
||||
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
|
||||
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
|
||||
+ "\",\"recipient_users\":\"[" + storyModel.getUserId() // <- string of array of number (not joking)
|
||||
+ "\",\"recipient_users\":\"[" + userId // <- string of array of number (not joking)
|
||||
+ "]\"}");
|
||||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
if (urlParameters != null) {
|
@ -10,6 +10,8 @@ import org.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
@ -28,25 +30,27 @@ import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
|
||||
public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
private final String endCursor;
|
||||
private final FetchListener<PostModel[]> fetchListener;
|
||||
public final class iLikedFetcher extends AsyncTask<Void, Void, List<PostModel>> {
|
||||
private static final String TAG = "iLikedFetcher";
|
||||
|
||||
public iLikedFetcher(final FetchListener<PostModel[]> fetchListener) {
|
||||
private final String endCursor;
|
||||
private final FetchListener<List<PostModel>> fetchListener;
|
||||
|
||||
public iLikedFetcher(final FetchListener<List<PostModel>> fetchListener) {
|
||||
this.endCursor = "";
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
|
||||
public iLikedFetcher(final String endCursor, final FetchListener<PostModel[]> fetchListener) {
|
||||
public iLikedFetcher(final String endCursor, final FetchListener<List<PostModel>> fetchListener) {
|
||||
this.endCursor = endCursor == null ? "" : endCursor;
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PostModel[] doInBackground(final Void... voids) {
|
||||
final String url = "https://i.instagram.com/api/v1/feed/liked/?max_id="+endCursor;
|
||||
protected List<PostModel> doInBackground(final Void... voids) {
|
||||
final String url = "https://i.instagram.com/api/v1/feed/liked/?max_id=" + endCursor;
|
||||
|
||||
PostModel[] result = null;
|
||||
List<PostModel> result = new ArrayList<>();
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
@ -68,8 +72,7 @@ public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
}
|
||||
|
||||
final JSONArray edges = body.getJSONArray("items");
|
||||
final PostModel[] models = new PostModel[edges.length()];
|
||||
for (int i = 0; i < models.length; ++i) {
|
||||
for (int i = 0; i < edges.length(); ++i) {
|
||||
final JSONObject mediaNode = edges.getJSONObject(i);
|
||||
|
||||
final boolean isSlider = mediaNode.has("carousel_media_count");
|
||||
@ -80,48 +83,57 @@ public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
|
||||
else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
|
||||
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
|
||||
|
||||
models[i] = new PostModel(itemType, mediaNode.getString(Constants.EXTRAS_ID),
|
||||
isSlider
|
||||
? ResponseBodyUtils.getHighQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
|
||||
: ResponseBodyUtils.getHighQualityImage(mediaNode),
|
||||
isSlider
|
||||
? ResponseBodyUtils.getLowQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
|
||||
: ResponseBodyUtils.getLowQualityImage(mediaNode),
|
||||
final PostModel model = new PostModel(
|
||||
itemType,
|
||||
mediaNode.getString(Constants.EXTRAS_ID),
|
||||
isSlider ? ResponseBodyUtils.getHighQualityImage(mediaNode.getJSONArray("carousel_media")
|
||||
.getJSONObject(0))
|
||||
: ResponseBodyUtils.getHighQualityImage(mediaNode),
|
||||
isSlider ? ResponseBodyUtils.getLowQualityImage(mediaNode.getJSONArray("carousel_media")
|
||||
.getJSONObject(0))
|
||||
: ResponseBodyUtils.getLowQualityImage(mediaNode),
|
||||
mediaNode.getString("code"),
|
||||
mediaNode.isNull("caption") ? null : mediaNode.getJSONObject("caption").optString("text"),
|
||||
mediaNode.getLong("taken_at"), true,
|
||||
mediaNode.optBoolean("has_viewer_saved"), mediaNode.getLong("like_count"));
|
||||
|
||||
mediaNode.getLong("taken_at"),
|
||||
true,
|
||||
mediaNode.optBoolean("has_viewer_saved"),
|
||||
mediaNode.getLong("like_count"));
|
||||
result.add(model);
|
||||
String username = mediaNode.getJSONObject("user").getString("username");
|
||||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
|
||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
|
||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
|
||||
File customDir = null;
|
||||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
||||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
|
||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
|
||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
|
||||
? ("/" + username)
|
||||
: ""));
|
||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
|
||||
}
|
||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, models[i]);
|
||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
|
||||
}
|
||||
|
||||
if (models[models.length - 1] != null)
|
||||
models[models.length - 1].setPageCursor(hasNextPage, endCursor);
|
||||
|
||||
result = models;
|
||||
final int length = result.size();
|
||||
if (length >= 1 && result.get(length - 1) != null) {
|
||||
result.get(length - 1).setPageCursor(hasNextPage, endCursor);
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
} catch (Exception e) {
|
||||
if (logCollector != null)
|
||||
if (logCollector != null) {
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final PostModel[] postModels) {
|
||||
protected void onPostExecute(final List<PostModel> postModels) {
|
||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
|
||||
private String username;
|
||||
private final boolean isLoc;
|
||||
private final boolean isHashtag;
|
||||
private final boolean storiesig;
|
||||
private final boolean highlight;
|
||||
private final FetchListener<StoryModel[]> fetchListener;
|
||||
|
||||
@ -38,14 +37,12 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
|
||||
final String username,
|
||||
final boolean isLoc,
|
||||
final boolean isHashtag,
|
||||
final boolean storiesig,
|
||||
final boolean highlight,
|
||||
final FetchListener<StoryModel[]> fetchListener) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.isLoc = isLoc;
|
||||
this.isHashtag = isHashtag;
|
||||
this.storiesig = storiesig;
|
||||
this.highlight = highlight;
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
@ -55,13 +52,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
|
||||
StoryModel[] result = null;
|
||||
final String userId = id.replace(":", "%3A");
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("https://");
|
||||
if (storiesig) {
|
||||
builder.append("storiesig");
|
||||
} else {
|
||||
builder.append("i.instagram");
|
||||
}
|
||||
builder.append(".com/api/v1/");
|
||||
builder.append("https://i.instagram.com/api/v1/");
|
||||
if (isLoc) {
|
||||
builder.append("locations/");
|
||||
}
|
||||
@ -75,24 +66,20 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
|
||||
}
|
||||
builder.append(userId);
|
||||
if (!highlight) {
|
||||
if (storiesig) {
|
||||
builder.append("/reel_media/");
|
||||
} else {
|
||||
builder.append("/story/");
|
||||
}
|
||||
builder.append("/story/");
|
||||
}
|
||||
final String url = builder.toString();
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setInstanceFollowRedirects(true);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestProperty("User-Agent", storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT);
|
||||
conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
|
||||
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));
|
||||
if (!storiesig && !highlight)
|
||||
if (!highlight)
|
||||
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
|
||||
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
|
||||
|
||||
|
@ -0,0 +1,169 @@
|
||||
package awais.instagrabber.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import awais.instagrabber.databinding.DialogCreateBackupBinding;
|
||||
import awais.instagrabber.utils.DirectoryChooser;
|
||||
import awais.instagrabber.utils.ExportImportUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.DownloadUtils.PERMS;
|
||||
|
||||
public class CreateBackupDialogFragment extends DialogFragment {
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
|
||||
private final OnResultListener onResultListener;
|
||||
private DialogCreateBackupBinding binding;
|
||||
|
||||
public CreateBackupDialogFragment(final OnResultListener onResultListener) {
|
||||
this.onResultListener = onResultListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
binding = DialogCreateBackupBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final Dialog dialog = getDialog();
|
||||
if (dialog == null) return;
|
||||
final Window window = dialog.getWindow();
|
||||
if (window == null) return;
|
||||
final int height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);
|
||||
window.setLayout(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
binding.etPassword.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
binding.btnSaveTo.setEnabled(!TextUtils.isEmpty(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {}
|
||||
});
|
||||
final Context context = getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
binding.cbPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
if (isChecked) {
|
||||
if (TextUtils.isEmpty(binding.etPassword.getText())) {
|
||||
binding.btnSaveTo.setEnabled(false);
|
||||
}
|
||||
binding.passwordField.setVisibility(View.VISIBLE);
|
||||
binding.etPassword.requestFocus();
|
||||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT);
|
||||
return;
|
||||
}
|
||||
binding.btnSaveTo.setEnabled(true);
|
||||
binding.passwordField.setVisibility(View.GONE);
|
||||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
imm.hideSoftInputFromWindow(binding.etPassword.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
|
||||
});
|
||||
binding.btnSaveTo.setOnClickListener(v -> {
|
||||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
|
||||
showChooser(context);
|
||||
} else {
|
||||
requestPermissions(PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
showChooser(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void showChooser(@NonNull final Context context) {
|
||||
final String folderPath = Utils.settingsHelper.getString(FOLDER_PATH);
|
||||
final Editable passwordText = binding.etPassword.getText();
|
||||
final String password = binding.cbPassword.isChecked()
|
||||
&& passwordText != null
|
||||
&& !TextUtils.isEmpty(passwordText.toString())
|
||||
? passwordText.toString().trim()
|
||||
: null;
|
||||
final DirectoryChooser directoryChooser = new DirectoryChooser()
|
||||
.setInitialDirectory(folderPath)
|
||||
.setInteractionListener(path -> {
|
||||
final File file = new File(path, String.format(Locale.ENGLISH, "barinsta_%d.backup", System.currentTimeMillis()));
|
||||
int flags = 0;
|
||||
if (binding.cbExportFavorites.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_FAVORITES;
|
||||
}
|
||||
if (binding.cbExportSettings.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_SETTINGS;
|
||||
}
|
||||
if (binding.cbExportLogins.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_COOKIES;
|
||||
}
|
||||
ExportImportUtils.exportData(password, flags, file, result -> {
|
||||
if (onResultListener != null) {
|
||||
onResultListener.onResult(result);
|
||||
}
|
||||
dismiss();
|
||||
}, context);
|
||||
|
||||
});
|
||||
directoryChooser.setEnterTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
directoryChooser.setExitTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
directoryChooser.show(getChildFragmentManager(), "directory_chooser");
|
||||
}
|
||||
|
||||
public interface OnResultListener {
|
||||
void onResult(boolean result);
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
package awais.instagrabber.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.SimpleAdapter;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class QuickAccessDialog extends BottomSheetDialogFragment implements DialogInterface.OnShowListener,
|
||||
View.OnClickListener, View.OnLongClickListener {
|
||||
private boolean cookieChanged, isQuery;
|
||||
private Activity activity;
|
||||
private String userQuery, displayName;
|
||||
private View btnFavorite, btnImportExport;
|
||||
private SimpleAdapter<DataBox.FavoriteModel> favoritesAdapter;
|
||||
private RecyclerView rvFavorites, rvQuickAccess;
|
||||
|
||||
public QuickAccessDialog setQuery(final String userQuery, final String displayName) {
|
||||
this.userQuery = userQuery;
|
||||
this.displayName = displayName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
final Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
|
||||
dialog.setOnShowListener(this);
|
||||
|
||||
final Context context = getContext();
|
||||
activity = context instanceof Activity ? (Activity) context : getActivity();
|
||||
|
||||
final View contentView = View.inflate(activity, R.layout.dialog_quick_access, null);
|
||||
|
||||
btnFavorite = contentView.findViewById(R.id.btnFavorite);
|
||||
btnImportExport = contentView.findViewById(R.id.importExport);
|
||||
|
||||
isQuery = !TextUtils.isEmpty(userQuery);
|
||||
btnFavorite.setVisibility(isQuery ? View.VISIBLE : View.GONE);
|
||||
Utils.setTooltipText(btnImportExport, R.string.import_export);
|
||||
|
||||
favoritesAdapter = new SimpleAdapter<>(activity, Utils.dataBox.getAllFavorites(), this, this);
|
||||
|
||||
btnFavorite.setOnClickListener(this);
|
||||
btnImportExport.setOnClickListener(this);
|
||||
|
||||
rvFavorites = contentView.findViewById(R.id.rvFavorites);
|
||||
rvQuickAccess = contentView.findViewById(R.id.rvQuickAccess);
|
||||
|
||||
final DividerItemDecoration itemDecoration = new DividerItemDecoration(activity, DividerItemDecoration.VERTICAL);
|
||||
rvFavorites.addItemDecoration(itemDecoration);
|
||||
rvFavorites.setAdapter(favoritesAdapter);
|
||||
|
||||
final String cookieStr = settingsHelper.getString(Constants.COOKIE);
|
||||
if (!TextUtils.isEmpty(cookieStr)
|
||||
|| Utils.dataBox.getCookieCount() > 0 // fallback for export / import
|
||||
) {
|
||||
rvQuickAccess.addItemDecoration(itemDecoration);
|
||||
final ArrayList<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies();
|
||||
if (!TextUtils.isEmpty(cookieStr) && allCookies != null) {
|
||||
for (final DataBox.CookieModel cookie : allCookies) {
|
||||
if (cookieStr.equals(cookie.getCookie())) {
|
||||
cookie.setSelected(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
rvQuickAccess.setAdapter(new SimpleAdapter<>(activity, allCookies, this, this));
|
||||
} else {
|
||||
((View) rvQuickAccess.getParent()).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
dialog.setContentView(contentView);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(@NonNull final View v) {
|
||||
final Object tag = v.getTag();
|
||||
if (v == btnFavorite) {
|
||||
if (isQuery) {
|
||||
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(userQuery, System.currentTimeMillis(), displayName));
|
||||
favoritesAdapter.setItems(Utils.dataBox.getAllFavorites());
|
||||
}
|
||||
} else if (v == btnImportExport) {
|
||||
if (ContextCompat.checkSelfPermission(activity, DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_DENIED)
|
||||
requestPermissions(DownloadUtils.PERMS, 6007);
|
||||
else Utils.showImportExportDialog(v.getContext());
|
||||
|
||||
} else if (tag instanceof DataBox.FavoriteModel) {
|
||||
// if (MainActivityBackup.scanHack != null) {
|
||||
// MainActivityBackup.scanHack.onResult(((DataBox.FavoriteModel) tag).getQuery());
|
||||
// dismiss();
|
||||
// }
|
||||
|
||||
} else if (tag instanceof DataBox.CookieModel) {
|
||||
final DataBox.CookieModel cookieModel = (DataBox.CookieModel) tag;
|
||||
if (!cookieModel.isSelected()) {
|
||||
settingsHelper.putString(Constants.COOKIE, cookieModel.getCookie());
|
||||
CookieUtils.setupCookies(cookieModel.getCookie());
|
||||
cookieChanged = true;
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(@NonNull final View v) {
|
||||
final Object tag = v.getTag();
|
||||
|
||||
if (tag instanceof DataBox.FavoriteModel) {
|
||||
final DataBox.FavoriteModel favoriteModel = (DataBox.FavoriteModel) tag;
|
||||
|
||||
new AlertDialog.Builder(activity).setPositiveButton(R.string.yes, (d, which) -> {
|
||||
Utils.dataBox.delFavorite(favoriteModel);
|
||||
favoritesAdapter.setItems(Utils.dataBox.getAllFavorites());
|
||||
})
|
||||
.setNegativeButton(R.string.no, null).setMessage(getString(R.string.quick_access_confirm_delete,
|
||||
favoriteModel.getQuery())).show();
|
||||
|
||||
} else if (tag instanceof DataBox.CookieModel) {
|
||||
final DataBox.CookieModel cookieModel = (DataBox.CookieModel) tag;
|
||||
|
||||
if (cookieModel.isSelected())
|
||||
Toast.makeText(v.getContext(), R.string.quick_access_cannot_delete_curr, Toast.LENGTH_SHORT).show();
|
||||
else
|
||||
new AlertDialog.Builder(activity)
|
||||
.setMessage(getString(R.string.quick_access_confirm_delete, cookieModel.getUsername()))
|
||||
.setPositiveButton(R.string.yes, (d, which) -> {
|
||||
Utils.dataBox.delUserCookie(cookieModel);
|
||||
rvQuickAccess.findViewWithTag(cookieModel).setVisibility(View.GONE);
|
||||
})
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(@NonNull final DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if (cookieChanged && activity != null) activity.recreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShow(final DialogInterface dialog) {
|
||||
if (settingsHelper.getBoolean(Constants.SHOW_QUICK_ACCESS_DIALOG))
|
||||
new AlertDialog.Builder(activity)
|
||||
.setMessage(R.string.quick_access_info_dialog)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNeutralButton(R.string.dont_show_again, (d, which) ->
|
||||
settingsHelper.putBoolean(Constants.SHOW_QUICK_ACCESS_DIALOG, false)).show();
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package awais.instagrabber.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import awais.instagrabber.databinding.DialogRestoreBackupBinding;
|
||||
import awais.instagrabber.utils.DirectoryChooser;
|
||||
import awais.instagrabber.utils.ExportImportUtils;
|
||||
import awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.DownloadUtils.PERMS;
|
||||
|
||||
public class RestoreBackupDialogFragment extends DialogFragment {
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
|
||||
private final OnResultListener onResultListener;
|
||||
|
||||
private DialogRestoreBackupBinding binding;
|
||||
private File file;
|
||||
private boolean isEncrypted;
|
||||
|
||||
public RestoreBackupDialogFragment(final OnResultListener onResultListener) {
|
||||
this.onResultListener = onResultListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
binding = DialogRestoreBackupBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final Dialog dialog = getDialog();
|
||||
if (dialog == null) return;
|
||||
final Window window = dialog.getWindow();
|
||||
if (window == null) return;
|
||||
final int height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);
|
||||
window.setLayout(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showChooser();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final Context context = getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
binding.btnRestore.setEnabled(false);
|
||||
binding.btnRestore.setOnClickListener(v -> {
|
||||
int flags = 0;
|
||||
if (binding.cbFavorites.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_FAVORITES;
|
||||
}
|
||||
if (binding.cbSettings.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_SETTINGS;
|
||||
}
|
||||
if (binding.cbAccounts.isChecked()) {
|
||||
flags |= ExportImportUtils.FLAG_COOKIES;
|
||||
}
|
||||
final Editable text = binding.etPassword.getText();
|
||||
if (isEncrypted && text == null) return;
|
||||
try {
|
||||
ExportImportUtils.importData(
|
||||
context,
|
||||
flags,
|
||||
file,
|
||||
!isEncrypted ? null : text.toString(),
|
||||
result -> {
|
||||
if (onResultListener != null) {
|
||||
onResultListener.onResult(result);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
);
|
||||
} catch (IncorrectPasswordException e) {
|
||||
binding.passwordField.setError("Incorrect password");
|
||||
}
|
||||
});
|
||||
binding.etPassword.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
binding.btnRestore.setEnabled(!TextUtils.isEmpty(s));
|
||||
binding.passwordField.setError(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {}
|
||||
});
|
||||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
|
||||
showChooser();
|
||||
return;
|
||||
}
|
||||
requestPermissions(PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||
}
|
||||
|
||||
private void showChooser() {
|
||||
final String folderPath = Utils.settingsHelper.getString(FOLDER_PATH);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final DirectoryChooser directoryChooser = new DirectoryChooser()
|
||||
.setInitialDirectory(folderPath)
|
||||
.setShowBackupFiles(true)
|
||||
.setInteractionListener(file -> {
|
||||
isEncrypted = ExportImportUtils.isEncrypted(file);
|
||||
if (isEncrypted) {
|
||||
binding.passwordGroup.setVisibility(View.VISIBLE);
|
||||
binding.passwordGroup.post(() -> {
|
||||
binding.etPassword.requestFocus();
|
||||
binding.etPassword.post(() -> {
|
||||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT);
|
||||
});
|
||||
binding.btnRestore.setEnabled(!TextUtils.isEmpty(binding.etPassword.getText()));
|
||||
});
|
||||
} else {
|
||||
binding.passwordGroup.setVisibility(View.GONE);
|
||||
binding.btnRestore.setEnabled(true);
|
||||
}
|
||||
this.file = file;
|
||||
binding.filePath.setText(file.getAbsolutePath());
|
||||
});
|
||||
directoryChooser.setEnterTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
directoryChooser.setExitTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
directoryChooser.setOnCancelListener(this::dismiss);
|
||||
directoryChooser.show(getChildFragmentManager(), "directory_chooser");
|
||||
}
|
||||
|
||||
public interface OnResultListener {
|
||||
void onResult(boolean result);
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
package awais.instagrabber.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.FavoritesAdapter;
|
||||
import awais.instagrabber.asyncs.LocationFetcher;
|
||||
import awais.instagrabber.asyncs.ProfileFetcher;
|
||||
import awais.instagrabber.databinding.FragmentFavoritesBinding;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.FavoritesViewModel;
|
||||
|
||||
public class FavoritesFragment extends Fragment {
|
||||
private static final String TAG = "FavoritesFragment";
|
||||
|
||||
private boolean shouldRefresh = true;
|
||||
private FragmentFavoritesBinding binding;
|
||||
private RecyclerView root;
|
||||
private FavoritesViewModel favoritesViewModel;
|
||||
private FavoritesAdapter adapter;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
}
|
||||
binding = FragmentFavoritesBinding.inflate(getLayoutInflater());
|
||||
root = binding.getRoot();
|
||||
binding.favoriteList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
init();
|
||||
shouldRefresh = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (favoritesViewModel == null || adapter == null) return;
|
||||
// refresh list every time in onViewStateRestored since it is cheaper than implementing pull down to refresh
|
||||
favoritesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||
final List<DataBox.FavoriteModel> allFavorites = Utils.dataBox.getAllFavorites();
|
||||
favoritesViewModel.getList().postValue(allFavorites);
|
||||
fetchMissingInfo(allFavorites);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
favoritesViewModel = new ViewModelProvider(this).get(FavoritesViewModel.class);
|
||||
adapter = new FavoritesAdapter(model -> {
|
||||
// navigate
|
||||
switch (model.getType()) {
|
||||
case USER: {
|
||||
final String username = model.getQuery();
|
||||
// Log.d(TAG, "username: " + username);
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("username", "@" + username);
|
||||
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||
break;
|
||||
}
|
||||
case LOCATION: {
|
||||
final String locationId = model.getQuery();
|
||||
// Log.d(TAG, "locationId: " + locationId);
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("locationId", locationId);
|
||||
navController.navigate(R.id.action_global_locationFragment, bundle);
|
||||
break;
|
||||
}
|
||||
case HASHTAG: {
|
||||
final String hashtag = model.getQuery();
|
||||
// Log.d(TAG, "hashtag: " + hashtag);
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("hashtag", "#" + hashtag);
|
||||
navController.navigate(R.id.action_global_hashTagFragment, bundle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}, model -> {
|
||||
// delete
|
||||
final Context context = getContext();
|
||||
if (context == null) return false;
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(getString(R.string.quick_access_confirm_delete, model.getQuery()))
|
||||
.setPositiveButton(R.string.yes, (d, which) -> {
|
||||
Utils.dataBox.deleteFavorite(model.getQuery(), model.getType());
|
||||
d.dismiss();
|
||||
favoritesViewModel.getList().postValue(Utils.dataBox.getAllFavorites());
|
||||
})
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
});
|
||||
binding.favoriteList.setAdapter(adapter);
|
||||
// favoritesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||
|
||||
}
|
||||
|
||||
private void fetchMissingInfo(final List<DataBox.FavoriteModel> allFavorites) {
|
||||
final Runnable runnable = () -> {
|
||||
final List<DataBox.FavoriteModel> updatedList = new ArrayList<>(allFavorites);
|
||||
// cyclic barrier is to make the async calls synchronous
|
||||
final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
|
||||
// Log.d(TAG, "fetchMissingInfo: barrier action");
|
||||
favoritesViewModel.getList().postValue(new ArrayList<>(updatedList));
|
||||
});
|
||||
try {
|
||||
for (final DataBox.FavoriteModel model : allFavorites) {
|
||||
cyclicBarrier.reset();
|
||||
// if the model has missing pic or display name (for user and location), fetch those details
|
||||
switch (model.getType()) {
|
||||
case LOCATION:
|
||||
if (TextUtils.isEmpty(model.getDisplayName())
|
||||
|| TextUtils.isEmpty(model.getPicUrl())) {
|
||||
new LocationFetcher(model.getQuery(), result -> {
|
||||
try {
|
||||
if (result == null) return;
|
||||
final int i = updatedList.indexOf(model);
|
||||
updatedList.remove(i);
|
||||
final DataBox.FavoriteModel updated = new DataBox.FavoriteModel(
|
||||
model.getId(),
|
||||
model.getQuery(),
|
||||
model.getType(),
|
||||
result.getName(),
|
||||
result.getSdProfilePic(),
|
||||
model.getDateAdded()
|
||||
);
|
||||
Utils.dataBox.addOrUpdateFavorite(updated);
|
||||
updatedList.add(i, updated);
|
||||
} finally {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
}).execute();
|
||||
cyclicBarrier.await();
|
||||
}
|
||||
break;
|
||||
case USER:
|
||||
if (TextUtils.isEmpty(model.getDisplayName())
|
||||
|| TextUtils.isEmpty(model.getPicUrl())) {
|
||||
new ProfileFetcher(model.getQuery(), result -> {
|
||||
try {
|
||||
if (result == null) return;
|
||||
final int i = updatedList.indexOf(model);
|
||||
updatedList.remove(i);
|
||||
final DataBox.FavoriteModel updated = new DataBox.FavoriteModel(
|
||||
model.getId(),
|
||||
model.getQuery(),
|
||||
model.getType(),
|
||||
result.getName(),
|
||||
result.getSdProfilePic(),
|
||||
model.getDateAdded()
|
||||
);
|
||||
Utils.dataBox.addOrUpdateFavorite(updated);
|
||||
updatedList.add(i, updated);
|
||||
} finally {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
}).execute();
|
||||
cyclicBarrier.await();
|
||||
}
|
||||
break;
|
||||
case HASHTAG:
|
||||
default:
|
||||
// hashtags don't require displayName or pic
|
||||
// updatedList.add(model);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
favoritesViewModel.getList().postValue(updatedList);
|
||||
};
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package awais.instagrabber.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@ -22,16 +21,18 @@ import androidx.activity.OnBackPressedDispatcher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
@ -49,21 +50,24 @@ import awais.instagrabber.databinding.FragmentHashtagBinding;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.HashtagModel;
|
||||
import awais.instagrabber.models.PostModel;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.models.enums.DownloadMethod;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awais.instagrabber.webservices.TagsService;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class HashTagFragment extends Fragment {
|
||||
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "HashTagFragment";
|
||||
|
||||
private MainActivity fragmentActivity;
|
||||
@ -79,7 +83,8 @@ public class HashTagFragment extends Fragment {
|
||||
private String endCursor;
|
||||
private AsyncTask<?, ?, ?> currentlyExecuting;
|
||||
private boolean isLoggedIn;
|
||||
private StoryModel[] storyModels;
|
||||
private TagsService tagsService;
|
||||
private boolean isPullToRefresh;
|
||||
|
||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||
@Override
|
||||
@ -116,19 +121,27 @@ public class HashTagFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
|
||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
||||
@Override
|
||||
public void onResult(final PostModel[] result) {
|
||||
public void onResult(final List<PostModel> result) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (result == null) return;
|
||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
||||
final List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>() : new ArrayList<>(postModels);
|
||||
finalList.addAll(Arrays.asList(result));
|
||||
List<PostModel> finalList = postModels == null || postModels.isEmpty()
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(postModels);
|
||||
if (isPullToRefresh) {
|
||||
finalList = result;
|
||||
isPullToRefresh = false;
|
||||
} else {
|
||||
finalList.addAll(result);
|
||||
}
|
||||
finalList.addAll(result);
|
||||
postsViewModel.getList().postValue(finalList);
|
||||
PostModel model = null;
|
||||
if (result.length != 0) {
|
||||
model = result[result.length - 1];
|
||||
if (!result.isEmpty()) {
|
||||
model = result.get(result.size() - 1);
|
||||
}
|
||||
if (model == null) return;
|
||||
endCursor = model.getEndCursor();
|
||||
@ -141,6 +154,7 @@ public class HashTagFragment extends Fragment {
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
tagsService = TagsService.getInstance();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -158,10 +172,18 @@ public class HashTagFragment extends Fragment {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
init();
|
||||
shouldRefresh = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
isPullToRefresh = true;
|
||||
endCursor = null;
|
||||
fetchHashtagModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
@ -257,7 +279,6 @@ public class HashTagFragment extends Fragment {
|
||||
|
||||
private void fetchPosts() {
|
||||
stopCurrentExecutor();
|
||||
binding.btnFollowTag.setVisibility(View.VISIBLE);
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
if (TextUtils.isEmpty(hashtag)) return;
|
||||
currentlyExecuting = new PostsFetcher(hashtag.substring(1), PostItemType.HASHTAG, endCursor, postsFetchListener)
|
||||
@ -265,30 +286,110 @@ public class HashTagFragment extends Fragment {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (isLoggedIn) {
|
||||
new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, false, false, stories -> {
|
||||
storyModels = stories;
|
||||
new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, false, stories -> {
|
||||
if (stories != null && stories.length > 0) {
|
||||
binding.mainHashtagImage.setStoriesBorder();
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
||||
binding.btnFollowTag.setVisibility(View.VISIBLE);
|
||||
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollowTag, ColorStateList.valueOf(
|
||||
ContextCompat.getColor(context, hashtagModel.getFollowing()
|
||||
? R.color.btn_purple_background
|
||||
: R.color.btn_pink_background)));
|
||||
binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing()
|
||||
? R.drawable.ic_outline_person_add_disabled_24
|
||||
: R.drawable.ic_outline_person_add_24);
|
||||
binding.btnFollowTag.setOnClickListener(v -> {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
binding.btnFollowTag.setClickable(false);
|
||||
if (!hashtagModel.getFollowing()) {
|
||||
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
binding.btnFollowTag.setClickable(true);
|
||||
if (!result) {
|
||||
Log.e(TAG, "onSuccess: result is false");
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Throwable t) {
|
||||
binding.btnFollowTag.setClickable(true);
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
final String message = t.getMessage();
|
||||
Snackbar.make(root,
|
||||
message != null ? message
|
||||
: getString(R.string.downloader_unknown_error),
|
||||
BaseTransientBottomBar.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
binding.btnFollowTag.setClickable(true);
|
||||
if (!result) {
|
||||
Log.e(TAG, "onSuccess: result is false");
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Throwable t) {
|
||||
binding.btnFollowTag.setClickable(true);
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
final String message = t.getMessage();
|
||||
Snackbar.make(root,
|
||||
message != null ? message
|
||||
: getString(R.string.downloader_unknown_error),
|
||||
BaseTransientBottomBar.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
binding.btnFollowTag.setText(Utils.dataBox.getFavorite(hashtag) != null
|
||||
? R.string.unfavorite_short
|
||||
: R.string.favorite_short);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollowTag, ColorStateList.valueOf(
|
||||
ContextCompat.getColor(context, Utils.dataBox.getFavorite(hashtag) != null
|
||||
? R.color.btn_purple_background
|
||||
: R.color.btn_pink_background)));
|
||||
binding.btnFollowTag.setVisibility(View.GONE);
|
||||
}
|
||||
final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG);
|
||||
final boolean isFav = favorite != null;
|
||||
binding.favChip.setVisibility(View.VISIBLE);
|
||||
binding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24
|
||||
: R.drawable.ic_outline_star_plus_24);
|
||||
binding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites);
|
||||
binding.favChip.setOnClickListener(v -> {
|
||||
final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG);
|
||||
final boolean isFavorite = fav != null;
|
||||
final String message;
|
||||
if (isFavorite) {
|
||||
Utils.dataBox.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG);
|
||||
binding.favChip.setText(R.string.add_to_favorites);
|
||||
binding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
|
||||
message = getString(R.string.removed_from_favs);
|
||||
} else {
|
||||
Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel(
|
||||
-1,
|
||||
hashtag.substring(1),
|
||||
FavoriteType.HASHTAG,
|
||||
hashtagModel.getName(),
|
||||
null,
|
||||
new Date()
|
||||
));
|
||||
binding.favChip.setText(R.string.favorite_short);
|
||||
binding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
message = getString(R.string.added_to_favs);
|
||||
}
|
||||
final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG);
|
||||
snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss())
|
||||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
|
||||
.setAnchorView(fragmentActivity.getBottomNavView())
|
||||
.show();
|
||||
});
|
||||
binding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic());
|
||||
final String postCount = String.valueOf(hashtagModel.getPostCount());
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count, postCount));
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
|
||||
binding.mainTagPostCount.setText(span);
|
||||
@ -312,9 +413,7 @@ public class HashTagFragment extends Fragment {
|
||||
if (actionBar != null) {
|
||||
Log.d(TAG, "setting title: " + hashtag);
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(() -> {
|
||||
actionBar.setTitle(hashtag);
|
||||
}, 200);
|
||||
handler.postDelayed(() -> actionBar.setTitle(hashtag), 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,14 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
@ -48,11 +52,12 @@ import awais.instagrabber.databinding.FragmentLocationBinding;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.LocationModel;
|
||||
import awais.instagrabber.models.PostModel;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.models.enums.DownloadMethod;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
@ -62,7 +67,7 @@ import awaisomereport.LogCollector;
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class LocationFragment extends Fragment {
|
||||
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "LocationFragment";
|
||||
|
||||
private MainActivity fragmentActivity;
|
||||
@ -78,7 +83,7 @@ public class LocationFragment extends Fragment {
|
||||
private String endCursor;
|
||||
private AsyncTask<?, ?, ?> currentlyExecuting;
|
||||
private boolean isLoggedIn;
|
||||
private StoryModel[] storyModels;
|
||||
private boolean isPullToRefresh;
|
||||
|
||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||
@Override
|
||||
@ -119,20 +124,25 @@ public class LocationFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
|
||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
||||
@Override
|
||||
public void onResult(final PostModel[] result) {
|
||||
public void onResult(final List<PostModel> result) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (result == null) return;
|
||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
||||
final List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
||||
: new ArrayList<>(postModels);
|
||||
finalList.addAll(Arrays.asList(result));
|
||||
List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
||||
: new ArrayList<>(postModels);
|
||||
if (isPullToRefresh) {
|
||||
finalList = result;
|
||||
isPullToRefresh = false;
|
||||
} else {
|
||||
finalList.addAll(result);
|
||||
}
|
||||
postsViewModel.getList().postValue(finalList);
|
||||
PostModel model = null;
|
||||
if (result.length != 0) {
|
||||
model = result[result.length - 1];
|
||||
if (!result.isEmpty()) {
|
||||
model = result.get(result.size() - 1);
|
||||
}
|
||||
if (model == null) return;
|
||||
endCursor = model.getEndCursor();
|
||||
@ -164,10 +174,18 @@ public class LocationFragment extends Fragment {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
init();
|
||||
shouldRefresh = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
isPullToRefresh = true;
|
||||
endCursor = null;
|
||||
fetchLocationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
@ -182,6 +200,8 @@ public class LocationFragment extends Fragment {
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments());
|
||||
locationId = fragmentArgs.getLocationId();
|
||||
binding.favChip.setVisibility(View.GONE);
|
||||
binding.btnMap.setVisibility(View.GONE);
|
||||
setTitle();
|
||||
setupPosts();
|
||||
fetchLocationModel();
|
||||
@ -273,9 +293,7 @@ public class LocationFragment extends Fragment {
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
stories -> {
|
||||
storyModels = stories;
|
||||
if (stories != null && stories.length > 0) {
|
||||
binding.mainLocationImage.setStoriesBorder();
|
||||
}
|
||||
@ -283,7 +301,7 @@ public class LocationFragment extends Fragment {
|
||||
}
|
||||
binding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
|
||||
final String postCount = String.valueOf(locationModel.getPostCount());
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count,
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
|
||||
postCount));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
|
||||
@ -329,6 +347,40 @@ public class LocationFragment extends Fragment {
|
||||
binding.locationUrl.setVisibility(View.VISIBLE);
|
||||
binding.locationUrl.setText(TextUtils.getSpannableUrl(url));
|
||||
}
|
||||
final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION);
|
||||
final boolean isFav = favorite != null;
|
||||
binding.favChip.setVisibility(View.VISIBLE);
|
||||
binding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24
|
||||
: R.drawable.ic_outline_star_plus_24);
|
||||
binding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites);
|
||||
binding.favChip.setOnClickListener(v -> {
|
||||
final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION);
|
||||
final boolean isFavorite = fav != null;
|
||||
final String message;
|
||||
if (isFavorite) {
|
||||
Utils.dataBox.deleteFavorite(locationId, FavoriteType.LOCATION);
|
||||
binding.favChip.setText(R.string.add_to_favorites);
|
||||
binding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
|
||||
message = getString(R.string.removed_from_favs);
|
||||
} else {
|
||||
Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel(
|
||||
-1,
|
||||
locationId,
|
||||
FavoriteType.LOCATION,
|
||||
locationModel.getName(),
|
||||
locationModel.getSdProfilePic(),
|
||||
new Date()
|
||||
));
|
||||
binding.favChip.setText(R.string.favorite_short);
|
||||
binding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
message = getString(R.string.added_to_favs);
|
||||
}
|
||||
final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG);
|
||||
snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss())
|
||||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
|
||||
.setAnchorView(fragmentActivity.getBottomNavView())
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void fetchPosts() {
|
||||
|
@ -27,7 +27,6 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -58,7 +57,6 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
private static AsyncTask<?, ?, ?> currentlyExecuting;
|
||||
private PostsAdapter postsAdapter;
|
||||
private boolean hasNextPage;
|
||||
private boolean autoloadPosts;
|
||||
private FragmentSavedBinding binding;
|
||||
private String username;
|
||||
private String endCursor;
|
||||
@ -107,17 +105,16 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
return false;
|
||||
}
|
||||
});
|
||||
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
|
||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
||||
@Override
|
||||
public void onResult(final PostModel[] result) {
|
||||
public void onResult(final List<PostModel> result) {
|
||||
final List<PostModel> current = postsViewModel.getList().getValue();
|
||||
if (result != null && result.length > 0) {
|
||||
final List<PostModel> resultList = Arrays.asList(result);
|
||||
if (result != null && !result.isEmpty()) {
|
||||
if (current == null) {
|
||||
postsViewModel.getList().postValue(resultList);
|
||||
postsViewModel.getList().postValue(result);
|
||||
} else {
|
||||
final List<PostModel> currentCopy = new ArrayList<>(current);
|
||||
currentCopy.addAll(resultList);
|
||||
currentCopy.addAll(result);
|
||||
postsViewModel.getList().postValue(currentCopy);
|
||||
}
|
||||
binding.mainPosts.post(() -> {
|
||||
@ -125,11 +122,11 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
binding.mainPosts.setVisibility(View.VISIBLE);
|
||||
});
|
||||
|
||||
final PostModel model = result.length > 0 ? result[result.length - 1] : null;
|
||||
final PostModel model = !result.isEmpty() ? result.get(result.size() - 1) : null;
|
||||
if (model != null) {
|
||||
endCursor = model.getEndCursor();
|
||||
hasNextPage = model.hasNextPage();
|
||||
if (autoloadPosts && hasNextPage) {
|
||||
if (hasNextPage) {
|
||||
fetchPosts();
|
||||
} else {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
@ -246,7 +243,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (!autoloadPosts && hasNextPage) {
|
||||
if (hasNextPage) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
fetchPosts();
|
||||
endCursor = null;
|
||||
@ -258,7 +255,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
|
||||
private void fetchPosts() {
|
||||
stopCurrentExecutor();
|
||||
final AsyncTask<Void, Void, PostModel[]> asyncTask;
|
||||
final AsyncTask<Void, Void, List<PostModel>> asyncTask;
|
||||
switch (type) {
|
||||
case LIKED:
|
||||
asyncTask = new iLikedFetcher(endCursor, postsFetchListener);
|
||||
|
@ -62,12 +62,12 @@ import java.util.List;
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.StoriesAdapter;
|
||||
import awais.instagrabber.asyncs.CommentAction;
|
||||
import awais.instagrabber.asyncs.DownloadAsync;
|
||||
import awais.instagrabber.asyncs.QuizAction;
|
||||
import awais.instagrabber.asyncs.RespondAction;
|
||||
import awais.instagrabber.asyncs.SeenAction;
|
||||
import awais.instagrabber.asyncs.VoteAction;
|
||||
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
||||
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
|
||||
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
|
||||
import awais.instagrabber.databinding.FragmentStoryViewerBinding;
|
||||
@ -196,7 +196,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.reply_story)
|
||||
.setView(input)
|
||||
.setPositiveButton(R.string.ok, (d, w) -> new CommentAction(cookie, currentStory, threadId -> {
|
||||
.setPositiveButton(R.string.ok, (d, w) -> new CreateThreadAction(cookie, currentStory.getUserId(), threadId -> {
|
||||
try {
|
||||
final DirectThreadBroadcaster.StoryReplyBroadcastOptions options = new DirectThreadBroadcaster.StoryReplyBroadcastOptions(
|
||||
input.getText().toString(),
|
||||
@ -544,7 +544,6 @@ public class StoryViewerFragment extends Fragment {
|
||||
};
|
||||
storiesService.getUserStory(currentStoryMediaId,
|
||||
username,
|
||||
!isLoggedIn && settingsHelper.getString(Constants.STORY_VIEWER) == StoryViewerChoice.STORIESIG.getValue(),
|
||||
false,
|
||||
false,
|
||||
isHighlight,
|
||||
|
@ -325,6 +325,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
binding.feedSwipeRefreshLayout.setRefreshing(false);
|
||||
if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
|
||||
videoAwareRecyclerScroller.startPlaying();
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package awais.instagrabber.fragments.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@ -30,8 +29,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@ -42,9 +39,12 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.ProfileNavGraphDirections;
|
||||
@ -56,6 +56,7 @@ import awais.instagrabber.asyncs.HighlightsFetcher;
|
||||
import awais.instagrabber.asyncs.PostsFetcher;
|
||||
import awais.instagrabber.asyncs.ProfileFetcher;
|
||||
import awais.instagrabber.asyncs.UsernameFetcher;
|
||||
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
||||
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
|
||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
|
||||
@ -71,6 +72,7 @@ import awais.instagrabber.models.PostModel;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.models.enums.DownloadMethod;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.models.enums.StoryViewerChoice;
|
||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||
@ -111,11 +113,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private StoryModel[] storyModels;
|
||||
private boolean hasNextPage;
|
||||
private String endCursor;
|
||||
private AsyncTask<Void, Void, PostModel[]> currentlyExecuting;
|
||||
private MenuItem favMenuItem;
|
||||
private AsyncTask<Void, Void, List<PostModel>> currentlyExecuting;
|
||||
private boolean isPullToRefresh;
|
||||
private HighlightsAdapter highlightsAdapter;
|
||||
private HighlightsViewModel highlightsViewModel;
|
||||
private MenuItem blockMenuItem;
|
||||
private MenuItem restrictMenuItem;
|
||||
|
||||
private final Runnable usernameSettingRunnable = () -> {
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
@ -161,11 +164,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
return false;
|
||||
}
|
||||
});
|
||||
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
|
||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
||||
@Override
|
||||
public void onResult(final PostModel[] result) {
|
||||
public void onResult(final List<PostModel> result) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (result == null || result.length <= 0) {
|
||||
if (result == null || result.isEmpty()) {
|
||||
binding.privatePage1.setImageResource(R.drawable.ic_cancel);
|
||||
binding.privatePage2.setText(R.string.empty_acc);
|
||||
binding.privatePage.setVisibility(View.VISIBLE);
|
||||
@ -175,15 +178,14 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
||||
List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
||||
: new ArrayList<>(postModels);
|
||||
final List<PostModel> resultList = Arrays.asList(result);
|
||||
if (isPullToRefresh) {
|
||||
finalList = resultList;
|
||||
finalList = result;
|
||||
isPullToRefresh = false;
|
||||
} else {
|
||||
finalList.addAll(resultList);
|
||||
finalList.addAll(result);
|
||||
}
|
||||
postsViewModel.getList().postValue(finalList);
|
||||
final PostModel lastPostModel = result[result.length - 1];
|
||||
final PostModel lastPostModel = result.get(result.size() - 1);
|
||||
if (lastPostModel == null) return;
|
||||
endCursor = lastPostModel.getEndCursor();
|
||||
hasNextPage = lastPostModel.hasNextPage();
|
||||
@ -262,7 +264,81 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.profile_menu, menu);
|
||||
favMenuItem = menu.findItem(R.id.favourites);
|
||||
// favMenuItem = menu.findItem(R.id.favourites);
|
||||
blockMenuItem = menu.findItem(R.id.block);
|
||||
if (blockMenuItem != null) {
|
||||
blockMenuItem.setVisible(false);
|
||||
}
|
||||
restrictMenuItem = menu.findItem(R.id.restrict);
|
||||
if (restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (item.getItemId() == R.id.restrict) {
|
||||
if (!isLoggedIn) return false;
|
||||
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
||||
friendshipService.toggleRestrict(
|
||||
profileModel.getId(),
|
||||
!profileModel.getRestricted(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoRestrictRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoRestrictRootResponse result) {
|
||||
Log.d(TAG, action + " success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error while performing " + action, t);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (item.getItemId() == R.id.block) {
|
||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
if (!isLoggedIn) return false;
|
||||
if (profileModel.getBlocked()) {
|
||||
friendshipService.unblock(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Unblock success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error unblocking", t);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
friendshipService.block(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Block success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error blocking", t);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -295,6 +371,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
if (TextUtils.isEmpty(username) && !isLoggedIn) {
|
||||
binding.infoContainer.setVisibility(View.GONE);
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24);
|
||||
binding.privatePage2.setText(R.string.no_acc);
|
||||
final NestedCoordinatorLayout.LayoutParams layoutParams = (NestedCoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams();
|
||||
@ -303,6 +380,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
binding.privatePage.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
binding.swipeRefreshLayout.setEnabled(true);
|
||||
setupPosts();
|
||||
setupHighlights();
|
||||
setupCommonListeners();
|
||||
@ -337,17 +415,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
|
||||
private void fetchProfileDetails() {
|
||||
if (TextUtils.isEmpty(username)) return;
|
||||
new ProfileFetcher(username.substring(1), profileModel -> {
|
||||
if (getContext() == null) return;
|
||||
this.profileModel = profileModel;
|
||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final boolean isSelf = isLoggedIn
|
||||
&& profileModel != null
|
||||
&& userIdFromCookie != null
|
||||
&& userIdFromCookie.equals(profileModel.getId());
|
||||
if (favMenuItem != null) {
|
||||
favMenuItem.setVisible(isSelf);
|
||||
}
|
||||
// final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
// final boolean isSelf = isLoggedIn
|
||||
// && profileModel != null
|
||||
// && userIdFromCookie != null
|
||||
// && userIdFromCookie.equals(profileModel.getId());
|
||||
// if (favMenuItem != null) {
|
||||
// favMenuItem.setVisible(isSelf);
|
||||
// }
|
||||
setProfileDetails();
|
||||
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@ -363,12 +442,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||
final String profileId = profileModel.getId();
|
||||
if (settingsHelper.getString(Constants.STORY_VIEWER).equals(StoryViewerChoice.STORIESIG.getValue()) || isLoggedIn) {
|
||||
if (isLoggedIn) {
|
||||
new iStoryStatusFetcher(profileId,
|
||||
profileModel.getUsername(),
|
||||
false,
|
||||
false,
|
||||
!isLoggedIn && settingsHelper.getString(Constants.STORY_VIEWER).equals(StoryViewerChoice.STORIESIG.getValue()),
|
||||
false,
|
||||
result -> {
|
||||
storyModels = result;
|
||||
@ -377,7 +455,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
new HighlightsFetcher(profileId,
|
||||
!isLoggedIn && settingsHelper.getString(Constants.STORY_VIEWER).equals(StoryViewerChoice.STORIESIG.getValue()),
|
||||
result -> {
|
||||
if (result != null) {
|
||||
binding.highlightsList.setVisibility(View.VISIBLE);
|
||||
@ -385,7 +462,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
} else binding.highlightsList.setVisibility(View.GONE);
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} else if (settingsHelper.getString(Constants.STORY_VIEWER).equals(StoryViewerChoice.ALOINSTAGRAM.getValue())) {
|
||||
Log.d("austin_debug", "alo triggered");
|
||||
// Log.d(TAG, "alo triggered");
|
||||
aloService.getUserStory(profileId, profileModel.getUsername(), false, new ServiceCallback<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> result) {
|
||||
@ -402,74 +479,66 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
});
|
||||
}
|
||||
|
||||
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
if (isLoggedIn) {
|
||||
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
if (profileId.equals(myId)) {
|
||||
binding.btnTagged.setVisibility(View.VISIBLE);
|
||||
binding.btnSaved.setVisibility(View.VISIBLE);
|
||||
binding.btnLiked.setVisibility(View.VISIBLE);
|
||||
binding.btnDM.setVisibility(View.GONE);
|
||||
binding.btnSaved.setText(R.string.saved);
|
||||
ViewCompat.setBackgroundTintList(binding.btnSaved,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_orange_background)));
|
||||
} else {
|
||||
binding.btnTagged.setVisibility(View.GONE);
|
||||
binding.btnSaved.setVisibility(View.GONE);
|
||||
binding.btnLiked.setVisibility(View.GONE);
|
||||
binding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
||||
binding.btnFollow.setVisibility(View.VISIBLE);
|
||||
if (profileModel.getFollowing()) {
|
||||
binding.btnFollow.setText(R.string.unfollow);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollow,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_purple_background)));
|
||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else if (profileModel.getRequested()) {
|
||||
binding.btnFollow.setText(R.string.cancel);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollow,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_purple_background)));
|
||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else {
|
||||
binding.btnFollow.setText(R.string.follow);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollow,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_pink_background)));
|
||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
||||
}
|
||||
binding.btnRestrict.setVisibility(View.VISIBLE);
|
||||
if (profileModel.getRestricted()) {
|
||||
binding.btnRestrict.setText(R.string.unrestrict);
|
||||
ViewCompat.setBackgroundTintList(binding.btnRestrict,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_green_background)));
|
||||
} else {
|
||||
binding.btnRestrict.setText(R.string.restrict);
|
||||
ViewCompat.setBackgroundTintList(binding.btnRestrict,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_orange_background)));
|
||||
if (restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
if (profileModel.getRestricted()) {
|
||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||
} else {
|
||||
restrictMenuItem.setTitle(R.string.restrict);
|
||||
}
|
||||
}
|
||||
binding.btnBlock.setVisibility(View.VISIBLE);
|
||||
binding.btnTagged.setVisibility(View.VISIBLE);
|
||||
if (profileModel.getBlocked()) {
|
||||
binding.btnBlock.setText(R.string.unblock);
|
||||
ViewCompat.setBackgroundTintList(binding.btnBlock,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_green_background)));
|
||||
} else {
|
||||
binding.btnBlock.setText(R.string.block);
|
||||
ViewCompat.setBackgroundTintList(binding.btnBlock,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_red_background)));
|
||||
binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
||||
if (blockMenuItem != null) {
|
||||
blockMenuItem.setVisible(true);
|
||||
if (profileModel.getBlocked()) {
|
||||
blockMenuItem.setTitle(R.string.unblock);
|
||||
} else {
|
||||
blockMenuItem.setTitle(R.string.block);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Utils.dataBox.getFavorite(username) != null) {
|
||||
binding.btnFollow.setText(R.string.unfavorite_short);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollow,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_purple_background)));
|
||||
} else {
|
||||
binding.btnFollow.setText(R.string.favorite_short);
|
||||
ViewCompat.setBackgroundTintList(binding.btnFollow,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_pink_background)));
|
||||
}
|
||||
binding.btnFollow.setVisibility(View.VISIBLE);
|
||||
if (!profileModel.isReallyPrivate()) {
|
||||
binding.btnRestrict.setVisibility(View.VISIBLE);
|
||||
binding.btnRestrict.setText(R.string.tagged);
|
||||
ViewCompat.setBackgroundTintList(binding.btnRestrict,
|
||||
ColorStateList.valueOf(ContextCompat.getColor(context, R.color.btn_blue_background)));
|
||||
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
if (profileModel.getRestricted()) {
|
||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||
} else {
|
||||
restrictMenuItem.setTitle(R.string.restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!profileId.equals(myId)) {
|
||||
binding.favCb.setVisibility(View.VISIBLE);
|
||||
final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null;
|
||||
binding.favCb.setChecked(isFav);
|
||||
binding.favCb.setButtonDrawable(isFav ? R.drawable.ic_star_check_24 : R.drawable.ic_outline_star_plus_24);
|
||||
} else {
|
||||
binding.favCb.setVisibility(View.GONE);
|
||||
}
|
||||
binding.mainProfileImage.setImageURI(profileModel.getSdProfilePic());
|
||||
|
||||
final long followersCount = profileModel.getFollowersCount();
|
||||
@ -563,23 +632,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
// final boolean isSelf = isLoggedIn && profileModel != null && userIdFromCookie != null && userIdFromCookie
|
||||
// .equals(profileModel.getId());
|
||||
final String favorite = Utils.dataBox.getFavorite(username);
|
||||
binding.btnFollow.setOnClickListener(v -> {
|
||||
if (!isLoggedIn) {
|
||||
if (favorite != null && v == binding.btnFollow) {
|
||||
Utils.dataBox.delFavorite(new DataBox.FavoriteModel(
|
||||
username,
|
||||
Long.parseLong(favorite.split("/")[1]),
|
||||
username.replaceAll("^@", "")));
|
||||
} else if (v == binding.btnFollow) {
|
||||
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(
|
||||
username,
|
||||
System.currentTimeMillis(),
|
||||
username.replaceAll("^@", "")));
|
||||
}
|
||||
fetchProfileDetails();
|
||||
return;
|
||||
}
|
||||
if (profileModel.getFollowing() || profileModel.getRequested()) {
|
||||
friendshipService.unfollow(
|
||||
userIdFromCookie,
|
||||
@ -589,7 +642,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Unfollow success: " + result);
|
||||
fetchProfileDetails();
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -606,7 +659,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Follow success: " + result);
|
||||
fetchProfileDetails();
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -616,64 +669,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
});
|
||||
}
|
||||
});
|
||||
binding.btnRestrict.setOnClickListener(v -> {
|
||||
if (!isLoggedIn) return;
|
||||
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
||||
friendshipService.toggleRestrict(
|
||||
profileModel.getId(),
|
||||
!profileModel.getRestricted(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoRestrictRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoRestrictRootResponse result) {
|
||||
Log.d(TAG, action + " success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error while performing " + action, t);
|
||||
}
|
||||
});
|
||||
});
|
||||
binding.btnBlock.setOnClickListener(v -> {
|
||||
if (!isLoggedIn) return;
|
||||
if (profileModel.getBlocked()) {
|
||||
friendshipService.unblock(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Unblock success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error unblocking", t);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
friendshipService.block(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
Log.d(TAG, "Block success: " + result);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error blocking", t);
|
||||
}
|
||||
});
|
||||
});
|
||||
binding.btnSaved.setOnClickListener(v -> {
|
||||
final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(),
|
||||
profileModel.getId(),
|
||||
@ -692,6 +687,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
PostItemType.TAGGED);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
binding.btnDM.setOnClickListener(v -> {
|
||||
new CreateThreadAction(cookie, profileModel.getId(), threadId -> {
|
||||
final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToDMThreadFragment(threadId, profileModel.getUsername());
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
}).execute();
|
||||
});
|
||||
binding.mainProfileImage.setOnClickListener(v -> {
|
||||
if (storyModels == null || storyModels.length <= 0) {
|
||||
// show profile pic
|
||||
@ -721,6 +722,41 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
binding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
// do not do anything if state matches the db, as listener is set before profile details are set
|
||||
final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
|
||||
final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(finalUsername, FavoriteType.USER);
|
||||
if ((isChecked && favorite != null) || (!isChecked && favorite == null)) {
|
||||
return;
|
||||
}
|
||||
buttonView.setVisibility(View.GONE);
|
||||
binding.favProgress.setVisibility(View.VISIBLE);
|
||||
final String message;
|
||||
if (isChecked) {
|
||||
final DataBox.FavoriteModel model = new DataBox.FavoriteModel(
|
||||
-1,
|
||||
finalUsername,
|
||||
FavoriteType.USER,
|
||||
profileModel.getName(),
|
||||
profileModel.getSdProfilePic(),
|
||||
new Date()
|
||||
);
|
||||
Utils.dataBox.addOrUpdateFavorite(model);
|
||||
binding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
|
||||
message = getString(R.string.added_to_favs);
|
||||
} else {
|
||||
Utils.dataBox.deleteFavorite(finalUsername, FavoriteType.USER);
|
||||
message = getString(R.string.removed_from_favs);
|
||||
binding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
|
||||
}
|
||||
final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG);
|
||||
snackbar.setAction(R.string.ok, v -> snackbar.dismiss())
|
||||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
|
||||
.setAnchorView(fragmentActivity.getBottomNavView())
|
||||
.show();
|
||||
binding.favProgress.setVisibility(View.GONE);
|
||||
binding.favCb.setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
private void showProfilePicDialog() {
|
||||
|
@ -29,12 +29,13 @@ public class AboutFragment extends BasePreferencesFragment {
|
||||
final PreferenceCategory thirdPartyCategory = new PreferenceCategory(context);
|
||||
screen.addPreference(thirdPartyCategory);
|
||||
thirdPartyCategory.setTitle(R.string.about_category_3pt);
|
||||
thirdPartyCategory.setSummary(R.string.about_category_3pt_summary);
|
||||
//thirdPartyCategory.setSummary(R.string.about_category_3pt_summary);
|
||||
thirdPartyCategory.setIconSpaceReserved(false);
|
||||
// alphabetical order!!!
|
||||
thirdPartyCategory.addPreference(getExoPlayerPreference());
|
||||
thirdPartyCategory.addPreference(getFrescoPreference());
|
||||
thirdPartyCategory.addPreference(getJsoupPreference());
|
||||
thirdPartyCategory.addPreference(getMDIPreference());
|
||||
thirdPartyCategory.addPreference(getRetrofitPreference());
|
||||
|
||||
final PreferenceCategory licenseCategory = new PreferenceCategory(context);
|
||||
@ -157,6 +158,22 @@ public class AboutFragment extends BasePreferencesFragment {
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getMDIPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle("Material Design Icons");
|
||||
preference.setSummary("Copyright (C) 2014 Austin Andrews & Google LLC. Apache Version 2.0.");
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(p -> {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse("https://materialdesignicons.com/"));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getLicensePreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
|
@ -0,0 +1,107 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.dialogs.CreateBackupDialogFragment;
|
||||
import awais.instagrabber.dialogs.RestoreBackupDialogFragment;
|
||||
|
||||
public class BackupPreferencesFragment extends BasePreferencesFragment {
|
||||
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
screen.addPreference(getCreatePreference(context));
|
||||
screen.addPreference(getRestorePreference(context));
|
||||
}
|
||||
|
||||
private Preference getCreatePreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.create_backup);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
final CreateBackupDialogFragment fragment = new CreateBackupDialogFragment(result -> {
|
||||
final View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view,
|
||||
result ? R.string.dialog_export_success
|
||||
: R.string.dialog_export_failed,
|
||||
BaseTransientBottomBar.LENGTH_LONG)
|
||||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
|
||||
.setAction(R.string.ok, v -> {})
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(context,
|
||||
result ? R.string.dialog_export_success
|
||||
: R.string.dialog_export_failed,
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
});
|
||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.add(fragment, "createBackup")
|
||||
.commit();
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getRestorePreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.restore_backup);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
final RestoreBackupDialogFragment fragment = new RestoreBackupDialogFragment(result -> {
|
||||
final View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view,
|
||||
result ? R.string.dialog_import_success
|
||||
: R.string.dialog_import_failed,
|
||||
BaseTransientBottomBar.LENGTH_LONG)
|
||||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
|
||||
.setAction(R.string.ok, v -> {})
|
||||
.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
@Override
|
||||
public void onDismissed(final Snackbar transientBottomBar, final int event) {
|
||||
recreateActivity(result);
|
||||
}
|
||||
})
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
recreateActivity(result);
|
||||
});
|
||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.add(fragment, "restoreBackup")
|
||||
.commit();
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private void recreateActivity(final boolean result) {
|
||||
if (!result) return;
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (activity == null) return;
|
||||
activity.recreate();
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ public abstract class BasePreferencesFragment extends PreferenceFragmentCompat i
|
||||
@Override
|
||||
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
|
||||
final PreferenceManager preferenceManager = getPreferenceManager();
|
||||
preferenceManager.setSharedPreferencesName("settings");
|
||||
preferenceManager.setSharedPreferencesName(Constants.SHARED_PREFERENCES_NAME);
|
||||
preferenceManager.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
|
@ -20,7 +20,7 @@ import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
@ -55,11 +55,11 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
accountCategory.setTitle(R.string.account);
|
||||
accountCategory.setIconSpaceReserved(false);
|
||||
screen.addPreference(accountCategory);
|
||||
final ArrayList<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies();
|
||||
final List<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies();
|
||||
if (isLoggedIn) {
|
||||
accountCategory.setSummary(R.string.account_hint);
|
||||
accountCategory.addPreference(getAccountSwitcherPreference(cookie));
|
||||
accountCategory.addPreference(getPreference(R.string.logout, R.string.logout_summary, R.drawable.ic_logout, preference -> {
|
||||
accountCategory.addPreference(getPreference(R.string.logout, R.string.logout_summary, R.drawable.ic_logout_24, preference -> {
|
||||
if (getContext() == null) return false;
|
||||
CookieUtils.setupCookies("LOGOUT");
|
||||
shouldRecreate();
|
||||
@ -79,7 +79,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
}
|
||||
|
||||
if (allCookies != null && allCookies.size() > 0) {
|
||||
accountCategory.addPreference(getPreference(R.string.remove_all_acc, null, R.drawable.ic_delete, preference -> {
|
||||
accountCategory.addPreference(getPreference(R.string.remove_all_acc, null, R.drawable.ic_account_multiple_remove_24, preference -> {
|
||||
if (getContext() == null) return false;
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.logout)
|
||||
@ -96,37 +96,49 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
}));
|
||||
}
|
||||
|
||||
final PreferenceCategory generalCategory = new PreferenceCategory(context);
|
||||
generalCategory.setTitle(R.string.pref_category_general);
|
||||
generalCategory.setIconSpaceReserved(false);
|
||||
screen.addPreference(generalCategory);
|
||||
// final PreferenceCategory generalCategory = new PreferenceCategory(context);
|
||||
// generalCategory.setTitle(R.string.pref_category_general);
|
||||
// generalCategory.setIconSpaceReserved(false);
|
||||
// screen.addPreference(generalCategory);
|
||||
screen.addPreference(getDivider(context));
|
||||
if (isLoggedIn) {
|
||||
generalCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
|
||||
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToNotificationsViewer();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
generalCategory.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
|
||||
screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
|
||||
screen.addPreference(getDivider(context));
|
||||
screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
final Preference aboutPreference = getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference -> {
|
||||
screen.addPreference(getPreference(R.string.backup_and_restore, R.drawable.ic_settings_backup_restore_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToBackupPreferencesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference1 -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToAboutFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
});
|
||||
generalCategory.addPreference(aboutPreference);
|
||||
}));
|
||||
|
||||
screen.addPreference(getDivider(context));
|
||||
|
||||
final Preference versionPreference = getPreference(R.string.version,
|
||||
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", -1, preference -> {
|
||||
FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true);
|
||||
return true;
|
||||
});
|
||||
screen.addPreference(versionPreference);
|
||||
screen.addPreference(getPreference(R.string.version,
|
||||
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")",
|
||||
-1,
|
||||
preference -> {
|
||||
FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true);
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getDivider(context));
|
||||
|
||||
final Preference reminderPreference = getPreference(R.string.reminder, R.string.reminder_summary, R.drawable.ic_warning, null);
|
||||
|
@ -180,9 +180,9 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||
if (context == null) return null;
|
||||
return new SaveToCustomFolderPreference(context, (resultCallback) -> new DirectoryChooser()
|
||||
.setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
|
||||
.setInteractionListener(path -> {
|
||||
settingsHelper.putString(FOLDER_PATH, path);
|
||||
resultCallback.onResult(path);
|
||||
.setInteractionListener(file -> {
|
||||
settingsHelper.putString(FOLDER_PATH, file.getAbsolutePath());
|
||||
resultCallback.onResult(file.getAbsolutePath());
|
||||
})
|
||||
.show(getParentFragmentManager(), null));
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package awais.instagrabber.models.enums;
|
||||
|
||||
public enum FavoriteType {
|
||||
USER,
|
||||
HASHTAG,
|
||||
LOCATION
|
||||
}
|
@ -4,9 +4,8 @@ import java.io.Serializable;
|
||||
|
||||
public enum StoryViewerChoice implements Serializable {
|
||||
NONE(0),
|
||||
STORIESIG(1),
|
||||
ALOINSTAGRAM(2),
|
||||
INSTADP(3);
|
||||
ALOINSTAGRAM(1),
|
||||
INSTADP(2);
|
||||
|
||||
private int value;
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package awais.instagrabber.repositories;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
|
||||
public interface TagsRepository {
|
||||
|
||||
@POST("/web/tags/follow/{tag}/")
|
||||
Call<String> follow(@Header("User-Agent") String userAgent,
|
||||
@Header("x-csrftoken") String csrfToken,
|
||||
@Path("tag") String tag);
|
||||
|
||||
@POST("/web/tags/unfollow/{tag}/")
|
||||
Call<String> unfollow(@Header("User-Agent") String userAgent,
|
||||
@Header("x-csrftoken") String csrfToken,
|
||||
@Path("tag") String tag);
|
||||
}
|
@ -9,6 +9,7 @@ import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
@ -115,18 +116,24 @@ public class ActivityCheckerService extends Service {
|
||||
}
|
||||
|
||||
private void showNotification(final String notificationString) {
|
||||
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
||||
.setAction(Constants.ACTION_SHOW_ACTIVITY)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setSmallIcon(R.drawable.ic_notif)
|
||||
.setAutoCancel(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentTitle(getString(R.string.action_notif))
|
||||
.setContentText(notificationString)
|
||||
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.setContentIntent(getPendingIntent())
|
||||
.build();
|
||||
notificationManager.notify(Constants.ACTIVITY_NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PendingIntent getPendingIntent() {
|
||||
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
||||
.setAction(Constants.ACTION_SHOW_ACTIVITY)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
return PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
@ -82,4 +82,6 @@ public final class Constants {
|
||||
public static final String ACTION_SHOW_ACTIVITY = "show_activity";
|
||||
public static final String PREF_DARK_THEME = "dark_theme";
|
||||
public static final String PREF_LIGHT_THEME = "light_theme";
|
||||
public static final String DEFAULT_HASH_TAG_PIC = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
|
||||
public static final String SHARED_PREFERENCES_NAME = "settings";
|
||||
}
|
@ -6,15 +6,18 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.ObjectsCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
@ -24,12 +27,9 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
|
||||
private static DataBox sInstance;
|
||||
|
||||
private final static int VERSION = 2;
|
||||
private final static int VERSION = 3;
|
||||
private final static String TABLE_COOKIES = "cookies";
|
||||
private final static String TABLE_FAVORITES = "favorites";
|
||||
private final static String KEY_DATE_ADDED = "date_added";
|
||||
private final static String KEY_QUERY_TEXT = "query_text";
|
||||
private final static String KEY_QUERY_DISPLAY = "query_display";
|
||||
|
||||
private final static String KEY_ID = "id";
|
||||
private final static String KEY_USERNAME = Constants.EXTRAS_USERNAME;
|
||||
@ -38,7 +38,12 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
private final static String KEY_FULL_NAME = "full_name";
|
||||
private final static String KEY_PROFILE_PIC = "profile_pic";
|
||||
|
||||
private final Context c;
|
||||
private final static String FAV_COL_ID = "id";
|
||||
private final static String FAV_COL_QUERY = "query_text";
|
||||
private final static String FAV_COL_TYPE = "type";
|
||||
private final static String FAV_COL_DISPLAY_NAME = "display_name";
|
||||
private final static String FAV_COL_PIC_URL = "pic_url";
|
||||
private final static String FAV_COL_DATE_ADDED = "date_added";
|
||||
|
||||
public static synchronized DataBox getInstance(final Context context) {
|
||||
if (sInstance == null) sInstance = new DataBox(context.getApplicationContext());
|
||||
@ -47,7 +52,6 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
|
||||
private DataBox(@Nullable final Context context) {
|
||||
super(context, "cookiebox.db", null, VERSION);
|
||||
c = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,42 +64,120 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
+ KEY_COOKIE + " TEXT,"
|
||||
+ KEY_FULL_NAME + " TEXT,"
|
||||
+ KEY_PROFILE_PIC + " TEXT)");
|
||||
db.execSQL("CREATE TABLE favorites (id INTEGER PRIMARY KEY, query_text TEXT, date_added INTEGER, query_display TEXT)");
|
||||
// db.execSQL("CREATE TABLE favorites (id INTEGER PRIMARY KEY, query_text TEXT, date_added INTEGER, query_display TEXT)");
|
||||
db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " ("
|
||||
+ FAV_COL_ID + " INTEGER PRIMARY KEY,"
|
||||
+ FAV_COL_QUERY + " TEXT,"
|
||||
+ FAV_COL_TYPE + " TEXT,"
|
||||
+ FAV_COL_DISPLAY_NAME + " TEXT,"
|
||||
+ FAV_COL_PIC_URL + " TEXT,"
|
||||
+ FAV_COL_DATE_ADDED + " INTEGER)");
|
||||
Log.i(TAG, "Tables created!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
||||
Log.i(TAG, String.format("Updating DB from v%d to v%d", oldVersion, newVersion));
|
||||
if (oldVersion == 1) {
|
||||
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT");
|
||||
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT");
|
||||
// switch without break, so that all migrations from a previous version to new are run
|
||||
switch (oldVersion) {
|
||||
case 1:
|
||||
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_FULL_NAME + " TEXT");
|
||||
db.execSQL("ALTER TABLE " + TABLE_COOKIES + " ADD " + KEY_PROFILE_PIC + " TEXT");
|
||||
case 2:
|
||||
final List<FavoriteModel> oldFavorites = backupOldFavorites(db);
|
||||
// recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions)
|
||||
db.execSQL("DROP TABLE " + TABLE_FAVORITES);
|
||||
db.execSQL("CREATE TABLE " + TABLE_FAVORITES + " ("
|
||||
+ FAV_COL_ID + " INTEGER PRIMARY KEY,"
|
||||
+ FAV_COL_QUERY + " TEXT,"
|
||||
+ FAV_COL_TYPE + " TEXT,"
|
||||
+ FAV_COL_DISPLAY_NAME + " TEXT,"
|
||||
+ FAV_COL_PIC_URL + " TEXT,"
|
||||
+ FAV_COL_DATE_ADDED + " INTEGER)");
|
||||
// add the old favorites back
|
||||
for (final FavoriteModel oldFavorite : oldFavorites) {
|
||||
addOrUpdateFavorite(db, oldFavorite);
|
||||
}
|
||||
}
|
||||
Log.i(TAG, String.format("DB update from v%d to v%d completed!", oldVersion, newVersion));
|
||||
}
|
||||
|
||||
public final void addFavorite(@NonNull final FavoriteModel favoriteModel) {
|
||||
final String query = favoriteModel.getQuery();
|
||||
final String display = favoriteModel.getDisplayName();
|
||||
@NonNull
|
||||
private List<FavoriteModel> backupOldFavorites(@NonNull final SQLiteDatabase db) {
|
||||
// check if old favorites table had the column query_display
|
||||
final boolean queryDisplayExists = checkColumnExists(db, TABLE_FAVORITES, "query_display");
|
||||
Log.d(TAG, "backupOldFavorites: queryDisplayExists: " + queryDisplayExists);
|
||||
final List<FavoriteModel> oldModels = new ArrayList<>();
|
||||
final String sql = "SELECT "
|
||||
+ "query_text,"
|
||||
+ "date_added"
|
||||
+ (queryDisplayExists ? ",query_display" : "")
|
||||
+ " FROM " + TABLE_FAVORITES;
|
||||
try (final Cursor cursor = db.rawQuery(sql, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
try {
|
||||
final String queryText = cursor.getString(cursor.getColumnIndex("query_text"));
|
||||
final Pair<FavoriteType, String> favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText);
|
||||
if (favoriteTypeQueryPair == null) continue;
|
||||
final FavoriteType type = favoriteTypeQueryPair.first;
|
||||
final String query = favoriteTypeQueryPair.second;
|
||||
oldModels.add(new FavoriteModel(
|
||||
-1,
|
||||
query,
|
||||
type,
|
||||
queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display"))
|
||||
: null,
|
||||
null,
|
||||
new Date(cursor.getLong(cursor.getColumnIndex("date_added")))
|
||||
));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onUpgrade", e);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onUpgrade", e);
|
||||
}
|
||||
Log.d(TAG, "backupOldFavorites: oldModels:" + oldModels);
|
||||
return oldModels;
|
||||
}
|
||||
|
||||
public boolean checkColumnExists(@NonNull final SQLiteDatabase db,
|
||||
@NonNull final String tableName,
|
||||
@NonNull final String columnName) {
|
||||
boolean exists = false;
|
||||
try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + tableName + ")", null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
final String currentColumn = cursor.getString(cursor.getColumnIndex("name"));
|
||||
if (currentColumn.equals(columnName)) {
|
||||
exists = true;
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "checkColumnExists", ex);
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
public final void addOrUpdateFavorite(@NonNull final FavoriteModel model) {
|
||||
final String query = model.getQuery();
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
try (final SQLiteDatabase db = getWritableDatabase()) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(KEY_DATE_ADDED, favoriteModel.getDate());
|
||||
values.put(KEY_QUERY_TEXT, query);
|
||||
values.put(KEY_QUERY_DISPLAY, display);
|
||||
|
||||
final int rows = db.update(TABLE_FAVORITES, values, KEY_QUERY_TEXT + "=?", new String[]{query});
|
||||
|
||||
if (rows != 1)
|
||||
db.insertOrThrow(TABLE_FAVORITES, null, values);
|
||||
|
||||
addOrUpdateFavorite(db, model);
|
||||
db.setTransactionSuccessful();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "addFavorite");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (logCollector != null) {
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "addOrUpdateFavorite");
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error adding/updating favorite", e);
|
||||
}
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
@ -103,23 +185,46 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized void delFavorite(@NonNull final FavoriteModel favoriteModel) {
|
||||
final String query = favoriteModel.getQuery();
|
||||
private void addOrUpdateFavorite(@NonNull final SQLiteDatabase db, @NonNull final FavoriteModel model) {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(FAV_COL_QUERY, model.getQuery());
|
||||
values.put(FAV_COL_TYPE, model.getType().toString());
|
||||
values.put(FAV_COL_DISPLAY_NAME, model.getDisplayName());
|
||||
values.put(FAV_COL_PIC_URL, model.getPicUrl());
|
||||
values.put(FAV_COL_DATE_ADDED, model.getDateAdded().getTime());
|
||||
int rows;
|
||||
if (model.getId() >= 1) {
|
||||
rows = db.update(TABLE_FAVORITES, values, FAV_COL_ID + "=?", new String[]{String.valueOf(model.getId())});
|
||||
} else {
|
||||
rows = db.update(TABLE_FAVORITES,
|
||||
values,
|
||||
FAV_COL_QUERY + "=?" +
|
||||
" AND " + FAV_COL_TYPE + "=?",
|
||||
new String[]{model.getQuery(), model.getType().toString()});
|
||||
}
|
||||
if (rows != 1) {
|
||||
db.insertOrThrow(TABLE_FAVORITES, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized void deleteFavorite(@NonNull final String query, @NonNull final FavoriteType type) {
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
try (final SQLiteDatabase db = getWritableDatabase()) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
final int rowsDeleted = db.delete(TABLE_FAVORITES, "query_text=? AND date_added=?",
|
||||
new String[]{query, Long.toString(favoriteModel.getDate())});
|
||||
final int rowsDeleted = db.delete(TABLE_FAVORITES,
|
||||
FAV_COL_QUERY + "=?" +
|
||||
" AND " + FAV_COL_TYPE + "=?",
|
||||
new String[]{query, type.toString()});
|
||||
|
||||
final int rowsDeletedTwo = db.delete(TABLE_FAVORITES, "query_text=? AND date_added=?",
|
||||
new String[]{query.replaceAll("@", ""), Long.toString(favoriteModel.getDate())});
|
||||
|
||||
if (rowsDeleted > 0 || rowsDeletedTwo > 0) db.setTransactionSuccessful();
|
||||
if (rowsDeleted > 0) db.setTransactionSuccessful();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "delFavorite");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "Error", e);
|
||||
if (logCollector != null) {
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "deleteFavorite");
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error", e);
|
||||
}
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
@ -127,76 +232,87 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final ArrayList<FavoriteModel> getAllFavorites() {
|
||||
ArrayList<FavoriteModel> favorites = null;
|
||||
FavoriteModel tempFav;
|
||||
@NonNull
|
||||
public final List<FavoriteModel> getAllFavorites() {
|
||||
final List<FavoriteModel> favorites = new ArrayList<>();
|
||||
final SQLiteDatabase db = getWritableDatabase();
|
||||
|
||||
try (final Cursor cursor = db.rawQuery("SELECT query_text, date_added, query_display FROM favorites ORDER BY date_added DESC", null)) {
|
||||
try (final Cursor cursor = db.rawQuery("SELECT "
|
||||
+ FAV_COL_ID + ","
|
||||
+ FAV_COL_QUERY + ","
|
||||
+ FAV_COL_TYPE + ","
|
||||
+ FAV_COL_DISPLAY_NAME + ","
|
||||
+ FAV_COL_PIC_URL + ","
|
||||
+ FAV_COL_DATE_ADDED
|
||||
+ " FROM " + TABLE_FAVORITES,
|
||||
null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
db.beginTransaction();
|
||||
favorites = new ArrayList<>();
|
||||
FavoriteModel tempFav;
|
||||
do {
|
||||
FavoriteType type = null;
|
||||
try {
|
||||
type = FavoriteType.valueOf(cursor.getString(cursor.getColumnIndex(FAV_COL_TYPE)));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
tempFav = new FavoriteModel(
|
||||
(cursor.getString(0).charAt(0) == '@' || cursor.getString(0).charAt(0) == '#' || cursor.getString(0).contains("/"))
|
||||
? cursor.getString(0)
|
||||
: "@" + cursor.getString(0), // query text
|
||||
cursor.getLong(1), // date added
|
||||
cursor.getString(2) == null ? (cursor.getString(0).charAt(0) == '@' || cursor.getString(0).charAt(0) == '#' || cursor
|
||||
.getString(0).contains("/"))
|
||||
? cursor.getString(0)
|
||||
: "@" + cursor.getString(0) : cursor.getString(2) // display
|
||||
cursor.getInt(cursor.getColumnIndex(FAV_COL_ID)),
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_QUERY)),
|
||||
type,
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_DISPLAY_NAME)),
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_PIC_URL)),
|
||||
new Date(cursor.getLong(cursor.getColumnIndex(FAV_COL_DATE_ADDED)))
|
||||
);
|
||||
if (cursor.getString(2) == null) {
|
||||
try {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(KEY_DATE_ADDED, tempFav.getDate());
|
||||
values.put(KEY_QUERY_TEXT, tempFav.getQuery());
|
||||
values.put(KEY_QUERY_DISPLAY, tempFav.getDisplayName());
|
||||
|
||||
final int rows = db.update(TABLE_FAVORITES, values, KEY_QUERY_TEXT + "=?", new String[]{tempFav.getQuery()});
|
||||
|
||||
if (rows != 1)
|
||||
db.insertOrThrow(TABLE_FAVORITES, null, values);
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "delFavorite");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
}
|
||||
favorites.add(tempFav);
|
||||
} while (cursor.moveToNext());
|
||||
db.endTransaction();
|
||||
}
|
||||
} catch (final Exception x) {
|
||||
Log.e("austin_debug", "", x);
|
||||
try {
|
||||
db.execSQL("ALTER TABLE favorites ADD query_display TEXT");
|
||||
Toast.makeText(c, "DB has migrated, launch quick access again.", Toast.LENGTH_SHORT).show();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.DATA_BOX_FAVORITES, "migrate");
|
||||
Toast.makeText(c, "DB migration failed, contact maintainer.", Toast.LENGTH_SHORT).show();
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
|
||||
return favorites;
|
||||
}
|
||||
|
||||
public final String getFavorite(@NonNull final String query) {
|
||||
@Nullable
|
||||
public final FavoriteModel getFavorite(@NonNull final String query, @NonNull final FavoriteType type) {
|
||||
try (final SQLiteDatabase db = getReadableDatabase();
|
||||
final Cursor cursor = db.rawQuery("SELECT query_text, date_added FROM favorites WHERE "
|
||||
+ KEY_QUERY_TEXT + "='" + query + "' ORDER BY date_added DESC", null)) {
|
||||
final Cursor cursor = db.rawQuery("SELECT "
|
||||
+ FAV_COL_ID + ","
|
||||
+ FAV_COL_QUERY + ","
|
||||
+ FAV_COL_TYPE + ","
|
||||
+ FAV_COL_DISPLAY_NAME + ","
|
||||
+ FAV_COL_PIC_URL + ","
|
||||
+ FAV_COL_DATE_ADDED
|
||||
+ " FROM " + TABLE_FAVORITES
|
||||
+ " WHERE " + FAV_COL_QUERY + "='" + query + "'"
|
||||
+ " AND " + FAV_COL_TYPE + "='" + type.toString() + "'",
|
||||
null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getString(0) + "/" + String.valueOf(cursor.getLong(1));
|
||||
FavoriteType favoriteType = null;
|
||||
try {
|
||||
favoriteType = FavoriteType.valueOf(cursor.getString(cursor.getColumnIndex(FAV_COL_TYPE)));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
return new FavoriteModel(
|
||||
cursor.getInt(cursor.getColumnIndex(FAV_COL_ID)),
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_QUERY)),
|
||||
favoriteType,
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_DISPLAY_NAME)),
|
||||
cursor.getString(cursor.getColumnIndex(FAV_COL_PIC_URL)),
|
||||
new Date(cursor.getLong(cursor.getColumnIndex(FAV_COL_DATE_ADDED)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void addOrUpdateUser(@NonNull final DataBox.CookieModel cookieModel) {
|
||||
addOrUpdateUser(
|
||||
cookieModel.getUid(),
|
||||
cookieModel.getUsername(),
|
||||
cookieModel.getCookie(),
|
||||
cookieModel.getFullName(),
|
||||
cookieModel.getProfilePic()
|
||||
);
|
||||
}
|
||||
|
||||
public final void addOrUpdateUser(final String uid,
|
||||
final String username,
|
||||
final String cookie,
|
||||
@ -261,15 +377,6 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public final int getCookieCount() {
|
||||
int cookieCount = 0;
|
||||
try (final SQLiteDatabase db = getReadableDatabase();
|
||||
final Cursor cursor = db.rawQuery("SELECT * FROM cookies", null)) {
|
||||
if (cursor != null) cookieCount = cursor.getCount();
|
||||
}
|
||||
return cookieCount;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final CookieModel getCookie(final String uid) {
|
||||
CookieModel cookie = null;
|
||||
@ -297,10 +404,9 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
return cookie;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final ArrayList<CookieModel> getAllCookies() {
|
||||
ArrayList<CookieModel> cookies = null;
|
||||
|
||||
@NonNull
|
||||
public final List<CookieModel> getAllCookies() {
|
||||
final List<CookieModel> cookies = new ArrayList<>();
|
||||
try (final SQLiteDatabase db = getReadableDatabase();
|
||||
final Cursor cursor = db.rawQuery(
|
||||
"SELECT "
|
||||
@ -312,7 +418,6 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
+ " FROM " + TABLE_COOKIES, null)
|
||||
) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
cookies = new ArrayList<>();
|
||||
do {
|
||||
cookies.add(new CookieModel(
|
||||
cursor.getString(cursor.getColumnIndex(KEY_UID)),
|
||||
@ -324,7 +429,6 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
@ -376,6 +480,12 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return !TextUtils.isEmpty(uid)
|
||||
&& !TextUtils.isEmpty(username)
|
||||
&& !TextUtils.isEmpty(cookie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
@ -394,36 +504,92 @@ public final class DataBox extends SQLiteOpenHelper {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return username;
|
||||
return "CookieModel{" +
|
||||
"uid='" + uid + '\'' +
|
||||
", username='" + username + '\'' +
|
||||
", cookie='" + cookie + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", profilePic='" + profilePic + '\'' +
|
||||
", selected=" + selected +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class FavoriteModel {
|
||||
private final String query, displayName;
|
||||
private final long date;
|
||||
private final int id;
|
||||
private final String query;
|
||||
private final FavoriteType type;
|
||||
private final String displayName;
|
||||
private final String picUrl;
|
||||
private final Date dateAdded;
|
||||
|
||||
public FavoriteModel(final String query, final long date, final String displayName) {
|
||||
public FavoriteModel(final int id,
|
||||
final String query,
|
||||
final FavoriteType type,
|
||||
final String displayName,
|
||||
final String picUrl,
|
||||
final Date dateAdded) {
|
||||
this.id = id;
|
||||
this.query = query;
|
||||
this.date = date;
|
||||
this.type = type;
|
||||
this.displayName = displayName;
|
||||
this.picUrl = picUrl;
|
||||
this.dateAdded = dateAdded;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public FavoriteType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public long getDate() {
|
||||
return date;
|
||||
public String getPicUrl() {
|
||||
return picUrl;
|
||||
}
|
||||
|
||||
public Date getDateAdded() {
|
||||
return dateAdded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final FavoriteModel that = (FavoriteModel) o;
|
||||
return id == that.id &&
|
||||
ObjectsCompat.equals(query, that.query) &&
|
||||
type == that.type &&
|
||||
ObjectsCompat.equals(displayName, that.displayName) &&
|
||||
ObjectsCompat.equals(picUrl, that.picUrl) &&
|
||||
ObjectsCompat.equals(dateAdded, that.dateAdded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectsCompat.hash(id, query, type, displayName, picUrl, dateAdded);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return query;
|
||||
return "FavoriteModel{" +
|
||||
"id=" + id +
|
||||
", query='" + query + '\'' +
|
||||
", type=" + type +
|
||||
", displayName='" + displayName + '\'' +
|
||||
", picUrl='" + picUrl + '\'' +
|
||||
", dateAdded=" + dateAdded +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@ -3,20 +3,26 @@ package awais.instagrabber.utils;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.FileObserver;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -24,22 +30,27 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.SimpleAdapter;
|
||||
import awais.instagrabber.adapters.DirectoryFilesAdapter;
|
||||
import awais.instagrabber.databinding.LayoutDirectoryChooserBinding;
|
||||
import awais.instagrabber.viewmodels.FileListViewModel;
|
||||
|
||||
public final class DirectoryChooser extends DialogFragment {
|
||||
private static final String TAG = "DirectoryChooser";
|
||||
|
||||
public static final String KEY_CURRENT_DIRECTORY = "CURRENT_DIRECTORY";
|
||||
private static final File sdcardPathFile = Environment.getExternalStorageDirectory();
|
||||
private static final String sdcardPath = sdcardPathFile.getPath();
|
||||
private final List<String> fileNames = new ArrayList<>();
|
||||
|
||||
private Context context;
|
||||
private View btnConfirm, btnNavUp, btnCancel;
|
||||
private LayoutDirectoryChooserBinding binding;
|
||||
private FileObserver fileObserver;
|
||||
private File selectedDir;
|
||||
private String initialDirectory;
|
||||
private TextView tvSelectedFolder;
|
||||
private FileObserver fileObserver;
|
||||
private SimpleAdapter<String> listDirectoriesAdapter;
|
||||
private OnFragmentInteractionListener interactionListener;
|
||||
private boolean showZaAiConfigFiles = false;
|
||||
private boolean showBackupFiles = false;
|
||||
private View.OnClickListener navigationOnClickListener;
|
||||
private FileListViewModel fileListViewModel;
|
||||
private OnCancelListener onCancelListener;
|
||||
|
||||
public DirectoryChooser() {
|
||||
super();
|
||||
@ -51,8 +62,8 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DirectoryChooser setShowZaAiConfigFiles(final boolean showZaAiConfigFiles) {
|
||||
this.showZaAiConfigFiles = showZaAiConfigFiles;
|
||||
public DirectoryChooser setShowBackupFiles(final boolean showBackupFiles) {
|
||||
this.showBackupFiles = showBackupFiles;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -74,60 +85,71 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
binding = LayoutDirectoryChooserBinding.inflate(inflater, container, false);
|
||||
init(container);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void init(final ViewGroup container) {
|
||||
Context context = this.context;
|
||||
if (context == null) context = getContext();
|
||||
if (context == null) context = getActivity();
|
||||
if (context == null) context = inflater.getContext();
|
||||
|
||||
final View view = inflater.inflate(R.layout.layout_directory_chooser, container, false);
|
||||
|
||||
btnNavUp = view.findViewById(R.id.btnNavUp);
|
||||
btnCancel = view.findViewById(R.id.btnCancel);
|
||||
btnConfirm = view.findViewById(R.id.btnConfirm);
|
||||
tvSelectedFolder = view.findViewById(R.id.txtvSelectedFolder);
|
||||
|
||||
if (context == null) return;
|
||||
if (ContextCompat.checkSelfPermission(context, DownloadUtils.PERMS[0]) != PackageManager.PERMISSION_GRANTED) {
|
||||
final String text = "Storage permissions denied!";
|
||||
if (container == null) {
|
||||
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Snackbar.make(container, text, BaseTransientBottomBar.LENGTH_LONG).show();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
final View.OnClickListener clickListener = v -> {
|
||||
final Object tag;
|
||||
if (v instanceof TextView && (tag = v.getTag()) instanceof CharSequence) {
|
||||
final File file = new File(selectedDir, tag.toString());
|
||||
if (file.isDirectory())
|
||||
changeDirectory(file);
|
||||
else if (showZaAiConfigFiles && file.isFile()) {
|
||||
if (interactionListener != null && file.canRead())
|
||||
interactionListener.onSelectDirectory(file.getAbsolutePath());
|
||||
dismiss();
|
||||
}
|
||||
|
||||
} else if (v == btnNavUp) {
|
||||
final File parent;
|
||||
if (selectedDir != null && (parent = selectedDir.getParentFile()) != null)
|
||||
changeDirectory(parent);
|
||||
|
||||
} else if (v == btnConfirm) {
|
||||
if (v == binding.btnConfirm) {
|
||||
if (interactionListener != null && isValidFile(selectedDir))
|
||||
interactionListener.onSelectDirectory(selectedDir.getAbsolutePath());
|
||||
interactionListener.onSelectDirectory(selectedDir);
|
||||
dismiss();
|
||||
} else if (v == btnCancel) {
|
||||
} else if (v == binding.btnCancel) {
|
||||
if (onCancelListener != null) {
|
||||
onCancelListener.onCancel();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
btnNavUp.setOnClickListener(clickListener);
|
||||
btnCancel.setOnClickListener(clickListener);
|
||||
btnConfirm.setOnClickListener(clickListener);
|
||||
|
||||
listDirectoriesAdapter = new SimpleAdapter<>(context, fileNames, clickListener);
|
||||
|
||||
final RecyclerView directoriesList = view.findViewById(R.id.directoryList);
|
||||
directoriesList.setLayoutManager(new LinearLayoutManager(context));
|
||||
directoriesList.setAdapter(listDirectoriesAdapter);
|
||||
|
||||
navigationOnClickListener = v -> {
|
||||
final File parent;
|
||||
if (selectedDir != null && (parent = selectedDir.getParentFile()) != null) {
|
||||
changeDirectory(parent);
|
||||
}
|
||||
};
|
||||
binding.toolbar.setNavigationOnClickListener(navigationOnClickListener);
|
||||
binding.toolbar.setSubtitle(showBackupFiles ? R.string.select_backup_file : R.string.select_folder);
|
||||
binding.btnCancel.setOnClickListener(clickListener);
|
||||
// no need to show confirm for file picker
|
||||
binding.btnConfirm.setVisibility(showBackupFiles ? View.GONE : View.VISIBLE);
|
||||
if (!showBackupFiles) {
|
||||
binding.btnConfirm.setOnClickListener(clickListener);
|
||||
}
|
||||
fileListViewModel = new ViewModelProvider(this).get(FileListViewModel.class);
|
||||
final DirectoryFilesAdapter listDirectoriesAdapter = new DirectoryFilesAdapter(file -> {
|
||||
if (file.isDirectory()) {
|
||||
changeDirectory(file);
|
||||
return;
|
||||
}
|
||||
if (showBackupFiles && file.isFile()) {
|
||||
if (interactionListener != null && file.canRead()) {
|
||||
interactionListener.onSelectDirectory(file);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
fileListViewModel.getList().observe(this, listDirectoriesAdapter::submitList);
|
||||
binding.directoryList.setLayoutManager(new LinearLayoutManager(context));
|
||||
binding.directoryList.setAdapter(listDirectoriesAdapter);
|
||||
final File initDir = new File(initialDirectory);
|
||||
final File initialDir = !TextUtils.isEmpty(initialDirectory) && isValidFile(initDir) ? initDir : Environment.getExternalStorageDirectory();
|
||||
|
||||
changeDirectory(initialDir);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -153,10 +175,14 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
public void onBackPressed() {
|
||||
if (selectedDir != null) {
|
||||
final String absolutePath = selectedDir.getAbsolutePath();
|
||||
if (absolutePath.equals(sdcardPath) || absolutePath.equals(sdcardPathFile.getAbsolutePath()))
|
||||
if (absolutePath.equals(sdcardPath) || absolutePath.equals(sdcardPathFile.getAbsolutePath())) {
|
||||
if (onCancelListener != null) {
|
||||
onCancelListener.onCancel();
|
||||
}
|
||||
dismiss();
|
||||
else
|
||||
} else {
|
||||
changeDirectory(selectedDir.getParentFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -189,21 +215,28 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
private void changeDirectory(final File dir) {
|
||||
if (dir != null && dir.isDirectory()) {
|
||||
final String path = dir.getAbsolutePath();
|
||||
|
||||
binding.toolbar.setTitle(path);
|
||||
final File[] contents = dir.listFiles();
|
||||
if (contents != null) {
|
||||
fileNames.clear();
|
||||
|
||||
final List<File> fileNames = new ArrayList<>();
|
||||
for (final File f : contents) {
|
||||
final String name = f.getName();
|
||||
if (f.isDirectory() || showZaAiConfigFiles && f.isFile() && name.toLowerCase().endsWith(".zaai"))
|
||||
fileNames.add(name);
|
||||
final String nameLowerCase = name.toLowerCase();
|
||||
final boolean isBackupFile = nameLowerCase.endsWith(".zaai") || nameLowerCase.endsWith(".backup");
|
||||
if (f.isDirectory() || (showBackupFiles && f.isFile() && isBackupFile))
|
||||
fileNames.add(f);
|
||||
}
|
||||
|
||||
Collections.sort(fileNames);
|
||||
Collections.sort(fileNames, (o1, o2) -> {
|
||||
if ((o1.isDirectory() && o2.isDirectory())
|
||||
|| (o1.isFile() && o2.isFile())) {
|
||||
return o1.getName().compareToIgnoreCase(o2.getName());
|
||||
}
|
||||
if (o1.isDirectory()) return -1;
|
||||
if (o2.isDirectory()) return 1;
|
||||
return 0;
|
||||
});
|
||||
fileListViewModel.getList().postValue(fileNames);
|
||||
selectedDir = dir;
|
||||
tvSelectedFolder.setText(path);
|
||||
listDirectoriesAdapter.notifyDataSetChanged();
|
||||
fileObserver = new FileObserver(path, FileObserver.CREATE | FileObserver.DELETE | FileObserver.MOVED_FROM | FileObserver.MOVED_TO) {
|
||||
private final Runnable currentDirRefresher = () -> changeDirectory(selectedDir);
|
||||
|
||||
@ -222,15 +255,15 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
if (selectedDir != null) {
|
||||
final String path = selectedDir.getAbsolutePath();
|
||||
toggleUpButton(!path.equals(sdcardPathFile.getAbsolutePath()) && selectedDir != sdcardPathFile);
|
||||
btnConfirm.setEnabled(isValidFile(selectedDir));
|
||||
binding.btnConfirm.setEnabled(isValidFile(selectedDir));
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleUpButton(final boolean enable) {
|
||||
if (btnNavUp != null) {
|
||||
btnNavUp.setEnabled(enable);
|
||||
btnNavUp.setAlpha(enable ? 1f : 0.617f);
|
||||
}
|
||||
binding.toolbar.setNavigationOnClickListener(enable ? navigationOnClickListener : null);
|
||||
final Drawable navigationIcon = binding.toolbar.getNavigationIcon();
|
||||
if (navigationIcon == null) return;
|
||||
navigationIcon.setAlpha(enable ? 255 : (int) (255 * 0.617));
|
||||
}
|
||||
|
||||
private boolean isValidFile(final File file) {
|
||||
@ -242,7 +275,17 @@ public final class DirectoryChooser extends DialogFragment {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setOnCancelListener(final OnCancelListener onCancelListener) {
|
||||
if (onCancelListener != null) {
|
||||
this.onCancelListener = onCancelListener;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnCancelListener {
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
public interface OnFragmentInteractionListener {
|
||||
void onSelectDirectory(final String path);
|
||||
void onSelectDirectory(final File file);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DirectoryUtils {
|
||||
private static final Pattern DIR_SEPORATOR = Pattern.compile("/");
|
||||
|
||||
/**
|
||||
* From: https://stackoverflow.com/a/18871043/1436766
|
||||
*
|
||||
* Returns all available SD-Cards in the system (include emulated)
|
||||
* <p>
|
||||
* Warning: Hack! Based on Android source code of version 4.3 (API 18)
|
||||
* Because there is no standard way to get it.
|
||||
* TODO: Test on future Android versions 4.4+
|
||||
*
|
||||
* @return paths to all available SD-Cards in the system (include emulated)
|
||||
*/
|
||||
public static Set<String> getStorageDirectories() {
|
||||
// Final set of paths
|
||||
final Set<String> rv = new HashSet<>();
|
||||
// Primary physical SD-CARD (not emulated)
|
||||
final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
|
||||
// All Secondary SD-CARDs (all exclude primary) separated by ":"
|
||||
final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
|
||||
// Primary emulated SD-CARD
|
||||
final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
|
||||
if (TextUtils.isEmpty(rawEmulatedStorageTarget)) {
|
||||
// Device has physical external storage; use plain paths.
|
||||
if (TextUtils.isEmpty(rawExternalStorage)) {
|
||||
// EXTERNAL_STORAGE undefined; falling back to default.
|
||||
rv.add("/storage/sdcard0");
|
||||
} else {
|
||||
rv.add(rawExternalStorage);
|
||||
}
|
||||
} else {
|
||||
// Device has emulated storage; external storage paths should have
|
||||
// userId burned into them.
|
||||
final String rawUserId;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
rawUserId = "";
|
||||
} else {
|
||||
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
final String[] folders = DIR_SEPORATOR.split(path);
|
||||
final String lastFolder = folders[folders.length - 1];
|
||||
boolean isDigit = false;
|
||||
try {
|
||||
Integer.valueOf(lastFolder);
|
||||
isDigit = true;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
rawUserId = isDigit ? lastFolder : "";
|
||||
}
|
||||
// /storage/emulated/0[1,2,...]
|
||||
if (TextUtils.isEmpty(rawUserId)) {
|
||||
rv.add(rawEmulatedStorageTarget);
|
||||
} else {
|
||||
rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
|
||||
}
|
||||
}
|
||||
// Add all secondary storages
|
||||
if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
|
||||
// All Secondary SD-CARDs splited into array
|
||||
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
|
||||
Collections.addAll(rv, rawSecondaryStorages);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -37,7 +37,9 @@ import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
||||
public final class DownloadUtils {
|
||||
public static final String[] PERMS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
|
||||
public static void batchDownload(@NonNull final Context context, @Nullable String username, final DownloadMethod method,
|
||||
public static void batchDownload(@NonNull final Context context,
|
||||
@Nullable String username,
|
||||
final DownloadMethod method,
|
||||
final List<? extends BasePostModel> itemsToDownload) {
|
||||
if (Utils.settingsHelper == null) Utils.settingsHelper = new SettingsHelper(context);
|
||||
|
||||
|
@ -1,40 +1,40 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException;
|
||||
import awaisomereport.LogCollector.LogFile;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class ExportImportUtils {
|
||||
private static final String TAG = "ExportImportUtils";
|
||||
|
||||
public static final int FLAG_COOKIES = 1;
|
||||
public static final int FLAG_FAVORITES = 1 << 1;
|
||||
public static final int FLAG_SETTINGS = 1 << 2;
|
||||
@ -42,292 +42,289 @@ public final class ExportImportUtils {
|
||||
@IntDef(value = {FLAG_COOKIES, FLAG_FAVORITES, FLAG_SETTINGS}, flag = true)
|
||||
@interface ExportImportFlags {}
|
||||
|
||||
public static void Export(@Nullable final String password, @ExportImportFlags final int flags, @NonNull final File filePath,
|
||||
final FetchListener<Boolean> fetchListener) {
|
||||
final String exportString = ExportImportUtils.getExportString(flags);
|
||||
if (!TextUtils.isEmpty(exportString)) {
|
||||
final boolean isPass = !TextUtils.isEmpty(password);
|
||||
byte[] exportBytes = null;
|
||||
|
||||
if (isPass) {
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] bytes = new byte[32];
|
||||
System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));
|
||||
|
||||
try {
|
||||
exportBytes = PasswordUtils.enc(exportString, bytes);
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::isPass");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
} else {
|
||||
exportBytes = Base64.encode(exportString.getBytes(), Base64.DEFAULT | Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
public static void exportData(@Nullable final String password,
|
||||
@ExportImportFlags final int flags,
|
||||
@NonNull final File filePath,
|
||||
final FetchListener<Boolean> fetchListener,
|
||||
@NonNull final Context context) {
|
||||
final String exportString = getExportString(flags, context);
|
||||
if (TextUtils.isEmpty(exportString)) return;
|
||||
final boolean isPass = !TextUtils.isEmpty(password);
|
||||
byte[] exportBytes = null;
|
||||
if (isPass) {
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] bytes = new byte[32];
|
||||
System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));
|
||||
try {
|
||||
exportBytes = PasswordUtils.enc(exportString, bytes);
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::isPass");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
|
||||
if (exportBytes != null && exportBytes.length > 1) {
|
||||
try (final FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(isPass ? 'A' : 'Z');
|
||||
fos.write(exportBytes);
|
||||
if (fetchListener != null) fetchListener.onResult(true);
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::notPass");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
} else if (fetchListener != null) fetchListener.onResult(false);
|
||||
} else {
|
||||
exportBytes = Base64.encode(exportString.getBytes(), Base64.DEFAULT | Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
if (exportBytes != null && exportBytes.length > 1) {
|
||||
try (final FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(isPass ? 'A' : 'Z');
|
||||
fos.write(exportBytes);
|
||||
if (fetchListener != null) fetchListener.onResult(true);
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_EXPORT, "Export::notPass");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
} else if (fetchListener != null) fetchListener.onResult(false);
|
||||
}
|
||||
|
||||
public static void Import(@NonNull final Context context, @ExportImportFlags final int flags, @NonNull final File filePath,
|
||||
final FetchListener<Boolean> fetchListener) {
|
||||
try (final FileInputStream fis = new FileInputStream(filePath)) {
|
||||
public static void importData(@NonNull final Context context,
|
||||
@ExportImportFlags final int flags,
|
||||
@NonNull final File file,
|
||||
final String password,
|
||||
final FetchListener<Boolean> fetchListener) throws IncorrectPasswordException {
|
||||
try (final FileInputStream fis = new FileInputStream(file)) {
|
||||
final int configType = fis.read();
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
int c;
|
||||
while ((c = fis.read()) != -1) {
|
||||
builder.append((char) c);
|
||||
}
|
||||
|
||||
if (configType == 'A') {
|
||||
// password
|
||||
final AppCompatEditText editText = new AppCompatEditText(context);
|
||||
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(32)});
|
||||
editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
new AlertDialog.Builder(context).setView(editText).setTitle(R.string.password)
|
||||
.setPositiveButton(R.string.confirm, (dialog, which) -> {
|
||||
final CharSequence text = editText.getText();
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
try {
|
||||
final byte[] passwordBytes = text.toString().getBytes();
|
||||
final byte[] bytes = new byte[32];
|
||||
System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));
|
||||
saveToSettings(new String(PasswordUtils.dec(builder.toString(), bytes)), flags,
|
||||
fetchListener);
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import::pass");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
|
||||
} else
|
||||
Toast.makeText(context, R.string.dialog_export_err_password_empty, Toast.LENGTH_SHORT).show();
|
||||
}).show();
|
||||
|
||||
if (TextUtils.isEmpty(password)) return;
|
||||
try {
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] bytes = new byte[32];
|
||||
System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));
|
||||
importJson(new String(PasswordUtils.dec(builder.toString(), bytes)),
|
||||
flags,
|
||||
fetchListener);
|
||||
} catch (final IncorrectPasswordException e) {
|
||||
throw e;
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import::pass");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "Error importing backup", e);
|
||||
}
|
||||
} else if (configType == 'Z') {
|
||||
saveToSettings(new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)),
|
||||
flags, fetchListener);
|
||||
importJson(new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)),
|
||||
flags,
|
||||
fetchListener);
|
||||
|
||||
} else {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(context, "File is corrupted!", Toast.LENGTH_LONG).show();
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
}
|
||||
} catch (IncorrectPasswordException e) {
|
||||
// separately handle incorrect password
|
||||
throw e;
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_IMPORT, "Import");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveToSettings(final String json, @ExportImportFlags final int flags, final FetchListener<Boolean> fetchListener) {
|
||||
private static void importJson(@NonNull final String json,
|
||||
@ExportImportFlags final int flags,
|
||||
final FetchListener<Boolean> fetchListener) {
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(json);
|
||||
|
||||
if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS && jsonObject.has("settings")) {
|
||||
final JSONObject objSettings = jsonObject.getJSONObject("settings");
|
||||
final Iterator<String> keys = objSettings.keys();
|
||||
while (keys.hasNext()) {
|
||||
final String key = keys.next();
|
||||
final Object val = objSettings.opt(key);
|
||||
if (val instanceof String) {
|
||||
settingsHelper.putString(key, (String) val);
|
||||
} else if (val instanceof Integer) {
|
||||
settingsHelper.putInteger(key, (int) val);
|
||||
} else if (val instanceof Boolean) {
|
||||
settingsHelper.putBoolean(key, (boolean) val);
|
||||
}
|
||||
}
|
||||
importSettings(jsonObject);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_COOKIES) == FLAG_COOKIES && jsonObject.has("cookies")) {
|
||||
final JSONArray cookies = jsonObject.getJSONArray("cookies");
|
||||
final int cookiesLen = cookies.length();
|
||||
for (int i = 0; i < cookiesLen; ++i) {
|
||||
final JSONObject cookieObject = cookies.getJSONObject(i);
|
||||
// final DataBox.CookieModel cookieModel = new DataBox.CookieModel(cookieObject.getString("i"),
|
||||
// cookieObject.getString("u"),
|
||||
// cookieObject.getString("c"),
|
||||
// fullName,
|
||||
// profilePic);
|
||||
// Utils.dataBox.addOrUpdateUser(cookieModel.getUid(), cookieModel.getUserInfo(), cookieModel.getCookie());
|
||||
}
|
||||
importAccounts(jsonObject);
|
||||
}
|
||||
|
||||
if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES && jsonObject.has("favs")) {
|
||||
final JSONArray favs = jsonObject.getJSONArray("favs");
|
||||
final int favsLen = favs.length();
|
||||
for (int i = 0; i < favsLen; ++i) {
|
||||
final JSONObject favsObject = favs.getJSONObject(i);
|
||||
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(favsObject.getString("q"),
|
||||
favsObject.getLong("d"),
|
||||
favsObject.has("s") ? favsObject.getString("s") : favsObject.getString("q")));
|
||||
}
|
||||
importFavorites(jsonObject);
|
||||
}
|
||||
|
||||
if (fetchListener != null) fetchListener.onResult(true);
|
||||
|
||||
} catch (final Exception e) {
|
||||
if (fetchListener != null) fetchListener.onResult(false);
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_IMPORT, "saveToSettings");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_IMPORT, "importJson");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void importFavorites(final JSONObject jsonObject) throws JSONException {
|
||||
final JSONArray favs = jsonObject.getJSONArray("favs");
|
||||
for (int i = 0; i < favs.length(); i++) {
|
||||
final JSONObject favsObject = favs.getJSONObject(i);
|
||||
final String queryText = favsObject.optString("q");
|
||||
if (TextUtils.isEmpty(queryText)) continue;
|
||||
final Pair<FavoriteType, String> favoriteTypeQueryPair;
|
||||
String query = null;
|
||||
FavoriteType favoriteType = null;
|
||||
if (queryText.contains("@")
|
||||
|| queryText.contains("#")
|
||||
|| queryText.contains("/")) {
|
||||
favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText);
|
||||
if (favoriteTypeQueryPair != null) {
|
||||
query = favoriteTypeQueryPair.second;
|
||||
favoriteType = favoriteTypeQueryPair.first;
|
||||
}
|
||||
} else {
|
||||
query = queryText;
|
||||
favoriteType = FavoriteType.valueOf(favsObject.optString("type"));
|
||||
}
|
||||
if (query == null || favoriteType == null) {
|
||||
continue;
|
||||
}
|
||||
final DataBox.FavoriteModel favoriteModel = new DataBox.FavoriteModel(
|
||||
-1,
|
||||
query,
|
||||
favoriteType,
|
||||
favsObject.optString("s"),
|
||||
favoriteType == FavoriteType.HASHTAG ? null
|
||||
: favsObject.optString("pic_url"),
|
||||
new Date(favsObject.getLong("d")));
|
||||
// Log.d(TAG, "importJson: favoriteModel: " + favoriteModel);
|
||||
Utils.dataBox.addOrUpdateFavorite(favoriteModel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void importAccounts(final JSONObject jsonObject) throws JSONException {
|
||||
final JSONArray cookies = jsonObject.getJSONArray("cookies");
|
||||
for (int i = 0; i < cookies.length(); i++) {
|
||||
final JSONObject cookieObject = cookies.getJSONObject(i);
|
||||
final DataBox.CookieModel cookieModel = new DataBox.CookieModel(
|
||||
cookieObject.optString("i"),
|
||||
cookieObject.optString("u"),
|
||||
cookieObject.optString("c"),
|
||||
cookieObject.optString("full_name"),
|
||||
cookieObject.optString("profile_pic")
|
||||
);
|
||||
if (!cookieModel.isValid()) continue;
|
||||
// Log.d(TAG, "importJson: cookieModel: " + cookieModel);
|
||||
Utils.dataBox.addOrUpdateUser(cookieModel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void importSettings(final JSONObject jsonObject) throws JSONException {
|
||||
final JSONObject objSettings = jsonObject.getJSONObject("settings");
|
||||
final Iterator<String> keys = objSettings.keys();
|
||||
while (keys.hasNext()) {
|
||||
final String key = keys.next();
|
||||
final Object val = objSettings.opt(key);
|
||||
// Log.d(TAG, "importJson: key: " + key + ", val: " + val);
|
||||
if (val instanceof String) {
|
||||
settingsHelper.putString(key, (String) val);
|
||||
} else if (val instanceof Integer) {
|
||||
settingsHelper.putInteger(key, (int) val);
|
||||
} else if (val instanceof Boolean) {
|
||||
settingsHelper.putBoolean(key, (boolean) val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEncrypted(final File file) {
|
||||
try (final FileInputStream fis = new FileInputStream(file)) {
|
||||
final int configType = fis.read();
|
||||
if (configType == 'A') {
|
||||
return true;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "isEncrypted", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getExportString(@ExportImportFlags final int flags) {
|
||||
private static String getExportString(@ExportImportFlags final int flags,
|
||||
@NonNull final Context context) {
|
||||
String result = null;
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
|
||||
String str;
|
||||
if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS) {
|
||||
str = getSettings();
|
||||
if (str != null) jsonObject.put("settings", new JSONObject(str));
|
||||
jsonObject.put("settings", getSettings(context));
|
||||
}
|
||||
|
||||
if ((flags & FLAG_COOKIES) == FLAG_COOKIES) {
|
||||
str = getCookies();
|
||||
if (str != null) jsonObject.put("cookies", new JSONArray(str));
|
||||
jsonObject.put("cookies", getCookies());
|
||||
}
|
||||
|
||||
if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES) {
|
||||
str = getFavorites();
|
||||
if (str != null) jsonObject.put("favs", new JSONArray(str));
|
||||
jsonObject.put("favs", getFavorites());
|
||||
}
|
||||
|
||||
result = jsonObject.toString();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getExportString");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getSettings() {
|
||||
String result = null;
|
||||
@NonNull
|
||||
private static JSONObject getSettings(@NonNull final Context context) {
|
||||
final SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
final Map<String, ?> allPrefs = sharedPreferences.getAll();
|
||||
if (allPrefs == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(allPrefs);
|
||||
jsonObject.remove(Constants.COOKIE);
|
||||
jsonObject.remove(Constants.DEVICE_UUID);
|
||||
jsonObject.remove(Constants.PREV_INSTALL_VERSION);
|
||||
return jsonObject;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error exporting settings", e);
|
||||
}
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
if (settingsHelper != null) {
|
||||
try {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put(Constants.APP_THEME, settingsHelper.getString(Constants.APP_THEME));
|
||||
json.put(Constants.APP_LANGUAGE, settingsHelper.getString(Constants.APP_LANGUAGE));
|
||||
|
||||
String str = settingsHelper.getString(Constants.FOLDER_PATH);
|
||||
if (!TextUtils.isEmpty(str)) json.put(Constants.FOLDER_PATH, str);
|
||||
|
||||
str = settingsHelper.getString(Constants.DATE_TIME_FORMAT);
|
||||
if (!TextUtils.isEmpty(str)) json.put(Constants.DATE_TIME_FORMAT, str);
|
||||
|
||||
str = settingsHelper.getString(Constants.DATE_TIME_SELECTION);
|
||||
if (!TextUtils.isEmpty(str)) json.put(Constants.DATE_TIME_SELECTION, str);
|
||||
|
||||
str = settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT);
|
||||
if (!TextUtils.isEmpty(str)) json.put(Constants.CUSTOM_DATE_TIME_FORMAT, str);
|
||||
|
||||
json.put(Constants.DOWNLOAD_USER_FOLDER, settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER));
|
||||
json.put(Constants.MUTED_VIDEOS, settingsHelper.getBoolean(Constants.MUTED_VIDEOS));
|
||||
json.put(Constants.AUTOPLAY_VIDEOS, settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
||||
json.put(Constants.AUTOLOAD_POSTS, settingsHelper.getBoolean(Constants.AUTOLOAD_POSTS));
|
||||
json.put(Constants.FOLDER_SAVE_TO, settingsHelper.getBoolean(Constants.FOLDER_SAVE_TO));
|
||||
|
||||
result = json.toString();
|
||||
} catch (final Exception e) {
|
||||
result = null;
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getSettings");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
@NonNull
|
||||
private static JSONArray getFavorites() {
|
||||
if (Utils.dataBox == null) return new JSONArray();
|
||||
try {
|
||||
final List<DataBox.FavoriteModel> allFavorites = Utils.dataBox.getAllFavorites();
|
||||
final JSONArray jsonArray = new JSONArray();
|
||||
for (final DataBox.FavoriteModel favorite : allFavorites) {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("q", favorite.getQuery());
|
||||
jsonObject.put("type", favorite.getType().toString());
|
||||
jsonObject.put("s", favorite.getDisplayName());
|
||||
jsonObject.put("pic_url", favorite.getPicUrl());
|
||||
jsonObject.put("d", favorite.getDateAdded().getTime());
|
||||
jsonArray.put(jsonObject);
|
||||
}
|
||||
return jsonArray;
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null) {
|
||||
logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites");
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error exporting favorites", e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getFavorites() {
|
||||
String result = null;
|
||||
if (Utils.dataBox != null) {
|
||||
try {
|
||||
final ArrayList<DataBox.FavoriteModel> allFavorites = Utils.dataBox.getAllFavorites();
|
||||
final int allFavoritesSize;
|
||||
if (allFavorites != null && (allFavoritesSize = allFavorites.size()) > 0) {
|
||||
final JSONArray jsonArray = new JSONArray();
|
||||
for (int i = 0; i < allFavoritesSize; i++) {
|
||||
final DataBox.FavoriteModel favorite = allFavorites.get(i);
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("q", favorite.getQuery());
|
||||
jsonObject.put("d", favorite.getDate());
|
||||
jsonObject.put("s", favorite.getDisplayName());
|
||||
jsonArray.put(jsonObject);
|
||||
}
|
||||
result = jsonArray.toString();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
result = null;
|
||||
if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, "getFavorites");
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
@NonNull
|
||||
private static JSONArray getCookies() {
|
||||
if (Utils.dataBox == null) return new JSONArray();
|
||||
try {
|
||||
final List<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies();
|
||||
final JSONArray jsonArray = new JSONArray();
|
||||
for (final DataBox.CookieModel cookie : allCookies) {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("i", cookie.getUid());
|
||||
jsonObject.put("u", cookie.getUsername());
|
||||
jsonObject.put("c", cookie.getCookie());
|
||||
jsonObject.put("full_name", cookie.getFullName());
|
||||
jsonObject.put("profile_pic", cookie.getProfilePic());
|
||||
jsonArray.put(jsonObject);
|
||||
}
|
||||
return jsonArray;
|
||||
} catch (final Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, "Error exporting accounts", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getCookies() {
|
||||
String result = null;
|
||||
if (Utils.dataBox != null) {
|
||||
try {
|
||||
final ArrayList<DataBox.CookieModel> allCookies = Utils.dataBox.getAllCookies();
|
||||
final int allCookiesSize;
|
||||
if (allCookies != null && (allCookiesSize = allCookies.size()) > 0) {
|
||||
final JSONArray jsonArray = new JSONArray();
|
||||
for (int i = 0; i < allCookiesSize; i++) {
|
||||
final DataBox.CookieModel cookieModel = allCookies.get(i);
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("i", cookieModel.getUid());
|
||||
jsonObject.put("u", cookieModel.getUsername());
|
||||
jsonObject.put("c", cookieModel.getCookie());
|
||||
jsonArray.put(jsonObject);
|
||||
}
|
||||
result = jsonArray.toString();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
result = null;
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private final static class PasswordUtils {
|
||||
private static final String cipherAlgo = "AES";
|
||||
private static final String cipherTran = "AES/CBC/PKCS5Padding";
|
||||
|
||||
private static byte[] dec(final String encrypted, final byte[] keyValue) throws Exception {
|
||||
final Cipher cipher = Cipher.getInstance(cipherTran);
|
||||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
|
||||
return cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP));
|
||||
}
|
||||
|
||||
private static byte[] enc(@NonNull final String str, final byte[] keyValue) throws Exception {
|
||||
final Cipher cipher = Cipher.getInstance(cipherTran);
|
||||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
|
||||
final byte[] bytes = cipher.doFinal(str.getBytes());
|
||||
return Base64.encode(bytes, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP);
|
||||
}
|
||||
return new JSONArray();
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public class NavigationExtensions {
|
||||
int firstFragmentGraphId = 0;
|
||||
for (int i = 0; i < navGraphIds.size(); i++) {
|
||||
final int navGraphId = navGraphIds.get(i);
|
||||
final String fragmentTag = getFragmentTag(i);
|
||||
final String fragmentTag = getFragmentTag(navGraphId);
|
||||
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
|
||||
final NavController navController = navHostFragment.getNavController();
|
||||
final int graphId = navController.getGraph().getId();
|
||||
@ -57,7 +57,8 @@ public class NavigationExtensions {
|
||||
return false;
|
||||
}
|
||||
String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
|
||||
if (!selectedItemTag[0].equals(newlySelectedItemTag)) {
|
||||
String tag = selectedItemTag[0];
|
||||
if (tag != null && !tag.equals(newlySelectedItemTag)) {
|
||||
fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag);
|
||||
if (fragment == null) {
|
||||
@ -176,7 +177,7 @@ public class NavigationExtensions {
|
||||
final Intent intent) {
|
||||
for (int i = 0; i < navGraphIds.size(); i++) {
|
||||
final int navGraphId = navGraphIds.get(i);
|
||||
final String fragmentTag = getFragmentTag(i);
|
||||
final String fragmentTag = getFragmentTag(navGraphId);
|
||||
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
|
||||
if (navHostFragment.getNavController().handleDeepLink(intent)) {
|
||||
final int selectedItemId = bottomNavigationView.getSelectedItemId();
|
||||
|
@ -0,0 +1,47 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public final class PasswordUtils {
|
||||
private static final String cipherAlgo = "AES";
|
||||
private static final String cipherTran = "AES/CBC/PKCS5Padding";
|
||||
|
||||
public static byte[] dec(final String encrypted, final byte[] keyValue) throws Exception {
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(cipherTran);
|
||||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
|
||||
return cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP));
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
|
||||
throw new IncorrectPasswordException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] enc(@NonNull final String str, final byte[] keyValue) throws Exception {
|
||||
final Cipher cipher = Cipher.getInstance(cipherTran);
|
||||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
|
||||
final byte[] bytes = cipher.doFinal(str.getBytes());
|
||||
return Base64.encode(bytes, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
public static class IncorrectPasswordException extends Exception {
|
||||
public IncorrectPasswordException(final GeneralSecurityException e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -232,8 +232,8 @@ public final class ResponseBodyUtils {
|
||||
final String threadV2Id = data.getString("thread_v2_id");
|
||||
final String threadTitle = data.getString("thread_title");
|
||||
|
||||
final String threadNewestCursor = data.getString("newest_cursor");
|
||||
final String threadOldestCursor = data.getString("oldest_cursor");
|
||||
final String threadNewestCursor = data.optString("newest_cursor");
|
||||
final String threadOldestCursor = data.optString("oldest_cursor");
|
||||
final String threadNextCursor = data.has("next_cursor") ? data.getString("next_cursor") : null;
|
||||
final String threadPrevCursor = data.has("prev_cursor") ? data.getString("prev_cursor") : null;
|
||||
|
||||
|
@ -40,7 +40,7 @@ public final class SettingsHelper {
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
public SettingsHelper(@NonNull final Context context) {
|
||||
this.sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE);
|
||||
this.sharedPreferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -1,28 +1,20 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.util.Pair;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
||||
@ -39,11 +31,9 @@ import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.databinding.DialogImportExportBinding;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
|
||||
public final class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024;
|
||||
@ -64,19 +54,6 @@ public final class Utils {
|
||||
return Math.round((dp * displayMetrics.densityDpi) / 160.0f);
|
||||
}
|
||||
|
||||
public static void setTooltipText(final View view, @StringRes final int tooltipTextRes) {
|
||||
if (view != null && tooltipTextRes != 0 && tooltipTextRes != -1) {
|
||||
final Context context = view.getContext();
|
||||
final String tooltipText = context.getResources().getString(tooltipTextRes);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) view.setTooltipText(tooltipText);
|
||||
else view.setOnLongClickListener(v -> {
|
||||
Toast.makeText(context, tooltipText, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyText(@NonNull final Context context, final CharSequence string) {
|
||||
if (clipboardManager == null)
|
||||
clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
@ -89,100 +66,6 @@ public final class Utils {
|
||||
Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static void showImportExportDialog(final Context context) {
|
||||
final DialogImportExportBinding importExportBinding = DialogImportExportBinding.inflate(LayoutInflater.from(context));
|
||||
|
||||
final View passwordParent = (View) importExportBinding.cbPassword.getParent();
|
||||
final View exportLoginsParent = (View) importExportBinding.cbExportLogins.getParent();
|
||||
final View exportFavoritesParent = (View) importExportBinding.cbExportFavorites.getParent();
|
||||
final View exportSettingsParent = (View) importExportBinding.cbExportSettings.getParent();
|
||||
final View importLoginsParent = (View) importExportBinding.cbImportLogins.getParent();
|
||||
final View importFavoritesParent = (View) importExportBinding.cbImportFavorites.getParent();
|
||||
final View importSettingsParent = (View) importExportBinding.cbImportSettings.getParent();
|
||||
|
||||
importExportBinding.cbPassword.setOnCheckedChangeListener((buttonView, isChecked) ->
|
||||
importExportBinding.etPassword.etPassword.setEnabled(isChecked));
|
||||
|
||||
final AlertDialog[] dialog = new AlertDialog[1];
|
||||
final View.OnClickListener onClickListener = v -> {
|
||||
if (v == passwordParent) importExportBinding.cbPassword.performClick();
|
||||
|
||||
else if (v == exportLoginsParent) importExportBinding.cbExportLogins.performClick();
|
||||
else if (v == exportFavoritesParent)
|
||||
importExportBinding.cbExportFavorites.performClick();
|
||||
|
||||
else if (v == importLoginsParent) importExportBinding.cbImportLogins.performClick();
|
||||
else if (v == importFavoritesParent)
|
||||
importExportBinding.cbImportFavorites.performClick();
|
||||
|
||||
else if (v == exportSettingsParent) importExportBinding.cbExportSettings.performClick();
|
||||
else if (v == importSettingsParent) importExportBinding.cbImportSettings.performClick();
|
||||
|
||||
else if (context instanceof AppCompatActivity) {
|
||||
final FragmentManager fragmentManager = ((AppCompatActivity) context).getSupportFragmentManager();
|
||||
final String folderPath = settingsHelper.getString(FOLDER_PATH);
|
||||
|
||||
if (v == importExportBinding.btnSaveTo) {
|
||||
final Editable text = importExportBinding.etPassword.etPassword.getText();
|
||||
final boolean passwordChecked = importExportBinding.cbPassword.isChecked();
|
||||
if (passwordChecked && TextUtils.isEmpty(text))
|
||||
Toast.makeText(context, R.string.dialog_export_err_password_empty, Toast.LENGTH_SHORT).show();
|
||||
else {
|
||||
new DirectoryChooser().setInitialDirectory(folderPath).setInteractionListener(path -> {
|
||||
final File file = new File(path, "InstaGrabber_Settings_" + System.currentTimeMillis() + ".zaai");
|
||||
final String password = passwordChecked ? text.toString() : null;
|
||||
int flags = 0;
|
||||
if (importExportBinding.cbExportFavorites.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_FAVORITES;
|
||||
if (importExportBinding.cbExportSettings.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_SETTINGS;
|
||||
if (importExportBinding.cbExportLogins.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_COOKIES;
|
||||
|
||||
ExportImportUtils.Export(password, flags, file, result -> {
|
||||
Toast.makeText(context, result ? R.string.dialog_export_success : R.string.dialog_export_failed, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
if (dialog[0] != null && dialog[0].isShowing()) dialog[0].dismiss();
|
||||
});
|
||||
|
||||
}).show(fragmentManager, null);
|
||||
}
|
||||
|
||||
} else if (v == importExportBinding.btnImport) {
|
||||
new DirectoryChooser().setInitialDirectory(folderPath).setShowZaAiConfigFiles(true).setInteractionListener(path -> {
|
||||
int flags = 0;
|
||||
if (importExportBinding.cbImportFavorites.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_FAVORITES;
|
||||
if (importExportBinding.cbImportSettings.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_SETTINGS;
|
||||
if (importExportBinding.cbImportLogins.isChecked())
|
||||
flags |= ExportImportUtils.FLAG_COOKIES;
|
||||
|
||||
ExportImportUtils.Import(context, flags, new File(path), result -> {
|
||||
((AppCompatActivity) context).recreate();
|
||||
Toast.makeText(context, result ? R.string.dialog_import_success : R.string.dialog_import_failed, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
if (dialog[0] != null && dialog[0].isShowing()) dialog[0].dismiss();
|
||||
});
|
||||
|
||||
}).show(fragmentManager, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
passwordParent.setOnClickListener(onClickListener);
|
||||
exportLoginsParent.setOnClickListener(onClickListener);
|
||||
exportSettingsParent.setOnClickListener(onClickListener);
|
||||
exportFavoritesParent.setOnClickListener(onClickListener);
|
||||
importLoginsParent.setOnClickListener(onClickListener);
|
||||
importSettingsParent.setOnClickListener(onClickListener);
|
||||
importFavoritesParent.setOnClickListener(onClickListener);
|
||||
importExportBinding.btnSaveTo.setOnClickListener(onClickListener);
|
||||
importExportBinding.btnImport.setOnClickListener(onClickListener);
|
||||
|
||||
dialog[0] = new AlertDialog.Builder(context).setView(importExportBinding.getRoot()).show();
|
||||
}
|
||||
|
||||
public static Map<String, String> sign(final Map<String, Object> form) {
|
||||
final String signed = sign(new JSONObject(form).toString());
|
||||
if (signed == null) {
|
||||
@ -251,4 +134,16 @@ public final class Utils {
|
||||
}
|
||||
return simpleCache;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Pair<FavoriteType, String> migrateOldFavQuery(final String queryText) {
|
||||
if (queryText.startsWith("@")) {
|
||||
return new Pair<>(FavoriteType.USER, queryText.substring(1));
|
||||
} else if (queryText.contains("/")) {
|
||||
return new Pair<>(FavoriteType.LOCATION, queryText.substring(0, queryText.indexOf("/")));
|
||||
} else if (queryText.startsWith("#")) {
|
||||
return new Pair<>(FavoriteType.HASHTAG, queryText.substring(1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.utils.DataBox;
|
||||
|
||||
public class FavoritesViewModel extends ViewModel {
|
||||
private MutableLiveData<List<DataBox.FavoriteModel>> list;
|
||||
|
||||
public MutableLiveData<List<DataBox.FavoriteModel>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class FileListViewModel extends ViewModel {
|
||||
private MutableLiveData<List<File>> list;
|
||||
|
||||
public MutableLiveData<List<File>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -98,14 +98,12 @@ public class StoriesService extends BaseService {
|
||||
|
||||
public void getUserStory(final String id,
|
||||
final String username,
|
||||
final boolean storiesig,
|
||||
final boolean isLoc,
|
||||
final boolean isHashtag,
|
||||
final boolean highlight,
|
||||
final ServiceCallback<List<StoryModel>> callback) {
|
||||
final String url = buildUrl(id, storiesig, isLoc, isHashtag, highlight);
|
||||
final String userAgent = storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT;
|
||||
final Call<String> userStoryCall = repository.getUserStory(userAgent, url);
|
||||
final String url = buildUrl(id, isLoc, isHashtag, highlight);
|
||||
final Call<String> userStoryCall = repository.getUserStory(Constants.I_USER_AGENT, url);
|
||||
userStoryCall.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -119,7 +117,7 @@ public class StoriesService extends BaseService {
|
||||
}
|
||||
data = new JSONObject(body);
|
||||
|
||||
if (!storiesig && !highlight)
|
||||
if (!highlight)
|
||||
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
|
||||
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
|
||||
|
||||
@ -243,16 +241,10 @@ public class StoriesService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
private String buildUrl(final String id, final boolean storiesig, final boolean isLoc, final boolean isHashtag, final boolean highlight) {
|
||||
private String buildUrl(final String id, final boolean isLoc, final boolean isHashtag, final boolean highlight) {
|
||||
final String userId = id.replace(":", "%3A");
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("https://");
|
||||
if (storiesig) {
|
||||
builder.append("storiesig");
|
||||
} else {
|
||||
builder.append("i.instagram");
|
||||
}
|
||||
builder.append(".com/api/v1/");
|
||||
builder.append("https://i.instagram.com/api/v1/");
|
||||
if (isLoc) {
|
||||
builder.append("locations/");
|
||||
}
|
||||
@ -266,11 +258,7 @@ public class StoriesService extends BaseService {
|
||||
}
|
||||
builder.append(userId);
|
||||
if (!highlight) {
|
||||
if (storiesig) {
|
||||
builder.append("/reel_media/");
|
||||
} else {
|
||||
builder.append("/story/");
|
||||
}
|
||||
builder.append("/story/");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
package awais.instagrabber.webservices;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import awais.instagrabber.repositories.TagsRepository;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class TagsService extends BaseService {
|
||||
|
||||
private static final String TAG = "TagsService";
|
||||
|
||||
// web for www.instagram.com
|
||||
private final TagsRepository webRepository;
|
||||
|
||||
private static TagsService instance;
|
||||
|
||||
private TagsService() {
|
||||
final Retrofit webRetrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://www.instagram.com/")
|
||||
.build();
|
||||
webRepository = webRetrofit.create(TagsRepository.class);
|
||||
}
|
||||
|
||||
public static TagsService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new TagsService();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void follow(@NonNull final String tag,
|
||||
@NonNull final String csrfToken,
|
||||
final ServiceCallback<Boolean> callback) {
|
||||
final Call<String> request = webRepository.follow(Constants.USER_AGENT,
|
||||
csrfToken,
|
||||
tag);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
callback.onFailure(new RuntimeException("body is null"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final String status = jsonObject.optString("status");
|
||||
callback.onSuccess(status.equals("ok"));
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "onResponse: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unfollow(@NonNull final String tag,
|
||||
@NonNull final String csrfToken,
|
||||
final ServiceCallback<Boolean> callback) {
|
||||
final Call<String> request = webRepository.unfollow(Constants.USER_AGENT,
|
||||
csrfToken,
|
||||
tag);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
callback.onFailure(new RuntimeException("body is null"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final String status = jsonObject.optString("status");
|
||||
callback.onSuccess(status.equals("ok"));
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "onResponse: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
10
app/src/main/res/drawable/ic_account_multiple_remove_24.xml
Normal file
10
app/src/main/res/drawable/ic_account_multiple_remove_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M24 17V19H21V17C21 15.45 20.3 14.06 19.18 13.06C24 13.55 24 17 24 17M18 5C19.66 5 21 6.34 21 8C21 9.66 19.66 11 18 11C17.69 11 17.38 10.95 17.1 10.86C17.67 10.05 18 9.07 18 8C18 6.94 17.67 5.95 17.1 5.14C17.38 5.05 17.69 5 18 5M13 5C14.66 5 16 6.34 16 8C16 9.66 14.66 11 13 11C11.34 11 10 9.66 10 8C10 6.34 11.34 5 13 5M19 17V19H7V17C7 14.79 9.69 13 13 13C16.31 13 19 14.79 19 17M.464 13.12L2.59 11L.464 8.88L1.88 7.46L4 9.59L6.12 7.46L7.54 8.88L5.41 11L7.54 13.12L6.12 14.54L4 12.41L1.88 14.54Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_block_24.xml
Normal file
10
app/src/main/res/drawable/ic_block_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_clock_alert_outline_24.xml
Normal file
10
app/src/main/res/drawable/ic_clock_alert_outline_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M11 7V13L16.2 16.1L17 14.9L12.5 12.2V7H11M20 12V18H22V12H20M20 20V22H22V20H20M18 20C16.3 21.3 14.3 22 12 22C6.5 22 2 17.5 2 12S6.5 2 12 2C16.8 2 20.9 5.4 21.8 10H19.7C18.8 6.6 15.7 4 12 4C7.6 4 4 7.6 4 12S7.6 20 12 20C14.4 20 16.5 18.9 18 17.3V20Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_file_24.xml
Normal file
10
app/src/main/res/drawable/ic_file_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,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,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||
</vector>
|
@ -6,5 +6,5 @@
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_highlight_off_24.xml
Normal file
10
app/src/main/res/drawable/ic_highlight_off_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_logout_24.xml
Normal file
10
app/src/main/res/drawable/ic_logout_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_class_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_class_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M18,2L6,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM9,4h2v5l-1,-0.75L9,9L9,4zM18,20L6,20L6,4h1v9l3,-2.25L13,13L13,4h5v16z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_map_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_map_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_person_add_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_person_add_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM15,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4zM9,18c0.22,-0.72 3.31,-2 6,-2 2.7,0 5.8,1.29 6,2L9,18zM6,15v-3h3v-2L6,10L6,7L4,7v3L1,10v2h3v3z"/>
|
||||
</vector>
|
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15,6c1.1,0 2,0.9 2,2 0,0.99 -0.73,1.82 -1.67,1.97l-2.31,-2.31C13.19,6.72 14.01,6 15,6m0,-2c-2.21,0 -4,1.79 -4,4 0,0.18 0.03,0.35 0.05,0.52l3.43,3.43c0.17,0.02 0.34,0.05 0.52,0.05 2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4zM16.69,14.16L22.53,20L23,20v-2c0,-2.14 -3.56,-3.5 -6.31,-3.84zM13.01,16.13L14.88,18L9,18c0.08,-0.24 0.88,-1.01 2.91,-1.57l1.1,-0.3M1.41,1.71L0,3.12l4,4L4,10L1,10v2h3v3h2v-3h2.88l2.51,2.51C9.19,15.11 7,16.3 7,18v2h9.88l4,4 1.41,-1.41L1.41,1.71zM6,10v-0.88l0.88,0.88L6,10z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_person_pin_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_person_pin_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,2L5,2c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM19,18h-4.83l-0.59,0.59L12,20.17l-1.59,-1.59 -0.58,-0.58L5,18L5,4h14v14zM12,11c1.65,0 3,-1.35 3,-3s-1.35,-3 -3,-3 -3,1.35 -3,3 1.35,3 3,3zM12,7c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM18,15.58c0,-2.5 -3.97,-3.58 -6,-3.58s-6,1.08 -6,3.58L6,17h12v-1.42zM8.48,15c0.74,-0.51 2.23,-1 3.52,-1s2.78,0.49 3.52,1L8.48,15z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_star_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_star_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_outline_star_plus_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_star_plus_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M5.8 21L7.4 14L2 9.2L9.2 8.6L12 2L14.8 8.6L22 9.2L18.8 12H18C17.3 12 16.6 12.1 15.9 12.4L18.1 10.5L13.7 10.1L12 6.1L10.3 10.1L5.9 10.5L9.2 13.4L8.2 17.7L12 15.4L12.5 15.7C12.3 16.2 12.1 16.8 12.1 17.3L5.8 21M17 14V17H14V19H17V22H19V19H22V17H19V14H17Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_settings_backup_restore_24.xml
Normal file
10
app/src/main/res/drawable/ic_settings_backup_restore_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
|
||||
</vector>
|
8
app/src/main/res/drawable/ic_star_check_24.xml
Normal file
8
app/src/main/res/drawable/ic_star_check_24.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<!-- drawable/star_check.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M5.8 21L7.4 14L2 9.2L9.2 8.6L12 2L14.8 8.6L22 9.2L18.8 12H18C14.9 12 12.4 14.3 12 17.3L5.8 21M17.8 21.2L22.6 16.4L21.3 15L17.7 18.6L16.2 17L15 18.2L17.8 21.2" />
|
||||
</vector>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<solid android:color="@color/semi_transparent_black" />
|
||||
<solid android:color="@color/black_a50" />
|
||||
<padding
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
|
6
app/src/main/res/drawable/sl_favourite_24.xml
Normal file
6
app/src/main/res/drawable/sl_favourite_24.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_outline_star_plus_24" android:state_checked="false" />
|
||||
<item android:drawable="@drawable/ic_star_check_24" android:state_checked="true" />
|
||||
<item android:drawable="@drawable/ic_outline_star_plus_24" />
|
||||
</selector>
|
@ -28,7 +28,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="none"
|
||||
app:popupTheme="@style/Widget.AppTheme.Toolbar.PrimarySurface"
|
||||
app:title="@string/app_name"
|
||||
tools:menu="@menu/main_menu" />
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
73
app/src/main/res/layout/dialog_create_backup.xml
Normal file
73
app/src/main/res/layout/dialog_create_backup.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?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:orientation="vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="0dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportSettings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/dialog_export_settings" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportLogins"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_export_accounts" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportFavorites"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_export_favorites" />
|
||||
|
||||
<include layout="@layout/item_pref_divider" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_password" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password_no_max"
|
||||
android:visibility="gone"
|
||||
app:counterEnabled="true"
|
||||
app:counterMaxLength="32"
|
||||
app:endIconMode="password_toggle"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="no"
|
||||
android:inputType="textPassword"
|
||||
android:maxLength="2200"
|
||||
android:scrollHorizontally="false"
|
||||
tools:text="test" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<include layout="@layout/item_pref_divider" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSaveTo"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/create_backup" />
|
||||
</LinearLayout>
|
@ -1,230 +0,0 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/TextAppearance.AppCompat.Headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:paddingStart="?attr/dialogPreferredPadding"
|
||||
android:paddingLeft="?attr/dialogPreferredPadding"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="?attr/dialogPreferredPadding"
|
||||
android:paddingRight="?attr/dialogPreferredPadding"
|
||||
android:paddingBottom="6dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/import_export" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportSettings"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:checked="true" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_export_settings"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportLogins"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_export_logins"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbExportFavorites"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_export_favorites"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbPassword"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/password"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<include
|
||||
android:id="@+id/etPassword"
|
||||
layout="@layout/layout_password" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnSaveTo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:text="@string/dialog_export_btn_export" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?android:attr/dividerVertical" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbImportSettings"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:checked="true" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_import_settings"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbImportLogins"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:checked="true" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_import_logins"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbImportFavorites"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:checked="true" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="@string/dialog_import_favorites"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnImport"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="8dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:text="@string/dialog_export_btn_import" />
|
||||
</LinearLayout>
|
@ -4,7 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/semi_transparent_black">
|
||||
android:background="@color/black_a50">
|
||||
|
||||
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
|
||||
android:id="@+id/imageViewer"
|
||||
|
143
app/src/main/res/layout/dialog_restore_backup.xml
Normal file
143
app/src/main/res/layout/dialog_restore_backup.xml
Normal file
@ -0,0 +1,143 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="0dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/file_chosen_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/file_chosen_label"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
app:layout_constraintBottom_toTopOf="@id/file_path"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/file_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/file_chosen_label"
|
||||
tools:text="file path file path file path file path file path file path file path file path file path file path file path " />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbSettings"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/dialog_export_settings"
|
||||
app:layout_constraintBottom_toTopOf="@id/cbAccounts"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/file_path" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbAccounts"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_export_accounts"
|
||||
app:layout_constraintBottom_toTopOf="@id/cbFavorites"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSettings" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/cbFavorites"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dialog_export_favorites"
|
||||
app:layout_constraintBottom_toTopOf="@id/top_password_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbAccounts" />
|
||||
|
||||
<include
|
||||
android:id="@+id/top_password_divider"
|
||||
layout="@layout/item_pref_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/enter_password_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbFavorites" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/enter_password_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:text="@string/enter_password"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
app:layout_constraintBottom_toTopOf="@id/passwordField"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_password_divider" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:hint="@string/password_no_max"
|
||||
app:counterEnabled="true"
|
||||
app:counterMaxLength="32"
|
||||
app:endIconMode="password_toggle"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_password_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/enter_password_label">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="no"
|
||||
android:inputType="textPassword"
|
||||
android:maxLength="2200"
|
||||
android:scrollHorizontally="false"
|
||||
tools:text="test" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottom_password_divider"
|
||||
layout="@layout/item_pref_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_restore"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/passwordField" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_restore"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/restore_backup"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bottom_password_divider" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/password_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="top_password_divider,bottom_password_divider,enter_password_label,passwordField"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
7
app/src/main/res/layout/fragment_favorites.xml
Normal file
7
app/src/main/res/layout/fragment_favorites.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/favorite_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_suggestion" />
|
@ -17,47 +17,120 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/tagInfoContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/profile_info_container_bottom_space"
|
||||
android:visibility="visible">
|
||||
android:padding="@dimen/profile_info_container_bottom_space">
|
||||
|
||||
<awais.instagrabber.customviews.CircularImageView
|
||||
android:id="@+id/mainHashtagImage"
|
||||
android:layout_width="@dimen/profile_picture_size"
|
||||
android:layout_height="@dimen/profile_picture_size"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="?selectableItemBackgroundBorderless" />
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/mainTagPostCount"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainTagPostCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
android:textSize="15sp"
|
||||
tools:text="35\nPosts" />
|
||||
app:layout_constraintBottom_toTopOf="@id/btnFollowTag"
|
||||
app:layout_constraintStart_toEndOf="@id/mainHashtagImage"
|
||||
app:layout_constraintTop_toTopOf="@id/mainHashtagImage"
|
||||
tools:text="35 Posts" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/btnFollowTag"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="2"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:text="@string/follow"
|
||||
android:textColor="@color/btn_pink_text_color"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_pink_background" />
|
||||
</LinearLayout>
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_person_add_24"
|
||||
app:chipIconTint="@color/deep_purple_800"
|
||||
app:layout_constraintBottom_toBottomOf="@id/mainHashtagImage"
|
||||
app:layout_constraintStart_toEndOf="@id/mainHashtagImage"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainTagPostCount"
|
||||
app:rippleColor="@color/purple_200" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/fav_chip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:text="@string/add_to_favorites"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_star_plus_24"
|
||||
app:chipIconTint="@color/yellow_800"
|
||||
app:layout_constraintBottom_toBottomOf="@id/mainHashtagImage"
|
||||
app:layout_constraintStart_toEndOf="@id/btnFollowTag"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainTagPostCount"
|
||||
app:rippleColor="@color/yellow_400" />
|
||||
|
||||
<!--<com.google.android.material.chip.Chip-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="@id/mainHashtagImage"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/mainHashtagImage"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/mainTagPostCount" />-->
|
||||
|
||||
<!--<com.google.android.material.button.MaterialButton-->
|
||||
<!-- android:id="@+id/btnFollowTag"-->
|
||||
<!-- style="@style/Widget.MaterialComponents.Button.TextButton"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="0dp"-->
|
||||
<!-- android:text="@string/follow"-->
|
||||
<!-- android:textColor="@color/deep_purple_200"-->
|
||||
<!-- android:visibility="gone"-->
|
||||
<!-- app:icon="@drawable/ic_outline_person_add_24"-->
|
||||
<!-- app:iconGravity="top"-->
|
||||
<!-- app:iconTint="@color/deep_purple_200"-->
|
||||
<!-- app:layout_constraintBottom_toTopOf="@id/fav_cb"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/mainTagPostCount"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="@id/mainHashtagImage"-->
|
||||
<!-- tools:visibility="visible" />-->
|
||||
|
||||
<!--<CheckBox-->
|
||||
<!-- android:id="@+id/fav_cb"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:button="@drawable/sl_favourite_24"-->
|
||||
<!-- android:paddingStart="8dp"-->
|
||||
<!-- android:paddingEnd="8dp"-->
|
||||
<!-- android:text="Add to favorites"-->
|
||||
<!-- android:visibility="gone"-->
|
||||
<!-- app:buttonTint="@color/yellow_800"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="@id/mainHashtagImage"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/mainHashtagImage"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/btnFollowTag"-->
|
||||
<!-- tools:visibility="gone" />-->
|
||||
|
||||
<!--<ProgressBar-->
|
||||
<!-- android:id="@+id/fav_progress"-->
|
||||
<!-- style="@style/Widget.MaterialComponents.ProgressIndicator.Circular.Indeterminate"-->
|
||||
<!-- android:layout_width="24dp"-->
|
||||
<!-- android:layout_height="24dp"-->
|
||||
<!-- android:paddingStart="8dp"-->
|
||||
<!-- android:paddingEnd="8dp"-->
|
||||
<!-- android:visibility="gone"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="@id/mainHashtagImage"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="@id/fav_cb"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/mainTagPostCount"-->
|
||||
<!-- tools:visibility="gone" />-->
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -17,101 +17,128 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
|
||||
<RelativeLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/locInfoContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@null"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="5dp">
|
||||
android:padding="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/locInfo"
|
||||
android:layout_width="match_parent"
|
||||
<awais.instagrabber.customviews.CircularImageView
|
||||
android:id="@+id/mainLocationImage"
|
||||
android:layout_width="@dimen/profile_picture_size"
|
||||
android:layout_height="@dimen/profile_picture_size"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@id/mainLocPostCount"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainLocPostCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toTopOf="@id/btnMap"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="35 Posts" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/btnMap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/profile_info_container_bottom_space">
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:text="@string/map"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_map_24"
|
||||
app:chipIconTint="@color/green_500"
|
||||
app:layout_constraintBottom_toTopOf="@id/locationFullName"
|
||||
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
|
||||
app:rippleColor="@color/grey_500"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<awais.instagrabber.customviews.CircularImageView
|
||||
android:id="@+id/mainLocationImage"
|
||||
android:layout_width="@dimen/profile_picture_size"
|
||||
android:layout_height="@dimen/profile_picture_size"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
app:actualImageScaleType="fitCenter" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainLocPostCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
android:textSize="15sp"
|
||||
tools:text="35\nPosts" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnMap"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="2"
|
||||
android:text="@string/map"
|
||||
android:textColor="@color/btn_green_text_color"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_green_background" />
|
||||
</LinearLayout>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/fav_chip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:text="@string/add_to_favorites"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_star_plus_24"
|
||||
app:chipIconTint="@color/yellow_800"
|
||||
app:layout_constraintBottom_toBottomOf="@id/mainLocationImage"
|
||||
app:layout_constraintStart_toEndOf="@id/btnMap"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
|
||||
app:rippleColor="@color/yellow_400" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/locationFullName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/locInfo"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textSize="16sp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/locationBiography"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainLocationImage"
|
||||
tools:text="OUR HOUSE" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
android:id="@+id/locationBiography"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/locationFullName"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
tools:text="IN THE MIDDLE OF OUR STREET" />
|
||||
app:layout_constraintBottom_toTopOf="@id/locationUrl"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationFullName"
|
||||
tools:text="IN THE MIDDLE OF OUR STREET"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
android:id="@+id/locationUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/locationBiography"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
tools:text="https://austinhuang.me/" />
|
||||
</RelativeLayout>
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationBiography"
|
||||
tools:text="https://austinhuang.me/"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -79,8 +79,10 @@
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textStyle="bold"
|
||||
@ -102,15 +104,34 @@
|
||||
app:srcCompat="@drawable/verified"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/fav_cb"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:button="@drawable/sl_favourite_24"
|
||||
android:visibility="gone"
|
||||
app:buttonTint="@color/yellow_800"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/mainFullName"
|
||||
app:layout_constraintBottom_toTopOf="@id/mainBiography"
|
||||
app:layout_constraintStart_toEndOf="@id/isVerified" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/fav_progress"
|
||||
style="@style/Widget.MaterialComponents.ProgressIndicator.Circular.Indeterminate"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/mainFullName"
|
||||
app:layout_constraintStart_toEndOf="@id/isVerified"
|
||||
app:layout_constraintTop_toTopOf="@id/mainFullName"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
android:id="@+id/mainBiography"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
app:layout_constraintBottom_toTopOf="@id/mainUrl"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -124,10 +145,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/mainBiography"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:padding="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -135,97 +153,96 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/mainBiography"
|
||||
tools:text="https://austinhuang.me/"
|
||||
tools:textColor="@android:color/holo_blue_dark"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnFollow"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/follow"
|
||||
android:textColor="@color/btn_pink_text_color"
|
||||
android:textColor="@color/deep_purple_200"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_pink_background"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnRestrict"
|
||||
app:icon="@drawable/ic_outline_person_add_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/deep_purple_200"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnTagged"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/purple_200"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnRestrict"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnTagged"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/restrict"
|
||||
android:textColor="@color/btn_orange_text_color"
|
||||
android:text="@string/tagged"
|
||||
android:textColor="@color/deep_orange_600"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_orange_background"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnBlock"
|
||||
app:icon="@drawable/ic_outline_person_pin_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/deep_orange_600"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnSaved"
|
||||
app:layout_constraintStart_toEndOf="@id/btnFollow"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnBlock"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/block"
|
||||
android:textColor="@color/btn_red_text_color"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_red_background"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnRestrict"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/button_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="btnFollow, btnRestrict, btnBlock" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnTagged"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tagged"
|
||||
android:textColor="@color/btn_blue_text_color"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_blue_background"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnSaved"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/button_barrier"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSaved"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/saved"
|
||||
android:textColor="@color/btn_orange_text_color"
|
||||
android:textColor="@color/blue_700"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_orange_background"
|
||||
app:icon="@drawable/ic_outline_class_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/blue_700"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnLiked"
|
||||
app:layout_constraintStart_toEndOf="@id/btnTagged"
|
||||
app:layout_constraintTop_toTopOf="@id/button_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/blue_A400"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnLiked"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/liked"
|
||||
android:textColor="@color/btn_lightpink_text_color"
|
||||
android:textColor="@color/red_600"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/btn_lightpink_background"
|
||||
app:icon="@drawable/ic_like"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/red_600"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnDM"
|
||||
app:layout_constraintStart_toEndOf="@id/btnSaved"
|
||||
app:layout_constraintTop_toTopOf="@id/button_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/red_300"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDM"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dm_person"
|
||||
android:textColor="@color/green"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_send_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/green"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnLiked"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/green"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
|
@ -1,15 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
tools:viewBindingIgnore="true" />
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:minHeight="56dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||
tools:text="Line line" />
|
||||
|
||||
</LinearLayout>
|
13
app/src/main/res/layout/item_fav_section_header.xml
Normal file
13
app/src/main/res/layout/item_fav_section_header.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:textColor="?colorAccent"
|
||||
android:textStyle="bold"
|
||||
tools:text="HEADERS" />
|
@ -18,7 +18,7 @@
|
||||
android:id="@+id/media_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:background="@color/semi_transparent_black" />
|
||||
tools:background="@color/black_a50" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="24dp"
|
||||
|
@ -22,10 +22,10 @@
|
||||
android:layout_weight="1"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@null"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:weightSum="2">
|
||||
@ -36,7 +36,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textColor="@color/feed_text_primary_color"
|
||||
tools:text="username" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
@ -46,7 +45,6 @@
|
||||
android:layout_below="@+id/title"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textColor="@color/feed_text_primary_color"
|
||||
android:textSize="15sp"
|
||||
android:visibility="visible"
|
||||
tools:text="location" />
|
||||
|
@ -20,37 +20,6 @@
|
||||
app:roundAsCircle="true"
|
||||
tools:placeholderImage="@mipmap/ic_launcher" />
|
||||
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/multi_pic1"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:actualImageScaleType="centerCrop"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/multi_pic2"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:layout_constraintBottom_toTopOf="@+id/multi_pic3"-->
|
||||
<!-- app:layout_constraintEnd_toStartOf="@id/barrier"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/multi_pic1"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/multi_pic3"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintEnd_toStartOf="@id/barrier"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/multi_pic1"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@+id/multi_pic2"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier"
|
||||
android:layout_width="wrap_content"
|
||||
@ -105,29 +74,10 @@
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintEnd_toStartOf="@id/tvDate"
|
||||
app:layout_constraintEnd_toStartOf="@id/preview_barrier"
|
||||
app:layout_constraintStart_toStartOf="@id/tvUsername"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="sub-comment" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivPreviewPic"
|
||||
app:layout_constraintStart_toEndOf="@id/tvSubComment"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvComment"
|
||||
tools:text="date" />
|
||||
tools:text="sub-comment long long long long long long long long long long" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/preview_barrier"
|
||||
@ -147,46 +97,22 @@
|
||||
tools:placeholderImage="@mipmap/ic_launcher"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/preview_pic1"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintEnd_toStartOf="@id/preview_pic2"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/preview_barrier"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/preview_pic2"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/preview_pic1"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<!--<com.facebook.drawee.view.SimpleDraweeView-->
|
||||
<!-- android:id="@+id/preview_pic3"-->
|
||||
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toEndOf="@id/preview_pic1"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/preview_pic2"-->
|
||||
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
|
||||
|
||||
<!--<androidx.constraintlayout.widget.Group-->
|
||||
<!-- android:id="@+id/multi_pic_group"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:visibility="visible"-->
|
||||
<!-- app:constraint_referenced_ids="multi_pic1, multi_pic2, multi_pic3" />-->
|
||||
|
||||
<!--<androidx.constraintlayout.widget.Group-->
|
||||
<!-- android:id="@+id/preview_pic_group"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:visibility="gone"-->
|
||||
<!-- app:constraint_referenced_ids="preview_pic1, preview_pic2, preview_pic3" />-->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvDate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="end"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/preview_barrier"
|
||||
app:layout_constraintStart_toEndOf="@id/ivProfilePic"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvSubComment"
|
||||
tools:text="some long long long long long date" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -7,7 +7,10 @@
|
||||
android:background="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="8dp">
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/ivProfilePic"
|
||||
@ -26,8 +29,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
@ -42,10 +45,10 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -1,116 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/footer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@android:color/darker_gray" />
|
||||
|
||||
<View
|
||||
android:id="@+id/horizontalDivider"
|
||||
android:layout_width="1dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@android:color/darker_gray" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toStartOf="@id/horizontalDivider"
|
||||
android:layout_toLeftOf="@id/horizontalDivider"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnConfirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toEndOf="@id/horizontalDivider"
|
||||
android:layout_toRightOf="@id/horizontalDivider"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:text="@string/confirm" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/directoryInfo"
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true">
|
||||
app:layout_constraintBottom_toTopOf="@id/directoryList"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnNavUp"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:contentDescription="@string/nav_up"
|
||||
android:padding="8dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_arrow_upward_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvSelectedFolderLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_toEndOf="@id/btnNavUp"
|
||||
android:layout_toRightOf="@id/btnNavUp"
|
||||
android:text="@string/selected_folder_label"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvSelectedFolder"
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/txtvSelectedFolderLabel"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_toEndOf="@id/btnNavUp"
|
||||
android:layout_toRightOf="@id/btnNavUp"
|
||||
android:ellipsize="start"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/btnNavUp"
|
||||
android:background="@android:color/darker_gray" />
|
||||
</RelativeLayout>
|
||||
app:navigationIcon="@drawable/ic_arrow_upward_24"
|
||||
tools:title="/this/that/thy" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/directoryList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_above="@id/footer"
|
||||
android:layout_below="@id/directoryInfo" />
|
||||
</RelativeLayout>
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_horizontal_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
|
||||
|
||||
<include
|
||||
android:id="@+id/bottom_horizontal_divider"
|
||||
layout="@layout/item_pref_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btnCancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/directoryList" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnCancel"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/cancel"
|
||||
app:icon="@drawable/ic_close_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/directoryList" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnConfirm"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/confirm"
|
||||
app:icon="@drawable/ic_check_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/directoryList" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -10,7 +10,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/expired" />
|
||||
app:srcCompat="@drawable/ic_clock_alert_outline_24" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -72,7 +72,8 @@
|
||||
android:layout_marginLeft="15dip"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnSaveTo"
|
||||
|
@ -1,12 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/favourites"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_star_24"
|
||||
android:title="@string/title_favorites"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom" />
|
||||
<!--<item-->
|
||||
<!-- android:id="@+id/favourites"-->
|
||||
<!-- android:enabled="true"-->
|
||||
<!-- android:icon="@drawable/ic_star_24"-->
|
||||
<!-- android:title="@string/title_favorites"-->
|
||||
<!-- android:visible="false"-->
|
||||
<!-- app:showAsAction="ifRoom" />-->
|
||||
|
||||
<item
|
||||
android:id="@+id/block"
|
||||
android:icon="@drawable/ic_block_24"
|
||||
android:title="@string/block"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/restrict"
|
||||
android:icon="@drawable/ic_highlight_off_24"
|
||||
android:title="@string/restrict"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
@ -34,6 +34,24 @@
|
||||
app:nullable="true" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_hashTagFragment"
|
||||
app:destination="@id/hashtag_nav_graph">
|
||||
<argument
|
||||
android:name="hashtag"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_locationFragment"
|
||||
app:destination="@id/location_nav_graph">
|
||||
<argument
|
||||
android:name="locationId"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/morePreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment"
|
||||
@ -47,6 +65,12 @@
|
||||
<action
|
||||
android:id="@+id/action_morePreferencesFragment_to_notificationsViewer"
|
||||
app:destination="@id/notificationsViewer" />
|
||||
<action
|
||||
android:id="@+id/action_morePreferencesFragment_to_favoritesFragment"
|
||||
app:destination="@id/favoritesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_morePreferencesFragment_to_backupPreferencesFragment"
|
||||
app:destination="@id/backupPreferencesFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/settingsPreferencesFragment"
|
||||
@ -69,4 +93,12 @@
|
||||
android:id="@+id/themePreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.ThemePreferencesFragment"
|
||||
android:label="@string/theme_settings" />
|
||||
<fragment
|
||||
android:id="@+id/favoritesFragment"
|
||||
android:name="awais.instagrabber.fragments.FavoritesFragment"
|
||||
android:label="@string/title_favorites" />
|
||||
<fragment
|
||||
android:id="@+id/backupPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
|
||||
android:label="@string/backup_and_restore" />
|
||||
</navigation>
|
@ -36,7 +36,7 @@
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_profileFragment"
|
||||
app:destination="@id/profileFragment">
|
||||
app:destination="@id/profile_nav_graph">
|
||||
<argument
|
||||
android:name="username"
|
||||
android:defaultValue=""
|
||||
@ -74,6 +74,9 @@
|
||||
<action
|
||||
android:id="@+id/action_profileFragment_to_storyViewerFragment"
|
||||
app:destination="@id/storyViewerFragment" />
|
||||
<action
|
||||
android:id="@+id/action_profileFragment_to_dMThreadFragment"
|
||||
app:destination="@id/directMessagesThreadFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/savedViewerFragment"
|
||||
@ -145,4 +148,31 @@
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directMessagesThreadFragment"
|
||||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment"
|
||||
android:label="DirectMessagesThreadFragment"
|
||||
tools:layout="@layout/fragment_direct_messages_thread">
|
||||
<argument
|
||||
android:name="threadId"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
<action
|
||||
android:id="@+id/action_dMThreadFragment_to_dMSettingsFragment"
|
||||
app:destination="@id/directMessagesSettingsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directMessagesSettingsFragment"
|
||||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment"
|
||||
android:label="DirectMessagesSettingsFragment"
|
||||
tools:layout="@layout/fragment_direct_messages_settings">
|
||||
<argument
|
||||
android:name="threadId"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -42,7 +42,6 @@
|
||||
</string-array>
|
||||
<string-array name="anonymous_story_viewer">
|
||||
<item>Deaktivieren</item>
|
||||
<item>storiesig</item>
|
||||
<item>Aloinstagram</item>
|
||||
<item>Instadp</item>
|
||||
</string-array>
|
||||
|
@ -42,7 +42,6 @@
|
||||
</string-array>
|
||||
<string-array name="anonymous_story_viewer">
|
||||
<item>Disable</item>
|
||||
<item>storiesig</item>
|
||||
<item>Aloinstagram</item>
|
||||
<item>Instadp</item>
|
||||
</string-array>
|
||||
|
@ -42,7 +42,6 @@
|
||||
</string-array>
|
||||
<string-array name="anonymous_story_viewer">
|
||||
<item>غیرفعال</item>
|
||||
<item>استوری های ig</item>
|
||||
<item>Aloinstagram</item>
|
||||
<item>Instadp</item>
|
||||
</string-array>
|
||||
|
@ -42,7 +42,6 @@
|
||||
</string-array>
|
||||
<string-array name="anonymous_story_viewer">
|
||||
<item>Disable</item>
|
||||
<item>storiesig</item>
|
||||
<item>Aloinstagram</item>
|
||||
<item>Instadp</item>
|
||||
</string-array>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user