diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 000000000..b62e7fc1c
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,2 @@
+Changelog : v4.0.1
+• Bug Fixes and Improvements
diff --git a/DISCLAIMER.md b/DISCLAIMER.md
new file mode 100644
index 000000000..5c23dc25a
--- /dev/null
+++ b/DISCLAIMER.md
@@ -0,0 +1,55 @@
+# Disclaimer
+
+### 1. Google Accounts.
+
+You hold yourself accountable if you are to use Aurora Store with your personal Google Account, which may cause your account to be blocked/blacklisted by Google.
+
+As per Google Play's Terms Of Service §4 and §2:
+
+#### §4 Rights and Restrictions
+
+> **Violation of License Terms.** If you violate any of the Content restriction provisions of the Terms, materially or repeatedly violate any of the other Terms or we are investigating you for suspected misconduct then Google may, without refund to you, terminate your access to Google Play, the Content or your Google Account and your rights under this licence will immediately terminate. We will notify you with the reason for our action unless we reasonably believe that to do so would cause Google or another person legal liability, would compromise an investigation, would compromise the operation of any Google products, services or systems, would cause harm to our users or would be in breach of the law or the direction of a legal enforcement authority. See Disabled Accounts in Section 2 for additional information.
+>
+> **Restrictions:** You may not:
+>
+> - display (in part or in whole) the Content as part of any public performance or display even if no fee is charged except (a) where such use would not constitute a copyright infringement or violate any other applicable right or (b) as specifically permitted and only in the exact manner provided.
+> - sell, rent, lease, redistribute, broadcast, transmit, communicate, modify, sublicense, transfer, assign any Content to any third party including with regard to any downloads of Content that you may obtain through Google Play except as specifically permitted and only in the exact manner provided.
+> - use Google Play or any Content in conjunction with any stream-ripping, stream capture or similar software to record or create a copy of any Content that is presented to you in streaming format.
+> - use Content as part of any service for sharing, lending or multi-person use, or for the purpose of any other institution, except as specifically permitted and only in the exact manner provided.
+> - attempt to, or assist, authorize or encourage others to circumvent, disable or defeat any of the security features or components that protect, obfuscate or otherwise restrict access to any Content or Google Play.
+> - remove any watermarks, labels or other legal or proprietary notices included in any Content, or attempt to modify any Content obtained through Google Play, including any modification for the purpose of disguising or changing any indications of the ownership or source of Content.
+>
+> **Multiple Accounts.** If you have multiple Google Accounts with different user names, in some cases you may transfer Content out of an account and into another account, provided you are the owner of each such account and provided Google has enabled a feature of the relevant service allowing such transfers.
+
+#### §2 Your Use of Google Play
+
+> **Disabled Accounts.** If Google disables access to your account in accordance with the Terms (for example if you materially or repeatedly violate the Terms), you may be prevented from accessing Google Play, your account details or any files or other Content that is stored with your account. See the Help Center for more information. If you are the family manager of a family on Google Play and Google disables access to your account, your family members may lose access to family features requiring a family group, such as a family payment method, family subscriptions, or Content shared by family members. If you are a family member of a family on Google Play and Google disables your account, your family members will lose access to Content you have shared with them.
+
+### 2. Installing **Aurora Store** from third-party sources.
+
+You are to hold yourself responsible by installing **Aurora Store** from other sources other than our own, in which you do not blame us for malicious programs, device bricking or anything else.
+
+The following sources are the only official links we provide:
+
+- **F-Droid:** https://f-droid.org/en/packages/com.aurora.store/
+- **AuroraOSS Website:** https://auroraoss.com/downloads.php
+- **Official Telegram Channel:** https://t.me/AuroraOfficial
+- **GitLab:** https://gitlab.com/AuroraOSS/AuroraStore/-/releases
+
+### 3. Links in Aurora Store
+
+**AuroraOSS**, makes no claim or representation regarding, and accepts no responsibility for third party websites accessible by hyperlink from the Service or websites linking to the Service. When you leave the Service, you should be aware that these Terms and our policies no longer govern.
+
+The only links we provide are:
+
+- **Donation links** Bitcoin, Bitcoin Cash, Etherum, BHIM-UPI, PayPal & Liberapay links
+- **Repository sources** GitLab repository link
+- **xda-developers** Aurora Store xda-developers thread
+- **Telegram** Aurora Support group link
+- **F-Droid** Aurora Store in F-Droid repository link
+
+### 4. Content
+
+We do not own any content within **Aurora Store**, all available content i.e. applications, information and links are provided "as is" directly from Google Play repositories/servers.
+
+For all content on **Aurora Store**, we don't review, verify or authenticate it, and it may include inaccuracies or false information. We make no representations, warranties, or guarantees relating to the quality, suitability, truth, accuracy or completeness of any content contained in the Service. You acknowledge sole responsibility for and assume all risk arising from your use of or reliance on any content.
diff --git a/POLICY.md b/POLICY.md
new file mode 100644
index 000000000..856f1b053
--- /dev/null
+++ b/POLICY.md
@@ -0,0 +1,35 @@
+# Aurora Store Privacy Policy
+
+Last Updated: **17.02.2021**
+
+## User data sent to Google by default
+
+The following data is mandatory to make the Service function, all of which Google needs for **Aurora Store** to be able to receive required data:
+
+- **IP Address** which is bound to network request
+- **Timezone, MCC & MNC** are stripped & replaced with a random constant
+- **BUILD_CONFIG** includes the app details - package name,
+ version-name, version-code
+- **BUILD** includes your device details, see for more info
+
+If you choose to use your own account, the following are added to the above:
+
+- **Account details** for Google sign-in verification
+
+We do not store, process or sell any of the collected data above. The data is sent to and processed by Google's servers directly untouched.
+
+All accounts being used within **Aurora Store** are saved onto the device as AAS Tokens & email pair for account verification. **No passwords** are saved to Aurora Store.
+
+## User data sent to our server by default
+
+The only data being sent to our server are **IP addresses**. This is to ensure the functionality and stability of our server, which detects the amount of GET requests from an IP address and if deemed them as spamming, will be banned from our server, otherwise rate-limited.
+
+Rate-limiting lasts for an hour. If you keep going over the 20 request per hour limit, an additional hour to the rate-limiting will be added for every addtional 20 requests. So do yourself a favour and don't do it.
+
+## Changes To This Privacy Policy
+
+We may update our Privacy Policy from time to time. You are thus advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
+
+## Contact Us
+
+If you have any questions about this Privacy Policy, please contact us at **aurora.dev@gmail.com** or via our support groups on Telegram.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..f9042320a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+
+

+
+
+

+
+
+# Aurora Store: A Google Playstore Client
+
+**Aurora Store** is an unofficial, FOSS client to Google's Play Store with an elegant design. Not only does Aurora Store download, update, and search for apps like the Play Store, it also empowers the user with new features.
+
+For those concerned with privacy, **Aurora Store** does not require Google's proprietary framework (spyware?) to operate. It works perfectly fine with or without Google Play Services or [MicroG](https://microg.org/).
+
+**Aurora Store** was originally based on Sergei Yeriomin's [Yalp store](https://github.com/yeriomin/YalpStore). **Aurora Store** v4.0 is a rewrite of version 3 in Kotlin that follows Material Design and runs on all devices running Android 4.4+. Read the roadmap page on our Wiki for more info!
+
+## Screenshots
+
+
+
+
+
+
+## Features
+
+- Free/Libre software — Has GPLv3 licence
+- Beautiful design — Built upon latest Material Design guidelines
+- Anonymous accounts — You can log in and download with anonymous accounts so you don't have to use your own account
+- Personal account login — You can download purchased apps or access your wishlist by using your own Google account
+- [Exodus](https://exodus-privacy.eu.org/) integration — Instantly see trackers an app is hiding in its code
+
+## Downloads
+
+Please only download the latest stable releases from GitLab or one of these sources:
+
+- GitLab: [Releases](https://gitlab.com/AuroraOSS/AuroraStore/-/releases)
+- AuroraOSS: [Downloads](https://auroraoss.com/downloads.php)
+- AuroraOSS: [Stable](https://auroraoss.com/AuroraStore/Stable/)
+- F-Droid: [Link](https://f-droid.org/en/packages/com.aurora.store/)
+- Telegram: [Channel](https://t.me/AuroraOfficial)
+- XDA Forum: [Thread](https://forum.xda-developers.com/android/apps-games/galaxy-playstore-alternative-t3739733)
+
+Nightlies are released here:
+
+- AuroraOSS: [Nightlies](https://auroraoss.com/AuroraStore/Nightly/)
+
+`Aurora Store v3 is still in development! Bugs are to be expected! Any bug reports are appreciated.`
+
+[
](https://f-droid.org/packages/com.dragons.aurora/)
+
+## FAQ
+
+Please visit the [GitLab Wiki](https://gitlab.com/AuroraOSS/AuroraStore/-/wikis/Home). You can also reach out to us on our support chat on [Telegram](https://t.me/AuroraSupport).
+
+## Translations
+
+Don't see your preffered language? Click on the widget below to help translate Aurora Store!
+
+
+
+
+
+## Project references
+
+Open Source libraries Aurora Store uses
+
+- [RX-Java](https://github.com/ReactiveX/RxJava)
+- [ButterKnife](https://github.com/JakeWharton/butterknife)
+- [OkHttp3](https://square.github.io/okhttp/)
+- [Glide](https://github.com/bumptech/glide)
+- [Fetch2](https://github.com/tonyofrancis/Fetch)
+- [GPlayApi](https://gitlab.com/AuroraOSS/gplayapi)
+- [PlayStoreApi-v2](https://github.com/whyorean/playstore-api-v2) (Deprecated! Used up till v3)
+
+
+
+Aurora Store is based on these projects
+
+- [YalpStore](https://github.com/yeriomin/YalpStore)
+- [AppCrawler](https://github.com/Akdeniz/google-play-crawler)
+- [Raccoon](https://github.com/onyxbits/raccoon4)
+- [SAI](https://github.com/Aefyr/SAI)
+
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 000000000..ae40c6b23
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,2 @@
+- [ ] screenshots
+- [ ] ratelimiting policy
diff --git a/TermsOfService.md b/TermsOfService.md
new file mode 100644
index 000000000..7bcfe4679
--- /dev/null
+++ b/TermsOfService.md
@@ -0,0 +1,69 @@
+# **Aurora Store** TERMS OF SERVICE
+
+Last Updated: **17.02.2021**
+
+These terms of service ("Terms") apply to your access and use of **Aurora Store**. Please read them carefully.
+
+## Accepting these Terms
+
+If you access or use the Service, it means you agree to be bound by all of the terms below. So, before you use the Service, please read all of the terms. If you don't agree to all of the terms below, please do not use the Service. Also, if a term does not make sense to you, please let us know by e-mailing **auroraoss.dev@gmail.com**.
+
+## Changes to these Terms
+
+We reserve the right to modify these Terms at any time. For instance, we may need to change these Terms if we come out with a new feature or for some other reason.
+
+Whenever we make changes to these Terms, the changes are effective **--.02.2021** after we post such revised Terms (indicated by revising the date at the top of these Terms) or upon your acceptance if we provide a mechanism for your immediate acceptance of the revised Terms (such as a click-through confirmation or acceptance button). It is your responsibility to check **Aurora Store** for changes to these Terms.
+
+If you continue to use the Service after the revised Terms go into effect, then you have accepted the changes to these Terms.
+
+## Privacy Policy
+
+We have a privacy policy covering the collection, use, and disclosure of user information, which can be found at **https://gitlab.com/AuroraOSS/AuroraStore/POLICY.md**.
+
+## Copyright Policy
+
+We take intellectual property rights seriously, and by using a copyright policy in accordance with the GNU GENERAL PUBLIC LICENSE Version 3, we have adopted a policy of terminating, in appropriate circumstances and, at our sole discretion, access to the service for users who are deemed to be repeat infringers. Persons who wish to fork the Aurora Store project or copy code from the repository MUST comply with the used license. The license in use can be found here: **https://www.gnu.org/licenses/gpl-3.0.en.html**
+
+## Disclaimer
+
+We have a disclaimer covering the use of our Service and the content within **Aurora Store**, which can be found at **https://gitlab.com/AuroraOSS/AuroraStore/DISCLAIMER.md**
+
+## Third-Party Services
+
+We provide users with two major third party services. Your use of the Service may also include the use of applications that are developed or owned by a third party. Your use of such third party applications, websites, and services is governed by that party's own terms of service or privacy policies. We encourage you to read the terms and conditions and privacy policy of any third party application, website or service that you visit or use.
+
+## Token Dispenser Server
+
+We provide users with the ability to use anonymous accounts within **Aurora Store**, provided from our server as tokens. Your use of the Service agrees to not affect our server in any way deemed harmful or with malicious intent.
+
+We may occasionally ask contributors to provide us with accounts. You hereby acknowledge that the accounts you make for **Aurora Store** to be given entire access to **AuroraOSS** and to be stored on our server. This does not apply to non-anonymous accounts.
+
+## **Aurora Store** Materials
+
+We put a lot of effort into creating the Service including, the logo and all designs, text, graphics, pictures, information and other content (excluding content within **Aurora Store**). This property is owned by us or our licensors and it is protected by open-source licenses. We grant you the right to use it.
+
+## Unavoidable Legal Stuff
+
+THE SERVICE AND ANY OTHER SERVICE AND CONTENT INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE SERVICE ARE PROVIDED TO YOU ON AN AS IS OR AS AVAILABLE BASIS WITHOUT ANY REPRESENTATIONS OR WARRANTIES OF ANY KIND. WE DISCLAIM ANY AND ALL WARRANTIES AND REPRESENTATIONS (EXPRESS OR IMPLIED, ORAL OR WRITTEN) WITH RESPECT TO THE SERVICE AND CONTENT INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE SERVICE WHETHER ALLEGED TO ARISE BY OPERATION OF LAW, BY REASON OF CUSTOM OR USAGE IN THE TRADE, BY COURSE OF DEALING OR OTHERWISE.
+
+IN NO EVENT WILL **AURORA OSS** BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY SPECIAL, INDIRECT, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT OF OR IN CONNECTION WITH THE SERVICE OR ANY OTHER SERVICE AND/OR CONTENT INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE SERVICE, REGARDLESS OF THE FORM OF ACTION, WHETHER IN CONTRACT, TORT, STRICT LIABILITY OR OTHERWISE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES OR ARE AWARE OF THE POSSIBILITY OF SUCH DAMAGES. OUR TOTAL LIABILITY FOR ALL CAUSES OF ACTION AND UNDER ALL THEORIES OF LIABILITY WILL BE LIMITED TO THE AMOUNT YOU PAID TO **AURORA OSS**. THIS SECTION WILL BE GIVEN FULL EFFECT EVEN IF ANY REMEDY SPECIFIED IN THIS AGREEMENT IS DEEMED TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
+
+You agree to defend, indemnify and hold us harmless from and against any and all costs, damages, liabilities, and expenses (including attorneys' fees, costs, penalties, interest and disbursements) we incur in relation to, arising from, or for the purpose of avoiding, any claim or demand from a third party relating to your use of the Service or the use of the Service by any person using your account, including any claim that your use of the Service violates any applicable law or regulation, or the rights of any third party, and/or your violation of these Terms.
+
+## Termination
+
+If you breach any of these Terms, we have the right to suspend or disable your access to or use of the Service.
+
+## Entire Agreement
+
+These Terms constitute the entire agreement between you and **AuroraOSS** regarding the use of the Service, superseding any prior agreements between you and **AuroraOSS** relating to your use of the Service.
+
+## Feedback
+
+Please let us know what you think of the Service, these Terms and, in general, **Aurora Store**. When you provide us with any feedback, comments or suggestions about the Service, these Terms and, in general, **Aurora Store**, you irrevocably assign to us all of your right, title and interest in and to your feedback, comments and suggestions.
+
+If you discover or suspect any Service security breaches, please let us know as soon as possible.
+
+## Questions & Contact Information
+
+Questions or comments about the Service may be directed to us at the email address **aurora.dev@gmail.com**.
diff --git a/app/build.gradle b/app/build.gradle
index 1ce4f64bf..5c921c11f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -172,7 +172,7 @@ dependencies {
implementation "com.github.topjohnwu.libsu:core:${versions.libsu}"
//Love <3
- api("com.gitlab.AuroraOSS:gplayapi:82843a04ce")
+ api("com.gitlab.AuroraOSS:gplayapi:7d7a0deab4")
}
Properties props = new Properties()
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8c66a5958..9d5bfeb2f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -55,17 +55,20 @@
android:name=".AuroraApplication"
android:allowBackup="true"
android:banner="@drawable/ic_launcher_background"
+ android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
+ android:killAfterRestore="false"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
+ android:restoreAnyVersion="true"
+ android:restoreNeedsApplication="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:targetApi="m">
-
+
@@ -82,11 +85,50 @@
android:name=".MainActivity"
android:launchMode="singleTask" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -101,6 +143,8 @@
+
+
diff --git a/app/src/main/assets/installers.json b/app/src/main/assets/installers.json
index 315f0f798..c52676ed1 100755
--- a/app/src/main/assets/installers.json
+++ b/app/src/main/assets/installers.json
@@ -3,14 +3,14 @@
"id": "0",
"title": "Session installer",
"subtitle": "Session based installer for bundled/split APKs",
- "description": "Best suited for devices running Android 5.0+.",
+ "description": "Best suited for devices running Android 5.0+",
"url": "https://developer.android.com/reference/android/content/pm/PackageInstaller.Session"
},
{
"id": "1",
"title": "Native installer",
"subtitle": "Intent based installer, available on all devices",
- "description": "Best suited for devices running below Android 4.4 or OEM modified ROMs like MIUI, One-UI.",
+ "description": "Best suited for devices running below Android 4.4",
"url": "https://developer.android.com/reference/android/content/Intent#setDataAndType"
},
{
diff --git a/app/src/main/java/com/aurora/extensions/Activity.kt b/app/src/main/java/com/aurora/extensions/Activity.kt
new file mode 100644
index 000000000..1e76dcd31
--- /dev/null
+++ b/app/src/main/java/com/aurora/extensions/Activity.kt
@@ -0,0 +1,75 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.extensions
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+fun AppCompatActivity.close() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ finishAfterTransition()
+ } else {
+ finish()
+ }
+}
+
+fun AppCompatActivity.restart() {
+ val intent = intent
+ overridePendingTransition(0, 0)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
+ finish()
+ overridePendingTransition(0, 0)
+ startActivity(intent)
+}
+
+inline fun Activity.startActivity(): Unit =
+ this.startActivity(newIntent())
+
+inline fun Activity.startActivity(flags: Int): Unit =
+ this.startActivity(newIntent(flags))
+
+inline fun Activity.startActivity(extras: Bundle): Unit =
+ this.startActivity(newIntent(extras))
+
+inline fun Activity.startActivity(flags: Int, extras: Bundle): Unit =
+ this.startActivity(newIntent(flags, extras))
+
+inline fun Activity.startActivityForResult(requestCode: Int): Unit =
+ this.startActivityForResult(newIntent(), requestCode)
+
+inline fun Activity.startActivityForResult(
+ requestCode: Int,
+ flags: Int
+): Unit =
+ this.startActivityForResult(newIntent(flags), requestCode)
+
+inline fun Activity.startActivityForResult(
+ extras: Bundle, requestCode: Int
+): Unit =
+ this.startActivityForResult(newIntent(extras), requestCode)
+
+inline fun Activity.startActivityForResult(
+ extras: Bundle, requestCode: Int, flags: Int
+): Unit =
+ this.startActivityForResult(newIntent(flags, extras), requestCode)
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Collection.kt b/app/src/main/java/com/aurora/extensions/Collection.kt
similarity index 95%
rename from app/src/main/java/com/aurora/store/util/extensions/Collection.kt
rename to app/src/main/java/com/aurora/extensions/Collection.kt
index 6acdca890..c987fc362 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Collection.kt
+++ b/app/src/main/java/com/aurora/extensions/Collection.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
fun MutableList.flushAndAdd(list: List) {
clear()
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Commons.kt b/app/src/main/java/com/aurora/extensions/Commons.kt
similarity index 69%
rename from app/src/main/java/com/aurora/store/util/extensions/Commons.kt
rename to app/src/main/java/com/aurora/extensions/Commons.kt
index f0ee7dbe0..8e6598acc 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Commons.kt
+++ b/app/src/main/java/com/aurora/extensions/Commons.kt
@@ -17,10 +17,11 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
-import android.os.Build
import android.text.format.DateFormat
+import java.io.PrintWriter
+import java.io.StringWriter
import java.util.*
fun Long.toDate(): String {
@@ -29,18 +30,10 @@ fun Long.toDate(): String {
return DateFormat.format("dd/MM/yy", calendar).toString()
}
-fun isLAndAbove(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
-}
-
-fun isNAndAbove(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-}
-
-fun isPAndAbove(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
-}
-
-fun isQAndAbove(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+fun Throwable.stackTraceToString(): String {
+ val stringWriter = StringWriter(1024)
+ val printWriter = PrintWriter(stringWriter)
+ printStackTrace(printWriter)
+ printWriter.close()
+ return stringWriter.toString()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/extensions/Conditional.kt b/app/src/main/java/com/aurora/extensions/Conditional.kt
new file mode 100644
index 000000000..0e8528de7
--- /dev/null
+++ b/app/src/main/java/com/aurora/extensions/Conditional.kt
@@ -0,0 +1,32 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.extensions
+
+inline fun doIf(condition: Boolean?, action: () -> Unit) {
+ if (condition == true) action()
+}
+
+inline fun doIf(condition: () -> Boolean?, action: () -> Unit) {
+ if (condition() == true) action()
+}
+
+inline fun doIf(any: Any?, action: () -> Unit) {
+ if (any != null) action()
+}
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Context.kt b/app/src/main/java/com/aurora/extensions/Context.kt
similarity index 78%
rename from app/src/main/java/com/aurora/store/util/extensions/Context.kt
rename to app/src/main/java/com/aurora/extensions/Context.kt
index 7c509c7f8..ea9397755 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Context.kt
+++ b/app/src/main/java/com/aurora/extensions/Context.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.app.AlarmManager
import android.app.PendingIntent
@@ -25,18 +25,27 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
+import android.graphics.Color
import android.net.Uri
-import android.os.Build
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.TypedValue
+import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.ShareCompat
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.util.Log
-import com.aurora.store.util.ViewUtil
import kotlin.system.exitProcess
+val Context.inflater: LayoutInflater
+ get() = LayoutInflater.from(this)
+
+val Context.displayMetrics: DisplayMetrics
+ get() = resources.displayMetrics
fun Context.browse(url: String) {
try {
@@ -82,18 +91,10 @@ fun Context.open(className: Class, newTask: Boolean = false) {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(
intent,
- ViewUtil.getEmptyActivityBundle(this)
+ getEmptyActivityBundle()
)
}
-fun AppCompatActivity.close() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- finishAfterTransition()
- } else {
- finish()
- }
-}
-
fun Context.restartApp() {
val pendingIntent = PendingIntent.getActivity(
this,
@@ -107,8 +108,23 @@ fun Context.restartApp() {
exitProcess(0)
}
+fun Context.getEmptyActivityBundle(): Bundle? {
+ return ActivityOptionsCompat.makeCustomAnimation(
+ this,
+ android.R.anim.fade_in,
+ android.R.anim.fade_out
+ ).toBundle()
+}
+
fun Context.copyToClipBoard(data: String?) {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Download Url", data)
clipboard.setPrimaryClip(clip)
}
+
+fun Context.getStyledAttributeColor(id: Int): Int {
+ val arr = obtainStyledAttributes(TypedValue().data, intArrayOf(id))
+ val styledAttr = arr.getColor(0, Color.WHITE)
+ arr.recycle()
+ return styledAttr
+}
diff --git a/app/src/main/java/com/aurora/extensions/Dialog.kt b/app/src/main/java/com/aurora/extensions/Dialog.kt
new file mode 100644
index 000000000..95c83e305
--- /dev/null
+++ b/app/src/main/java/com/aurora/extensions/Dialog.kt
@@ -0,0 +1,63 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.extensions
+
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.drawable.ColorDrawable
+import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+
+fun Context.showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
+ runOnUiThread {
+ val backgroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
+ val builder = MaterialAlertDialogBuilder(this).apply {
+ setTitle(titleId)
+ setMessage(messageId)
+ setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
+ background = ColorDrawable(backgroundColor)
+ }.create()
+
+ builder.show()
+ }
+}
+
+fun Context.showDialog(title: String?, message: String?) {
+ runOnUiThread {
+ val backgroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
+ val builder = MaterialAlertDialogBuilder(this).apply {
+ setTitle(title)
+ setMessage(message)
+ setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
+ background = ColorDrawable(backgroundColor)
+ }.create()
+
+ builder.show()
+ }
+}
+
+fun Fragment.showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
+ requireContext().showDialog(titleId, messageId)
+}
+
+fun Fragment.showDialog(title: String, message: String) {
+ requireContext().showDialog(title, message)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Glide.kt b/app/src/main/java/com/aurora/extensions/Glide.kt
similarity index 99%
rename from app/src/main/java/com/aurora/store/util/extensions/Glide.kt
rename to app/src/main/java/com/aurora/extensions/Glide.kt
index 048e8c1d1..dd7ae9500 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Glide.kt
+++ b/app/src/main/java/com/aurora/extensions/Glide.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
diff --git a/app/src/main/java/com/aurora/extensions/Intent.kt b/app/src/main/java/com/aurora/extensions/Intent.kt
new file mode 100644
index 000000000..c270615ab
--- /dev/null
+++ b/app/src/main/java/com/aurora/extensions/Intent.kt
@@ -0,0 +1,42 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+
+inline fun Context.newIntent(): Intent =
+ Intent(this, T::class.java)
+
+inline fun Context.newIntent(flags: Int): Intent {
+ val intent = newIntent()
+ intent.flags = flags
+ return intent
+}
+
+inline fun Context.newIntent(extras: Bundle): Intent =
+ newIntent(0, extras)
+
+inline fun Context.newIntent(flags: Int, extras: Bundle): Intent {
+ val intent = newIntent(flags)
+ intent.putExtras(extras)
+ return intent
+}
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Number.kt b/app/src/main/java/com/aurora/extensions/Number.kt
similarity index 96%
rename from app/src/main/java/com/aurora/store/util/extensions/Number.kt
rename to app/src/main/java/com/aurora/extensions/Number.kt
index d58817d19..95396ec8a 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Number.kt
+++ b/app/src/main/java/com/aurora/extensions/Number.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.content.res.Resources
diff --git a/app/src/main/java/com/aurora/extensions/Platform.kt b/app/src/main/java/com/aurora/extensions/Platform.kt
new file mode 100644
index 000000000..21e7375d8
--- /dev/null
+++ b/app/src/main/java/com/aurora/extensions/Platform.kt
@@ -0,0 +1,96 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.extensions
+
+import android.annotation.SuppressLint
+import android.os.Build
+import java.util.*
+
+
+fun isLAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
+}
+
+fun isMAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+}
+
+fun isNAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+}
+
+fun isOAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+}
+
+fun isPAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
+}
+
+fun isQAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+}
+
+fun isRAndAbove(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
+}
+
+fun isMIUI(): Boolean {
+ return getSystemProperty("ro.miui.ui.version.name").isNotEmpty()
+}
+
+fun isHuawei(): Boolean {
+ return Build.MANUFACTURER.toLowerCase(Locale.getDefault()).contains("huawei")
+ || Build.HARDWARE.toLowerCase(Locale.getDefault()).contains("kirin")
+ || Build.HARDWARE.toLowerCase(Locale.getDefault()).contains("hi3")
+}
+
+fun getMIUIVersion(): String {
+ val version = getSystemProperty("ro.miui.ui.version.name")
+ val versionCode = getSystemProperty("ro.miui.ui.version.code")
+ return if (version.isNotEmpty() && versionCode.isNotEmpty())
+ "$version.$versionCode"
+ else
+ "unknown"
+}
+
+@SuppressLint("PrivateApi")
+fun isMiuiOptimizationDisabled(): Boolean {
+ if ("0" == getSystemProperty("persist.sys.miui_optimization")) {
+ return true
+ } else try {
+ return Class.forName("android.miui.AppOpsUtils")
+ .getDeclaredMethod("isXOptMode")
+ .invoke(null) as Boolean
+ } catch (e: java.lang.Exception) {
+ return false
+ }
+}
+
+@SuppressLint("PrivateApi")
+fun getSystemProperty(key: String): String {
+ return try {
+ Class.forName("android.os.SystemProperties")
+ .getDeclaredMethod("get", String::class.java)
+ .invoke(null, key) as String
+ } catch (e: Exception) {
+ ""
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/util/extensions/ThemeEngine.kt b/app/src/main/java/com/aurora/extensions/ThemeEngine.kt
similarity index 96%
rename from app/src/main/java/com/aurora/store/util/extensions/ThemeEngine.kt
rename to app/src/main/java/com/aurora/extensions/ThemeEngine.kt
index 79192f10a..06121fb41 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/ThemeEngine.kt
+++ b/app/src/main/java/com/aurora/extensions/ThemeEngine.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.content.Intent
import android.content.res.Configuration
@@ -33,8 +33,6 @@ import androidx.fragment.app.Fragment
import com.aurora.Constants
import com.aurora.store.R
import com.aurora.store.util.CommonUtil
-import com.aurora.store.util.ViewUtil
-
fun Fragment.applyTheme(
themeId: Int,
@@ -140,8 +138,7 @@ private fun AppCompatActivity.setLightStatusBar() {
private fun AppCompatActivity.setLightNavigationBar() {
var flags = window.decorView.systemUiVisibility
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- window.navigationBarColor =
- ViewUtil.getStyledAttribute(this, android.R.attr.colorBackground)
+ window.navigationBarColor = getStyledAttributeColor(android.R.attr.colorBackground)
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
window.decorView.systemUiVisibility = flags
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Threading.kt b/app/src/main/java/com/aurora/extensions/Threading.kt
similarity index 64%
rename from app/src/main/java/com/aurora/store/util/extensions/Threading.kt
rename to app/src/main/java/com/aurora/extensions/Threading.kt
index 8a6ef856d..d7d94e7ba 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Threading.kt
+++ b/app/src/main/java/com/aurora/extensions/Threading.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.os.Handler
import android.os.Looper
@@ -25,17 +25,10 @@ import android.os.Looper
fun runAsync(action: () -> Unit) = Thread(Runnable(action)).start()
fun runOnUiThread(action: () -> Unit) {
- if (isMainLooperAlive()) {
- action()
- } else {
- Handler(Looper.getMainLooper()).post(Runnable(action))
+ when {
+ isMainThread() -> action.invoke()
+ else -> Handler(Looper.getMainLooper()).post(Runnable(action))
}
}
-fun runDelayed(delayMillis: Long, action: () -> Unit) =
- Handler().postDelayed(Runnable(action), delayMillis)
-
-fun runDelayedOnUiThread(delayMillis: Long, action: () -> Unit) =
- Handler(Looper.getMainLooper()).postDelayed(Runnable(action), delayMillis)
-
-private fun isMainLooperAlive() = Looper.myLooper() == Looper.getMainLooper()
\ No newline at end of file
+private fun isMainThread() = Looper.myLooper() == Looper.getMainLooper()
diff --git a/app/src/main/java/com/aurora/store/util/extensions/Toast.kt b/app/src/main/java/com/aurora/extensions/Toast.kt
similarity index 65%
rename from app/src/main/java/com/aurora/store/util/extensions/Toast.kt
rename to app/src/main/java/com/aurora/extensions/Toast.kt
index b83289a6e..9f5e4f77a 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/Toast.kt
+++ b/app/src/main/java/com/aurora/extensions/Toast.kt
@@ -17,20 +17,32 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.content.Context
import android.widget.Toast
-import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
-fun Context.toast(text: CharSequence): Toast =
- Toast.makeText(this, text, Toast.LENGTH_SHORT).apply { show() }
-
-fun Context.longToast(text: CharSequence): Toast =
- Toast.makeText(this, text, Toast.LENGTH_LONG).apply { show() }
-
-fun Context.toast(@StringRes resId: Int): Toast =
+fun Context.toast(resId: Int) = runOnUiThread {
Toast.makeText(this, resId, Toast.LENGTH_SHORT).apply { show() }
+}
-fun Context.longToast(@StringRes resId: Int): Toast =
+fun Context.toast(text: CharSequence) = runOnUiThread {
+ Toast.makeText(this, text, Toast.LENGTH_SHORT).apply { show() }
+}
+
+fun Context.toastLong(resId: Int) = runOnUiThread {
Toast.makeText(this, resId, Toast.LENGTH_LONG).apply { show() }
+}
+
+fun Context.toastLong(text: CharSequence) = runOnUiThread {
+ Toast.makeText(this, text, Toast.LENGTH_LONG).apply { show() }
+}
+
+fun Fragment.toast(resId: Int) = requireContext().toast(resId)
+
+fun Fragment.toast(text: CharSequence) = requireContext().toast(text)
+
+fun Fragment.toastLong(resId: Int) = requireContext().toastLong(resId)
+
+fun Fragment.toastLong(text: CharSequence) = requireContext().toastLong(text)
diff --git a/app/src/main/java/com/aurora/store/util/extensions/View.kt b/app/src/main/java/com/aurora/extensions/View.kt
similarity index 98%
rename from app/src/main/java/com/aurora/store/util/extensions/View.kt
rename to app/src/main/java/com/aurora/extensions/View.kt
index 06284e99f..6b7857932 100644
--- a/app/src/main/java/com/aurora/store/util/extensions/View.kt
+++ b/app/src/main/java/com/aurora/extensions/View.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.util.extensions
+package com.aurora.extensions
import android.content.Context
import android.view.View
diff --git a/app/src/main/java/com/aurora/store/AuroraApplication.kt b/app/src/main/java/com/aurora/store/AuroraApplication.kt
index 8aeef42f6..f4196dcbd 100644
--- a/app/src/main/java/com/aurora/store/AuroraApplication.kt
+++ b/app/src/main/java/com/aurora/store/AuroraApplication.kt
@@ -36,6 +36,10 @@ class AuroraApplication : MultiDexApplication() {
private lateinit var fetch: Fetch
private lateinit var packageManagerReceiver: PackageManagerReceiver
+ companion object{
+ val enqueuedInstalls: MutableSet = mutableSetOf()
+ }
+
override fun onCreate() {
super.onCreate()
diff --git a/app/src/main/java/com/aurora/store/MainActivity.kt b/app/src/main/java/com/aurora/store/MainActivity.kt
index 606de519a..24d2bdc1c 100644
--- a/app/src/main/java/com/aurora/store/MainActivity.kt
+++ b/app/src/main/java/com/aurora/store/MainActivity.kt
@@ -21,7 +21,10 @@ package com.aurora.store
import android.Manifest
import android.content.Intent
+import android.os.Build
import android.os.Bundle
+import android.os.Environment
+import android.provider.Settings
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -36,15 +39,14 @@ import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.Navigation
import androidx.navigation.ui.NavigationUI
+import com.aurora.extensions.getEmptyActivityBundle
+import com.aurora.extensions.getStyledAttributeColor
+import com.aurora.extensions.load
+import com.aurora.extensions.open
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.ActivityMainBinding
import com.aurora.store.util.Log
-import com.aurora.store.util.ViewUtil
-import com.aurora.store.util.ViewUtil.getStyledAttribute
-import com.aurora.store.util.extensions.isQAndAbove
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.open
import com.aurora.store.view.ui.about.AboutActivity
import com.aurora.store.view.ui.account.AccountActivity
import com.aurora.store.view.ui.all.AppsGamesActivity
@@ -68,6 +70,18 @@ class MainActivity : BaseActivity() {
private var lastBackPressed = 0L
+ override fun onConnected() {
+ hideNetworkConnectivitySheet()
+ }
+
+ override fun onDisconnected() {
+ showNetworkConnectivitySheet()
+ }
+
+ override fun onReconnected() {
+
+ }
+
companion object {
@JvmStatic
private fun matchDestination(
@@ -97,16 +111,20 @@ class MainActivity : BaseActivity() {
attachSearch()
checkPermission()
+ checkStoragePermission()
}
private fun checkPermission() = runWithPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) {
- Log.i("External Storage Access Available")
+ Log.i("Required permissions available")
+ }
- if (isQAndAbove()) {
- //pickExternalFileDir()
+ private fun checkStoragePermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (!Environment.isExternalStorageManager())
+ startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))
}
}
@@ -116,8 +134,7 @@ class MainActivity : BaseActivity() {
}
B.viewToolbar.imgActionSecondary.setOnClickListener {
- val userAppIntent = Intent(this, DownloadActivity::class.java)
- startActivity(userAppIntent, ViewUtil.getEmptyActivityBundle(this))
+ open(DownloadActivity::class.java)
}
}
@@ -125,7 +142,7 @@ class MainActivity : BaseActivity() {
B.searchFab.setOnClickListener {
startActivity(
Intent(this, SearchSuggestionActivity::class.java),
- ViewUtil.getEmptyActivityBundle(this)
+ getEmptyActivityBundle()
)
}
}
@@ -134,7 +151,7 @@ class MainActivity : BaseActivity() {
val bottomNavigationView: BottomNavigationView = B.navView
navController = Navigation.findNavController(this, R.id.nav_host_fragment)
- val backGroundColor = getStyledAttribute(this, android.R.attr.colorBackground)
+ val backGroundColor = getStyledAttributeColor(android.R.attr.colorBackground)
bottomNavigationView.setBackgroundColor(ColorUtils.setAlphaComponent(backGroundColor, 245))
@@ -215,18 +232,6 @@ class MainActivity : BaseActivity() {
}
}
- override fun onConnected() {
- hideNetworkConnectivitySheet()
- }
-
- override fun onDisconnected() {
- showNetworkConnectivitySheet()
- }
-
- override fun onReconnected() {
-
- }
-
private fun pickExternalFileDir() {
val getContentIntent = Intent(Intent.ACTION_GET_CONTENT)
getContentIntent.type = "*/*"
diff --git a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt
index 32acba5e1..dd266875e 100644
--- a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt
+++ b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt
@@ -20,12 +20,37 @@
package com.aurora.store.data.event
sealed class BusEvent {
- data class InstallEvent(var packageName: String, var error: String = String()) : BusEvent()
- data class UninstallEvent(var packageName: String, var error: String = String()) : BusEvent()
- data class Blacklisted(var packageName: String, var error: String = String()) : BusEvent()
+ data class InstallEvent(
+ var packageName: String,
+ var extra: String? = ""
+ ) : BusEvent()
+
+ data class UninstallEvent(
+ var packageName: String,
+ var extra: String? = ""
+ ) : BusEvent()
+
+ data class Blacklisted(
+ var packageName: String,
+ var error: String? = ""
+ ) : BusEvent()
+
data class GoogleAAS(
var success: Boolean,
var email: String = String(),
var aasToken: String = String()
) : BusEvent()
+}
+
+sealed class InstallerEvent {
+ data class Success(
+ var packageName: String? = "",
+ var extra: String? = ""
+ ) : InstallerEvent()
+
+ data class Failed(
+ var packageName: String? = "",
+ var error: String? = "",
+ var extra: String? = ""
+ ) : InstallerEvent()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt
index dd38a6587..1b6fe0f98 100644
--- a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt
@@ -25,9 +25,7 @@ import com.aurora.store.data.SingletonHolder
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
-open class AppInstaller private constructor(var context: Context) {
-
- companion object : SingletonHolder(::AppInstaller)
+open class AppInstaller constructor(var context: Context) {
fun getPreferredInstaller(): IInstaller {
val prefValue = Preferences.getInteger(
diff --git a/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt b/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt
index 20514489d..f08c24a99 100644
--- a/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt
@@ -24,23 +24,22 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.content.FileProvider
+import com.aurora.store.AuroraApplication
import com.aurora.store.BuildConfig
import java.io.File
abstract class InstallerBase(protected var context: Context) : IInstaller {
- private val enqueuedInstalls: MutableSet = mutableSetOf()
-
override fun clearQueue() {
- enqueuedInstalls.clear()
+ AuroraApplication.enqueuedInstalls.clear()
}
override fun isAlreadyQueued(packageName: String): Boolean {
- return enqueuedInstalls.contains(packageName)
+ return AuroraApplication.enqueuedInstalls.contains(packageName)
}
override fun removeFromInstallQueue(packageName: String) {
- enqueuedInstalls.remove(packageName)
+ AuroraApplication.enqueuedInstalls.remove(packageName)
}
override fun uninstall(packageName: String) {
diff --git a/app/src/main/java/com/aurora/store/data/installer/InstallerService.kt b/app/src/main/java/com/aurora/store/data/installer/InstallerService.kt
index af0a3d2af..3272241da 100644
--- a/app/src/main/java/com/aurora/store/data/installer/InstallerService.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/InstallerService.kt
@@ -25,52 +25,69 @@ import android.content.pm.PackageInstaller
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
-import org.apache.commons.lang3.StringUtils
+import com.aurora.store.R
+import com.aurora.store.data.event.InstallerEvent
+import com.aurora.store.util.Log
+import org.greenrobot.eventbus.EventBus
class InstallerService : Service() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
- val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
+ val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -69)
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
-
- //Send broadcast for the installation status of the package
- sendStatusBroadcast(status, packageName)
-
- //Launch user confirmation activity
- if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
- val confirmationIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT)
- confirmationIntent!!.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
- confirmationIntent.putExtra(
- Intent.EXTRA_INSTALLER_PACKAGE_NAME,
- "com.android.vending"
- )
- confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- try {
- startActivity(confirmationIntent)
- } catch (e: Exception) {
- sendStatusBroadcast(PackageInstaller.STATUS_FAILURE, packageName)
- }
+ val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ when (status) {
+ PackageInstaller.STATUS_PENDING_USER_ACTION -> promptUser(intent)
+ else -> postStatus(status, packageName, extra)
}
+
stopSelf()
return START_NOT_STICKY
}
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- private fun sendStatusBroadcast(status: Int, packageName: String?) {
- if (StringUtils.isNotEmpty(packageName)) {
- val statusIntent = Intent(ACTION_SESSION_INSTALLER)
- statusIntent.putExtra(PackageInstaller.EXTRA_STATUS, status)
- statusIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName)
- sendBroadcast(statusIntent)
+ private fun promptUser(intent: Intent) {
+ val confirmationIntent: Intent? = intent.getParcelableExtra(Intent.EXTRA_INTENT)
+
+ confirmationIntent?.let {
+ it.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
+ it.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, "com.android.vending")
+ it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ try {
+ startActivity(it)
+ } catch (e: Exception) {
+ Log.e(e.message)
+ }
+ }
+ }
+
+ private fun postStatus(status: Int, packageName: String?, extra: String?) {
+ when (status) {
+ PackageInstaller.STATUS_SUCCESS -> {
+ EventBus.getDefault().post(InstallerEvent.Success(packageName, "Success"))
+ }
+ else -> {
+ val errorString = getErrorString(status)
+ Log.e("$packageName : $errorString")
+ EventBus.getDefault().post(InstallerEvent.Failed(packageName, errorString, extra))
+ }
+ }
+ }
+
+ private fun getErrorString(status: Int): String {
+ return when (status) {
+ PackageInstaller.STATUS_FAILURE_ABORTED -> getString(R.string.installer_status_user_action)
+ PackageInstaller.STATUS_FAILURE_BLOCKED -> getString(R.string.installer_status_failure_blocked)
+ PackageInstaller.STATUS_FAILURE_CONFLICT -> getString(R.string.installer_status_failure_conflict)
+ PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> getString(R.string.installer_status_failure_incompatible)
+ PackageInstaller.STATUS_FAILURE_INVALID -> getString(R.string.installer_status_failure_invalid)
+ PackageInstaller.STATUS_FAILURE_STORAGE -> getString(R.string.installer_status_failure_storage)
+ else -> getString(R.string.installer_status_failure)
}
}
override fun onBind(intent: Intent): IBinder? {
return null
}
-
- companion object {
- private const val ACTION_SESSION_INSTALLER = "ACTION_SESSION_INSTALLER"
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt
index 438e7aea9..11a442ccb 100644
--- a/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt
@@ -20,11 +20,12 @@
package com.aurora.store.data.installer
import android.content.Context
+import com.aurora.extensions.isLAndAbove
import com.aurora.store.R
+import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.util.Log
-import com.aurora.store.util.extensions.isLAndAbove
-import com.aurora.store.util.extensions.toast
import com.topjohnwu.superuser.Shell
+import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.regex.Pattern
@@ -50,7 +51,12 @@ class RootInstaller(context: Context) : InstallerBase(context) {
xInstallLegacy(packageName, it)
}
} else {
- context.toast(context.getString(R.string.installer_root_unavailable))
+ val event = InstallerEvent.Failed(
+ packageName,
+ context.getString(R.string.installer_status_failure),
+ context.getString(R.string.installer_root_unavailable)
+ )
+ EventBus.getDefault().post(event)
Log.e(" >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
}
}
@@ -80,12 +86,34 @@ class RootInstaller(context: Context) : InstallerBase(context) {
.exec()
}
- Shell.su("pm install-commit $sessionId").exec()
+ val shellResult = Shell.su("pm install-commit $sessionId").exec()
+
+ if (!shellResult.isSuccess) {
+ removeFromInstallQueue(packageName)
+ val event = InstallerEvent.Failed(
+ packageName,
+ context.getString(R.string.installer_status_failure),
+ parseError(shellResult)
+ )
+ EventBus.getDefault().post(event)
+ }
} else {
removeFromInstallQueue(packageName)
+ val event = InstallerEvent.Failed(
+ packageName,
+ context.getString(R.string.installer_status_failure),
+ context.getString(R.string.installer_root_unavailable)
+ )
+ EventBus.getDefault().post(event)
}
} else {
removeFromInstallQueue(packageName)
+ val event = InstallerEvent.Failed(
+ packageName,
+ context.getString(R.string.installer_status_failure),
+ context.getString(R.string.installer_status_failure_session)
+ )
+ EventBus.getDefault().post(event)
}
}
@@ -96,4 +124,8 @@ class RootInstaller(context: Context) : InstallerBase(context) {
removeFromInstallQueue(packageName)
}
}
+
+ private fun parseError(result: Shell.Result): String {
+ return result.err.joinToString(separator = "\n")
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt
index b9dd86cf6..591125aac 100644
--- a/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt
@@ -28,10 +28,15 @@ import android.os.Build
import android.os.IBinder
import android.os.RemoteException
import androidx.annotation.RequiresApi
+import androidx.core.content.FileProvider
import com.aurora.services.IPrivilegedCallback
import com.aurora.services.IPrivilegedService
import com.aurora.store.BuildConfig
+import com.aurora.store.R
+import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.util.Log
+import com.aurora.store.util.PackageUtil
+import org.greenrobot.eventbus.EventBus
import java.io.File
class ServiceInstaller(context: Context) : InstallerBase(context) {
@@ -44,47 +49,81 @@ class ServiceInstaller(context: Context) : InstallerBase(context) {
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun install(packageName: String, files: List) {
- if (isAlreadyQueued(packageName)) {
- Log.i("$packageName already queued")
- } else {
- Log.i("Received service install request for $packageName")
- val uriList = files.map {
- when (it) {
- is File -> getUri(it)
- is String -> getUri(File(it))
- else -> {
- throw Exception("Invalid data, expecting listOf() File or String")
- }
- }
+
+ when {
+ isAlreadyQueued(packageName) -> {
+ Log.i("$packageName already queued")
}
- xInstall(packageName, uriList)
+ PackageUtil.isInstalled(context, PRIVILEGED_EXTENSION_PACKAGE_NAME) -> {
+ Log.i("Received service install request for $packageName")
+ val uriList = files.map {
+ when (it) {
+ is File -> getUri(it)
+ is String -> getUri(File(it))
+ else -> {
+ throw Exception("Invalid data, expecting listOf() File or String")
+ }
+ }
+ }
+
+ xInstall(packageName, uriList)
+ }
+ else -> {
+ val event = InstallerEvent.Failed(
+ packageName,
+ context.getString(R.string.installer_status_failure),
+ context.getString(R.string.installer_service_unavailable)
+ )
+ EventBus.getDefault().post(event)
+ }
}
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun xInstall(packageName: String, uriList: List) {
-
val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
val service = IPrivilegedService.Stub.asInterface(binder)
- val callback = object : IPrivilegedCallback.Stub() {
- override fun handleResult(packageName: String, returnCode: Int) {
- removeFromInstallQueue(packageName)
- }
- }
- try {
- service.installSplitPackage(
+ if (service.hasPrivilegedPermissions()) {
+ Log.i(context.getString(R.string.installer_service_available))
+ val callback = object : IPrivilegedCallback.Stub() {
+ override fun handleResult(packageName: String, returnCode: Int) {
+ Log.e("$packageName : $returnCode")
+ removeFromInstallQueue(packageName)
+ }
+ }
+
+ try {
+ service.installSplitPackage(
+ packageName,
+ uriList,
+ ACTION_INSTALL_REPLACE_EXISTING,
+ BuildConfig.APPLICATION_ID,
+ callback
+ )
+ } catch (e: RemoteException) {
+ removeFromInstallQueue(packageName)
+ EventBus
+ .getDefault()
+ .post(
+ InstallerEvent.Failed(
+ packageName,
+ e.localizedMessage,
+ e.stackTraceToString()
+ )
+ )
+ Log.e("Failed to connect Aurora Services")
+ }
+ } else {
+ Log.e(context.getString(R.string.installer_service_misconfigured))
+ val event = InstallerEvent.Failed(
packageName,
- uriList,
- ACTION_INSTALL_REPLACE_EXISTING,
- BuildConfig.APPLICATION_ID,
- callback
+ context.getString(R.string.installer_status_failure),
+ context.getString(R.string.installer_service_misconfigured)
)
- } catch (e: RemoteException) {
- removeFromInstallQueue(packageName)
- Log.e("Failed to connect Aurora Services")
+ EventBus.getDefault().post(event)
}
}
@@ -103,4 +142,20 @@ class ServiceInstaller(context: Context) : InstallerBase(context) {
Context.BIND_AUTO_CREATE
)
}
+
+ override fun getUri(file: File): Uri {
+ val uri = FileProvider.getUriForFile(
+ context,
+ "${BuildConfig.APPLICATION_ID}.fileProvider",
+ file
+ )
+
+ context.grantUriPermission(
+ PRIVILEGED_EXTENSION_PACKAGE_NAME,
+ uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
+
+ return uri
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt
index c87138a9a..63e72e17b 100644
--- a/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt
+++ b/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt
@@ -27,9 +27,12 @@ import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider
+import com.aurora.extensions.isNAndAbove
import com.aurora.store.BuildConfig
+import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.util.Log
import org.apache.commons.io.IOUtils
+import org.greenrobot.eventbus.EventBus
import java.io.File
class SessionInstaller(context: Context) : InstallerBase(context) {
@@ -57,18 +60,22 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun xInstall(packageName: String, uriList: List) {
val packageInstaller = context.packageManager.packageInstaller
- val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setAppPackageName(packageName)
+ if (isNAndAbove()) {
+ setOriginatingUid(android.os.Process.myUid())
+ }
+ }
val sessionId = packageInstaller.createSession(sessionParams)
val session = packageInstaller.openSession(sessionId)
try {
-
Log.i("Writing splits to session for $packageName")
- var apkId = 1
+
for (uri in uriList) {
val inputStream = context.contentResolver.openInputStream(uri)
val outputStream = session.openWrite(
- "${packageName}_${apkId++}",
+ "${packageName}_${System.currentTimeMillis()}",
0,
-1
)
@@ -81,11 +88,11 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
IOUtils.close(outputStream)
}
- val intent = Intent(context, InstallerService::class.java)
+ val callBackIntent = Intent(context, InstallerService::class.java)
val pendingIntent = PendingIntent.getService(
context,
sessionId,
- intent,
+ callBackIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
@@ -95,6 +102,12 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
} catch (e: Exception) {
session.abandon()
removeFromInstallQueue(packageName)
+ val event = InstallerEvent.Failed(
+ packageName,
+ e.localizedMessage,
+ e.stackTraceToString()
+ )
+ EventBus.getDefault().post(event)
Log.e("Failed to install $packageName : %s", e.message)
}
}
@@ -106,13 +119,11 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
file
)
- uri.apply {
- context.grantUriPermission(
- BuildConfig.APPLICATION_ID,
- uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION
- )
- }
+ context.grantUriPermission(
+ BuildConfig.APPLICATION_ID,
+ uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
return uri
}
diff --git a/app/src/main/java/com/aurora/store/data/model/AppPermission.kt b/app/src/main/java/com/aurora/store/data/model/AppPermission.kt
new file mode 100644
index 000000000..98cd19d74
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/data/model/AppPermission.kt
@@ -0,0 +1,39 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.data.model
+
+data class AppPermission(
+ var name: String,
+ var permission: String,
+ var raw: String,
+ var flags: Int,
+ var icon: Int
+) {
+ override fun equals(other: Any?): Boolean {
+ return when (other) {
+ is AppPermission -> other.permission == permission
+ else -> false
+ }
+ }
+
+ override fun hashCode(): Int {
+ return permission.hashCode()
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/data/providers/SpoofProvider.kt b/app/src/main/java/com/aurora/store/data/providers/SpoofProvider.kt
index 084687423..a606afce0 100644
--- a/app/src/main/java/com/aurora/store/data/providers/SpoofProvider.kt
+++ b/app/src/main/java/com/aurora/store/data/providers/SpoofProvider.kt
@@ -20,16 +20,15 @@
package com.aurora.store.data.providers
import android.content.Context
-import com.aurora.store.data.SingletonHolder
import com.aurora.store.util.Preferences
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.lang.reflect.Modifier
import java.util.*
-class SpoofProvider private constructor(var context: Context) {
+class SpoofProvider constructor(var context: Context) {
- companion object : SingletonHolder(::SpoofProvider) {
+ companion object {
const val LOCALE_SPOOF_ENABLED = "LOCALE_SPOOF_ENABLED"
const val LOCALE_SPOOF_LANG = "LOCALE_SPOOF_LANG"
const val LOCALE_SPOOF_COUNTRY = "LOCALE_SPOOF_COUNTRY"
diff --git a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt
index 8e2703657..90854ac28 100644
--- a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt
+++ b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt
@@ -28,6 +28,7 @@ import com.aurora.store.data.event.BusEvent.UninstallEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.util.PathUtil
import com.aurora.store.util.Preferences
+import com.aurora.store.util.isExternalStorageEnable
import org.greenrobot.eventbus.EventBus
import java.io.File
@@ -39,19 +40,20 @@ open class PackageManagerReceiver : BroadcastReceiver() {
when (intent.action) {
Intent.ACTION_PACKAGE_ADDED -> {
- EventBus.getDefault()
- .post(InstallEvent(packageName, ""))
-
- //Clear installation queue
- AppInstaller.with(context)
- .getPreferredInstaller()
- .removeFromInstallQueue(packageName)
+ EventBus.getDefault().post(InstallEvent(packageName, ""))
+ }
+
+ Intent.ACTION_PACKAGE_REMOVED -> {
+ EventBus.getDefault().post(UninstallEvent(packageName, ""))
}
- Intent.ACTION_PACKAGE_REMOVED -> EventBus.getDefault()
- .post(UninstallEvent(packageName, ""))
}
- clearNotification(context, packageName)
+ //Clear installation queue
+ AppInstaller(context)
+ .getPreferredInstaller()
+ .removeFromInstallQueue(packageName)
+
+ //clearNotification(context, packageName)
val isAutoDeleteAPKEnabled = Preferences.getBoolean(
context,
diff --git a/app/src/main/java/com/aurora/store/data/service/NotificationService.kt b/app/src/main/java/com/aurora/store/data/service/NotificationService.kt
index 2dd2b7946..98677e079 100644
--- a/app/src/main/java/com/aurora/store/data/service/NotificationService.kt
+++ b/app/src/main/java/com/aurora/store/data/service/NotificationService.kt
@@ -29,9 +29,12 @@ import android.util.ArrayMap
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.aurora.Constants
+import com.aurora.extensions.getStyledAttributeColor
+import com.aurora.extensions.isLAndAbove
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager
+import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.receiver.DownloadCancelReceiver
import com.aurora.store.data.receiver.DownloadPauseReceiver
@@ -39,13 +42,14 @@ import com.aurora.store.data.receiver.DownloadResumeReceiver
import com.aurora.store.data.receiver.InstallReceiver
import com.aurora.store.util.CommonUtil
import com.aurora.store.util.Log
-import com.aurora.store.util.extensions.isLAndAbove
import com.aurora.store.view.ui.details.AppDetailsActivity
import com.aurora.store.view.ui.downloads.DownloadActivity
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.tonyodev.fetch2.*
import org.apache.commons.lang3.StringUtils
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
import java.lang.reflect.Modifier
import java.util.*
@@ -83,6 +87,8 @@ class NotificationService : Service() {
Log.i("Notification Service Started")
+ EventBus.getDefault().register(this)
+
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
//Create Notification Channels : General & Alert
@@ -97,9 +103,6 @@ class NotificationService : Service() {
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
showNotification(groupId, download, fetchGroup)
- if (fetchGroup.groupDownloadProgress == 100) {
- install(download.tag!!, fetchGroup.downloads)
- }
}
override fun onError(
@@ -214,8 +217,8 @@ class NotificationService : Service() {
}
val progress = fetchGroup.groupDownloadProgress
val progressBigText = NotificationCompat.BigTextStyle()
- when (status) {
+ when (status) {
Status.QUEUED -> {
builder.setProgress(100, 0, true)
progressBigText.bigText(getString(R.string.download_queued))
@@ -308,7 +311,6 @@ class NotificationService : Service() {
app.id,
builder.build()
)
- //}
}
private fun getPauseIntent(groupId: Int): PendingIntent {
@@ -356,9 +358,28 @@ class NotificationService : Service() {
)
}
+ @Subscribe()
+ fun onEventMainThread(event: Any) {
+ when (event) {
+ is InstallerEvent.Success -> {
+ val app = appMap[event.packageName.hashCode()]
+ if (app != null)
+ notifyInstallationStatus(app, event.extra)
+ }
+ is InstallerEvent.Failed -> {
+ val app = appMap[event.packageName.hashCode()]
+ if (app != null)
+ notifyInstallationStatus(app, event.error)
+ }
+ else -> {
+
+ }
+ }
+ }
+
@Synchronized
private fun install(packageName: String, files: List) {
- AppInstaller.with(this)
+ AppInstaller(this)
.getPreferredInstaller()
.install(
packageName,
@@ -370,9 +391,25 @@ class NotificationService : Service() {
)
}
+ private fun notifyInstallationStatus(app: App, status: String?) {
+ val builder = NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_ALERT)
+ builder.color = getStyledAttributeColor(R.attr.colorAccent)
+ builder.setSmallIcon(R.drawable.ic_install)
+ builder.setContentTitle(app.displayName)
+ builder.setContentText(status)
+ builder.setSubText(app.packageName)
+
+ notificationManager.notify(
+ app.packageName,
+ app.id,
+ builder.build()
+ )
+ }
+
override fun onDestroy() {
Log.i("Notification Service Stopped")
fetch.removeListener(fetchListener)
+ EventBus.getDefault().unregister(this);
super.onDestroy()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/util/AC2DMTask.kt b/app/src/main/java/com/aurora/store/util/AC2DMTask.kt
index 334431cbb..e8b0bac3e 100644
--- a/app/src/main/java/com/aurora/store/util/AC2DMTask.kt
+++ b/app/src/main/java/com/aurora/store/util/AC2DMTask.kt
@@ -52,7 +52,7 @@ class AC2DMTask {
.response()
return response.third.fold(success = {
- Util.parseResponse(String(it))
+ AC2DMUtil.parseResponse(String(it))
}, failure = {
mapOf()
})
diff --git a/app/src/main/java/com/aurora/store/util/Util.java b/app/src/main/java/com/aurora/store/util/AC2DMUtil.java
similarity index 98%
rename from app/src/main/java/com/aurora/store/util/Util.java
rename to app/src/main/java/com/aurora/store/util/AC2DMUtil.java
index e08648db5..91f959e1a 100644
--- a/app/src/main/java/com/aurora/store/util/Util.java
+++ b/app/src/main/java/com/aurora/store/util/AC2DMUtil.java
@@ -25,7 +25,7 @@ import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class Util {
+public class AC2DMUtil {
public static Map parseResponse(String response) {
Map keyValueMap = new HashMap();
diff --git a/app/src/main/java/com/aurora/store/util/ApkCopier.kt b/app/src/main/java/com/aurora/store/util/ApkCopier.kt
index c1f690735..d6f8c95b0 100644
--- a/app/src/main/java/com/aurora/store/util/ApkCopier.kt
+++ b/app/src/main/java/com/aurora/store/util/ApkCopier.kt
@@ -22,12 +22,9 @@ package com.aurora.store.util
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
-import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
-import androidx.core.content.FileProvider
-import com.aurora.store.BuildConfig
-import com.aurora.store.util.extensions.isLAndAbove
+import com.aurora.extensions.isLAndAbove
import org.apache.commons.io.IOUtils
import java.io.File
import java.io.FileOutputStream
@@ -111,12 +108,4 @@ class ApkCopier(private val context: Context, private val packageName: String) {
Log.e("ApkCopier : %s", e.message)
}
}
-
- fun getUri(file: File): Uri {
- return FileProvider.getUriForFile(
- context,
- "${BuildConfig.APPLICATION_ID}.fileProvider",
- file
- )
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/util/CertUtil.kt b/app/src/main/java/com/aurora/store/util/CertUtil.kt
index e2d18353e..4e924b1b3 100644
--- a/app/src/main/java/com/aurora/store/util/CertUtil.kt
+++ b/app/src/main/java/com/aurora/store/util/CertUtil.kt
@@ -21,7 +21,7 @@ package com.aurora.store.util
import android.content.Context
import android.content.pm.PackageManager
-import com.aurora.store.util.extensions.isPAndAbove
+import com.aurora.extensions.isPAndAbove
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.cert.CertificateFactory
@@ -35,22 +35,24 @@ object CertUtil {
private fun getX509Certificates(
context: Context,
packageName: String
- ): List {
- val certificates: MutableList = mutableListOf()
+ ): List {
+ val certificates: MutableList = mutableListOf()
val packageManager = context.applicationContext.packageManager
try {
- val packageInfo = if (isPAndAbove())
+ val packageInfo = if (isPAndAbove()) {
packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNING_CERTIFICATES
)
- else
+ }
+ else {
packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
+ }
val certificateFactory = CertificateFactory.getInstance("X509")
@@ -63,7 +65,7 @@ object CertUtil {
)
}
} else {
- for (i in 0..packageInfo.signatures.size) {
+ for (i in packageInfo.signatures.indices) {
val bytes = packageInfo.signatures[i].toByteArray()
val inStream: InputStream = ByteArrayInputStream(bytes)
certificates.add(
@@ -72,7 +74,7 @@ object CertUtil {
}
}
} catch (e: Exception) {
- Log.e(e.message)
+
}
return certificates
@@ -85,13 +87,10 @@ object CertUtil {
false
else {
val cert = certificates[0]
- if (cert != null) {
- if (cert.subjectDN != null) {
- val DN = cert.subjectDN.name.toUpperCase(Locale.getDefault())
- DN.contains(FDROID) || DN.contains(GUARDIAN)
- } else {
- false
- }
+
+ if (cert.subjectDN != null) {
+ val DN = cert.subjectDN.name.toUpperCase(Locale.getDefault())
+ DN.contains(FDROID) || DN.contains(GUARDIAN)
} else {
false
}
diff --git a/app/src/main/java/com/aurora/store/util/CommonUtil.kt b/app/src/main/java/com/aurora/store/util/CommonUtil.kt
index 1fb79e36b..1e647c108 100644
--- a/app/src/main/java/com/aurora/store/util/CommonUtil.kt
+++ b/app/src/main/java/com/aurora/store/util/CommonUtil.kt
@@ -21,8 +21,6 @@ package com.aurora.store.util
import android.content.Context
import android.os.Build
-import android.os.Bundle
-import androidx.core.app.ActivityOptionsCompat
import com.aurora.store.R
import java.text.DecimalFormat
import java.util.*
@@ -37,6 +35,7 @@ object CommonUtil {
Pair(6, " MB"),
Pair(9, " GB")
)
+
private val diPrefixes: Map = hashMapOf(
Pair(1, ""),
Pair(3, " K"),
@@ -134,14 +133,6 @@ object CommonUtil {
}
}
- fun getEmptyActivityBundle(context: Context): Bundle? {
- return ActivityOptionsCompat.makeCustomAnimation(
- context,
- android.R.anim.fade_in,
- android.R.anim.fade_out
- ).toBundle()
- }
-
fun cleanupInstallationSessions(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val packageInstaller = context.packageManager.packageInstaller
diff --git a/app/src/main/java/com/aurora/store/util/PackageUtil.kt b/app/src/main/java/com/aurora/store/util/PackageUtil.kt
index a6f80058d..59c432381 100644
--- a/app/src/main/java/com/aurora/store/util/PackageUtil.kt
+++ b/app/src/main/java/com/aurora/store/util/PackageUtil.kt
@@ -51,6 +51,19 @@ object PackageUtil {
}
}
+ fun getInstalledVersion(context: Context, packageName: String): String {
+ return try {
+ val packageInfo = getPackageInfo(context, packageName)
+ if (packageInfo != null) {
+ "${packageInfo.versionName}.${packageInfo.versionCode}"
+ } else {
+ ""
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ ""
+ }
+ }
+
fun isTv(context: Context): Boolean {
val uiMode = context.resources.configuration.uiMode
return uiMode and Configuration.UI_MODE_TYPE_MASK == Configuration.UI_MODE_TYPE_TELEVISION
@@ -111,7 +124,7 @@ object PackageUtil {
}
if (isFdroidFilterEnabled) {
- packageInfoList
+ packageInfoList = packageInfoList
.filter {
val packageInstaller = packageManager.getInstallerPackageName(it.packageName)
packageInstaller != "org.fdroid.fdroid.privileged"
diff --git a/app/src/main/java/com/aurora/store/util/PathUtil.kt b/app/src/main/java/com/aurora/store/util/PathUtil.kt
index 2d442982a..c23dafc66 100644
--- a/app/src/main/java/com/aurora/store/util/PathUtil.kt
+++ b/app/src/main/java/com/aurora/store/util/PathUtil.kt
@@ -21,9 +21,9 @@ package com.aurora.store.util
import android.content.Context
import android.os.Environment
+import com.aurora.extensions.isLAndAbove
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.File
-import com.aurora.store.util.extensions.isLAndAbove
fun Context.getInternalBaseDirectory(): String {
return filesDir.path
@@ -32,14 +32,18 @@ fun Context.getInternalBaseDirectory(): String {
object PathUtil {
private fun getDownloadDirectory(context: Context): String {
- return if (isLAndAbove())
- context.getInternalBaseDirectory() + "/Downloads"
- else
+ return if (isLAndAbove()) {
+ if (context.isExternalStorageEnable())
+ getExternalPath()
+ else
+ context.getInternalBaseDirectory() + "/Downloads"
+ } else {
getExternalPath()
+ }
}
fun getPackageDirectory(context: Context, packageName: String): String {
- return getDownloadDirectory(context) + "/$packageName"
+ return getDownloadDirectory(context) + "/Downloads/$packageName"
}
private fun getVersionDirectory(
@@ -59,11 +63,11 @@ object PathUtil {
}
fun getExternalPath(): String {
- return Environment.getExternalStorageDirectory().toString() + "/Aurora/"
+ return Environment.getExternalStorageDirectory().toString() + "/Aurora/Store"
}
fun getBaseCopyDirectory(): String {
- return "${getExternalPath()}/files/export/"
+ return "${getExternalPath()}/Exports/"
}
private fun getObbDownloadPath(context: Context, app: App): String {
@@ -75,4 +79,8 @@ object PathUtil {
val obbDir = getObbDownloadPath(context, app)
return "$obbDir/${file.name}"
}
+}
+
+fun Context.isExternalStorageEnable(): Boolean {
+ return Preferences.getBoolean(this, Preferences.PREFERENCE_DOWNLOAD_EXTERNAL)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/util/Preferences.kt b/app/src/main/java/com/aurora/store/util/Preferences.kt
index c539146b5..2651f7caf 100644
--- a/app/src/main/java/com/aurora/store/util/Preferences.kt
+++ b/app/src/main/java/com/aurora/store/util/Preferences.kt
@@ -43,6 +43,7 @@ object Preferences {
const val INSTALLATION_ABANDON_SESSION = "INSTALLATION_ABANDON_SESSION"
const val PREFERENCE_DOWNLOAD_ACTIVE = "PREFERENCE_DOWNLOAD_ACTIVE"
+ const val PREFERENCE_DOWNLOAD_EXTERNAL = "PREFERENCE_DOWNLOAD_EXTERNAL"
const val PREFERENCE_DOWNLOAD_WIFI = "PREFERENCE_DOWNLOAD_WIFI"
@@ -91,26 +92,17 @@ object Preferences {
}
}
-fun Context.save(key: String, value: Int) {
- Preferences.putInteger(this, key, value)
-}
+/*Preference Extensions*/
-fun Fragment.save(key: String, value: Int) {
- Preferences.putInteger(requireContext(), key, value)
-}
+fun Context.save(key: String, value: Int) = Preferences.putInteger(this, key, value)
-fun Context.save(key: String, value: Boolean) {
- Preferences.putBoolean(this, key, value)
-}
+fun Context.save(key: String, value: Boolean) = Preferences.putBoolean(this, key, value)
-fun Fragment.save(key: String, value: Boolean) {
- Preferences.putBoolean(requireContext(), key, value)
-}
+fun Context.save(key: String, value: String) = Preferences.putString(this, key, value)
-fun Context.save(key: String, value: String) {
- Preferences.putString(this, key, value)
-}
-fun Fragment.save(key: String, value: String) {
- Preferences.putString(requireContext(), key, value)
-}
+fun Fragment.save(key: String, value: Int) = requireContext().save(key, value)
+
+fun Fragment.save(key: String, value: Boolean) = requireContext().save(key, value)
+
+fun Fragment.save(key: String, value: String) = requireContext().save(key, value)
diff --git a/app/src/main/java/com/aurora/store/util/ViewUtil.kt b/app/src/main/java/com/aurora/store/util/ViewUtil.kt
deleted file mode 100644
index 2bf47cb31..000000000
--- a/app/src/main/java/com/aurora/store/util/ViewUtil.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Aurora Store
- * Copyright (C) 2021, Rahul Kumar Patel
- *
- * Aurora Store is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * Aurora Store is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Aurora Store. If not, see .
- *
- */
-
-package com.aurora.store.util
-
-import android.app.Activity
-import android.content.Context
-import android.graphics.Color
-import android.os.Build
-import android.os.Bundle
-import android.util.TypedValue
-import android.view.View
-import android.view.WindowManager
-import androidx.annotation.RequiresApi
-import androidx.core.app.ActivityOptionsCompat
-
-
-object ViewUtil {
-
- fun getEmptyActivityBundle(context: Context?): Bundle? {
- return ActivityOptionsCompat.makeCustomAnimation(
- context!!,
- android.R.anim.fade_in,
- android.R.anim.fade_out
- ).toBundle()
- }
-
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
- fun configureActivityLayout(activity: Activity, isLight: Boolean) {
- val window = activity.window
- val params = window.attributes
- params.flags = params.flags and WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- window.attributes = params
- window.statusBarColor = Color.TRANSPARENT
- setFullScreenLightStatusBar(activity, isLight)
- }
-
- private fun setFullScreenLightStatusBar(activity: Activity, isLight: Boolean) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- var flags = activity.window.decorView.systemUiVisibility
- if (isLight)
- flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
- flags = flags or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- flags = flags or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- activity.window.decorView.systemUiVisibility = flags
- }
- }
-
- fun getStyledAttribute(context: Context, styleID: Int): Int {
- val arr = context.obtainStyledAttributes(TypedValue().data, intArrayOf(styleID))
- val styledColor = arr.getColor(0, Color.WHITE)
- arr.recycle()
- return styledColor
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/custom/layouts/PermissionGroup.kt b/app/src/main/java/com/aurora/store/view/custom/layouts/PermissionGroup.kt
new file mode 100644
index 000000000..26840b18c
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/custom/layouts/PermissionGroup.kt
@@ -0,0 +1,166 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+package com.aurora.store.view.custom.layouts
+
+import android.annotation.TargetApi
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import android.content.res.Resources.NotFoundException
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.util.AttributeSet
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import com.aurora.extensions.showDialog
+import com.aurora.store.R
+import java.util.*
+
+class PermissionGroup : LinearLayout {
+
+ private lateinit var permissionGroupInfo: PermissionGroupInfo
+ private lateinit var packageManager: PackageManager
+
+ private val permissionMap: MutableMap = HashMap()
+
+ constructor(context: Context?) : super(context) {
+ init()
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
+ init()
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ ) {
+ init()
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ defStyleRes: Int
+ ) : super(context, attrs, defStyleAttr, defStyleRes) {
+ init()
+ }
+
+ private fun init() {
+ inflate(context, R.layout.view_permission, this)
+ packageManager = context.packageManager
+ }
+
+ fun setPermissionGroupInfo(permissionGroupInfo: PermissionGroupInfo) {
+ this.permissionGroupInfo = permissionGroupInfo
+ val imageView = findViewById(R.id.img)
+ imageView.setImageDrawable(getPermissionGroupIcon(permissionGroupInfo))
+ //imageView.setColorFilter(getContext().getStyledAttributeColor(imageView.getContext(), android.R.attr.colorAccent));
+ }
+
+ fun addPermission(permissionInfo: PermissionInfo) {
+ val title = permissionInfo.loadLabel(packageManager)
+ val description = permissionInfo.loadDescription(packageManager)
+
+ permissionMap[getReadableLabel(title.toString(), permissionInfo.packageName)] =
+ if (description.isNullOrEmpty())
+ "No description"
+ else
+ description.toString()
+
+ val permissionLabels: List = ArrayList(permissionMap.keys)
+ val permissionLabelsView = findViewById(R.id.permission_labels)
+ permissionLabelsView.removeAllViews()
+
+ permissionLabels
+ .filter { it.isNotEmpty() }
+ .sortedBy { it }
+ .forEach {
+ addPermissionLabel(
+ permissionLabelsView,
+ it,
+ permissionMap[it]
+ )
+ }
+ }
+
+ private fun addPermissionLabel(
+ permissionLabelsView: LinearLayout,
+ label: String,
+ description: String?
+ ) {
+ val textView = TextView(context)
+ textView.text = label
+ textView.setOnClickListener {
+ var title: String = permissionGroupInfo.loadLabel(packageManager).toString()
+
+ if (title.contains("UNDEFINED")) {
+ title = "Android"
+ }
+
+ title = title.capitalize(Locale.getDefault())
+
+ context.showDialog(title, description)
+ }
+
+ permissionLabelsView.addView(textView)
+ }
+
+ private fun getPermissionGroupIcon(permissionGroupInfo: PermissionGroupInfo?): Drawable? {
+ return try {
+ ContextCompat.getDrawable(context, permissionGroupInfo!!.icon)
+ } catch (e: NotFoundException) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
+ permissionGroupInfo!!.loadUnbadgedIcon(packageManager)
+ else
+ permissionGroupInfo!!.loadIcon(packageManager)
+ }
+ }
+
+ private fun getReadableLabel(label: String, packageName: String): String {
+ val prefixes: MutableList = mutableListOf(
+ "android",
+ packageName
+ )
+
+ if (label.contains("UNDEFINED")) {
+ return "Android"
+ }
+
+ prefixes
+ .map { "$it.permission." }
+ .forEach {
+ if (label.startsWith(it)) {
+ return it.replace(it, "")
+ .replace("_", " ")
+ .toLowerCase(Locale.getDefault())
+ .capitalize(Locale.getDefault())
+ }
+ }
+
+ return label.capitalize(Locale.getDefault())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/custom/ActionButton.kt b/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt
similarity index 57%
rename from app/src/main/java/com/aurora/store/view/custom/ActionButton.kt
rename to app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt
index 53bbe5ed6..3584cb0b4 100644
--- a/app/src/main/java/com/aurora/store/view/custom/ActionButton.kt
+++ b/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt
@@ -17,16 +17,22 @@
*
*/
-package com.aurora.store.view.custom
+package com.aurora.store.view.custom.layouts.button
import android.content.Context
+import android.content.res.ColorStateList
import android.os.Build
import android.util.AttributeSet
-import android.view.View
import android.widget.RelativeLayout
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
+import com.aurora.extensions.getString
+import com.aurora.extensions.isLAndAbove
+import com.aurora.extensions.runOnUiThread
import com.aurora.store.R
import com.aurora.store.databinding.ViewActionButtonBinding
+import nl.komponents.kovenant.task
+import java.util.concurrent.TimeUnit
class ActionButton : RelativeLayout {
@@ -64,23 +70,70 @@ class ActionButton : RelativeLayout {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ActionButton)
val btnTxt = typedArray.getString(R.styleable.ActionButton_btnActionText)
+
+ val btnTxtColor = typedArray.getResourceId(
+ R.styleable.ActionButton_btnActionTextColor,
+ R.color.colorWhite
+ )
+
+ val stateIcon = typedArray.getResourceId(
+ R.styleable.ActionButton_btnActionIcon,
+ R.drawable.ic_check
+ )
+
+ val stateColor = ContextCompat.getColor(context, btnTxtColor)
+
B.btn.text = btnTxt
+ B.btn.setTextColor(stateColor)
+ B.img.setImageDrawable(ContextCompat.getDrawable(context, stateIcon))
+ if (isLAndAbove()) {
+ B.img.imageTintList = ColorStateList.valueOf(stateColor)
+ }
+
typedArray.recycle()
}
fun setText(text: String) {
+ B.viewFlipper.displayedChild = 0
B.btn.text = text
}
- fun updateProgress(isVisible: Boolean) {
- if (isVisible)
- B.progress.visibility = View.VISIBLE
- else
+ fun setText(text: Int) {
+ B.viewFlipper.displayedChild = 0
+ B.btn.text = getString(text)
+ }
- B.progress.visibility = View.INVISIBLE
+ fun updateState(state: State) {
+ val displayChild = when (state) {
+ State.IDLE -> 0
+ State.PROGRESS -> 1
+ State.COMPLETE -> 2
+ }
+
+ if (B.viewFlipper.displayedChild != displayChild) {
+ runOnUiThread {
+ B.viewFlipper.displayedChild = displayChild
+ if (displayChild == 2)
+ switchToIdle()
+ }
+ }
+ }
+
+ private fun switchToIdle() {
+ task {
+ TimeUnit.SECONDS.sleep(3)
+ } success {
+ updateState(State.IDLE)
+ }
}
fun addOnClickListener(onClickListener: OnClickListener) {
B.btn.setOnClickListener(onClickListener)
}
+
+ enum class State {
+ IDLE,
+ PROGRESS,
+ COMPLETE,
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/custom/StateButton.kt b/app/src/main/java/com/aurora/store/view/custom/layouts/button/StateButton.kt
similarity index 98%
rename from app/src/main/java/com/aurora/store/view/custom/StateButton.kt
rename to app/src/main/java/com/aurora/store/view/custom/layouts/button/StateButton.kt
index 91b05954f..ed480e896 100644
--- a/app/src/main/java/com/aurora/store/view/custom/StateButton.kt
+++ b/app/src/main/java/com/aurora/store/view/custom/layouts/button/StateButton.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.custom
+package com.aurora.store.view.custom.layouts.button
import android.content.Context
import android.os.Build
diff --git a/app/src/main/java/com/aurora/store/view/custom/progress/AuroraProgressView.java b/app/src/main/java/com/aurora/store/view/custom/progress/AuroraProgressView.java
new file mode 100644
index 000000000..8459192e4
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/custom/progress/AuroraProgressView.java
@@ -0,0 +1,398 @@
+package com.aurora.store.view.custom.progress;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.aurora.store.R;
+import com.aurora.store.view.custom.progress.indicators.BallPulseIndicator;
+import com.aurora.store.view.custom.progress.indicators.Indicator;
+
+
+public class AuroraProgressView extends View {
+
+ private static final String TAG = "AuroraProgressView";
+
+ private static final BallPulseIndicator DEFAULT_INDICATOR = new BallPulseIndicator();
+
+ private static final int MIN_SHOW_TIME = 500; // ms
+ private static final int MIN_DELAY = 500; // ms
+
+ private long startTime = -1;
+
+ private boolean postedHide = false;
+ private boolean postedShow = false;
+ private boolean dismissed = false;
+
+ private final Runnable mDelayedHide = new Runnable() {
+
+ @Override
+ public void run() {
+ postedHide = false;
+ startTime = -1;
+ setVisibility(View.GONE);
+ }
+ };
+
+ private final Runnable mDelayedShow = new Runnable() {
+
+ @Override
+ public void run() {
+ postedShow = false;
+ if (!dismissed) {
+ startTime = System.currentTimeMillis();
+ setVisibility(View.VISIBLE);
+ }
+ }
+ };
+
+ int minWidth;
+ int maxWidth;
+ int minHeight;
+ int maxHeight;
+
+ private Indicator indicator;
+ private int indicatorColor;
+
+ private boolean mShouldStartAnimationDrawable;
+
+ public AuroraProgressView(Context context) {
+ super(context);
+ init(context, null, 0, 0);
+ }
+
+ public AuroraProgressView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0, R.style.AuroraProgressView);
+ }
+
+ public AuroraProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr, R.style.AuroraProgressView);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public AuroraProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs, defStyleAttr, R.style.AuroraProgressView);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ minWidth = 24;
+ maxWidth = 48;
+ minHeight = 24;
+ maxHeight = 48;
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.AuroraProgressView, defStyleAttr, defStyleRes);
+
+ minWidth = a.getDimensionPixelSize(R.styleable.AuroraProgressView_minWidth, minWidth);
+ maxWidth = a.getDimensionPixelSize(R.styleable.AuroraProgressView_maxWidth, maxWidth);
+ minHeight = a.getDimensionPixelSize(R.styleable.AuroraProgressView_minHeight, minHeight);
+ maxHeight = a.getDimensionPixelSize(R.styleable.AuroraProgressView_maxHeight, maxHeight);
+ String indicatorName = a.getString(R.styleable.AuroraProgressView_indicatorName);
+ indicatorColor = a.getColor(R.styleable.AuroraProgressView_indicatorColor, Color.WHITE);
+ setIndicator(indicatorName);
+
+ if (indicator == null) {
+ setIndicator(DEFAULT_INDICATOR);
+ }
+
+ a.recycle();
+ }
+
+ public Indicator getIndicator() {
+ return indicator;
+ }
+
+ public void setIndicator(Indicator d) {
+ if (indicator != d) {
+ if (indicator != null) {
+ indicator.setCallback(null);
+ unscheduleDrawable(indicator);
+ }
+
+ indicator = d;
+ //need to set indicator color again if you didn't specified when you update the indicator .
+ setIndicatorColor(indicatorColor);
+ if (d != null) {
+ d.setCallback(this);
+ }
+ postInvalidate();
+ }
+ }
+
+ public void setIndicatorColor(int color) {
+ this.indicatorColor = color;
+ indicator.setColor(color);
+ }
+
+ public void setIndicator(String indicatorName) {
+ if (TextUtils.isEmpty(indicatorName)) {
+ return;
+ }
+ StringBuilder drawableClassName = new StringBuilder();
+ if (!indicatorName.contains(".")) {
+ String defaultPackageName = getClass().getPackage().getName();
+ drawableClassName.append(defaultPackageName)
+ .append(".indicators")
+ .append(".");
+ }
+ drawableClassName.append(indicatorName);
+ try {
+ Class> drawableClass = Class.forName(drawableClassName.toString());
+ Indicator indicator = (Indicator) drawableClass.newInstance();
+ setIndicator(indicator);
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Didn't find your class , check the name again !");
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void smoothToShow() {
+ startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in));
+ setVisibility(VISIBLE);
+ }
+
+ public void smoothToHide() {
+ startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out));
+ setVisibility(GONE);
+ }
+
+ public void hide() {
+ dismissed = true;
+ removeCallbacks(mDelayedShow);
+ long diff = System.currentTimeMillis() - startTime;
+ if (diff >= MIN_SHOW_TIME || startTime == -1) {
+ // The progress spinner has been shown long enough
+ // OR was not shown yet. If it wasn't shown yet,
+ // it will just never be shown.
+ setVisibility(View.GONE);
+ } else {
+ // The progress spinner is shown, but not long enough,
+ // so put a delayed message in to hide it when its been
+ // shown long enough.
+ if (!postedHide) {
+ postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
+ postedHide = true;
+ }
+ }
+ }
+
+ public void show() {
+ // Reset the start time.
+ startTime = -1;
+ dismissed = false;
+ removeCallbacks(mDelayedHide);
+ if (!postedShow) {
+ postDelayed(mDelayedShow, MIN_DELAY);
+ postedShow = true;
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == indicator
+ || super.verifyDrawable(who);
+ }
+
+ void startAnimation() {
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+
+ if (indicator != null) {
+ mShouldStartAnimationDrawable = true;
+ }
+ postInvalidate();
+ }
+
+ void stopAnimation() {
+ if (indicator != null) {
+ indicator.stop();
+ mShouldStartAnimationDrawable = false;
+ }
+ postInvalidate();
+ }
+
+ @Override
+ public void setVisibility(int v) {
+ if (getVisibility() != v) {
+ super.setVisibility(v);
+ if (v == GONE || v == INVISIBLE) {
+ stopAnimation();
+ } else {
+ startAnimation();
+ }
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (visibility == GONE || visibility == INVISIBLE) {
+ stopAnimation();
+ } else {
+ startAnimation();
+ }
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable dr) {
+ if (verifyDrawable(dr)) {
+ final Rect dirty = dr.getBounds();
+ final int scrollX = getScrollX() + getPaddingLeft();
+ final int scrollY = getScrollY() + getPaddingTop();
+
+ invalidate(dirty.left + scrollX, dirty.top + scrollY,
+ dirty.right + scrollX, dirty.bottom + scrollY);
+ } else {
+ super.invalidateDrawable(dr);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateDrawableBounds(w, h);
+ }
+
+ private void updateDrawableBounds(int w, int h) {
+ // onDraw will translate the canvas so we draw starting at 0,0.
+ // Subtract out padding for the purposes of the calculations below.
+ w -= getPaddingRight() + getPaddingLeft();
+ h -= getPaddingTop() + getPaddingBottom();
+
+ int right = w;
+ int bottom = h;
+ int top = 0;
+ int left = 0;
+
+ if (indicator != null) {
+ // Maintain aspect ratio. Certain kinds of animated drawables
+ // get very confused otherwise.
+ final int intrinsicWidth = indicator.getIntrinsicWidth();
+ final int intrinsicHeight = indicator.getIntrinsicHeight();
+ final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
+ final float boundAspect = (float) w / h;
+ if (intrinsicAspect != boundAspect) {
+ if (boundAspect > intrinsicAspect) {
+ // New width is larger. Make it smaller to match height.
+ final int width = (int) (h * intrinsicAspect);
+ left = (w - width) / 2;
+ right = left + width;
+ } else {
+ // New height is larger. Make it smaller to match width.
+ final int height = (int) (w * (1 / intrinsicAspect));
+ top = (h - height) / 2;
+ bottom = top + height;
+ }
+ }
+ indicator.setBounds(left, top, right, bottom);
+ }
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawTrack(canvas);
+ }
+
+ void drawTrack(Canvas canvas) {
+ final Drawable d = indicator;
+ if (d != null) {
+ // Translate canvas so a indeterminate circular progress bar with padding
+ // rotates properly in its animation
+ final int saveCount = canvas.save();
+
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+
+ d.draw(canvas);
+ canvas.restoreToCount(saveCount);
+
+ if (mShouldStartAnimationDrawable && d instanceof Animatable) {
+ ((Animatable) d).start();
+ mShouldStartAnimationDrawable = false;
+ }
+ }
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int dw = 0;
+ int dh = 0;
+
+ final Drawable d = indicator;
+ if (d != null) {
+ dw = Math.max(minWidth, Math.min(maxWidth, d.getIntrinsicWidth()));
+ dh = Math.max(minHeight, Math.min(maxHeight, d.getIntrinsicHeight()));
+ }
+
+ updateDrawableState();
+
+ dw += getPaddingLeft() + getPaddingRight();
+ dh += getPaddingTop() + getPaddingBottom();
+
+ final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0);
+ final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0);
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ updateDrawableState();
+ }
+
+ private void updateDrawableState() {
+ final int[] state = getDrawableState();
+ if (indicator != null && indicator.isStateful()) {
+ indicator.setState(state);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @Override
+ public void drawableHotspotChanged(float x, float y) {
+ super.drawableHotspotChanged(x, y);
+
+ if (indicator != null) {
+ indicator.setHotspot(x, y);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ startAnimation();
+ removeCallbacks();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ stopAnimation();
+ // This should come after stopAnimation(), otherwise an invalidate message remains in the
+ // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
+ super.onDetachedFromWindow();
+ removeCallbacks();
+ }
+
+ private void removeCallbacks() {
+ removeCallbacks(mDelayedHide);
+ removeCallbacks(mDelayedShow);
+ }
+
+
+}
+
diff --git a/app/src/main/java/com/aurora/store/view/custom/progress/indicators/BallPulseIndicator.kt b/app/src/main/java/com/aurora/store/view/custom/progress/indicators/BallPulseIndicator.kt
new file mode 100644
index 000000000..6ea91d495
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/custom/progress/indicators/BallPulseIndicator.kt
@@ -0,0 +1,51 @@
+package com.aurora.store.view.custom.progress.indicators
+
+import android.animation.ValueAnimator
+import android.graphics.Canvas
+import android.graphics.Paint
+import java.util.*
+
+class BallPulseIndicator : Indicator() {
+
+ private val scaleFloats = floatArrayOf(
+ 0.75f,
+ 1.0f,
+ 0.75f
+ )
+
+ override fun draw(canvas: Canvas, paint: Paint) {
+ val circleSpacing = 4f
+ val radius = (width.coerceAtMost(height) - circleSpacing * 2) / 6
+ val x = width / 2f - (radius * 2 + circleSpacing)
+ val y = height / 2f
+
+ for (i in 0..2) {
+ canvas.save()
+ val translateX = x + radius * 2 * i + circleSpacing * i
+ canvas.translate(translateX, y)
+ canvas.scale(scaleFloats[i], scaleFloats[i])
+ canvas.drawCircle(0f, 0f, radius, paint)
+ canvas.restore()
+ }
+ }
+
+ override fun onCreateAnimators(): ArrayList {
+ val animators = ArrayList()
+ val delays = intArrayOf(120, 240, 360)
+
+ for (i in 0..2) {
+ val scaleAnim = ValueAnimator.ofFloat(1f, 0.3f, 1f)
+ scaleAnim.duration = 1000
+ scaleAnim.repeatCount = -1
+ scaleAnim.startDelay = delays[i].toLong()
+
+ addUpdateListener(scaleAnim) {
+ scaleFloats[i] = it.animatedValue as Float
+ postInvalidate()
+ }
+
+ animators.add(scaleAnim)
+ }
+ return animators
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/custom/progress/indicators/Indicator.java b/app/src/main/java/com/aurora/store/view/custom/progress/indicators/Indicator.java
new file mode 100644
index 000000000..4e3104664
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/custom/progress/indicators/Indicator.java
@@ -0,0 +1,191 @@
+package com.aurora.store.view.custom.progress.indicators;
+
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public abstract class Indicator extends Drawable implements Animatable {
+
+ private static final Rect ZERO_BOUNDS_RECT = new Rect();
+
+ private final HashMap updateListenerHashMap = new HashMap<>();
+ private final Paint paint = new Paint();
+
+ private int alpha = 255;
+ private ArrayList animators;
+ private boolean hasAnimators;
+
+ protected Rect drawBounds = ZERO_BOUNDS_RECT;
+
+ public Indicator() {
+ paint.setColor(Color.WHITE);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+ }
+
+ public int getColor() {
+ return paint.getColor();
+ }
+
+ public void setColor(int color) {
+ paint.setColor(color);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ this.alpha = alpha;
+ }
+
+ @Override
+ public int getAlpha() {
+ return alpha;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ draw(canvas, paint);
+ }
+
+ public abstract void draw(Canvas canvas, Paint paint);
+
+ public abstract ArrayList onCreateAnimators();
+
+ @Override
+ public void start() {
+ ensureAnimators();
+
+ if (animators == null) {
+ return;
+ }
+
+ // If the animators has not ended, do nothing.
+ if (isStarted()) {
+ return;
+ }
+ startAnimators();
+ invalidateSelf();
+ }
+
+ private void startAnimators() {
+ for (int i = 0; i < animators.size(); i++) {
+ ValueAnimator animator = animators.get(i);
+
+ //when the animator restart , add the updateListener again because they
+ // was removed by animator stop .
+ ValueAnimator.AnimatorUpdateListener updateListener = updateListenerHashMap.get(animator);
+ if (updateListener != null) {
+ animator.addUpdateListener(updateListener);
+ }
+
+ animator.start();
+ }
+ }
+
+ private void stopAnimators() {
+ if (animators != null) {
+ for (ValueAnimator animator : animators) {
+ if (animator != null && animator.isStarted()) {
+ animator.removeAllUpdateListeners();
+ animator.end();
+ }
+ }
+ }
+ }
+
+ private void ensureAnimators() {
+ if (!hasAnimators) {
+ animators = onCreateAnimators();
+ hasAnimators = true;
+ }
+ }
+
+ @Override
+ public void stop() {
+ stopAnimators();
+ }
+
+ private boolean isStarted() {
+ for (ValueAnimator animator : animators) {
+ return animator.isStarted();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isRunning() {
+ for (ValueAnimator animator : animators) {
+ return animator.isRunning();
+ }
+ return false;
+ }
+
+ public void addUpdateListener(ValueAnimator animator, ValueAnimator.AnimatorUpdateListener updateListener) {
+ updateListenerHashMap.put(animator, updateListener);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ setDrawBounds(bounds);
+ }
+
+ public void setDrawBounds(Rect drawBounds) {
+ setDrawBounds(drawBounds.left, drawBounds.top, drawBounds.right, drawBounds.bottom);
+ }
+
+ public void setDrawBounds(int left, int top, int right, int bottom) {
+ this.drawBounds = new Rect(left, top, right, bottom);
+ }
+
+ public void postInvalidate() {
+ invalidateSelf();
+ }
+
+ public Rect getDrawBounds() {
+ return drawBounds;
+ }
+
+ public int getWidth() {
+ return drawBounds.width();
+ }
+
+ public int getHeight() {
+ return drawBounds.height();
+ }
+
+ public int centerX() {
+ return drawBounds.centerX();
+ }
+
+ public int centerY() {
+ return drawBounds.centerY();
+ }
+
+ public float exactCenterX() {
+ return drawBounds.exactCenterX();
+ }
+
+ public float exactCenterY() {
+ return drawBounds.exactCenterY();
+ }
+
+}
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/controller/DetailsCarouselController.kt b/app/src/main/java/com/aurora/store/view/epoxy/controller/DetailsCarouselController.kt
new file mode 100644
index 000000000..a7ba410cf
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/controller/DetailsCarouselController.kt
@@ -0,0 +1,70 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.controller
+
+import com.aurora.gplayapi.data.models.StreamBundle
+import com.aurora.store.view.epoxy.groups.CarouselModelGroup
+import com.aurora.store.view.epoxy.groups.CarouselShimmerGroup
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
+
+open class DetailsCarouselController(private val callbacks: Callbacks) :
+ GenericCarouselController(callbacks) {
+
+ override fun buildModels(streamBundle: StreamBundle?) {
+ setFilterDuplicates(true)
+ if (streamBundle == null) {
+ for (i in 1..2) {
+ add(
+ CarouselShimmerGroup()
+ .id(i)
+ )
+ }
+ } else {
+ if (streamBundle.streamClusters.isNotEmpty()) {
+ if (streamBundle.streamClusters.size == 1) {
+ streamBundle
+ .streamClusters
+ .values
+ .filter { applyFilter(it) }
+ .forEach { streamCluster ->
+ streamCluster.clusterAppList.forEach {
+ add(
+ AppListViewModel_()
+ .id(it.id)
+ .app(it)
+ .click { _ -> callbacks.onAppClick(it) }
+ )
+ }
+ }
+
+ } else {
+ streamBundle
+ .streamClusters
+ .values
+ .filter { applyFilter(it) }
+ .forEach { streamCluster ->
+ add(CarouselModelGroup(streamCluster, callbacks))
+ }
+
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt b/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt
new file mode 100644
index 000000000..cf5b1c697
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt
@@ -0,0 +1,54 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.controller
+
+import com.aurora.gplayapi.data.models.StreamBundle
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.store.view.epoxy.groups.CarouselShimmerGroup
+import com.aurora.store.view.epoxy.groups.DeveloperModelGroup
+
+open class DeveloperCarouselController(private val callbacks: Callbacks) :
+ GenericCarouselController(callbacks) {
+
+ override fun applyFilter(streamBundle: StreamCluster): Boolean {
+ return streamBundle.clusterTitle.isNotBlank() //Filter noisy cluster
+ && streamBundle.clusterAppList.isNotEmpty() //Filter empty clusters
+ }
+
+ override fun buildModels(streamBundle: StreamBundle?) {
+ setFilterDuplicates(true)
+ if (streamBundle == null) {
+ for (i in 1..2) {
+ add(
+ CarouselShimmerGroup()
+ .id(i)
+ )
+ }
+ } else {
+ streamBundle
+ .streamClusters
+ .values
+ .filter { applyFilter(it) }
+ .forEach { streamCluster ->
+ add(DeveloperModelGroup(streamCluster, callbacks))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/controller/EditorChoiceController.kt b/app/src/main/java/com/aurora/store/view/epoxy/controller/EditorChoiceController.kt
index cf6252840..9dd885e97 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/controller/EditorChoiceController.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/controller/EditorChoiceController.kt
@@ -23,7 +23,7 @@ import com.airbnb.epoxy.TypedEpoxyController
import com.aurora.gplayapi.data.models.editor.EditorChoiceBundle
import com.aurora.gplayapi.data.models.editor.EditorChoiceCluster
import com.aurora.store.view.epoxy.groups.EditorChoiceModelGroup
-import com.aurora.store.view.epoxy.views.EditorHeadViewModel_
+import com.aurora.store.view.epoxy.views.HeaderViewModel_
class EditorChoiceController(private val callbacks: Callbacks) :
TypedEpoxyController>() {
@@ -37,7 +37,7 @@ class EditorChoiceController(private val callbacks: Callbacks) :
val idPrefix = editorChoiceBundle.id
add(
- EditorHeadViewModel_()
+ HeaderViewModel_()
.id("header_${idPrefix}")
.title(editorChoiceBundle.bundleTitle)
)
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/controller/GenericCarouselController.kt b/app/src/main/java/com/aurora/store/view/epoxy/controller/GenericCarouselController.kt
index 5a25b5e58..a3965edae 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/controller/GenericCarouselController.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/controller/GenericCarouselController.kt
@@ -26,7 +26,7 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R
import com.aurora.store.view.epoxy.groups.CarouselModelGroup
import com.aurora.store.view.epoxy.groups.CarouselShimmerGroup
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt b/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt
index 697048f2a..e0fb0aded 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt
@@ -26,7 +26,7 @@ import com.aurora.store.R
import com.aurora.store.util.Log
import com.aurora.store.view.epoxy.controller.GenericCarouselController
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
-import com.aurora.store.view.epoxy.views.AppViewModel_
+import com.aurora.store.view.epoxy.views.app.AppViewModel_
import com.aurora.store.view.epoxy.views.HeaderViewModel_
class CarouselModelGroup(
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt b/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt
new file mode 100644
index 000000000..2a0352981
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt
@@ -0,0 +1,127 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.groups
+
+import com.airbnb.epoxy.EpoxyModel
+import com.airbnb.epoxy.EpoxyModelGroup
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.store.R
+import com.aurora.store.util.Log
+import com.aurora.store.view.epoxy.controller.GenericCarouselController
+import com.aurora.store.view.epoxy.views.AppProgressViewModel_
+import com.aurora.store.view.epoxy.views.HeaderViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppViewModel_
+import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
+
+class DeveloperModelGroup(
+ streamCluster: StreamCluster,
+ callbacks: GenericCarouselController.Callbacks
+) :
+ EpoxyModelGroup(
+ R.layout.model_developer_carousel_group, buildModels(
+ streamCluster,
+ callbacks
+ )
+ ) {
+ companion object {
+ private fun buildModels(
+ streamCluster: StreamCluster,
+ callbacks: GenericCarouselController.Callbacks
+ ): List> {
+ val models = ArrayList>()
+ val clusterViewModels = mutableListOf>()
+ val screenshotsViewModels = mutableListOf>()
+
+ val idPrefix = streamCluster.id
+
+ models.add(
+ HeaderViewModel_()
+ .id("${idPrefix}_header")
+ .title(streamCluster.clusterTitle)
+ .browseUrl(streamCluster.clusterBrowseUrl)
+ .click { _ ->
+ callbacks.onHeaderClicked(streamCluster)
+ }
+ )
+
+ if (streamCluster.clusterAppList.size == 1) {
+ val app = streamCluster.clusterAppList[0]
+
+ for (artwork in app.screenshots) {
+ screenshotsViewModels.add(
+ ScreenshotViewModel_()
+ .id(artwork.url)
+ .artwork(artwork)
+ )
+ }
+
+ clusterViewModels.add(
+ AppListViewModel_()
+ .id(app.id)
+ .app(app)
+ .click { _ ->
+ callbacks.onAppClick(app)
+ }
+ )
+ } else {
+ for (app in streamCluster.clusterAppList) {
+ clusterViewModels.add(
+ AppViewModel_()
+ .id(app.id)
+ .app(app)
+ .click { _ ->
+ callbacks.onAppClick(app)
+ }
+ .longClick { _ ->
+ callbacks.onAppLongClick(app)
+ false
+ }
+ .onBind { _, _, position ->
+ val itemCount = clusterViewModels.count()
+ if (itemCount >= 2) {
+ if (position == clusterViewModels.count() - 2) {
+ callbacks.onClusterScrolled(streamCluster)
+ Log.i("Cluster %s Scrolled", streamCluster.clusterTitle)
+ }
+ }
+ }
+ )
+ }
+ }
+
+ if (screenshotsViewModels.isNotEmpty()) {
+ models.add(
+ CarouselHorizontalModel_()
+ .id("${idPrefix}_screenshots")
+ .models(screenshotsViewModels)
+ )
+ }
+
+ models.add(
+ CarouselHorizontalModel_()
+ .id("${idPrefix}_cluster")
+ .models(clusterViewModels)
+ )
+
+ return models
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/groups/EditorChoiceModelGroup.kt b/app/src/main/java/com/aurora/store/view/epoxy/groups/EditorChoiceModelGroup.kt
index 2db7ba125..d69129eb0 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/groups/EditorChoiceModelGroup.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/groups/EditorChoiceModelGroup.kt
@@ -24,19 +24,16 @@ import com.airbnb.epoxy.EpoxyModelGroup
import com.aurora.gplayapi.data.models.editor.EditorChoiceCluster
import com.aurora.store.R
import com.aurora.store.view.epoxy.controller.EditorChoiceController
+import com.aurora.store.view.epoxy.views.EditorHeadViewModel_
import com.aurora.store.view.epoxy.views.EditorImageViewModel_
-import com.aurora.store.view.epoxy.views.HeaderViewModel_
class EditorChoiceModelGroup(
editorChoiceCluster: EditorChoiceCluster,
callbacks: EditorChoiceController.Callbacks
-) :
- EpoxyModelGroup(
- R.layout.model_editorchoice_group, buildModels(
- editorChoiceCluster,
- callbacks
- )
- ) {
+) : EpoxyModelGroup(
+ R.layout.model_editorchoice_group,
+ buildModels(editorChoiceCluster, callbacks)
+) {
companion object {
private fun buildModels(
editorChoiceCluster: EditorChoiceCluster,
@@ -48,21 +45,33 @@ class EditorChoiceModelGroup(
val idPrefix = editorChoiceCluster.id
+
models.add(
- HeaderViewModel_()
+ EditorImageViewModel_()
+ .id("artwork_header_${idPrefix}")
+ .artwork(editorChoiceCluster.clusterArtwork[0])
+ .click { _ -> callbacks.onClick(editorChoiceCluster) }
+
+ )
+
+ models.add(
+ EditorHeadViewModel_()
.id("header_${idPrefix}")
.title(editorChoiceCluster.clusterTitle)
- .browseUrl(editorChoiceCluster.clusterBrowseUrl)
.click { _ -> callbacks.onClick(editorChoiceCluster) }
)
- editorChoiceCluster.clusterArtwork.forEach {
- clusterViewModels.add(
- EditorImageViewModel_()
- .id("artwork_${idPrefix}")
- .artwork(it)
- )
- }
+ editorChoiceCluster.clusterArtwork
+ .drop(1)
+ .forEach {
+ clusterViewModels.add(
+ EditorImageViewModel_()
+ .id("artwork_${idPrefix}_${it.url}")
+ .artwork(it)
+ .click { _ -> callbacks.onClick(editorChoiceCluster) }
+
+ )
+ }
models.add(
CarouselHorizontalModel_()
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/BaseView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/BaseView.kt
index 9f16de7b8..66e065f4b 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/BaseView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/BaseView.kt
@@ -22,6 +22,7 @@ package com.aurora.store.view.epoxy.views
import android.view.View
import android.view.animation.AnimationUtils
import com.airbnb.epoxy.EpoxyModel
+import com.aurora.store.view.epoxy.views.app.AppListView
abstract class BaseView : EpoxyModel() {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/BlackView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt
similarity index 95%
rename from app/src/main/java/com/aurora/store/view/epoxy/views/BlackView.kt
rename to app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt
index 2cac41662..6f4b0bd53 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/BlackView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/BlackListView.kt
@@ -30,15 +30,15 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.store.R
import com.aurora.store.data.model.Black
import com.aurora.store.databinding.ViewBlackBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
-class BlackView : RelativeLayout {
+class BlackListView : RelativeLayout {
private lateinit var B: ViewBlackBinding
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/CategoryView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/CategoryView.kt
index 7f220207b..1c26eeb0b 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/CategoryView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/CategoryView.kt
@@ -29,12 +29,12 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.Category
import com.aurora.store.R
import com.aurora.store.databinding.ViewCategoryBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class CategoryView : RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/DownloadView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/DownloadView.kt
index 8ed0476d9..802d0c4a6 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/DownloadView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/DownloadView.kt
@@ -34,8 +34,8 @@ import com.aurora.store.databinding.ViewDownloadBinding
import com.aurora.store.util.CommonUtil.getDownloadSpeedString
import com.aurora.store.util.CommonUtil.getETAString
import com.aurora.store.util.CommonUtil.humanReadableByteValue
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.google.gson.Gson
@@ -45,7 +45,7 @@ import java.lang.reflect.Modifier
import java.util.*
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class DownloadView : RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/EditorHeadView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/EditorHeadView.kt
index a1d30ce61..a43d8c769 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/EditorHeadView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/EditorHeadView.kt
@@ -22,13 +22,15 @@ package com.aurora.store.view.epoxy.views
import android.content.Context
import android.util.AttributeSet
import android.widget.RelativeLayout
+import androidx.core.text.HtmlCompat
+import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.aurora.store.R
import com.aurora.store.databinding.ViewEditorHeadBinding
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class EditorHeadView : RelativeLayout {
@@ -56,6 +58,11 @@ class EditorHeadView : RelativeLayout {
B = ViewEditorHeadBinding.bind(view)
}
+ @CallbackProp
+ fun click(onClickListener: OnClickListener?) {
+ B.root.setOnClickListener(onClickListener)
+ }
+
@ModelProp
fun title(title: String) {
B.title.text = title
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/EditorImageView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/EditorImageView.kt
index a18738179..a227792ca 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/EditorImageView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/EditorImageView.kt
@@ -22,14 +22,16 @@ package com.aurora.store.view.epoxy.views
import android.content.Context
import android.util.AttributeSet
import android.widget.RelativeLayout
+import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.Artwork
import com.aurora.store.R
import com.aurora.store.databinding.ViewEditorImageBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
+import com.aurora.extensions.px
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
@@ -66,23 +68,30 @@ class EditorImageView : RelativeLayout {
fun artwork(artwork: Artwork) {
when (artwork.type) {
14 -> {
- B.img.layoutParams.width =
- resources.getDimension(R.dimen.icon_size_large).toInt() * 2
+ B.img.layoutParams.height = 108.px.toInt()
+ B.img.layoutParams.width = 192.px.toInt()
B.img.requestLayout()
+
B.img.load(artwork.url, DrawableTransitionOptions.withCrossFade()) {
- transform(RoundedCorners(16))
+ transform(RoundedCorners(8.px.toInt()))
}
}
else -> {
- B.img.layoutParams.width = resources.getDimension(R.dimen.icon_size_large).toInt()
+ B.img.layoutParams.width = 24.px.toInt()
+ B.img.layoutParams.height = 24.px.toInt()
B.img.requestLayout()
B.img.load(artwork.url, DrawableTransitionOptions.withCrossFade()) {
- transform(RoundedCorners(16))
+ transform(RoundedCorners(4.px.toInt()))
}
}
}
}
+ @CallbackProp
+ fun click(onClickListener: OnClickListener?) {
+ B.root.setOnClickListener(onClickListener)
+ }
+
@OnViewRecycled
fun clear() {
B.img.clear()
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/HeaderView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/HeaderView.kt
index 51a46e85a..75672e6ee 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/HeaderView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/HeaderView.kt
@@ -32,7 +32,7 @@ import com.aurora.store.databinding.ViewHeaderBinding
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class HeaderView : RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/HorizontalDividerView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/HorizontalDividerView.kt
new file mode 100644
index 000000000..6b7d0fc82
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/HorizontalDividerView.kt
@@ -0,0 +1,57 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.RelativeLayout
+import com.airbnb.epoxy.ModelView
+import com.aurora.store.R
+import com.aurora.store.databinding.ViewDividerBinding
+
+@ModelView(
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
+ baseModelClass = BaseView::class
+)
+class HorizontalDividerView : RelativeLayout {
+
+ private lateinit var B: ViewDividerBinding
+
+ constructor(context: Context?) : super(context) {
+ init(context, null)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
+ init(context, attrs)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ ) {
+ init(context, attrs)
+ }
+
+ private fun init(context: Context?, attrs: AttributeSet?) {
+ val view = inflate(context, R.layout.view_divider, this)
+ B = ViewDividerBinding.bind(view)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/MinimalHeaderView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/MinimalHeaderView.kt
index cc359b46c..8fe5457f8 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/MinimalHeaderView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/MinimalHeaderView.kt
@@ -31,7 +31,7 @@ import com.aurora.store.databinding.ViewActionHeaderBinding
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class MinimalHeaderView : RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/SearchSuggestionView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/SearchSuggestionView.kt
index 723eb3a0e..7100cfbed 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/SearchSuggestionView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/SearchSuggestionView.kt
@@ -30,8 +30,8 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.store.R
import com.aurora.store.databinding.ViewSearchSuggestionBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@ModelView(
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/UpdateHeaderView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/UpdateHeaderView.kt
index 4beaaf6f2..6f89ca936 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/UpdateHeaderView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/UpdateHeaderView.kt
@@ -31,7 +31,7 @@ import com.aurora.store.databinding.ViewHeaderUpdateBinding
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class UpdateHeaderView : RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppListView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppListView.kt
index 8f87fefc9..28228c66b 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppListView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppListView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.app
import android.content.Context
import android.util.AttributeSet
@@ -30,9 +30,10 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.ViewAppListBinding
import com.aurora.store.util.CommonUtil
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.getString
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.getString
+import com.aurora.extensions.load
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@ModelView(
@@ -90,7 +91,7 @@ class AppListView : RelativeLayout {
if (app.dependencies.dependentPackages.isNotEmpty())
extras.add(getString(R.string.details_gsf_dependent))
- B.txtLine3.text = extras.joinToString(separator = " • ")
+ B.txtLine3.text = extras.joinToString(separator = " • ")
}
@CallbackProp
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt
index 17d411f54..2539e3898 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.app
import android.content.Context
import android.util.AttributeSet
@@ -28,17 +28,18 @@ import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.airbnb.epoxy.OnViewRecycled
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.ViewAppUpdateBinding
import com.aurora.store.util.CommonUtil
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
@ModelView(
- autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
)
class AppUpdateView : RelativeLayout {
@@ -76,7 +77,8 @@ class AppUpdateView : RelativeLayout {
}
B.txtLine2.text = app.developerName
- B.txtLine3.text = CommonUtil.addSiPrefix(app.size)
+ B.txtLine3.text = ("${CommonUtil.addSiPrefix(app.size)} • ${app.updatedOn}")
+ B.txtLine4.text = ("v${app.versionName}.${app.versionCode}")
B.txtChangelog.text = if (app.changes.isNotEmpty())
HtmlCompat.fromHtml(
app.changes,
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppView.kt
index 45a49d1e2..28f39baf0 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.app
import android.content.Context
import android.util.AttributeSet
@@ -30,8 +30,9 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.ViewAppBinding
import com.aurora.store.util.CommonUtil
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/AppDependentView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/AppDependentView.kt
index 49306da25..c17a4513d 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/AppDependentView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/AppDependentView.kt
@@ -29,8 +29,8 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.ViewAppDependentBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/BadgeView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/BadgeView.kt
index f6cb92f4b..1cf280312 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/BadgeView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/BadgeView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
@@ -29,8 +29,8 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.details.Badge
import com.aurora.store.R
import com.aurora.store.databinding.ViewBadgeBinding
-import com.aurora.store.util.extensions.load
-
+import com.aurora.extensions.load
+import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt
index d876675eb..edd3d069a 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
@@ -29,7 +29,7 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.store.R
import com.aurora.store.data.model.ExodusTracker
import com.aurora.store.databinding.ViewExodusBinding
-
+import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt
index 2af3c2edf..18be0e50c 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
@@ -28,6 +28,7 @@ import com.aurora.gplayapi.data.models.File
import com.aurora.store.R
import com.aurora.store.databinding.ViewFileBinding
import com.aurora.store.util.CommonUtil
+import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/InfoView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/InfoView.kt
new file mode 100644
index 000000000..ba0d75a65
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/InfoView.kt
@@ -0,0 +1,74 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.views.details
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.RelativeLayout
+import com.airbnb.epoxy.ModelProp
+import com.airbnb.epoxy.ModelView
+import com.airbnb.epoxy.OnViewRecycled
+import com.aurora.store.R
+import com.aurora.store.databinding.ViewInfoBinding
+import com.aurora.store.view.epoxy.views.BaseView
+import java.util.*
+
+@ModelView(
+ autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
+ baseModelClass = BaseView::class
+)
+class InfoView : RelativeLayout {
+
+ private lateinit var B: ViewInfoBinding
+
+ constructor(context: Context?) : super(context) {
+ init(context, null)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
+ init(context, attrs)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ ) {
+ init(context, attrs)
+ }
+
+ private fun init(context: Context?, attrs: AttributeSet?) {
+ val view = inflate(context, R.layout.view_info, this)
+ B = ViewInfoBinding.bind(view)
+ }
+
+ @ModelProp
+ fun badge(info: Map.Entry) {
+ B.txtTitle.text = info.key
+ .replace("_", " ")
+ .toLowerCase(Locale.getDefault())
+ .capitalize(Locale.getDefault())
+ B.txtSubtitle.text = info.value
+ }
+
+ @OnViewRecycled
+ fun clear() {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt
index 096404ea3..12ae0f7c0 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.content.res.Resources
@@ -31,16 +31,16 @@ import com.aurora.gplayapi.data.models.Artwork
import com.aurora.store.GlideApp
import com.aurora.store.R
import com.aurora.store.databinding.ViewScreenshotLargeBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.px
-import com.aurora.store.util.extensions.runOnUiThread
+import com.aurora.extensions.clear
+import com.aurora.extensions.px
+import com.aurora.extensions.runOnUiThread
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
-
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_MATCH_HEIGHT,
baseModelClass = BaseView::class
@@ -72,8 +72,10 @@ class LargeScreenshotView : RelativeLayout {
@ModelProp
fun artwork(artwork: Artwork) {
+ val displayMetrics = Resources.getSystem().displayMetrics
GlideApp.with(context)
- .load(artwork.url)
+ .load("${artwork.url}=rw-w${displayMetrics.widthPixels}-v1-e15")
+ .placeholder(R.drawable.bg_placeholder)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.addListener(object : RequestListener {
override fun onLoadFailed(
@@ -97,7 +99,6 @@ class LargeScreenshotView : RelativeLayout {
B.img.layoutParams.height = artwork.height.px.toInt()
B.img.layoutParams.width = artwork.width.px.toInt()
} else {
- val displayMetrics = Resources.getSystem().displayMetrics
val height = displayMetrics.heightPixels
val width = displayMetrics.widthPixels
B.img.layoutParams.width = width
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt
new file mode 100644
index 000000000..8fedbfd37
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt
@@ -0,0 +1,126 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.epoxy.views.details
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.RelativeLayout
+import com.airbnb.epoxy.CallbackProp
+import com.airbnb.epoxy.ModelProp
+import com.airbnb.epoxy.ModelView
+import com.airbnb.epoxy.OnViewRecycled
+import com.aurora.gplayapi.data.models.Artwork
+import com.aurora.store.R
+import com.aurora.store.databinding.ViewScreenshotMiniBinding
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
+import com.aurora.extensions.px
+import com.aurora.store.view.epoxy.views.BaseView
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+
+@ModelView(
+ autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
+ baseModelClass = BaseView::class
+)
+class MiniScreenshotView : RelativeLayout {
+
+ private lateinit var B: ViewScreenshotMiniBinding
+
+ private var position: Int = 0
+
+ interface ScreenshotCallback {
+ fun onClick(position: Int = 0)
+ }
+
+ constructor(context: Context?) : super(context) {
+ init(context, null)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
+ init(context, attrs)
+ }
+
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ ) {
+ init(context, attrs)
+ }
+
+ private fun init(context: Context?, attrs: AttributeSet?) {
+ val view = inflate(context, R.layout.view_screenshot_mini, this)
+ B = ViewScreenshotMiniBinding.bind(view)
+ }
+
+ @ModelProp
+ fun position(pos: Int) {
+ position = pos
+ }
+
+ @ModelProp
+ fun artwork(artwork: Artwork) {
+ normalizeSize(artwork)
+ B.img.load("${artwork.url}=rw-w480-v1-e15", DrawableTransitionOptions.withCrossFade()) {
+ placeholder(R.drawable.bg_rounded)
+ transform(RoundedCorners(8.px.toInt()))
+ }
+ }
+
+ private fun normalizeSize(artwork: Artwork) {
+ if (artwork.height != 0 && artwork.width != 0) {
+
+ val artworkHeight = artwork.height
+ val artworkWidth = artwork.width
+
+ val normalizedHeight: Float
+ val normalizedWidth: Float
+
+ when {
+ artworkHeight == artworkWidth -> {
+ normalizedHeight = 120f
+ normalizedWidth = 120f
+ }
+ else -> {
+ val factor = artworkHeight / 120f
+ normalizedHeight = 120f
+ normalizedWidth = (artworkWidth / factor)
+ }
+ }
+
+ B.img.layoutParams.height = normalizedHeight.px.toInt()
+ B.img.layoutParams.width = normalizedWidth.px.toInt()
+ B.img.requestLayout()
+ }
+ }
+
+ @CallbackProp
+ fun callback(screenshotCallback: ScreenshotCallback?) {
+ B.img.setOnClickListener {
+ screenshotCallback?.onClick(position)
+ }
+ }
+
+ @OnViewRecycled
+ fun clear() {
+ B.img.clear()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/MoreBadgeView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/MoreBadgeView.kt
index aaae0a9d5..73b30e4b3 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/MoreBadgeView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/MoreBadgeView.kt
@@ -30,10 +30,9 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.details.Badge
import com.aurora.store.R
import com.aurora.store.databinding.ViewMoreBadgeBinding
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.load
import com.aurora.store.view.epoxy.views.BaseView
-
@ModelView(
autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt
index 5dd2cdeb0..5a64d43fb 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
@@ -29,9 +29,10 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.Review
import com.aurora.store.R
import com.aurora.store.databinding.ViewReviewBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.toDate
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
+import com.aurora.extensions.toDate
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt
index 9ea8fa281..15471f09b 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt
@@ -17,7 +17,7 @@
*
*/
-package com.aurora.store.view.epoxy.views
+package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
@@ -29,13 +29,13 @@ import com.airbnb.epoxy.OnViewRecycled
import com.aurora.gplayapi.data.models.Artwork
import com.aurora.store.R
import com.aurora.store.databinding.ViewScreenshotBinding
-import com.aurora.store.util.extensions.clear
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.px
+import com.aurora.extensions.clear
+import com.aurora.extensions.load
+import com.aurora.extensions.px
+import com.aurora.store.view.epoxy.views.BaseView
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
-
@ModelView(
autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseView::class
@@ -79,7 +79,7 @@ class ScreenshotView : RelativeLayout {
@ModelProp
fun artwork(artwork: Artwork) {
normalizeSize(artwork)
- B.img.load(artwork.url, DrawableTransitionOptions.withCrossFade()) {
+ B.img.load("${artwork.url}=rw-w480-v1-e15", DrawableTransitionOptions.withCrossFade()) {
placeholder(R.drawable.bg_rounded)
transform(RoundedCorners(8.px.toInt()))
}
@@ -88,22 +88,22 @@ class ScreenshotView : RelativeLayout {
private fun normalizeSize(artwork: Artwork) {
if (artwork.height != 0 && artwork.width != 0) {
- val viewHeight = artwork.height
- val viewWidth = artwork.width
+ val artworkHeight = artwork.height
+ val artworkWidth = artwork.width
- var normalizedHeight: Int = viewHeight
- var normalizedWidth: Int = viewWidth
+ val normalizedHeight: Float
+ val normalizedWidth: Float
-
- if (viewHeight == viewWidth) {
- normalizedHeight = 240
- normalizedWidth = 240
- } else if (viewHeight > viewWidth) {
- normalizedHeight = 240
- normalizedWidth = 135
- } else if (viewHeight < viewWidth) {
- normalizedHeight = 240
- normalizedWidth = 427
+ when {
+ artworkHeight == artworkWidth -> {
+ normalizedHeight = 192f
+ normalizedWidth = 192f
+ }
+ else -> {
+ val factor = artworkHeight / 192f
+ normalizedHeight = 192f
+ normalizedWidth = (artworkWidth / factor)
+ }
}
B.img.layoutParams.height = normalizedHeight.px.toInt()
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/preference/InstallerView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/preference/InstallerView.kt
index e117e8faf..0c725abfd 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/preference/InstallerView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/preference/InstallerView.kt
@@ -76,4 +76,9 @@ class InstallerView : RelativeLayout {
fun checked(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?) {
B.checkbox.setOnCheckedChangeListener(onCheckedChangeListener)
}
+
+ @CallbackProp
+ fun click(onClickListener: OnClickListener?) {
+ B.root.setOnClickListener(onClickListener)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/preference/LinkView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/preference/LinkView.kt
index 5bca62dfb..4b4b0536b 100644
--- a/app/src/main/java/com/aurora/store/view/epoxy/views/preference/LinkView.kt
+++ b/app/src/main/java/com/aurora/store/view/epoxy/views/preference/LinkView.kt
@@ -28,9 +28,9 @@ import com.airbnb.epoxy.ModelView
import com.aurora.store.R
import com.aurora.store.data.model.Link
import com.aurora.store.databinding.ViewLinkBinding
-import com.aurora.store.util.extensions.hide
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.show
+import com.aurora.extensions.hide
+import com.aurora.extensions.load
+import com.aurora.extensions.show
import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
diff --git a/app/src/main/java/com/aurora/store/view/ui/about/AboutActivity.kt b/app/src/main/java/com/aurora/store/view/ui/about/AboutActivity.kt
index 8f2eebf83..b351b0189 100644
--- a/app/src/main/java/com/aurora/store/view/ui/about/AboutActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/about/AboutActivity.kt
@@ -26,10 +26,10 @@ import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.model.Link
import com.aurora.store.databinding.ActivityAboutBinding
-import com.aurora.store.util.extensions.browse
-import com.aurora.store.util.extensions.close
-import com.aurora.store.util.extensions.copyToClipBoard
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.browse
+import com.aurora.extensions.close
+import com.aurora.extensions.copyToClipBoard
+import com.aurora.extensions.load
import com.aurora.store.view.epoxy.views.preference.LinkViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
diff --git a/app/src/main/java/com/aurora/store/view/ui/account/AccountActivity.kt b/app/src/main/java/com/aurora/store/view/ui/account/AccountActivity.kt
index d55ff92bd..334883f61 100644
--- a/app/src/main/java/com/aurora/store/view/ui/account/AccountActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/account/AccountActivity.kt
@@ -19,22 +19,19 @@
package com.aurora.store.view.ui.account
-import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
+import com.aurora.extensions.browse
+import com.aurora.extensions.close
+import com.aurora.extensions.load
import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.data.AuthState
import com.aurora.store.data.event.BusEvent
import com.aurora.store.data.providers.AccountProvider
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.ActivityAccountBinding
-import com.aurora.store.util.CommonUtil.getEmptyActivityBundle
-import com.aurora.store.util.extensions.browse
-import com.aurora.store.util.extensions.close
-import com.aurora.store.util.extensions.load
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.viewmodel.auth.AuthViewModel
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@@ -224,14 +221,6 @@ class AccountActivity : BaseActivity() {
}
}
- private fun moveToContent() {
- runOnUiThread {
- val intent = Intent(this, MainActivity::class.java)
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- startActivity(intent, getEmptyActivityBundle(this))
- }
- }
-
override fun onConnected() {
hideNetworkConnectivitySheet()
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/account/GoogleActivity.kt b/app/src/main/java/com/aurora/store/view/ui/account/GoogleActivity.kt
index 5f8913763..a5f6ce6ba 100644
--- a/app/src/main/java/com/aurora/store/view/ui/account/GoogleActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/account/GoogleActivity.kt
@@ -32,9 +32,9 @@ import com.aurora.store.data.event.BusEvent
import com.aurora.store.databinding.ActivityGoogleBinding
import com.aurora.store.util.AC2DMTask
import com.aurora.store.util.Preferences
-import com.aurora.store.util.Util
-import com.aurora.store.util.extensions.close
-import com.aurora.store.util.extensions.isLAndAbove
+import com.aurora.store.util.AC2DMUtil
+import com.aurora.extensions.close
+import com.aurora.extensions.isLAndAbove
import com.aurora.store.view.ui.commons.BaseActivity
import nl.komponents.kovenant.task
import org.greenrobot.eventbus.EventBus
@@ -79,7 +79,7 @@ class GoogleActivity : BaseActivity() {
B.webview.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
val cookies = CookieManager.getInstance().getCookie(url)
- val cookieMap = Util.parseCookieString(cookies)
+ val cookieMap = AC2DMUtil.parseCookieString(cookies)
if (cookieMap.isNotEmpty() && cookieMap[AUTH_TOKEN] != null) {
val oauthToken = cookieMap[AUTH_TOKEN]
B.webview.evaluateJavascript("(function() { return document.getElementById('profileIdentifier').innerHTML; })();") {
diff --git a/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesActivity.kt b/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesActivity.kt
index 9d6fea394..5ae5dbc9d 100644
--- a/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesActivity.kt
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.AuthData
import com.aurora.store.R
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.ActivityGenericPagerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.ui.commons.BaseActivity
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
@@ -73,6 +73,7 @@ class AppsGamesActivity : BaseActivity() {
when (position) {
0 -> tab.text = getString(R.string.title_installed)
1 -> tab.text = getString(R.string.title_library)
+ 2 -> tab.text = getString(R.string.title_purchase_history)
else -> {
}
}
@@ -89,6 +90,7 @@ class AppsGamesActivity : BaseActivity() {
return when (position) {
0 -> InstalledAppsFragment.newInstance()
1 -> LibraryAppsFragment.newInstance()
+ 2 -> PurchasedAppsFragment.newInstance()
else -> Fragment()
}
}
@@ -97,7 +99,7 @@ class AppsGamesActivity : BaseActivity() {
return if (isAnonymous)
1
else
- 2
+ 3
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt
index 75ea2fc9c..c7d835444 100644
--- a/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt
@@ -28,7 +28,7 @@ import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.FragmentUpdatesBinding
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.HeaderViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.view.ui.commons.BaseFragment
diff --git a/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt
index 0ebba9183..17e66fd77 100644
--- a/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R
import com.aurora.store.databinding.FragmentUpdatesBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.HeaderViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
diff --git a/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt
new file mode 100644
index 000000000..1ed3ac004
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt
@@ -0,0 +1,145 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.all
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.ViewModelProvider
+import com.aurora.Constants
+import com.aurora.gplayapi.data.models.App
+import com.aurora.store.R
+import com.aurora.store.databinding.FragmentUpdatesBinding
+import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
+import com.aurora.store.view.epoxy.views.HeaderViewModel_
+import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
+import com.aurora.store.view.ui.commons.BaseFragment
+import com.aurora.store.view.ui.sheets.AppMenuSheet
+import com.aurora.store.viewmodel.all.InstalledViewModel
+import com.aurora.store.viewmodel.all.PurchasedViewModel
+
+class PurchasedAppsFragment : BaseFragment() {
+
+ private lateinit var VM: PurchasedViewModel
+ private lateinit var B: FragmentUpdatesBinding
+ lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
+
+ companion object {
+ @JvmStatic
+ fun newInstance(): PurchasedAppsFragment {
+ return PurchasedAppsFragment().apply {
+
+ }
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ B = FragmentUpdatesBinding.bind(
+ inflater.inflate(
+ R.layout.fragment_updates,
+ container,
+ false
+ )
+ )
+
+ VM = ViewModelProvider(requireActivity()).get(PurchasedViewModel::class.java)
+
+ return B.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ VM.liveData.observe(viewLifecycleOwner, {
+ updateController(it)
+ B.swipeRefreshLayout.isRefreshing = false
+ })
+
+ B.swipeRefreshLayout.setOnRefreshListener {
+ VM.observe()
+ }
+
+ attachRecycler()
+
+ updateController(null)
+ }
+
+ private fun attachRecycler() {
+ endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
+ override fun onLoadMore(currentPage: Int) {
+ VM.observe()
+ }
+ }
+ B.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
+ }
+
+
+ private fun updateController(appList: List?) {
+ B.recycler.withModels {
+ setFilterDuplicates(true)
+ if (appList == null) {
+ for (i in 1..6) {
+ add(
+ AppListViewShimmerModel_()
+ .id(i)
+ )
+ }
+ } else {
+ add(
+ HeaderViewModel_()
+ .id("header")
+ .title("${appList.size} apps purchased")
+ )
+ appList.forEach { app ->
+ add(
+ AppListViewModel_()
+ .id(app.id)
+ .app(app)
+ .click { _ -> openDetailsActivity(app) }
+ .longClick { _ ->
+ openAppMenuSheet(app)
+ false
+ }
+ )
+ }
+ }
+ }
+ }
+
+ private fun openAppMenuSheet(app: App) {
+ val fragment = childFragmentManager.findFragmentByTag(AppMenuSheet.TAG)
+ if (fragment != null)
+ childFragmentManager.beginTransaction().remove(fragment)
+
+ AppMenuSheet().apply {
+ arguments = Bundle().apply {
+ putString(Constants.STRING_EXTRA, gson.toJson(app))
+ }
+ }.show(
+ childFragmentManager,
+ AppMenuSheet.TAG
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt
index 36ec47819..307f68001 100644
--- a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt
@@ -70,10 +70,10 @@ class AppsContainerFragment : Fragment() {
TabLayoutMediator(B.tabLayout, B.pager, true) { tab: TabLayout.Tab, position: Int ->
when (position) {
- 0 -> tab.text = "For you"
- 1 -> tab.text = "Top charts"
- 2 -> tab.text = "Categories"
- 3 -> tab.text = "Editor's choice"
+ 0 -> tab.text = getString(R.string.tab_for_you)
+ 1 -> tab.text = getString(R.string.tab_top_charts)
+ 2 -> tab.text = getString(R.string.tab_categories)
+ 3 -> tab.text = getString(R.string.tab_editor_choice)
else -> {
}
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/apps/TopChartFragment.kt b/app/src/main/java/com/aurora/store/view/ui/apps/TopChartFragment.kt
index 9ff6b1c13..4690038bb 100644
--- a/app/src/main/java/com/aurora/store/view/ui/apps/TopChartFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/apps/TopChartFragment.kt
@@ -29,8 +29,8 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R
import com.aurora.store.databinding.FragmentTopContainerBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.topchart.*
@@ -40,6 +40,8 @@ class TopChartFragment : BaseFragment() {
private lateinit var VM: BaseChartViewModel
private lateinit var B: FragmentTopContainerBinding
+ private lateinit var streamCluster: StreamCluster
+
private var chartType = 0
private var chartCategory = 0
@@ -88,16 +90,23 @@ class TopChartFragment : BaseFragment() {
super.onViewCreated(view, savedInstanceState)
VM.liveData.observe(viewLifecycleOwner, {
- updateController(it)
+ if (!::streamCluster.isInitialized)
+ attachRecycler()
+
+ streamCluster = it
+
+ updateController(streamCluster)
})
+ updateController(null)
+ }
+
+ private fun attachRecycler() {
B.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
VM.nextCluster()
}
})
-
- updateController(null)
}
private fun updateController(streamCluster: StreamCluster?) {
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BaseActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BaseActivity.kt
index f74cb3fc6..762bb2922 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/BaseActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/BaseActivity.kt
@@ -24,19 +24,16 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
import com.aurora.Constants
+import com.aurora.extensions.applyTheme
+import com.aurora.extensions.getEmptyActivityBundle
import com.aurora.gplayapi.data.models.App
import com.aurora.store.data.providers.NetworkProvider
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
-import com.aurora.store.util.ViewUtil
-import com.aurora.store.util.extensions.applyTheme
import com.aurora.store.view.ui.account.GoogleActivity
-import com.aurora.store.view.ui.details.AppDetailsActivity
-import com.aurora.store.view.ui.details.DetailsMoreActivity
-import com.aurora.store.view.ui.details.DetailsReviewActivity
+import com.aurora.store.view.ui.details.*
import com.aurora.store.view.ui.sheets.NetworkDialogSheet
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -99,42 +96,66 @@ abstract class BaseActivity : AppCompatActivity(), NetworkProvider.NetworkListen
}
}
- fun openStreamBrowseActivity(browseUrl: String) {
- val intent = Intent(this, StreamBrowseActivity::class.java)
+ fun openStreamBrowseActivity(browseUrl: String, title: String = "") {
+ val intent = if (browseUrl.toLowerCase().contains("expanded"))
+ Intent(this, ExpandedStreamBrowseActivity::class.java)
+ else if (browseUrl.toLowerCase().contains("developer"))
+ Intent(this, DevProfileActivity::class.java)
+ else
+ Intent(this, StreamBrowseActivity::class.java)
intent.putExtra(Constants.BROWSE_EXTRA, browseUrl)
+ intent.putExtra(Constants.STRING_EXTRA, title)
startActivity(
intent,
- ViewUtil.getEmptyActivityBundle(this)
+ getEmptyActivityBundle()
)
}
+ fun openScreenshotActivity(app: App, position: Int) {
+ val intent = Intent(
+ this,
+ ScreenshotActivity::class.java
+ ).apply {
+ putExtra(Constants.STRING_EXTRA, gson.toJson(app.screenshots))
+ putExtra(Constants.INT_EXTRA, position)
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val options =
+ ActivityOptions.makeSceneTransitionAnimation(this)
+ startActivity(intent, options.toBundle())
+ } else {
+ startActivity(intent)
+ }
+ }
+
fun openGoogleActivity() {
val intent = Intent(this, GoogleActivity::class.java)
startActivity(
intent,
- ViewUtil.getEmptyActivityBundle(this)
+ getEmptyActivityBundle()
)
}
fun showNetworkConnectivitySheet() {
- supportFragmentManager.beginTransaction()
- .add(NetworkDialogSheet.newInstance(0), "NDS")
- .commitAllowingStateLoss()
+ runOnUiThread {
+ supportFragmentManager.beginTransaction()
+ .add(NetworkDialogSheet.newInstance(), NetworkDialogSheet.TAG)
+ .commitAllowingStateLoss()
+ }
}
fun hideNetworkConnectivitySheet() {
- val fragment: Fragment? = supportFragmentManager.findFragmentByTag("NDS")
- if (fragment != null) {
- supportFragmentManager.beginTransaction()
- .remove(fragment)
- .commitAllowingStateLoss()
+ runOnUiThread {
+ val fragment = supportFragmentManager.findFragmentByTag(NetworkDialogSheet.TAG)
+ fragment?.let {
+ supportFragmentManager.beginTransaction().remove(fragment).commitAllowingStateLoss()
+ }
}
}
override fun onStart() {
super.onStart()
NetworkProvider.addListener(this)
-
}
override fun onStop() {
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt
index d8c414009..f8c1fef91 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt
@@ -24,10 +24,11 @@ import android.content.Intent
import android.os.Build
import androidx.fragment.app.Fragment
import com.aurora.Constants
+import com.aurora.extensions.getEmptyActivityBundle
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.Category
-import com.aurora.store.util.ViewUtil
import com.aurora.store.view.ui.details.AppDetailsActivity
+import com.aurora.store.view.ui.details.DevProfileActivity
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.lang.reflect.Modifier
@@ -63,12 +64,23 @@ open class BaseFragment : Fragment() {
}
}
- fun openStreamBrowseActivity(browseUrl: String) {
- val intent = Intent(requireContext(), StreamBrowseActivity::class.java)
+ fun openStreamBrowseActivity(browseUrl: String, title: String = "") {
+ val intent = if (browseUrl.toLowerCase().contains("expanded"))
+ Intent(requireContext(), ExpandedStreamBrowseActivity::class.java)
+ else if (browseUrl.toLowerCase().contains("developer"))
+ Intent(requireContext(), DevProfileActivity::class.java)
+ else
+ Intent(requireContext(), StreamBrowseActivity::class.java)
+
intent.putExtra(Constants.BROWSE_EXTRA, browseUrl)
- startActivity(
- intent,
- ViewUtil.getEmptyActivityBundle(requireContext())
- )
+ intent.putExtra(Constants.STRING_EXTRA, title)
+ startActivity(intent, requireContext().getEmptyActivityBundle())
+ }
+
+ fun openEditorStreamBrowseActivity(browseUrl: String, title: String = "") {
+ val intent = Intent(requireContext(), EditorStreamBrowseActivity::class.java)
+ intent.putExtra(Constants.BROWSE_EXTRA, browseUrl)
+ intent.putExtra(Constants.STRING_EXTRA, title)
+ startActivity(intent, requireContext().getEmptyActivityBundle())
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BlacklistActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BlacklistActivity.kt
index 4a894cfaa..d9fcc1bfa 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/BlacklistActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/BlacklistActivity.kt
@@ -25,8 +25,8 @@ import com.aurora.store.R
import com.aurora.store.data.model.Black
import com.aurora.store.data.providers.BlacklistProvider
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.close
-import com.aurora.store.view.epoxy.views.BlackViewModel_
+import com.aurora.extensions.close
+import com.aurora.store.view.epoxy.views.BlackListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.viewmodel.all.BlacklistViewModel
@@ -94,7 +94,7 @@ class BlacklistActivity : BaseActivity() {
} else {
blackList.forEach {
add(
- BlackViewModel_()
+ BlackListViewModel_()
.id(it.packageName.hashCode())
.black(it)
.markChecked(VM.selected.contains(it.packageName))
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseActivity.kt
index 7ef64d098..b92105332 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseActivity.kt
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.data.ViewState
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
import com.aurora.store.view.epoxy.controller.GenericCarouselController
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/EarlyAccessFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/EarlyAccessFragment.kt
index 606cec963..a78917fe0 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/EarlyAccessFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/EarlyAccessFragment.kt
@@ -123,8 +123,6 @@ class EarlyAccessFragment : BaseFragment(), GenericCarouselController.Callbacks
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
- else
- Toast.makeText(requireContext(), "Browse page unavailable", Toast.LENGTH_SHORT).show()
}
override fun onClusterScrolled(streamCluster: StreamCluster) {
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt
index 7452c9fde..0fc158316 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt
@@ -23,7 +23,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.editor.EditorChoiceBundle
@@ -35,7 +34,7 @@ import com.aurora.store.viewmodel.editorschoice.AppEditorChoiceViewModel
import com.aurora.store.viewmodel.editorschoice.BaseEditorChoiceViewModel
import com.aurora.store.viewmodel.editorschoice.GameEditorChoiceViewModel
-class EditorChoiceFragment : Fragment(), EditorChoiceController.Callbacks {
+class EditorChoiceFragment : BaseFragment(), EditorChoiceController.Callbacks {
private lateinit var B: FragmentForYouBinding
private lateinit var C: EditorChoiceController
@@ -97,6 +96,9 @@ class EditorChoiceFragment : Fragment(), EditorChoiceController.Callbacks {
}
override fun onClick(editorChoiceCluster: EditorChoiceCluster) {
-
+ openEditorStreamBrowseActivity(
+ editorChoiceCluster.clusterBrowseUrl,
+ editorChoiceCluster.clusterTitle
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseActivity.kt
new file mode 100644
index 000000000..4b6f5b7a0
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseActivity.kt
@@ -0,0 +1,164 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.commons
+
+import android.os.Bundle
+import androidx.lifecycle.ViewModelProvider
+import com.airbnb.epoxy.EpoxyModel
+import com.aurora.Constants
+import com.aurora.gplayapi.data.models.App
+import com.aurora.store.databinding.ActivityGenericRecyclerBinding
+import com.aurora.extensions.close
+import com.aurora.store.view.epoxy.groups.CarouselHorizontalModel_
+import com.aurora.store.view.epoxy.views.EditorHeadViewModel_
+import com.aurora.store.view.epoxy.views.HorizontalDividerViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
+import com.aurora.store.view.epoxy.views.details.MiniScreenshotView
+import com.aurora.store.view.epoxy.views.details.MiniScreenshotViewModel_
+import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
+import com.aurora.store.viewmodel.editorschoice.EditorBrowseViewModel
+
+
+class EditorStreamBrowseActivity : BaseActivity() {
+
+ lateinit var B: ActivityGenericRecyclerBinding
+ lateinit var VM: EditorBrowseViewModel
+ lateinit var title: String
+
+ override fun onConnected() {
+ hideNetworkConnectivitySheet()
+ }
+
+ override fun onDisconnected() {
+ showNetworkConnectivitySheet()
+ }
+
+ override fun onReconnected() {
+
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ B = ActivityGenericRecyclerBinding.inflate(layoutInflater)
+ VM = ViewModelProvider(this).get(EditorBrowseViewModel::class.java)
+
+ setContentView(B.root)
+
+ attachToolbar()
+
+ VM.liveData.observe(this, {
+ updateController(it)
+ })
+
+ intent.apply {
+ getStringExtra(Constants.BROWSE_EXTRA)?.let {
+ VM.getEditorStreamBundle(it)
+ }
+ getStringExtra(Constants.STRING_EXTRA)?.let {
+ B.layoutToolbarAction.txtTitle.text = it
+ }
+ }
+
+ updateController(null)
+ }
+
+ private fun attachToolbar() {
+ B.layoutToolbarAction.imgActionPrimary.setOnClickListener {
+ close()
+ }
+ }
+
+ private fun updateController(appList: MutableList?) {
+ B.recycler.withModels {
+ setFilterDuplicates(true)
+ if (appList == null) {
+ for (i in 1..6) {
+ add(
+ AppListViewShimmerModel_()
+ .id(i)
+ )
+ }
+ } else {
+ appList.forEach { app ->
+ val screenshotsViewModels = mutableListOf>()
+
+ for ((position, artwork) in app.screenshots.withIndex()) {
+ screenshotsViewModels.add(
+ MiniScreenshotViewModel_()
+ .id(artwork.url)
+ .position(position)
+ .artwork(artwork)
+ .callback(object : MiniScreenshotView.ScreenshotCallback {
+ override fun onClick(position: Int) {
+ openScreenshotActivity(app, position)
+ }
+ })
+ )
+ }
+
+ add(
+ AppListViewModel_()
+ .id("app_${app.id}")
+ .app(app)
+ .click { _ -> openDetailsActivity(app) }
+ )
+
+ app.editorReason?.let { editorReason ->
+ add(
+ EditorHeadViewModel_()
+ .id("bulletin_${app.id}")
+ .title(
+ editorReason.bulletins
+ .joinToString(transform = { "\n• $it" })
+ .substringAfter(delimiter = "\n")
+ )
+ .click { _ -> openDetailsActivity(app) }
+ )
+ }
+
+ if (screenshotsViewModels.isNotEmpty()) {
+ add(
+ CarouselHorizontalModel_()
+ .id("screenshots_${app.id}")
+ .models(screenshotsViewModels)
+ )
+ }
+
+ app.editorReason?.let { editorReason ->
+ if (editorReason.description.isNotEmpty()) {
+ add(
+ EditorHeadViewModel_()
+ .id("description_${app.id}")
+ .title(editorReason.description)
+ .click { _ -> openDetailsActivity(app) }
+ )
+ }
+ }
+
+ add(
+ HorizontalDividerViewModel_()
+ .id("divider_${app.id}")
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseActivity.kt
new file mode 100644
index 000000000..dd5ba1534
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseActivity.kt
@@ -0,0 +1,166 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.commons
+
+import android.os.Bundle
+import androidx.lifecycle.ViewModelProvider
+import com.airbnb.epoxy.EpoxyModel
+import com.aurora.Constants
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.store.databinding.ActivityGenericRecyclerBinding
+import com.aurora.extensions.close
+import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
+import com.aurora.store.view.epoxy.groups.CarouselHorizontalModel_
+import com.aurora.store.view.epoxy.views.AppProgressViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
+import com.aurora.store.view.epoxy.views.details.MiniScreenshotView
+import com.aurora.store.view.epoxy.views.details.MiniScreenshotViewModel_
+import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
+import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
+
+
+class ExpandedStreamBrowseActivity : BaseActivity() {
+
+ lateinit var B: ActivityGenericRecyclerBinding
+ lateinit var VM: ExpandedStreamBrowseViewModel
+
+ lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
+
+ lateinit var title: String
+ lateinit var cluster: StreamCluster
+
+ override fun onConnected() {
+ hideNetworkConnectivitySheet()
+ }
+
+ override fun onDisconnected() {
+ showNetworkConnectivitySheet()
+ }
+
+ override fun onReconnected() {
+
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ B = ActivityGenericRecyclerBinding.inflate(layoutInflater)
+ VM = ViewModelProvider(this).get(ExpandedStreamBrowseViewModel::class.java)
+
+ setContentView(B.root)
+
+ attachToolbar()
+
+ VM.liveData.observe(this, {
+ if (!::cluster.isInitialized)
+ attachRecycler()
+
+ cluster = it
+
+ updateController(cluster)
+ updateTitle(cluster)
+ })
+
+ intent.apply {
+ getStringExtra(Constants.BROWSE_EXTRA)?.let {
+ VM.getInitialCluster(it)
+ }
+ getStringExtra(Constants.STRING_EXTRA)?.let {
+ B.layoutToolbarAction.txtTitle.text = it
+ }
+ }
+
+ updateController(null)
+ }
+
+ private fun updateTitle(streamCluster: StreamCluster) {
+ if (streamCluster.clusterTitle.isNotEmpty())
+ B.layoutToolbarAction.txtTitle.text = streamCluster.clusterTitle
+ }
+
+ private fun attachToolbar() {
+ B.layoutToolbarAction.imgActionPrimary.setOnClickListener {
+ close()
+ }
+ }
+
+ private fun attachRecycler() {
+ endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
+ override fun onLoadMore(currentPage: Int) {
+ VM.next()
+ }
+ }
+ B.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
+ }
+
+ private fun updateController(streamCluster: StreamCluster?) {
+ B.recycler.withModels {
+ setFilterDuplicates(true)
+ if (streamCluster == null) {
+ for (i in 1..6) {
+ add(
+ AppListViewShimmerModel_()
+ .id(i)
+ )
+ }
+ } else {
+ streamCluster.clusterAppList.forEach {
+ val screenshotsViewModels = mutableListOf>()
+
+ for ((position, artwork) in it.screenshots.withIndex()) {
+ screenshotsViewModels.add(
+ MiniScreenshotViewModel_()
+ .id(artwork.url)
+ .position(position)
+ .artwork(artwork)
+ .callback(object : MiniScreenshotView.ScreenshotCallback {
+ override fun onClick(position: Int) {
+ openScreenshotActivity(it, position)
+ }
+ })
+ )
+ }
+
+ if (screenshotsViewModels.isNotEmpty()) {
+ add(
+ CarouselHorizontalModel_()
+ .id("${it.id}_screenshots")
+ .models(screenshotsViewModels)
+ )
+ }
+
+ add(
+ AppListViewModel_()
+ .id(it.packageName.hashCode())
+ .app(it)
+ .click { _ -> openDetailsActivity(it) }
+ )
+ }
+
+ if (streamCluster.hasNext()) {
+ add(
+ AppProgressViewModel_()
+ .id("progress")
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt
index f27390f39..8341a4ae8 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt
@@ -23,7 +23,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
@@ -46,6 +45,7 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
private lateinit var C: GenericCarouselController
private lateinit var VM: BaseClusterViewModel
+ private lateinit var streamBundle: StreamBundle
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
private var pageType = 0
@@ -110,11 +110,18 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
}
is ViewState.Success<*> -> {
- updateController(it.data as StreamBundle)
+ if (!::streamBundle.isInitialized)
+ attachRecycler()
+
+ streamBundle = it.data as StreamBundle
+
+ updateController(streamBundle)
}
}
})
+ }
+ private fun attachRecycler() {
endlessRecyclerOnScrollListener =
object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
@@ -131,9 +138,7 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
- openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
- else
- Toast.makeText(requireContext(), "Browse page unavailable", Toast.LENGTH_SHORT).show()
+ openStreamBrowseActivity(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
}
override fun onClusterScrolled(streamCluster: StreamCluster) {
@@ -145,6 +150,6 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
}
override fun onAppLongClick(app: App) {
- AppPeekDialogSheet.newInstance(app).show(parentFragmentManager, "APDS")
+
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseActivity.kt b/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseActivity.kt
index 1525cd0fc..1bdfb40c8 100644
--- a/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseActivity.kt
@@ -24,10 +24,10 @@ import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.viewmodel.browse.StreamBrowseViewModel
@@ -40,6 +40,7 @@ class StreamBrowseActivity : BaseActivity() {
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
lateinit var title: String
+ lateinit var cluster: StreamCluster
override fun onConnected() {
hideNetworkConnectivitySheet()
@@ -63,11 +64,15 @@ class StreamBrowseActivity : BaseActivity() {
setContentView(B.root)
attachToolbar()
- attachRecycler()
VM.liveData.observe(this, {
- updateController(it)
- updateTitle(it)
+ if (!::cluster.isInitialized)
+ attachRecycler()
+
+ cluster = it
+
+ updateController(cluster)
+ updateTitle(cluster)
})
intent.apply {
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsActivity.kt
index 778a74e3a..186500659 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsActivity.kt
@@ -29,22 +29,26 @@ import android.view.MenuItem
import android.view.View
import android.widget.LinearLayout
import com.aurora.Constants
+import com.aurora.extensions.*
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.PurchaseHelper
+import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager
import com.aurora.store.data.downloader.RequestBuilder
import com.aurora.store.data.event.BusEvent
+import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.ActivityDetailsBinding
import com.aurora.store.util.*
-import com.aurora.store.util.extensions.*
+import com.aurora.store.view.custom.layouts.button.ActionButton
import com.aurora.store.view.ui.downloads.DownloadActivity
+import com.aurora.store.view.ui.sheets.InstallErrorDialogSheet
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
@@ -70,6 +74,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
private lateinit var fetch: Fetch
private lateinit var fetchGroupListener: FetchGroupListener
+ private var isExternal = false
private var isNone = false
private var status = Status.NONE
private var isInstalled: Boolean = false
@@ -97,13 +102,29 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
- fun onEventMainThread(event: BusEvent) {
+ fun onEventMainThread(event: Any) {
when (event) {
is BusEvent.InstallEvent -> {
- attachActions()
+ if (app.packageName == event.packageName) {
+ attachActions()
+ }
}
is BusEvent.UninstallEvent -> {
- attachActions()
+ if (app.packageName == event.packageName) {
+ attachActions()
+ }
+ }
+ is InstallerEvent.Failed -> {
+ if (app.packageName == event.packageName) {
+ InstallErrorDialogSheet.newInstance(
+ app,
+ event.packageName,
+ event.error,
+ event.extra
+ ).show(supportFragmentManager, "SED")
+ attachActions()
+ updateActionState(ActionButton.State.IDLE)
+ }
}
else -> {
@@ -119,6 +140,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
onNewIntent(intent)
}
+ override fun onBackPressed() {
+ if (isExternal) {
+ open(MainActivity::class.java, true)
+ }
+ super.onBackPressed()
+
+ }
+
override fun onResume() {
if (!isLAndAbove()) {
checkAndSetupInstall()
@@ -128,16 +157,27 @@ class AppDetailsActivity : BaseDetailsActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
- val itemRaw: String? = intent.getStringExtra(Constants.STRING_EXTRA)
- if (itemRaw != null) {
- app = gson.fromJson(itemRaw, App::class.java)
- isInstalled = PackageUtil.isInstalled(this, app.packageName)
- inflatePartialApp()
-
- fetchCompleteApp()
+ if (intent.scheme != null && (intent.scheme == "market" || intent.scheme == "http" || intent.scheme == "https")) {
+ val packageName = intent.data!!.getQueryParameter("id")
+ if (packageName.isNullOrEmpty()) {
+ close()
+ } else {
+ isExternal = true
+ app = App(packageName)
+ fetchCompleteApp()
+ }
} else {
- close()
+ val rawApp: String? = intent.getStringExtra(Constants.STRING_EXTRA)
+ if (rawApp != null) {
+ app = gson.fromJson(rawApp, App::class.java)
+ isInstalled = PackageUtil.isInstalled(this, app.packageName)
+
+ inflatePartialApp()
+ fetchCompleteApp()
+ } else {
+ close()
+ }
}
}
@@ -164,6 +204,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
open(DownloadActivity::class.java)
return true
}
+ R.id.action_playstore -> {
+ browse("${Constants.SHARE_URL}${app.packageName}")
+ return true
+ }
}
return super.onOptionsItemSelected(item)
}
@@ -184,6 +228,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
checkAndSetupInstall()
}
+ private fun updateActionState(state: ActionButton.State) {
+ B.layoutDetailsInstall.btnDownload.updateState(state)
+ }
+
private fun openApp() {
val intent = PackageUtil.getLaunchIntent(this, app.packageName)
if (intent != null) {
@@ -197,8 +245,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
@Synchronized
private fun install(files: List) {
+ updateActionState(ActionButton.State.IDLE)
+
task {
- AppInstaller.with(this)
+ AppInstaller(this)
.getPreferredInstaller()
.install(
app.packageName,
@@ -217,7 +267,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
@Synchronized
private fun uninstallApp() {
- AppInstaller.with(this)
+ AppInstaller(this)
.getPreferredInstaller()
.uninstall(app.packageName)
}
@@ -233,6 +283,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
.using(HttpClient.getPreferredClient())
.getAppByPackageName(app.packageName)
} successUi {
+ if (isExternal) {
+ app = it
+ inflatePartialApp()
+ }
inflateExtraDetails(it)
} failUi {
toast("Failed to fetch app details")
@@ -240,12 +294,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
private fun inflatePartialApp() {
- attachWhiteListStatus()
- attachHeader()
- attachToolbar()
- attachBottomSheet()
- attachFetch()
- attachActions()
+ if (::app.isInitialized) {
+ attachWhiteListStatus()
+ attachHeader()
+ attachToolbar()
+ attachBottomSheet()
+ attachFetch()
+ attachActions()
+ }
}
private fun attachHeader() {
@@ -287,6 +343,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
inflateAppRatingAndReviews(B.layoutDetailsReview, app)
inflateAppDevInfo(B.layoutDetailsDev, app)
inflateAppPrivacy(B.layoutDetailsPrivacy, app)
+ inflateAppPermission(B.layoutDetailsPermissions, app)
if (!authData.isAnonymous) {
app.testingProgram?.let {
@@ -297,6 +354,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
inflateBetaSubscription(B.layoutDetailsBeta, app)
}
+
+ inflateAppStream(B.epoxyRecyclerStream, app)
}
}
@@ -331,6 +390,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
private fun purchase() {
+ updateActionState(ActionButton.State.PROGRESS)
task {
val authData = AuthProvider
.with(this)
@@ -344,9 +404,11 @@ class AppDetailsActivity : BaseDetailsActivity() {
enqueue(it)
} else {
Log.e("Failed to download : ${app.displayName}")
+ updateActionState(ActionButton.State.IDLE)
}
} failUi {
expandBottomSheet(it.message)
+ updateActionState(ActionButton.State.IDLE)
Log.e("Failed to purchase ${app.displayName} : ${it.message}")
}
}
@@ -370,6 +432,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
Log.i("Downloading Apks : %s", app.displayName)
}
} else {
+ updateActionState(ActionButton.State.IDLE)
expandBottomSheet(getString(R.string.purchase_no_file))
}
}
@@ -411,8 +474,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
- B.layoutDetailsInstall.txtPurchaseError.text = message
- B.layoutDetailsInstall.btnDownload.updateProgress(false)
+ with(B.layoutDetailsInstall) {
+ txtPurchaseError.text = message
+ btnDownload.updateState(ActionButton.State.IDLE)
+ if (app.isFree)
+ btnDownload.setText(R.string.action_install)
+ else
+ btnDownload.setText(app.price)
+ }
}
private fun checkAndSetupInstall() {
@@ -426,40 +495,42 @@ class AppDetailsActivity : BaseDetailsActivity() {
app.versionCode.toLong()
)
+ val installedVersion = PackageUtil.getInstalledVersion(this, app.packageName)
+
if (isUpdatable) {
- btn.setText(getString(R.string.action_update))
- btn.addOnClickListener {
- btn.updateProgress(true)
- startDownload()
- }
+ B.layoutDetailsApp.txtLine3.text =
+ ("$installedVersion > ${app.versionName}.${app.versionCode}")
+ btn.setText(R.string.action_update)
+ btn.addOnClickListener { startDownload() }
} else {
- btn.setText(getString(R.string.action_open))
+ B.layoutDetailsApp.txtLine3.text = installedVersion
+ btn.setText(R.string.action_open)
btn.addOnClickListener { openApp() }
}
} else {
if (app.isFree) {
- btn.setText(getString(R.string.action_install))
+ btn.setText(R.string.action_install)
} else {
btn.setText(app.price)
}
btn.addOnClickListener {
- btn.setText(getString(R.string.download_metadata))
- btn.updateProgress(true)
+ btn.setText(R.string.download_metadata)
startDownload()
}
}
-
- btn.updateProgress(false)
}
}
@Synchronized
private fun flip(nextView: Int) {
runOnUiThread {
- B.layoutDetailsInstall.viewFlipper.displayedChild = nextView
- if (nextView == 0)
- checkAndSetupInstall()
+ val displayChild = B.layoutDetailsInstall.viewFlipper.displayedChild
+ if (displayChild != nextView) {
+ B.layoutDetailsInstall.viewFlipper.displayedChild = nextView
+ if (nextView == 0)
+ checkAndSetupInstall()
+ }
}
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/BaseDetailsActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/BaseDetailsActivity.kt
index 6933a1e56..60f0223a0 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/BaseDetailsActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/BaseDetailsActivity.kt
@@ -19,7 +19,6 @@
package com.aurora.store.view.ui.details
-import android.app.ActivityOptions
import android.content.Intent
import android.os.Build
import android.text.Html
@@ -29,12 +28,18 @@ import android.widget.RelativeLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
+import androidx.lifecycle.ViewModelProvider
+import com.airbnb.epoxy.EpoxyRecyclerView
import com.aurora.Constants
-import com.aurora.gplayapi.data.models.App
-import com.aurora.gplayapi.data.models.Review
+import com.aurora.extensions.hide
+import com.aurora.extensions.load
+import com.aurora.extensions.show
+import com.aurora.extensions.toast
+import com.aurora.gplayapi.data.models.*
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.ReviewsHelper
import com.aurora.store.R
+import com.aurora.store.data.ViewState
import com.aurora.store.data.model.ExodusReport
import com.aurora.store.data.model.Report
import com.aurora.store.data.network.HttpClient
@@ -42,19 +47,23 @@ import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.*
import com.aurora.store.util.CommonUtil
import com.aurora.store.util.NavigationUtil
-import com.aurora.store.util.extensions.hide
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.show
-import com.aurora.store.util.extensions.toast
import com.aurora.store.view.custom.RatingView
+import com.aurora.store.view.epoxy.controller.DetailsCarouselController
+import com.aurora.store.view.epoxy.controller.GenericCarouselController
import com.aurora.store.view.epoxy.views.*
+import com.aurora.store.view.epoxy.views.details.ReviewViewModel_
+import com.aurora.store.view.epoxy.views.details.ScreenshotView
+import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
+import com.aurora.store.view.ui.sheets.PermissionBottomSheet
+import com.aurora.store.viewmodel.details.DetailsClusterViewModel
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.json.JSONObject
import java.util.*
+
abstract class BaseDetailsActivity : BaseActivity() {
private val exodusBaseUrl = "https://reports.exodus-privacy.eu.org/api/search/"
@@ -65,7 +74,8 @@ abstract class BaseDetailsActivity : BaseActivity() {
B.txtInstalls.text = CommonUtil.addDiPrefix(app.installs)
B.txtSize.text = CommonUtil.addSiPrefix(app.size)
B.txtRating.text = app.labeledRating
- B.txtSdk.text = String.format("Target SDK %s", "${app.targetSdk}")
+ B.txtSdk.text = ("Target SDK ${app.targetSdk}")
+ B.txtUpdated.text = app.updatedOn
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
B.txtDescription.justificationMode = Layout.JUSTIFICATION_MODE_INTER_WORD
@@ -267,7 +277,7 @@ abstract class BaseDetailsActivity : BaseActivity() {
}
} failUi {
updateBetaActions(B, betaProgram.isSubscribed)
- toast("Failed to update beta status")
+ toast(getString(R.string.details_beta_delay))
}
}
} else {
@@ -276,6 +286,68 @@ abstract class BaseDetailsActivity : BaseActivity() {
}
}
+ fun inflateAppStream(epoxyRecyclerView: EpoxyRecyclerView, app: App) {
+ app.detailsStreamUrl?.let {
+ val VM = ViewModelProvider(this).get(DetailsClusterViewModel::class.java)
+
+ val carouselController =
+ DetailsCarouselController(object : GenericCarouselController.Callbacks {
+ override fun onHeaderClicked(streamCluster: StreamCluster) {
+ if (streamCluster.clusterBrowseUrl.isNotEmpty())
+ openStreamBrowseActivity(
+ streamCluster.clusterBrowseUrl,
+ streamCluster.clusterTitle
+ )
+ else
+ toast("Browse page unavailable")
+ }
+
+ override fun onClusterScrolled(streamCluster: StreamCluster) {
+ VM.observeCluster(streamCluster)
+ }
+
+ override fun onAppClick(app: App) {
+ openDetailsActivity(app)
+ }
+
+ override fun onAppLongClick(app: App) {
+
+ }
+ })
+
+ VM.liveData.observe(this, {
+ when (it) {
+ is ViewState.Empty -> {
+ }
+ is ViewState.Loading -> {
+
+ }
+ is ViewState.Error -> {
+
+ }
+ is ViewState.Status -> {
+
+ }
+ is ViewState.Success<*> -> {
+ carouselController.setData(it.data as StreamBundle)
+ }
+ }
+ })
+
+ epoxyRecyclerView.setController(carouselController)
+
+ VM.getStreamBundle(it)
+ }
+ }
+
+ fun inflateAppPermission(B: LayoutDetailsPermissionsBinding, app: App) {
+ B.headerPermission.addClickListener {
+ PermissionBottomSheet.newInstance(app)
+ .show(supportFragmentManager, PermissionBottomSheet.TAG)
+ }
+ B.txtPermissionCount.text = ("${app.permissions.size} permissions")
+ }
+
private fun updateBetaActions(B: LayoutDetailsBetaBinding, isSubscribed: Boolean) {
if (isSubscribed) {
B.btnBetaAction.text = getString(R.string.action_leave)
@@ -286,23 +358,19 @@ abstract class BaseDetailsActivity : BaseActivity() {
}
}
-
//Helpers
- private fun openScreenshotActivity(app: App, position: Int) {
- val intent = Intent(
- this,
- ScreenshotActivity::class.java
- ).apply {
- putExtra(Constants.STRING_EXTRA, gson.toJson(app.screenshots))
- putExtra(Constants.INT_EXTRA, position)
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- val options =
- ActivityOptions.makeSceneTransitionAnimation(this)
- startActivity(intent, options.toBundle())
- } else {
- startActivity(intent)
+
+ open fun getIntentPackageName(intent: Intent): String? {
+ if (intent.hasExtra(Constants.STRING_EXTRA)) {
+ return intent.getStringExtra(Constants.STRING_EXTRA)
+ } else if (intent.scheme != null && (intent.scheme == "market" || intent.scheme == "http" || intent.scheme == "https")
+ ) {
+ return intent.data!!.getQueryParameter("id")
+ } else if (intent.extras != null) {
+ val bundle = intent.extras
+ return bundle!!.getString(Constants.STRING_EXTRA)
}
+ return null
}
private fun addAvgReviews(number: Int, max: Long, rating: Long): RelativeLayout {
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusActivity.kt
index bc824178c..5cc2c962b 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusActivity.kt
@@ -27,10 +27,10 @@ import com.aurora.store.data.model.ExodusTracker
import com.aurora.store.data.model.Report
import com.aurora.store.data.providers.ExodusDataProvider
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.browse
-import com.aurora.store.util.extensions.close
-import com.aurora.store.view.epoxy.views.ExodusViewModel_
+import com.aurora.extensions.browse
+import com.aurora.extensions.close
import com.aurora.store.view.epoxy.views.HeaderViewModel_
+import com.aurora.store.view.epoxy.views.details.ExodusViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import org.json.JSONObject
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreActivity.kt
index 7a01692af..7891a708d 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreActivity.kt
@@ -28,13 +28,15 @@ import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.store.R
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.ActivityDetailsMoreBinding
-import com.aurora.store.util.extensions.close
-import com.aurora.store.view.epoxy.views.FileViewModel_
+import com.aurora.extensions.close
import com.aurora.store.view.epoxy.views.HeaderViewModel_
import com.aurora.store.view.epoxy.views.app.NoAppAltViewModel_
import com.aurora.store.view.epoxy.views.details.AppDependentViewModel_
+import com.aurora.store.view.epoxy.views.details.FileViewModel_
+import com.aurora.store.view.epoxy.views.details.InfoViewModel_
import com.aurora.store.view.epoxy.views.details.MoreBadgeViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
+import com.google.protobuf.MapEntry
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
@@ -135,6 +137,21 @@ class DetailsMoreActivity : BaseActivity() {
}
}
}
+
+ if (app.appInfo.appInfoMap.isNotEmpty()){
+ add(
+ HeaderViewModel_()
+ .id("info_header")
+ .title("Info")
+ )
+ app.appInfo.appInfoMap.forEach{
+ add(
+ InfoViewModel_()
+ .id(it.key)
+ .badge(it)
+ )
+ }
+ }
}
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewActivity.kt
index 8fec8e22e..d7f245682 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewActivity.kt
@@ -27,9 +27,9 @@ import com.aurora.gplayapi.data.models.Review
import com.aurora.store.R
import com.aurora.store.data.ViewState
import com.aurora.store.databinding.ActivityDetailsReviewBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.ReviewViewModel_
+import com.aurora.store.view.epoxy.views.details.ReviewViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.viewmodel.review.ReviewViewModel
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DevAppsActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/DevAppsActivity.kt
index 23e1f5b1c..777a6328b 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/DevAppsActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/DevAppsActivity.kt
@@ -27,9 +27,9 @@ import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.viewmodel.search.SearchResultViewModel
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DevProfileActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/DevProfileActivity.kt
new file mode 100644
index 000000000..24e6a3c1c
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/details/DevProfileActivity.kt
@@ -0,0 +1,158 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.details
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.lifecycle.ViewModelProvider
+import com.aurora.Constants
+import com.aurora.gplayapi.data.models.App
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.gplayapi.data.models.details.DevStream
+import com.aurora.store.R
+import com.aurora.store.data.ViewState
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.databinding.ActivityDevProfileBinding
+import com.aurora.extensions.close
+import com.aurora.extensions.load
+import com.aurora.store.view.epoxy.controller.DeveloperCarouselController
+import com.aurora.store.view.epoxy.controller.GenericCarouselController
+import com.aurora.store.view.ui.commons.BaseActivity
+import com.aurora.store.viewmodel.details.DevProfileViewModel
+
+class DevProfileActivity : BaseActivity(), GenericCarouselController.Callbacks {
+
+ private lateinit var B: ActivityDevProfileBinding
+ private lateinit var VM: DevProfileViewModel
+ private lateinit var C: DeveloperCarouselController
+ private lateinit var authData: AuthData
+
+ override fun onConnected() {
+ hideNetworkConnectivitySheet()
+ }
+
+ override fun onDisconnected() {
+ showNetworkConnectivitySheet()
+ }
+
+ override fun onReconnected() {
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ B = ActivityDevProfileBinding.inflate(layoutInflater)
+ C = DeveloperCarouselController(this)
+ VM = ViewModelProvider(this).get(DevProfileViewModel::class.java)
+
+ authData = AuthProvider.with(this).getAuthData()
+
+ setContentView(B.root)
+
+ attachToolbar()
+ attachRecycler()
+
+ VM.liveData.observe(this, {
+ when (it) {
+ is ViewState.Empty -> {
+ }
+ is ViewState.Loading -> {
+
+ }
+ is ViewState.Error -> {
+
+ }
+ is ViewState.Status -> {
+
+ }
+ is ViewState.Success<*> -> {
+ updateInfo(it.data as DevStream)
+ updateController(it.data as DevStream)
+ }
+ }
+ })
+
+ B.viewFlipper.displayedChild = 1
+
+ onNewIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
+ if (intent != null) {
+ if (intent.scheme != null && (intent.scheme == "http" || intent.scheme == "https")) {
+ val devId = intent.data!!.getQueryParameter("id")
+ if (devId.isNullOrEmpty()) {
+ close()
+ } else {
+ VM.getStreamBundle(devId)
+ }
+ } else {
+ intent.getStringExtra(Constants.BROWSE_EXTRA)?.let {
+ VM.getStreamBundle(it.substringAfter("developer-"))
+ }
+ intent.getStringExtra(Constants.STRING_EXTRA)?.let {
+ B.layoutToolbarAction.txtTitle.text = it
+ }
+ }
+ } else {
+ close()
+ }
+ }
+
+ private fun attachToolbar() {
+ B.layoutToolbarAction.toolbar.setOnClickListener {
+ close()
+ }
+ B.layoutToolbarAction.txtTitle.text = getString(R.string.details_dev_profile)
+ }
+
+ private fun attachRecycler() {
+ B.recycler.setController(C)
+ }
+
+ private fun updateInfo(devStream: DevStream) {
+ B.layoutToolbarAction.txtTitle.text = devStream.title
+ B.txtDevName.text = devStream.title
+ B.txtDevDescription.text = devStream.description
+ B.imgIcon.load(devStream.imgUrl)
+ B.viewFlipper.displayedChild = 0
+ }
+
+ private fun updateController(devStream: DevStream) {
+ C.setData(devStream.streamBundle)
+ }
+
+ override fun onHeaderClicked(streamCluster: StreamCluster) {
+ openStreamBrowseActivity(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
+ }
+
+ override fun onClusterScrolled(streamCluster: StreamCluster) {
+ VM.observeCluster(streamCluster)
+ }
+
+ override fun onAppClick(app: App) {
+ openDetailsActivity(app)
+ }
+
+ override fun onAppLongClick(app: App) {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotActivity.kt b/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotActivity.kt
index 8d925e598..faee8c7db 100644
--- a/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotActivity.kt
@@ -26,13 +26,11 @@ import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import com.aurora.Constants
import com.aurora.gplayapi.data.models.Artwork
-import com.aurora.store.R
import com.aurora.store.databinding.ActivityScreenshotBinding
-import com.aurora.store.util.extensions.close
-import com.aurora.store.view.epoxy.views.LargeScreenshotViewModel_
+import com.aurora.extensions.close
+import com.aurora.store.view.epoxy.views.details.LargeScreenshotViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.google.gson.reflect.TypeToken
-import com.tonyodev.fetch2.Status
class ScreenshotActivity : BaseActivity() {
diff --git a/app/src/main/java/com/aurora/store/view/ui/games/TopChartFragment.kt b/app/src/main/java/com/aurora/store/view/ui/games/TopChartFragment.kt
index c9c284562..588b94fd6 100644
--- a/app/src/main/java/com/aurora/store/view/ui/games/TopChartFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/games/TopChartFragment.kt
@@ -29,7 +29,7 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R
import com.aurora.store.databinding.FragmentTopContainerBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.view.ui.commons.BaseFragment
diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/AccentFragment.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/AccentFragment.kt
index e7f71001c..8f43678d6 100644
--- a/app/src/main/java/com/aurora/store/view/ui/onboarding/AccentFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/AccentFragment.kt
@@ -35,10 +35,10 @@ import com.aurora.store.databinding.FragmentOnboardingAccentBinding
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
-import com.aurora.store.util.extensions.applyTheme
-import com.aurora.store.util.extensions.hide
-import com.aurora.store.util.extensions.isVisible
-import com.aurora.store.util.extensions.show
+import com.aurora.extensions.applyTheme
+import com.aurora.extensions.hide
+import com.aurora.extensions.isVisible
+import com.aurora.extensions.show
import com.aurora.store.util.save
import com.aurora.store.view.custom.CubicBezierInterpolator
import com.aurora.store.view.epoxy.views.AccentViewModel_
diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/InstallerFragment.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/InstallerFragment.kt
index 666ef5a97..e23379beb 100644
--- a/app/src/main/java/com/aurora/store/view/ui/onboarding/InstallerFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/InstallerFragment.kt
@@ -23,16 +23,20 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import com.aurora.extensions.isMIUI
+import com.aurora.extensions.isMiuiOptimizationDisabled
+import com.aurora.extensions.showDialog
import com.aurora.store.R
+import com.aurora.store.data.installer.ServiceInstaller
import com.aurora.store.data.model.Installer
import com.aurora.store.databinding.FragmentOnboardingInstallerBinding
+import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
-import com.aurora.store.util.extensions.runOnUiThread
-import com.aurora.store.util.extensions.toast
import com.aurora.store.util.save
import com.aurora.store.view.epoxy.views.preference.InstallerViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
+import com.aurora.store.view.ui.sheets.DeviceMiuiSheet
import com.google.gson.reflect.TypeToken
import com.topjohnwu.superuser.Shell
import java.nio.charset.StandardCharsets
@@ -81,34 +85,53 @@ class InstallerFragment : BaseFragment() {
.id(it.id)
.installer(it)
.markChecked(installerId == it.id)
- .checked { _, checked ->
- if (checked) {
- installerId = it.id
- save(installerId)
- requestModelBuild()
- }
+ .click { _ ->
+ save(it.id)
+ requestModelBuild()
}
)
}
}
+
+ if (isMIUI() && !isMiuiOptimizationDisabled()) {
+ DeviceMiuiSheet.newInstance().show(childFragmentManager, DeviceMiuiSheet.TAG)
+ }
}
private fun save(installerId: Int) {
- if (installerId == 2) {
- checkRoot()
- }
- save(PREFERENCE_INSTALLER_ID, installerId)
- }
-
- private fun checkRoot() {
- Shell.getShell {
- runOnUiThread {
- requireContext().toast(
- if (it.isRoot)
- getString(R.string.installer_root_available)
- else
- getString(R.string.installer_root_unavailable)
- )
+ when (installerId) {
+ 0 -> {
+ if (isMIUI() && !isMiuiOptimizationDisabled()) {
+ DeviceMiuiSheet.newInstance().show(childFragmentManager, DeviceMiuiSheet.TAG)
+ }
+ this.installerId = installerId
+ save(PREFERENCE_INSTALLER_ID, installerId)
+ }
+ 2 -> {
+ if (checkRootAvailability()) {
+ this.installerId = installerId
+ save(PREFERENCE_INSTALLER_ID, installerId)
+ } else {
+ showDialog(
+ R.string.action_installations,
+ R.string.installer_root_unavailable
+ )
+ }
+ }
+ 3 -> {
+ if (checkServicesAvailability()) {
+ this.installerId = installerId
+ save(PREFERENCE_INSTALLER_ID, installerId)
+ } else {
+ showDialog(
+ R.string.action_installations,
+ R.string.installer_service_unavailable
+ )
+ }
+ }
+ else -> {
+ this.installerId = installerId
+ save(PREFERENCE_INSTALLER_ID, installerId)
}
}
}
@@ -125,4 +148,15 @@ class InstallerFragment : BaseFragment() {
object : TypeToken?>() {}.type
)
}
+
+ private fun checkRootAvailability(): Boolean {
+ return Shell.getShell().isRoot
+ }
+
+ private fun checkServicesAvailability(): Boolean {
+ return PackageUtil.isInstalled(
+ requireContext(),
+ ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/OnboardingActivity.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/OnboardingActivity.kt
index 7500f9c96..0aeb1d698 100644
--- a/app/src/main/java/com/aurora/store/view/ui/onboarding/OnboardingActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/OnboardingActivity.kt
@@ -32,13 +32,14 @@ import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT
import com.aurora.store.util.Preferences.PREFERENCE_DOWNLOAD_ACTIVE
+import com.aurora.store.util.Preferences.PREFERENCE_DOWNLOAD_EXTERNAL
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_GOOGLE
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_INTRO
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
-import com.aurora.store.util.extensions.open
+import com.aurora.extensions.open
import com.aurora.store.util.save
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.view.ui.splash.SplashActivity
@@ -149,13 +150,14 @@ class OnboardingActivity : BaseActivity() {
/*Downloader*/
save(PREFERENCE_DOWNLOAD_ACTIVE, 3)
+ save(PREFERENCE_DOWNLOAD_EXTERNAL, false)
/*Theme*/
save(PREFERENCE_THEME_TYPE, 0)
save(PREFERENCE_THEME_ACCENT, 0)
/*Installer*/
- save(PREFERENCE_AUTO_DELETE, false)
+ save(PREFERENCE_AUTO_DELETE, true)
save(PREFERENCE_INSTALLER_ID, 0)
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/ThemeFragment.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/ThemeFragment.kt
index 63783e452..1722de6b5 100644
--- a/app/src/main/java/com/aurora/store/view/ui/onboarding/ThemeFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/ThemeFragment.kt
@@ -35,10 +35,10 @@ import com.aurora.store.databinding.FragmentOnboardingThemeBinding
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
-import com.aurora.store.util.extensions.applyTheme
-import com.aurora.store.util.extensions.hide
-import com.aurora.store.util.extensions.isVisible
-import com.aurora.store.util.extensions.show
+import com.aurora.extensions.applyTheme
+import com.aurora.extensions.hide
+import com.aurora.extensions.isVisible
+import com.aurora.extensions.show
import com.aurora.store.util.save
import com.aurora.store.view.custom.CubicBezierInterpolator
import com.aurora.store.view.epoxy.views.ThemeViewModel_
diff --git a/app/src/main/java/com/aurora/store/view/ui/onboarding/WelcomeFragment.kt b/app/src/main/java/com/aurora/store/view/ui/onboarding/WelcomeFragment.kt
index fdbc4e99b..5f754f8b4 100644
--- a/app/src/main/java/com/aurora/store/view/ui/onboarding/WelcomeFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/onboarding/WelcomeFragment.kt
@@ -26,7 +26,7 @@ import android.view.ViewGroup
import com.aurora.store.R
import com.aurora.store.data.model.Dash
import com.aurora.store.databinding.FragmentOnboardingWelcomeBinding
-import com.aurora.store.util.extensions.browse
+import com.aurora.extensions.browse
import com.aurora.store.view.epoxy.views.preference.DashViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
import com.google.gson.reflect.TypeToken
diff --git a/app/src/main/java/com/aurora/store/view/ui/preferences/DownloadPreference.kt b/app/src/main/java/com/aurora/store/view/ui/preferences/DownloadPreference.kt
index f1cb2a0c5..814a8ad9a 100644
--- a/app/src/main/java/com/aurora/store/view/ui/preferences/DownloadPreference.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/preferences/DownloadPreference.kt
@@ -20,12 +20,44 @@
package com.aurora.store.view.ui.preferences
import android.os.Bundle
+import android.view.View
+import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.SwitchPreferenceCompat
import com.aurora.store.R
+import com.aurora.store.util.Preferences
class DownloadPreference : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_download, rootKey)
}
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val downloadExternalPreference: SwitchPreferenceCompat? =
+ findPreference(Preferences.PREFERENCE_DOWNLOAD_EXTERNAL)
+
+ val autoDeletePreference: SwitchPreferenceCompat? =
+ findPreference(Preferences.PREFERENCE_AUTO_DELETE)
+
+
+ downloadExternalPreference?.let { switchPreferenceCompat ->
+ switchPreferenceCompat.onPreferenceChangeListener =
+ Preference.OnPreferenceChangeListener { _, newValue ->
+ val checked = newValue.toString().toBoolean()
+ autoDeletePreference?.let {
+ if (checked) {
+ it.isEnabled = true
+ } else {
+ it.isEnabled = false
+ it.isChecked = true
+ }
+ }
+
+ true
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/preferences/InstallationPreference.kt b/app/src/main/java/com/aurora/store/view/ui/preferences/InstallationPreference.kt
index 5244ebdc7..03798f25a 100644
--- a/app/src/main/java/com/aurora/store/view/ui/preferences/InstallationPreference.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/preferences/InstallationPreference.kt
@@ -23,11 +23,17 @@ import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import com.aurora.extensions.runOnUiThread
+import com.aurora.extensions.showDialog
+import com.aurora.extensions.toast
import com.aurora.store.R
+import com.aurora.store.data.installer.ServiceInstaller
import com.aurora.store.util.CommonUtil
+import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
-import com.aurora.store.util.extensions.runOnUiThread
-import com.aurora.store.util.extensions.toast
+import com.aurora.store.util.save
+import com.aurora.store.view.custom.preference.AuroraListPreference
+import com.topjohnwu.superuser.Shell
class InstallationPreference : PreferenceFragmentCompat() {
@@ -40,6 +46,7 @@ class InstallationPreference : PreferenceFragmentCompat() {
val abandonPreference: Preference? =
findPreference(Preferences.INSTALLATION_ABANDON_SESSION)
+
abandonPreference?.let {
it.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
@@ -50,5 +57,52 @@ class InstallationPreference : PreferenceFragmentCompat() {
false
}
}
+
+ val installerPreference: AuroraListPreference? =
+ findPreference(Preferences.PREFERENCE_INSTALLER_ID)
+
+ installerPreference?.let {
+ it.onPreferenceChangeListener =
+ Preference.OnPreferenceChangeListener { _, newValue ->
+ val selectedId = Integer.parseInt(newValue as String)
+ if (selectedId == 2) {
+ if (checkRootAvailability()) {
+ save(Preferences.PREFERENCE_INSTALLER_ID, selectedId)
+ true
+ } else {
+ showDialog(
+ R.string.action_installations,
+ R.string.installer_root_unavailable
+ )
+ false
+ }
+ } else if (selectedId == 3) {
+ if (checkServicesAvailability()) {
+ save(Preferences.PREFERENCE_INSTALLER_ID, selectedId)
+ true
+ } else {
+ showDialog(
+ R.string.action_installations,
+ R.string.installer_service_unavailable
+ )
+ false
+ }
+ } else {
+ save(Preferences.PREFERENCE_INSTALLER_ID, selectedId)
+ true
+ }
+ }
+ }
+ }
+
+ private fun checkRootAvailability(): Boolean {
+ return Shell.getShell().isRoot
+ }
+
+ private fun checkServicesAvailability(): Boolean {
+ return PackageUtil.isInstalled(
+ requireContext(),
+ ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
+ )
}
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/preferences/SettingsActivity.kt b/app/src/main/java/com/aurora/store/view/ui/preferences/SettingsActivity.kt
index 94a5d39ea..bbceea140 100644
--- a/app/src/main/java/com/aurora/store/view/ui/preferences/SettingsActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/preferences/SettingsActivity.kt
@@ -24,10 +24,10 @@ import android.os.Bundle
import android.view.MenuItem
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import com.aurora.extensions.getStyledAttributeColor
+import com.aurora.extensions.restartApp
import com.aurora.store.R
import com.aurora.store.databinding.ActivitySettingBinding
-import com.aurora.store.util.ViewUtil.getStyledAttribute
-import com.aurora.store.util.extensions.restartApp
import com.aurora.store.view.ui.commons.BaseActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -144,7 +144,7 @@ class SettingsActivity : BaseActivity(),
restartApp()
}
.setNegativeButton(getString(R.string.action_later)) { dialog, _ -> dialog.dismiss() }
- val backGroundColor = getStyledAttribute(this, android.R.attr.colorBackground)
+ val backGroundColor = getStyledAttributeColor(android.R.attr.colorBackground)
builder.background = ColorDrawable(backGroundColor)
builder.create()
builder.show()
diff --git a/app/src/main/java/com/aurora/store/view/ui/preferences/UIPreference.kt b/app/src/main/java/com/aurora/store/view/ui/preferences/UIPreference.kt
index cd353a2a1..f1942e11c 100644
--- a/app/src/main/java/com/aurora/store/view/ui/preferences/UIPreference.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/preferences/UIPreference.kt
@@ -25,7 +25,7 @@ import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import com.aurora.store.R
import com.aurora.store.util.Preferences
-import com.aurora.store.util.extensions.applyTheme
+import com.aurora.extensions.applyTheme
import com.aurora.store.util.save
diff --git a/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesActivity.kt b/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesActivity.kt
index 521431f96..224854081 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesActivity.kt
@@ -24,9 +24,9 @@ import androidx.lifecycle.ViewModelProvider
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.view.ui.commons.BaseActivity
diff --git a/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsActivity.kt b/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsActivity.kt
index 3b8f89d52..cfb30b5d0 100644
--- a/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/search/SearchResultsActivity.kt
@@ -30,10 +30,10 @@ import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.store.databinding.ActivitySearchResultBinding
-import com.aurora.store.util.extensions.close
-import com.aurora.store.util.extensions.open
+import com.aurora.extensions.close
+import com.aurora.extensions.open
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
-import com.aurora.store.view.epoxy.views.AppListViewModel_
+import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.view.ui.downloads.DownloadActivity
diff --git a/app/src/main/java/com/aurora/store/view/ui/search/SearchSuggestionActivity.kt b/app/src/main/java/com/aurora/store/view/ui/search/SearchSuggestionActivity.kt
index b91d96b0e..a440c4ffa 100644
--- a/app/src/main/java/com/aurora/store/view/ui/search/SearchSuggestionActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/search/SearchSuggestionActivity.kt
@@ -28,12 +28,12 @@ import android.view.inputmethod.EditorInfo
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
+import com.aurora.extensions.close
+import com.aurora.extensions.getEmptyActivityBundle
+import com.aurora.extensions.open
+import com.aurora.extensions.showKeyboard
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.store.databinding.ActivitySearchSuggestionBinding
-import com.aurora.store.util.ViewUtil
-import com.aurora.store.util.extensions.close
-import com.aurora.store.util.extensions.open
-import com.aurora.store.util.extensions.showKeyboard
import com.aurora.store.view.epoxy.views.SearchSuggestionViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.view.ui.downloads.DownloadActivity
@@ -151,9 +151,6 @@ class SearchSuggestionActivity : BaseActivity() {
private fun search(query: String) {
val intent = Intent(this, SearchResultsActivity::class.java)
intent.putExtra(Constants.STRING_EXTRA, query)
- startActivity(
- intent,
- ViewUtil.getEmptyActivityBundle(this)
- )
+ startActivity(intent, getEmptyActivityBundle())
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt
index f3d93f60d..e4f5fa1a6 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt
@@ -34,9 +34,8 @@ import com.aurora.store.data.providers.BlacklistProvider
import com.aurora.store.databinding.SheetAppMenuBinding
import com.aurora.store.util.ApkCopier
import com.aurora.store.util.PackageUtil
-import com.aurora.store.util.extensions.isQAndAbove
-import com.aurora.store.util.extensions.openInfo
-import com.aurora.store.util.extensions.toast
+import com.aurora.extensions.openInfo
+import com.aurora.extensions.toast
import com.livinglifetechway.quickpermissions_kotlin.runWithPermissions
import nl.komponents.kovenant.task
import org.greenrobot.eventbus.EventBus
@@ -87,11 +86,6 @@ class AppMenuSheet : BaseBottomSheet() {
menu.findItem(R.id.action_uninstall).isVisible = installed
menu.findItem(R.id.action_local).isVisible = installed
- if (isQAndAbove()) {
- //TODO: Add Scoped Storage Access
- menu.findItem(R.id.action_local).isVisible = false
- }
-
setNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.action_blacklist -> {
@@ -113,8 +107,7 @@ class AppMenuSheet : BaseBottomSheet() {
}
R.id.action_uninstall -> {
- AppInstaller
- .with(requireContext())
+ AppInstaller(requireContext())
.getPreferredInstaller().uninstall(app.packageName)
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/AppPeekDialogSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/AppPeekDialogSheet.kt
index 6c469e1cd..8617650d2 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sheets/AppPeekDialogSheet.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/AppPeekDialogSheet.kt
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.databinding.SheetAppPeekBinding
import com.aurora.store.util.CommonUtil
-import com.aurora.store.util.extensions.load
+import com.aurora.extensions.load
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
class AppPeekDialogSheet : BaseBottomSheet() {
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/BaseBottomSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/BaseBottomSheet.kt
index a65fbb73c..c4f8c9539 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sheets/BaseBottomSheet.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/BaseBottomSheet.kt
@@ -38,7 +38,9 @@ abstract class BaseBottomSheet : BottomSheetDialogFragment() {
lateinit var VM: SheetBaseBinding
- var gson: Gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
+ var gson: Gson = GsonBuilder()
+ .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
+ .create()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val bottomSheetDialog = BottomSheetDialog(
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceHuaweiSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceHuaweiSheet.kt
new file mode 100644
index 000000000..3237de820
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceHuaweiSheet.kt
@@ -0,0 +1,102 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.sheets
+
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.aurora.extensions.load
+import com.aurora.extensions.toast
+import com.aurora.gplayapi.DeviceManager
+import com.aurora.store.R
+import com.aurora.store.data.providers.SpoofProvider
+import com.aurora.store.databinding.SheetDeviceHuaweiBinding
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+import java.util.*
+
+class DeviceHuaweiSheet : BaseBottomSheet() {
+
+ private lateinit var B: SheetDeviceHuaweiBinding
+
+ companion object {
+
+ const val TAG = "DeviceHuaweiSheet"
+
+ @JvmStatic
+ fun newInstance(): DeviceHuaweiSheet {
+ return DeviceHuaweiSheet().apply {
+ arguments = Bundle().apply {
+
+ }
+ }
+ }
+ }
+
+ override fun onCreateContentView(
+ inflater: LayoutInflater,
+ container: ViewGroup,
+ savedInstanceState: Bundle?
+ ): View {
+ B = SheetDeviceHuaweiBinding.inflate(inflater, container, false)
+
+ inflateData()
+ attachAction()
+
+ return B.root
+ }
+
+ override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ }
+
+ private fun inflateData() {
+ B.imgIcon.load(R.drawable.ic_huawei_logo) {
+ transform(CircleCrop())
+ }
+ }
+
+ private fun attachAction() {
+ B.btnPrimary.setOnClickListener {
+ applySpoof()
+ }
+
+ B.btnSecondary.setOnClickListener {
+ dismissAllowingStateLoss()
+ }
+ }
+
+ private fun applySpoof() {
+ val properties: Properties? = when (Build.VERSION.SDK_INT) {
+ 30, 29 -> DeviceManager.loadProperties("op_8_pro.properties")
+ 28 -> DeviceManager.loadProperties("nk_9.properties")
+ 27 -> DeviceManager.loadProperties("mi_8_se.properties")
+ 26 -> DeviceManager.loadProperties("op_3.properties")
+ else -> DeviceManager.loadProperties("op_x.properties")
+ }
+
+ properties?.let {
+ SpoofProvider(requireContext()).setSpoofDeviceProperties(it)
+ toast(R.string.toast_spoof_applied)
+ dismissAllowingStateLoss()
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceMiuiSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceMiuiSheet.kt
new file mode 100644
index 000000000..220b2878b
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DeviceMiuiSheet.kt
@@ -0,0 +1,92 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.sheets
+
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.aurora.extensions.load
+import com.aurora.extensions.toast
+import com.aurora.store.R
+import com.aurora.store.databinding.SheetDeviceMiuiBinding
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+
+class DeviceMiuiSheet : BaseBottomSheet() {
+
+ private lateinit var B: SheetDeviceMiuiBinding
+
+ companion object {
+
+ const val TAG = "DeviceMiuiSheet"
+
+ @JvmStatic
+ fun newInstance(): DeviceMiuiSheet {
+ return DeviceMiuiSheet().apply {
+ arguments = Bundle().apply {
+
+ }
+ }
+ }
+ }
+
+ override fun onCreateContentView(
+ inflater: LayoutInflater,
+ container: ViewGroup,
+ savedInstanceState: Bundle?
+ ): View {
+ B = SheetDeviceMiuiBinding.inflate(inflater, container, false)
+
+ inflateData()
+ attachAction()
+
+ return B.root
+ }
+
+ override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ }
+
+ private fun inflateData() {
+ B.imgIcon.load(R.drawable.ic_xiaomi_logo) {
+ transform(CircleCrop())
+ }
+ }
+
+ private fun attachAction() {
+ B.btnPrimary.setOnClickListener {
+ openDeveloperSettings()
+ }
+
+ B.btnSecondary.setOnClickListener {
+ dismissAllowingStateLoss()
+ }
+ }
+
+ private fun openDeveloperSettings() {
+ try {
+ startActivity(Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS))
+ } catch (e: Exception) {
+ toast(R.string.toast_developer_setting_failed)
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt
index 25dee9d3c..7492ea119 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt
@@ -26,8 +26,8 @@ import android.view.ViewGroup
import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager
import com.aurora.store.databinding.SheetDownloadMenuBinding
-import com.aurora.store.util.extensions.copyToClipBoard
-import com.aurora.store.util.extensions.toast
+import com.aurora.extensions.copyToClipBoard
+import com.aurora.extensions.toast
import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.Status
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/InstallErrorDialogSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/InstallErrorDialogSheet.kt
new file mode 100644
index 000000000..b85443f85
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/InstallErrorDialogSheet.kt
@@ -0,0 +1,124 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.view.ui.sheets
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.aurora.Constants
+import com.aurora.extensions.copyToClipBoard
+import com.aurora.extensions.load
+import com.aurora.extensions.px
+import com.aurora.extensions.toast
+import com.aurora.gplayapi.data.models.App
+import com.aurora.store.R
+import com.aurora.store.databinding.SheetInstallErrorBinding
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+
+class InstallErrorDialogSheet : BaseBottomSheet() {
+
+ private lateinit var B: SheetInstallErrorBinding
+
+ private lateinit var app: App
+ private lateinit var title: String
+ private lateinit var error: String
+ private lateinit var extra: String
+
+ private var rawApp = String()
+
+ companion object {
+ private const val DIALOG_TITLE = "DIALOG_TITLE"
+ private const val DIALOG_ERROR = "DIALOG_ERROR"
+ private const val DIALOG_EXTRA = "DIALOG_EXTRA"
+
+ @JvmStatic
+ fun newInstance(
+ app: App,
+ title: String?,
+ error: String?,
+ extra: String?
+ ): InstallErrorDialogSheet {
+ return InstallErrorDialogSheet().apply {
+ arguments = Bundle().apply {
+ putString(Constants.STRING_EXTRA, gson.toJson(app))
+ putString(DIALOG_TITLE, title)
+ putString(DIALOG_ERROR, error)
+ putString(DIALOG_EXTRA, extra)
+ }
+ }
+ }
+ }
+
+ override fun onCreateContentView(
+ inflater: LayoutInflater,
+ container: ViewGroup,
+ savedInstanceState: Bundle?
+ ): View {
+ B = SheetInstallErrorBinding.inflate(inflater, container, false)
+
+ val bundle = arguments
+ bundle?.let {
+ rawApp = bundle.getString(Constants.STRING_EXTRA, "{}")
+ app = gson.fromJson(rawApp, App::class.java)
+ title = bundle.getString(DIALOG_TITLE, "")
+ error = bundle.getString(DIALOG_ERROR, "")
+ extra = bundle.getString(DIALOG_EXTRA, "")
+
+ if (app.packageName.isNotEmpty()) {
+ inflateData()
+ } else {
+ dismissAllowingStateLoss()
+ }
+ }
+
+ attachAction()
+
+ return B.root
+ }
+
+ override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ }
+
+ private fun inflateData() {
+ B.imgIcon.load(app.iconArtwork.url) {
+ transform(CircleCrop())
+ }
+
+ B.txtLine1.text = app.displayName
+ B.txtLine2.text = error
+ B.txtLine3.text = extra
+ }
+
+ private fun attachAction() {
+ B.btnPrimary.setOnClickListener {
+ dismissAllowingStateLoss()
+ }
+
+ B.btnSecondary.setOnClickListener {
+ if (::extra.isInitialized) {
+ requireContext().copyToClipBoard(extra)
+ requireContext().toast(R.string.toast_clipboard_copied)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/NetworkDialogSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/NetworkDialogSheet.kt
index ecee09a52..e9ebaeea5 100644
--- a/app/src/main/java/com/aurora/store/view/ui/sheets/NetworkDialogSheet.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/NetworkDialogSheet.kt
@@ -23,22 +23,20 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.aurora.Constants
import com.aurora.store.databinding.SheetNetworkBinding
class NetworkDialogSheet : BaseBottomSheet() {
lateinit var B: SheetNetworkBinding
- private var type = 0
-
companion object {
+
+ const val TAG = "NetworkDialogSheet"
+
@JvmStatic
- fun newInstance(type: Int): NetworkDialogSheet {
+ fun newInstance(): NetworkDialogSheet {
return NetworkDialogSheet().apply {
- arguments = Bundle().apply {
- putInt(Constants.INT_EXTRA, type)
- }
+
}
}
}
@@ -47,18 +45,14 @@ class NetworkDialogSheet : BaseBottomSheet() {
inflater: LayoutInflater,
container: ViewGroup,
savedInstanceState: Bundle?
- ): View? {
+ ): View {
B = SheetNetworkBinding.inflate(inflater, container, false)
-
- val bundle = arguments
- if (bundle != null) {
- type = bundle.getInt(Constants.INT_EXTRA, 0)
- }
-
return B.root
}
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
-
+ B.btnAction.setOnClickListener {
+ dismissAllowingStateLoss()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/PermissionBottomSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/PermissionBottomSheet.kt
new file mode 100644
index 000000000..0a41ea15b
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/view/ui/sheets/PermissionBottomSheet.kt
@@ -0,0 +1,165 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2019, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ *
+ */
+package com.aurora.store.view.ui.sheets
+
+import android.content.pm.PackageManager
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import com.aurora.Constants
+import com.aurora.extensions.hide
+import com.aurora.extensions.show
+import com.aurora.gplayapi.data.models.App
+import com.aurora.store.R
+import com.aurora.store.databinding.SheetPermissionsBinding
+import com.aurora.store.view.custom.layouts.PermissionGroup
+import java.util.*
+
+class PermissionBottomSheet : BaseBottomSheet() {
+
+ private lateinit var B: SheetPermissionsBinding
+ private lateinit var app: App
+ private lateinit var packageManager: PackageManager
+
+ companion object {
+
+ const val TAG = "PermissionBottomSheet"
+
+ @JvmStatic
+ fun newInstance(
+ app: App
+ ): PermissionBottomSheet {
+ return PermissionBottomSheet().apply {
+ arguments = Bundle().apply {
+ putString(Constants.STRING_APP, gson.toJson(app))
+ }
+ }
+ }
+ }
+
+ override fun onCreateContentView(
+ inflater: LayoutInflater,
+ container: ViewGroup,
+ savedInstanceState: Bundle?
+ ): View {
+ B = SheetPermissionsBinding.inflate(inflater)
+ return B.root
+ }
+
+ override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val bundle = arguments
+ bundle?.let {
+ val rawApp = bundle.getString(Constants.STRING_APP, "{}")
+ app = gson.fromJson(rawApp, App::class.java)
+ if (app.packageName.isNotEmpty()) {
+ inflateData()
+ } else {
+ dismissAllowingStateLoss()
+ }
+ }
+ }
+
+ private fun inflateData() {
+ packageManager = requireContext().packageManager
+
+ val permissionGroupWidgets: MutableMap =
+ HashMap()
+ for (permissionName in app.permissions) {
+
+ val permissionInfo = getPermissionInfo(permissionName) ?: continue
+ val permissionGroupInfo = getPermissionGroupInfo(permissionInfo)
+ var permissionGroup: PermissionGroup?
+
+ if (permissionGroupWidgets.containsKey(permissionGroupInfo.name)) {
+ permissionGroup = permissionGroupWidgets[permissionGroupInfo.name]
+ } else {
+ permissionGroup = PermissionGroup(context)
+ permissionGroup.setPermissionGroupInfo(permissionGroupInfo)
+ permissionGroup.layoutParams = LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ )
+ permissionGroupWidgets[permissionGroupInfo.name] = permissionGroup
+ }
+
+ permissionGroup?.addPermission(permissionInfo)
+ }
+
+ B.permissionsContainer.removeAllViews()
+
+ val permissionGroupLabels: List = ArrayList(permissionGroupWidgets.keys)
+ permissionGroupLabels.sortedBy { it }.forEach {
+ B.permissionsContainer.addView(permissionGroupWidgets[it])
+ }
+
+ if (permissionGroupLabels.isEmpty())
+ B.permissionsNone.show()
+ else
+ B.permissionsNone.hide()
+ }
+
+ private fun getPermissionInfo(permissionName: String): PermissionInfo? {
+ return try {
+ packageManager.getPermissionInfo(permissionName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
+
+ private fun getPermissionGroupInfo(permissionInfo: PermissionInfo): PermissionGroupInfo {
+ val permissionGroupInfo: PermissionGroupInfo = if (null == permissionInfo.group) {
+ getFakePermissionGroupInfo(permissionInfo.packageName)
+ } else {
+ try {
+ packageManager.getPermissionGroupInfo(permissionInfo.group!!, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ getFakePermissionGroupInfo(permissionInfo.packageName)
+ }
+ }
+ if (permissionGroupInfo.icon == 0) {
+ permissionGroupInfo.icon = R.drawable.ic_permission_android
+ }
+ return permissionGroupInfo
+ }
+
+ private fun getFakePermissionGroupInfo(packageName: String): PermissionGroupInfo {
+ val permissionGroupInfo = PermissionGroupInfo()
+ when (packageName) {
+ "android" -> {
+ permissionGroupInfo.icon = R.drawable.ic_permission_android
+ permissionGroupInfo.name = "android"
+ }
+ "com.google.android.gsf", "com.android.vending" -> {
+ permissionGroupInfo.icon = R.drawable.ic_permission_google
+ permissionGroupInfo.name = "google"
+ }
+ else -> {
+ permissionGroupInfo.icon = R.drawable.ic_permission_unknown
+ permissionGroupInfo.name = "unknown"
+ }
+ }
+ return permissionGroupInfo
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/view/ui/splash/SplashActivity.kt b/app/src/main/java/com/aurora/store/view/ui/splash/SplashActivity.kt
index 7eccb4cbf..4d3110c8a 100644
--- a/app/src/main/java/com/aurora/store/view/ui/splash/SplashActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/splash/SplashActivity.kt
@@ -23,17 +23,15 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.ViewModelProvider
+import com.aurora.extensions.*
import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.data.AuthState
import com.aurora.store.data.event.BusEvent
import com.aurora.store.databinding.ActivitySplashBinding
-import com.aurora.store.util.extensions.hide
-import com.aurora.store.util.extensions.load
-import com.aurora.store.util.extensions.open
-import com.aurora.store.util.extensions.show
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.view.ui.commons.BlacklistActivity
+import com.aurora.store.view.ui.sheets.DeviceHuaweiSheet
import com.aurora.store.view.ui.spoof.SpoofActivity
import com.aurora.store.viewmodel.auth.AuthViewModel
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@@ -91,6 +89,13 @@ class SplashActivity : BaseActivity() {
AuthState.Unavailable -> {
updateStatus("You need to login first")
updateActionLayout(true)
+
+ if (isHuawei()) {
+ runOnUiThread {
+ DeviceHuaweiSheet.newInstance()
+ .show(supportFragmentManager, DeviceHuaweiSheet.TAG)
+ }
+ }
}
AuthState.SignedIn -> {
diff --git a/app/src/main/java/com/aurora/store/view/ui/spoof/DeviceSpoofFragment.kt b/app/src/main/java/com/aurora/store/view/ui/spoof/DeviceSpoofFragment.kt
index cbe71b64a..b7278ee2f 100644
--- a/app/src/main/java/com/aurora/store/view/ui/spoof/DeviceSpoofFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/spoof/DeviceSpoofFragment.kt
@@ -23,13 +23,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import com.aurora.extensions.toast
import com.aurora.store.R
import com.aurora.store.data.providers.NativeDeviceInfoProvider
import com.aurora.store.data.providers.SpoofDeviceProvider
import com.aurora.store.data.providers.SpoofProvider
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
import com.aurora.store.util.Log
-import com.aurora.store.util.extensions.toast
import com.aurora.store.view.epoxy.views.preference.DeviceViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
import nl.komponents.kovenant.task
@@ -68,7 +68,7 @@ class DeviceSpoofFragment : BaseFragment() {
)
properties = NativeDeviceInfoProvider(requireContext()).getNativeDeviceProperties()
- spoofProvider = SpoofProvider.with(requireContext())
+ spoofProvider = SpoofProvider(requireContext())
return B.root
}
@@ -91,21 +91,27 @@ class DeviceSpoofFragment : BaseFragment() {
private fun updateController(locales: List) {
B.recycler.withModels {
setFilterDuplicates(true)
- locales.forEach {
- add(
- DeviceViewModel_()
- .id(it.hashCode())
- .markChecked(properties == it)
- .checked { _, checked ->
- if (checked) {
- properties = it
- saveSelection(it)
- requestModelBuild()
+ locales
+ .sortedBy { it.getProperty("UserReadableName") }
+ .forEach {
+ add(
+ DeviceViewModel_()
+ .id(it.hashCode())
+ .markChecked(
+ properties.getProperty("UserReadableName") == it.getProperty(
+ "UserReadableName"
+ )
+ )
+ .checked { _, checked ->
+ if (checked) {
+ properties = it
+ saveSelection(it)
+ requestModelBuild()
+ }
}
- }
- .properties(it)
- )
- }
+ .properties(it)
+ )
+ }
}
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/spoof/LocaleSpoofFragment.kt b/app/src/main/java/com/aurora/store/view/ui/spoof/LocaleSpoofFragment.kt
index 4e211ba59..0c0c45706 100644
--- a/app/src/main/java/com/aurora/store/view/ui/spoof/LocaleSpoofFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/spoof/LocaleSpoofFragment.kt
@@ -27,7 +27,7 @@ import com.aurora.store.R
import com.aurora.store.data.providers.SpoofProvider
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
import com.aurora.store.util.Log
-import com.aurora.store.util.extensions.toast
+import com.aurora.extensions.toast
import com.aurora.store.view.epoxy.views.preference.LocaleViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
import nl.komponents.kovenant.task
@@ -65,7 +65,7 @@ class LocaleSpoofFragment : BaseFragment() {
)
)
- spoofProvider = SpoofProvider.with(requireContext())
+ spoofProvider = SpoofProvider(requireContext())
return B.root
}
diff --git a/app/src/main/java/com/aurora/store/view/ui/spoof/SpoofActivity.kt b/app/src/main/java/com/aurora/store/view/ui/spoof/SpoofActivity.kt
index 914a12e37..8deef26e6 100644
--- a/app/src/main/java/com/aurora/store/view/ui/spoof/SpoofActivity.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/spoof/SpoofActivity.kt
@@ -26,7 +26,7 @@ import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.aurora.store.R
import com.aurora.store.databinding.ActivityGenericPagerBinding
-import com.aurora.store.util.extensions.close
+import com.aurora.extensions.close
import com.aurora.store.view.ui.commons.BaseActivity
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
diff --git a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt
index f0941747b..e973f4043 100644
--- a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt
+++ b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt
@@ -35,9 +35,9 @@ import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.databinding.FragmentUpdatesBinding
import com.aurora.store.util.Log
-import com.aurora.store.util.extensions.flushAndAdd
-import com.aurora.store.util.extensions.toast
-import com.aurora.store.view.epoxy.views.AppUpdateViewModel_
+import com.aurora.extensions.flushAndAdd
+import com.aurora.extensions.toast
+import com.aurora.store.view.epoxy.views.app.AppUpdateViewModel_
import com.aurora.store.view.epoxy.views.UpdateHeaderViewModel_
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
@@ -93,16 +93,22 @@ class UpdatesFragment : BaseFragment() {
}
}
- fetch.addListener(fetchListener)
return B.root
}
- override fun onDestroyView() {
+ override fun onResume() {
+ super.onResume()
+ if (::fetch.isInitialized && ::fetchListener.isInitialized) {
+ fetch.addListener(fetchListener)
+ }
+ }
+
+ override fun onPause() {
if (::fetch.isInitialized && ::fetchListener.isInitialized) {
fetch.removeListener(fetchListener)
}
- super.onDestroyView()
+ super.onPause()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -249,7 +255,7 @@ class UpdatesFragment : BaseFragment() {
@Synchronized
private fun install(packageName: String, files: List) {
task {
- AppInstaller.with(requireContext())
+ AppInstaller(requireContext())
.getPreferredInstaller()
.install(
packageName,
diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt
index 0e43c31ed..75c7e89e4 100644
--- a/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt
+++ b/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt
@@ -23,7 +23,7 @@ import android.app.Application
import androidx.lifecycle.viewModelScope
import com.aurora.store.data.RequestState
import com.aurora.store.data.event.BusEvent
-import com.aurora.store.util.extensions.flushAndAdd
+import com.aurora.extensions.flushAndAdd
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt
new file mode 100644
index 000000000..b10373f80
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt
@@ -0,0 +1,71 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.viewmodel.all
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.aurora.gplayapi.data.models.App
+import com.aurora.gplayapi.helpers.PurchaseHelper
+import com.aurora.store.data.RequestState
+import com.aurora.store.data.network.HttpClient
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.viewmodel.BaseAndroidViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.supervisorScope
+import org.greenrobot.eventbus.EventBus
+
+class PurchasedViewModel(application: Application) : BaseAndroidViewModel(application) {
+
+ private val authData = AuthProvider
+ .with(application)
+ .getAuthData()
+
+ private val purchaseHelper = PurchaseHelper(authData)
+ .using(HttpClient.getPreferredClient())
+
+ val liveData: MutableLiveData> = MutableLiveData()
+
+ var appList: MutableList = mutableListOf()
+
+ init {
+ requestState = RequestState.Init
+ observe()
+ }
+
+ override fun observe() {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ appList.addAll(purchaseHelper.getPurchaseHistory(appList.size))
+ liveData.postValue(appList)
+ requestState = RequestState.Complete
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ }
+ }
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt
index eea21b568..633768ed2 100644
--- a/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt
+++ b/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt
@@ -24,7 +24,7 @@ import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.App
import com.aurora.store.data.RequestState
import com.aurora.store.data.event.BusEvent
-import com.aurora.store.util.extensions.flushAndAdd
+import com.aurora.extensions.flushAndAdd
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
diff --git a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt
index 7a12943c5..19c5d48a1 100644
--- a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt
+++ b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt
@@ -45,7 +45,7 @@ import java.net.UnknownHostException
class AuthViewModel(application: Application) : BaseAndroidViewModel(application) {
- private val spoofProvider = SpoofProvider.with(getApplication())
+ private val spoofProvider = SpoofProvider(getApplication())
val liveData: MutableLiveData = MutableLiveData()
diff --git a/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt
new file mode 100644
index 000000000..e3a022fe6
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt
@@ -0,0 +1,95 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.viewmodel.browse
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.gplayapi.helpers.ExpandedBrowseHelper
+import com.aurora.store.data.RequestState
+import com.aurora.store.data.network.HttpClient
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.util.Log
+import com.aurora.store.viewmodel.BaseAndroidViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.supervisorScope
+
+class ExpandedStreamBrowseViewModel(application: Application) : BaseAndroidViewModel(application) {
+
+ private val authData: AuthData = AuthProvider.with(application).getAuthData()
+ private val streamHelper: ExpandedBrowseHelper = ExpandedBrowseHelper(authData)
+ .using(HttpClient.getPreferredClient())
+
+ val liveData: MutableLiveData = MutableLiveData()
+ var streamCluster: StreamCluster = StreamCluster()
+
+ override fun observe() {
+ requestState = RequestState.Init
+ }
+
+ fun getInitialCluster(expandedStreamUrl: String) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ val browseResponse = streamHelper.getBrowseStreamResponse(expandedStreamUrl)
+ if (browseResponse.hasBrowseTab()) {
+ streamCluster =
+ streamHelper.getExpandedBrowseClusters(browseResponse.browseTab.listUrl)
+ liveData.postValue(streamCluster)
+ } else {
+ requestState = RequestState.Complete
+ }
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ }
+ }
+ }
+ }
+
+ fun next() {
+ Log.e("NEXT CALED")
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ val newCluster = streamHelper.getExpandedBrowseClusters(
+ streamCluster.clusterNextPageUrl
+ )
+
+ streamCluster.apply {
+ clusterAppList.addAll(newCluster.clusterAppList)
+ clusterNextPageUrl = newCluster.clusterNextPageUrl
+ }
+
+ liveData.postValue(streamCluster)
+
+ if (!streamCluster.hasNext()) {
+ Log.i("End of Bundle")
+ requestState = RequestState.Complete
+ }
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/DetailsClusterViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsClusterViewModel.kt
new file mode 100644
index 000000000..3684208ce
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsClusterViewModel.kt
@@ -0,0 +1,99 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.viewmodel.details
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.data.models.StreamBundle
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.gplayapi.helpers.AppDetailsHelper
+import com.aurora.gplayapi.helpers.StreamHelper
+import com.aurora.store.data.RequestState
+import com.aurora.store.data.ViewState
+import com.aurora.store.data.network.HttpClient
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.util.Log
+import com.aurora.store.viewmodel.BaseAndroidViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.supervisorScope
+
+class DetailsClusterViewModel(application: Application) : BaseAndroidViewModel(application) {
+
+ private var authData: AuthData = AuthProvider.with(application).getAuthData()
+ private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient())
+ private var streamHelper = StreamHelper(authData)
+
+ val liveData: MutableLiveData = MutableLiveData()
+ var streamBundle: StreamBundle = StreamBundle()
+
+ lateinit var type: StreamHelper.Type
+ lateinit var category: StreamHelper.Category
+
+ override fun observe() {
+
+ }
+
+ fun getStreamBundle(
+ streamUrl: String
+ ) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ streamBundle = appDetailsHelper.getDetailsStream(streamUrl)
+ liveData.postValue(ViewState.Success(streamBundle))
+ requestState = RequestState.Complete
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ liveData.postValue(ViewState.Error(e.message))
+ }
+ }
+ }
+ }
+
+ fun observeCluster(streamCluster: StreamCluster) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ if (streamCluster.hasNext()) {
+ val newCluster =
+ streamHelper.getNextStreamCluster(streamCluster.clusterNextPageUrl)
+ updateCluster(newCluster)
+ liveData.postValue(ViewState.Success(streamBundle))
+ } else {
+ Log.i("End of cluster")
+ streamCluster.clusterNextPageUrl = String()
+ }
+ } catch (e: Exception) {
+ liveData.postValue(ViewState.Error(e.message))
+ }
+ }
+ }
+ }
+
+ private fun updateCluster(newCluster: StreamCluster) {
+ streamBundle.streamClusters[newCluster.id]?.apply {
+ clusterAppList.addAll(newCluster.clusterAppList)
+ clusterNextPageUrl = newCluster.clusterNextPageUrl
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt
new file mode 100644
index 000000000..98912ebf7
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt
@@ -0,0 +1,102 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.viewmodel.details
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.data.models.StreamBundle
+import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.gplayapi.data.models.details.DevStream
+import com.aurora.gplayapi.helpers.AppDetailsHelper
+import com.aurora.gplayapi.helpers.StreamHelper
+import com.aurora.store.data.RequestState
+import com.aurora.store.data.ViewState
+import com.aurora.store.data.network.HttpClient
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.util.Log
+import com.aurora.store.viewmodel.BaseAndroidViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.supervisorScope
+
+class DevProfileViewModel(application: Application) : BaseAndroidViewModel(application) {
+
+ private var authData: AuthData = AuthProvider.with(application).getAuthData()
+ private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient())
+ private var streamHelper = StreamHelper(authData)
+
+ val liveData: MutableLiveData = MutableLiveData()
+ var devStream:DevStream = DevStream()
+ var streamBundle: StreamBundle = StreamBundle()
+
+ lateinit var type: StreamHelper.Type
+ lateinit var category: StreamHelper.Category
+
+ override fun observe() {
+
+ }
+
+ fun getStreamBundle(
+ devId: String
+ ) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ devStream = appDetailsHelper.getDeveloperStream(devId)
+ streamBundle = devStream.streamBundle
+ liveData.postValue(ViewState.Success(devStream))
+ requestState = RequestState.Complete
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ liveData.postValue(ViewState.Error(e.message))
+ }
+ }
+ }
+ }
+
+ fun observeCluster(streamCluster: StreamCluster) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+ if (streamCluster.hasNext()) {
+ val newCluster = streamHelper.getNextStreamCluster(streamCluster.clusterNextPageUrl)
+ updateCluster(newCluster)
+ devStream.streamBundle = streamBundle
+ liveData.postValue(ViewState.Success(devStream))
+ } else {
+ Log.i("End of cluster")
+ streamCluster.clusterNextPageUrl = String()
+ }
+ } catch (e: Exception) {
+ liveData.postValue(ViewState.Error(e.message))
+ }
+ }
+ }
+ }
+
+ private fun updateCluster(newCluster: StreamCluster) {
+ streamBundle.streamClusters[newCluster.id]?.apply {
+ clusterAppList.addAll(newCluster.clusterAppList)
+ clusterNextPageUrl = newCluster.clusterNextPageUrl
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt
new file mode 100644
index 000000000..6b8354fca
--- /dev/null
+++ b/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt
@@ -0,0 +1,79 @@
+/*
+ * Aurora Store
+ * Copyright (C) 2021, Rahul Kumar Patel
+ *
+ * Aurora Store is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Aurora Store is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Aurora Store. If not, see .
+ *
+ */
+
+package com.aurora.store.viewmodel.editorschoice
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.aurora.gplayapi.data.models.App
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.helpers.StreamHelper
+import com.aurora.store.data.RequestState
+import com.aurora.store.data.network.HttpClient
+import com.aurora.store.data.providers.AuthProvider
+import com.aurora.store.viewmodel.BaseAndroidViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.supervisorScope
+
+class EditorBrowseViewModel(application: Application) : BaseAndroidViewModel(application) {
+
+ private val authData: AuthData = AuthProvider.with(application).getAuthData()
+ private val streamHelper: StreamHelper = StreamHelper(authData)
+ .using(HttpClient.getPreferredClient())
+
+ val liveData: MutableLiveData> = MutableLiveData()
+ val appList: MutableList = mutableListOf()
+
+ override fun observe() {
+ requestState = RequestState.Init
+ }
+
+ fun getEditorStreamBundle(
+ browseUrl: String
+ ) {
+ viewModelScope.launch(Dispatchers.IO) {
+ supervisorScope {
+ try {
+
+ requestState = RequestState.Init
+
+ val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl)
+ val listResponse =
+ streamHelper.getNextStreamResponse(browseResponse.browseTab.listUrl)
+
+ listResponse.itemList.forEach {
+ it?.let {
+ it.subItemList.forEach {
+ appList.addAll(streamHelper.getAppsFromItem(it))
+ }
+ }
+ }
+
+ liveData.postValue(appList)
+ requestState = RequestState.Complete
+
+ } catch (e: Exception) {
+ requestState = RequestState.Pending
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt
index 17efca2b4..640d5e272 100644
--- a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt
+++ b/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.gplayapi.helpers.SearchHelper
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
-import com.aurora.store.util.extensions.flushAndAdd
+import com.aurora.extensions.flushAndAdd
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
diff --git a/app/src/main/res/drawable-v21/divider_line.xml b/app/src/main/res/drawable-v21/divider_line.xml
index 1211d8fba..fe70d55df 100644
--- a/app/src/main/res/drawable-v21/divider_line.xml
+++ b/app/src/main/res/drawable-v21/divider_line.xml
@@ -21,6 +21,6 @@
android:shape="rectangle">
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_section.xml b/app/src/main/res/drawable/bg_section.xml
new file mode 100644
index 000000000..f2a3598a7
--- /dev/null
+++ b/app/src/main/res/drawable/bg_section.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_huawei_logo.xml b/app/src/main/res/drawable/ic_huawei_logo.xml
new file mode 100644
index 000000000..a4f627970
--- /dev/null
+++ b/app/src/main/res/drawable/ic_huawei_logo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_install.xml b/app/src/main/res/drawable/ic_install.xml
new file mode 100644
index 000000000..ee4cc10f4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_install.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_permission_android.xml b/app/src/main/res/drawable/ic_permission_android.xml
new file mode 100644
index 000000000..710d8de53
--- /dev/null
+++ b/app/src/main/res/drawable/ic_permission_android.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_permission_google.xml b/app/src/main/res/drawable/ic_permission_google.xml
new file mode 100644
index 000000000..b51a7931b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_permission_google.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_permission_unknown.xml b/app/src/main/res/drawable/ic_permission_unknown.xml
new file mode 100644
index 000000000..f001d8abd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_permission_unknown.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_xiaomi_logo.xml b/app/src/main/res/drawable/ic_xiaomi_logo.xml
new file mode 100644
index 000000000..30f23a43e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_xiaomi_logo.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-land/activity_account.xml b/app/src/main/res/layout-land/activity_account.xml
index 46c7f0c15..438477d15 100644
--- a/app/src/main/res/layout-land/activity_account.xml
+++ b/app/src/main/res/layout-land/activity_account.xml
@@ -137,7 +137,7 @@
android:text="@string/account_login_using"
android:textAlignment="center" />
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:animateFirstView="true"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ tools:ignore="UselessParent">
-
-
-
@@ -51,7 +50,7 @@
android:layout_below="@id/txt_title"
android:layout_alignStart="@id/txt_title"
android:layout_alignEnd="@id/txt_title"
- android:autoLink="map"
+ android:autoLink="all"
android:linksClickable="true"
android:textAppearance="@style/TextAppearance.Aurora.Line2"
android:textColorLink="?android:attr/textColorSecondary"
diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml
index 118dd56ea..d13789e83 100644
--- a/app/src/main/res/layout/activity_account.xml
+++ b/app/src/main/res/layout/activity_account.xml
@@ -131,7 +131,7 @@
android:text="@string/account_login_using"
android:textAlignment="center" />
-
-
-
+
+
+
+
diff --git a/app/src/main/res/layout/activity_dev_profile.xml b/app/src/main/res/layout/activity_dev_profile.xml
new file mode 100644
index 000000000..c45c42d48
--- /dev/null
+++ b/app/src/main/res/layout/activity_dev_profile.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml
index 3752f37e6..3d97613c5 100644
--- a/app/src/main/res/layout/activity_splash.xml
+++ b/app/src/main/res/layout/activity_splash.xml
@@ -89,7 +89,7 @@
android:text="@string/account_login_using"
android:textAlignment="center" />
-
-
-
+ android:scrollbars="none">
-
+ android:divider="@drawable/divider_line"
+ android:orientation="horizontal"
+ android:showDividers="middle">
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+ android:layout_height="wrap_content"
+ android:animateFirstView="true"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ tools:ignore="UselessParent">
-
-
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/model_developer_carousel_group.xml b/app/src/main/res/layout/model_developer_carousel_group.xml
new file mode 100644
index 000000000..8804d25f5
--- /dev/null
+++ b/app/src/main/res/layout/model_developer_carousel_group.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/model_editorchoice_group.xml b/app/src/main/res/layout/model_editorchoice_group.xml
index 8f258ad49..050a013fa 100644
--- a/app/src/main/res/layout/model_editorchoice_group.xml
+++ b/app/src/main/res/layout/model_editorchoice_group.xml
@@ -17,18 +17,38 @@
~
-->
-
-
-
+ android:layout_gravity="start"
+ android:layout_marginStart="@dimen/margin_small"
+ android:layout_marginEnd="@dimen/margin_small"
+ android:orientation="horizontal">
-
\ No newline at end of file
+ android:layout_centerVertical="true"
+ android:inflatedId="@+id/header_view" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sheet_device_huawei.xml b/app/src/main/res/layout/sheet_device_huawei.xml
new file mode 100644
index 000000000..e2042a367
--- /dev/null
+++ b/app/src/main/res/layout/sheet_device_huawei.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sheet_device_miui.xml b/app/src/main/res/layout/sheet_device_miui.xml
new file mode 100644
index 000000000..7d4091617
--- /dev/null
+++ b/app/src/main/res/layout/sheet_device_miui.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sheet_install_error.xml b/app/src/main/res/layout/sheet_install_error.xml
new file mode 100644
index 000000000..539ef0655
--- /dev/null
+++ b/app/src/main/res/layout/sheet_install_error.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sheet_network.xml b/app/src/main/res/layout/sheet_network.xml
index 146ae90e0..9b308d5b4 100644
--- a/app/src/main/res/layout/sheet_network.xml
+++ b/app/src/main/res/layout/sheet_network.xml
@@ -24,20 +24,10 @@
android:orientation="vertical"
android:padding="@dimen/padding_small">
-
-
-
+ android:text="@string/action_try_later" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/sheet_permissions.xml b/app/src/main/res/layout/sheet_permissions.xml
new file mode 100644
index 000000000..bbc82a803
--- /dev/null
+++ b/app/src/main/res/layout/sheet_permissions.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_action_button.xml b/app/src/main/res/layout/view_action_button.xml
index b4369a3c8..440ad3085 100644
--- a/app/src/main/res/layout/view_action_button.xml
+++ b/app/src/main/res/layout/view_action_button.xml
@@ -21,37 +21,59 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="@dimen/height_button">
-
+ android:layout_height="match_parent"
+ android:animateFirstView="true"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ tools:ignore="UselessParent">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/view_action_header.xml b/app/src/main/res/layout/view_action_header.xml
index b5cabb350..021a150ad 100644
--- a/app/src/main/res/layout/view_action_header.xml
+++ b/app/src/main/res/layout/view_action_header.xml
@@ -35,7 +35,7 @@
+ android:paddingStart="@dimen/padding_small"
+ android:paddingEnd="@dimen/padding_xsmall">
+ android:paddingStart="@dimen/padding_small"
+ android:paddingEnd="@dimen/padding_xsmall">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_editor_head.xml b/app/src/main/res/layout/view_editor_head.xml
index 442c31f3c..44ef8eae6 100644
--- a/app/src/main/res/layout/view_editor_head.xml
+++ b/app/src/main/res/layout/view_editor_head.xml
@@ -18,14 +18,14 @@
-->
+ android:layout_centerInParent="true"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_editor_image.xml b/app/src/main/res/layout/view_editor_image.xml
index ba9d86d2c..cea3602c0 100644
--- a/app/src/main/res/layout/view_editor_image.xml
+++ b/app/src/main/res/layout/view_editor_image.xml
@@ -23,6 +23,6 @@
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_header.xml b/app/src/main/res/layout/view_header.xml
index 437a5e720..39efa379b 100644
--- a/app/src/main/res/layout/view_header.xml
+++ b/app/src/main/res/layout/view_header.xml
@@ -57,6 +57,7 @@
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/margin_small"
+ android:background="?selectableItemBackgroundBorderless"
android:padding="@dimen/padding_xsmall"
app:srcCompat="@drawable/ic_arrow_right"
app:tint="?android:textColorPrimary" />
diff --git a/app/src/main/res/layout/view_info.xml b/app/src/main/res/layout/view_info.xml
new file mode 100644
index 000000000..2d9559df8
--- /dev/null
+++ b/app/src/main/res/layout/view_info.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_installer.xml b/app/src/main/res/layout/view_installer.xml
index 53ad1e8f0..f5ed4726f 100644
--- a/app/src/main/res/layout/view_installer.xml
+++ b/app/src/main/res/layout/view_installer.xml
@@ -56,5 +56,7 @@
android:layout_height="@dimen/icon_size_category"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
+ android:clickable="false"
+ android:focusable="false"
android:minWidth="0dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_more_badge.xml b/app/src/main/res/layout/view_more_badge.xml
index 5fb17d4da..299f801f4 100644
--- a/app/src/main/res/layout/view_more_badge.xml
+++ b/app/src/main/res/layout/view_more_badge.xml
@@ -22,15 +22,15 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="@dimen/padding_xsmall">
+ android:paddingStart="@dimen/padding_small"
+ android:paddingEnd="@dimen/padding_small">
+ android:padding="@dimen/padding_xxsmall" />
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/view_screenshot_large.xml b/app/src/main/res/layout/view_screenshot_large.xml
index 0012b2816..921e4b1cf 100644
--- a/app/src/main/res/layout/view_screenshot_large.xml
+++ b/app/src/main/res/layout/view_screenshot_large.xml
@@ -25,6 +25,5 @@
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:cropToPadding="false" />
+ android:layout_centerInParent="true" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_screenshot_mini.xml b/app/src/main/res/layout/view_screenshot_mini.xml
new file mode 100644
index 000000000..96eca2c8f
--- /dev/null
+++ b/app/src/main/res/layout/view_screenshot_mini.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_details.xml b/app/src/main/res/menu/menu_details.xml
index ed4b61de8..1c40d93fb 100644
--- a/app/src/main/res/menu/menu_details.xml
+++ b/app/src/main/res/menu/menu_details.xml
@@ -31,4 +31,8 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_download_single.xml b/app/src/main/res/menu/menu_download_single.xml
index 87b4484b4..9b8b52913 100644
--- a/app/src/main/res/menu/menu_download_single.xml
+++ b/app/src/main/res/menu/menu_download_single.xml
@@ -20,7 +20,7 @@