Initial v4
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Built application files
|
||||||
|
*.apk
|
||||||
|
*.ap_
|
||||||
|
|
||||||
|
# Files for the Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
|
# Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
bin/
|
||||||
|
gen/
|
||||||
|
|
||||||
|
# Gitlab files
|
||||||
|
.gitlab/
|
||||||
|
|
||||||
|
# Gradle files
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
/*/build/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Proguard folder generated by Eclipse
|
||||||
|
proguard/
|
||||||
|
|
||||||
|
# Log Files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# From .gitignore generated by Android Studio
|
||||||
|
*.iml
|
||||||
|
.DS_Store
|
||||||
|
/captures
|
||||||
|
/.idea
|
||||||
|
/.gradle*
|
||||||
|
gradle-app.setting
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
app/release/*
|
||||||
|
app/beta/*
|
||||||
|
app/alpha/*
|
||||||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
166
app/build.gradle
Normal file
166
app/build.gradle
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 30
|
||||||
|
buildToolsVersion "30.0.3"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.aurora.store"
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 30
|
||||||
|
|
||||||
|
versionCode 30
|
||||||
|
versionName "4.0.1"
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
multiDexEnabled true
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
zipAlignEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
|
||||||
|
beta {
|
||||||
|
initWith release
|
||||||
|
applicationIdSuffix = ".beta"
|
||||||
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
versions = [
|
||||||
|
okhttp3 : "4.9.0",
|
||||||
|
fetch2 : "3.1.6",
|
||||||
|
fuel : "2.3.0",
|
||||||
|
glide : "4.11.0",
|
||||||
|
lifecycle: "2.3.0",
|
||||||
|
epoxy : "4.3.1",
|
||||||
|
libsu : "2.5.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
|
||||||
|
//MultiDex for Kitkat support
|
||||||
|
implementation("androidx.multidex:multidex:2.0.1")
|
||||||
|
|
||||||
|
//Protobuf
|
||||||
|
implementation("com.google.protobuf:protobuf-java:3.14.0")
|
||||||
|
|
||||||
|
//Apache's Goodies
|
||||||
|
implementation("commons-io:commons-io:2.7")
|
||||||
|
implementation("org.apache.commons:commons-text:1.8")
|
||||||
|
|
||||||
|
//Google's Goodies
|
||||||
|
implementation("com.google.android.material:material:1.3.0")
|
||||||
|
implementation("com.google.android:flexbox:2.0.1")
|
||||||
|
implementation("com.google.code.gson:gson:2.8.6")
|
||||||
|
|
||||||
|
//AndroidX
|
||||||
|
implementation("androidx.core:core-ktx:1.3.2")
|
||||||
|
implementation("androidx.viewpager2:viewpager2:1.0.0")
|
||||||
|
implementation("androidx.vectordrawable:vectordrawable:1.1.0")
|
||||||
|
implementation("androidx.preference:preference-ktx:1.1.1")
|
||||||
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
|
//Arch LifeCycle
|
||||||
|
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${versions.lifecycle}")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${versions.lifecycle}")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:${versions.lifecycle}")
|
||||||
|
|
||||||
|
//Arch Navigation
|
||||||
|
implementation("androidx.navigation:navigation-fragment-ktx:${navigation}")
|
||||||
|
implementation("androidx.navigation:navigation-runtime-ktx:${navigation}")
|
||||||
|
implementation("androidx.navigation:navigation-ui-ktx:${navigation}")
|
||||||
|
|
||||||
|
//UI Addons
|
||||||
|
implementation("com.github.florent37:expansionpanel:1.2.4")
|
||||||
|
|
||||||
|
//Easy Permission
|
||||||
|
implementation("com.github.quickpermissions:quickpermissions-kotlin:0.4.0")
|
||||||
|
|
||||||
|
//Glide
|
||||||
|
implementation("com.github.bumptech.glide:glide:${versions.glide}")
|
||||||
|
kapt("com.github.bumptech.glide:compiler:${versions.glide}")
|
||||||
|
|
||||||
|
//Shimmer
|
||||||
|
implementation("com.facebook.shimmer:shimmer:0.5.0")
|
||||||
|
|
||||||
|
//Epoxy
|
||||||
|
implementation("com.airbnb.android:epoxy:${versions.epoxy}")
|
||||||
|
kapt("com.airbnb.android:epoxy-processor:${versions.epoxy}")
|
||||||
|
|
||||||
|
//Merlin
|
||||||
|
implementation("com.novoda:merlin:1.2.0")
|
||||||
|
|
||||||
|
//HTTP Clients
|
||||||
|
implementation("com.github.kittinunf.fuel:fuel:${versions.fuel}")
|
||||||
|
implementation("com.squareup.okhttp3:okhttp:${versions.okhttp3}")
|
||||||
|
|
||||||
|
//Fetch - Downloader
|
||||||
|
implementation "androidx.tonyodev.fetch2:xfetch2:${versions.fetch2}"
|
||||||
|
|
||||||
|
//Kovenant
|
||||||
|
implementation("nl.komponents.kovenant:kovenant:3.3.0")
|
||||||
|
implementation("nl.komponents.kovenant:kovenant-android:3.3.0")
|
||||||
|
|
||||||
|
//EventBus
|
||||||
|
implementation("org.greenrobot:eventbus:3.2.0")
|
||||||
|
|
||||||
|
//Lib-SU
|
||||||
|
implementation "com.github.topjohnwu.libsu:core:${versions.libsu}"
|
||||||
|
|
||||||
|
//Love <3
|
||||||
|
api("com.gitlab.AuroraOSS:gplayapi:514f061739")
|
||||||
|
}
|
||||||
120
app/proguard-rules.pro
vendored
Normal file
120
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
# ServiceLoader support
|
||||||
|
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
|
||||||
|
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
|
||||||
|
|
||||||
|
# Most of volatile fields are updated with AFU and should not be mangled
|
||||||
|
-keepclassmembernames class kotlinx.** {
|
||||||
|
volatile <fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Same story for the standard library's SafeContinuation that also uses AtomicReferenceFieldUpdater
|
||||||
|
-keepclassmembernames class kotlin.coroutines.SafeContinuation {
|
||||||
|
volatile <fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
# These classes are only required by kotlinx.coroutines.debug.AgentPremain, which is only loaded when
|
||||||
|
# kotlinx-coroutines-core is used as a Java agent, so these are not needed in contexts where ProGuard is used.
|
||||||
|
-dontwarn java.lang.instrument.ClassFileTransformer
|
||||||
|
-dontwarn sun.misc.SignalHandler
|
||||||
|
-dontwarn java.lang.instrument.Instrumentation
|
||||||
|
-dontwarn sun.misc.Signal
|
||||||
|
|
||||||
|
# JSR 305 annotations are for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||||
|
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||||
|
|
||||||
|
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||||
|
|
||||||
|
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
|
||||||
|
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||||
|
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
||||||
|
|
||||||
|
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||||
|
|
||||||
|
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
|
||||||
|
# EnclosingMethod is required to use InnerClasses.
|
||||||
|
-keepattributes Signature, InnerClasses, EnclosingMethod
|
||||||
|
|
||||||
|
# Retrofit does reflection on method and parameter annotations.
|
||||||
|
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
|
||||||
|
|
||||||
|
# Retain service method parameters when optimizing.
|
||||||
|
-keepclassmembers,allowshrinking,allowobfuscation interface * {
|
||||||
|
@retrofit2.http.* <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ignore annotation used for build tooling.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
|
||||||
|
# Ignore JSR 305 annotations for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
|
||||||
|
-dontwarn kotlin.Unit
|
||||||
|
|
||||||
|
# Top-level functions that can only be used by Kotlin.
|
||||||
|
-dontwarn retrofit2.KotlinExtensions
|
||||||
|
-dontwarn retrofit2.KotlinExtensions$*
|
||||||
|
|
||||||
|
#Kovenant
|
||||||
|
-dontwarn rx.internal.util.unsafe.**
|
||||||
|
-dontwarn nl.komponents.kovenant.unsafe.**
|
||||||
|
|
||||||
|
-dontwarn okio.**
|
||||||
|
-keep class com.google.**
|
||||||
|
-dontwarn com.google.**
|
||||||
|
-keep class com.google.gson.Gson {*;}
|
||||||
|
|
||||||
|
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
|
||||||
|
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
|
||||||
|
-if interface * { @retrofit2.http.* <methods>; }
|
||||||
|
-keep,allowobfuscation interface <1>
|
||||||
|
|
||||||
|
#Event Bus
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
-keepclassmembers class * {
|
||||||
|
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||||
|
}
|
||||||
|
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||||
|
|
||||||
|
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
||||||
|
<init>(java.lang.Throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keepclassmembers enum * { *; }
|
||||||
|
-keep class com.aurora.store.view.ui.preferences.**
|
||||||
|
-dontwarn com.aurora.store.view.ui.preferences.**
|
||||||
|
|
||||||
|
-keepclassmembers class * {
|
||||||
|
private <fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#GPlay API
|
||||||
|
-keep public class com.aurora.gplayapi.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.junit.Assert.*
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.aurora.aurorastore", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
128
app/src/main/AndroidManifest.xml
Normal file
128
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Aurora Store
|
||||||
|
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.aurora.store">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.INSTALL_PACKAGES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.DELETE_PACKAGES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <!-- MIUI -->
|
||||||
|
<uses-permission android:name="com.miui.securitycenter.permission.GLOBAL_PACKAGEINSTALLER" />
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.software.leanback"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".AuroraApplication"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:banner="@drawable/ic_launcher_background"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:targetApi="m">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".view.ui.onboarding.OnboardingActivity"
|
||||||
|
android:screenOrientation="portrait">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".view.ui.splash.SplashActivity"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
|
<activity android:name=".view.ui.search.SearchSuggestionActivity" />
|
||||||
|
<activity android:name=".view.ui.search.SearchResultsActivity" />
|
||||||
|
<activity android:name=".view.ui.commons.StreamBrowseActivity" />
|
||||||
|
<activity android:name=".view.ui.sale.AppSalesActivity" />
|
||||||
|
<activity android:name=".view.ui.details.AppDetailsActivity" />
|
||||||
|
<activity android:name=".view.ui.details.ScreenshotActivity" />
|
||||||
|
<activity android:name=".view.ui.all.AppsGamesActivity" />
|
||||||
|
<activity android:name=".view.ui.commons.CategoryBrowseActivity" />
|
||||||
|
<activity android:name=".view.ui.details.DetailsMoreActivity" />
|
||||||
|
<activity android:name=".view.ui.details.DetailsReviewActivity" />
|
||||||
|
<activity android:name=".view.ui.downloads.DownloadActivity" />
|
||||||
|
<activity android:name=".view.ui.account.GoogleActivity" />
|
||||||
|
<activity android:name=".view.ui.spoof.SpoofActivity" />
|
||||||
|
<activity android:name=".view.ui.account.AccountActivity" />
|
||||||
|
<activity android:name=".view.ui.details.DetailsExodusActivity" />
|
||||||
|
<activity android:name=".view.ui.details.DevAppsActivity" />
|
||||||
|
<activity android:name=".view.ui.commons.BlacklistActivity" />
|
||||||
|
<activity android:name=".view.ui.preferences.SettingsActivity" />
|
||||||
|
<activity android:name=".view.ui.about.AboutActivity" />
|
||||||
|
|
||||||
|
<service android:name=".data.service.NotificationService" />
|
||||||
|
<service android:name=".data.installer.InstallerService" />
|
||||||
|
<service
|
||||||
|
android:name="com.novoda.merlin.MerlinService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<receiver android:name=".data.receiver.PackageManagerReceiver" />
|
||||||
|
<receiver android:name=".data.receiver.DownloadResumeReceiver" />
|
||||||
|
<receiver android:name=".data.receiver.DownloadPauseReceiver" />
|
||||||
|
<receiver android:name=".data.receiver.DownloadCancelReceiver" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.services;
|
||||||
|
|
||||||
|
interface IPrivilegedCallback {
|
||||||
|
void handleResult(
|
||||||
|
in String packageName,
|
||||||
|
in int returnCode
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.services;
|
||||||
|
|
||||||
|
import com.aurora.services.IPrivilegedCallback;
|
||||||
|
|
||||||
|
interface IPrivilegedService {
|
||||||
|
|
||||||
|
boolean hasPrivilegedPermissions();
|
||||||
|
|
||||||
|
oneway void installPackage(
|
||||||
|
in String packageName,
|
||||||
|
in Uri uri,
|
||||||
|
in int flags,
|
||||||
|
in String installerPackageName,
|
||||||
|
in IPrivilegedCallback callback
|
||||||
|
);
|
||||||
|
|
||||||
|
oneway void installSplitPackage(
|
||||||
|
in String packageName,
|
||||||
|
in List<Uri> uriList,
|
||||||
|
in int flags,
|
||||||
|
in String installerPackageName,
|
||||||
|
in IPrivilegedCallback callback
|
||||||
|
);
|
||||||
|
|
||||||
|
oneway void deletePackage(
|
||||||
|
in String packageName,
|
||||||
|
in int flags,
|
||||||
|
in IPrivilegedCallback callback
|
||||||
|
);
|
||||||
|
}
|
||||||
50
app/src/main/assets/accent.json
Executable file
50
app/src/main/assets/accent.json
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"accent": "#6C63FF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"accent": "#FF00FF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"accent": "#F50057"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"accent": "#F9A826"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"accent": "#49A942"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"accent": "#6633cc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7",
|
||||||
|
"accent": "#52565e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8",
|
||||||
|
"accent": "#ee70a6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9",
|
||||||
|
"accent": "#b5c327"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10",
|
||||||
|
"accent": "#f38654"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "11",
|
||||||
|
"accent": "#61A7E0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "12",
|
||||||
|
"accent": "#7289da"
|
||||||
|
}
|
||||||
|
]
|
||||||
37
app/src/main/assets/dash.json
Executable file
37
app/src/main/assets/dash.json
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "0",
|
||||||
|
"title": "FAQs",
|
||||||
|
"subtitle": "Have questions? Find out the answers.",
|
||||||
|
"icon": "ic_faq",
|
||||||
|
"url": "https://gitlab.com/AuroraOSS/AuroraStore"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"title": "Source code",
|
||||||
|
"icon": "ic_code",
|
||||||
|
"subtitle": "Find out what's inside.",
|
||||||
|
"url": "https://gitlab.com/AuroraOSS/AuroraStore/-/blob/master"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"title": "License",
|
||||||
|
"subtitle": "Yes we do have one ;)",
|
||||||
|
"icon": "ic_license",
|
||||||
|
"url": "https://gitlab.com/AuroraOSS/AuroraStore/-/raw/master/LICENSE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"title": "Privacy",
|
||||||
|
"subtitle": "Find out if we have your nudes :p",
|
||||||
|
"icon": "ic_privacy",
|
||||||
|
"url": "https://gitlab.com/AuroraOSS/AuroraStore/-/raw/master/PRIVACY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"title": "Disclaimer",
|
||||||
|
"subtitle": "Hold your own beer!",
|
||||||
|
"icon": "ic_disclaimer",
|
||||||
|
"url": "https://gitlab.com/AuroraOSS/AuroraStore/-/raw/master/DISCLAIMER"
|
||||||
|
}
|
||||||
|
]
|
||||||
2724
app/src/main/assets/exodus_trackers.json
Executable file
2724
app/src/main/assets/exodus_trackers.json
Executable file
File diff suppressed because it is too large
Load Diff
28
app/src/main/assets/installers.json
Executable file
28
app/src/main/assets/installers.json
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "0",
|
||||||
|
"title": "Session installer",
|
||||||
|
"subtitle": "Session based installer for bundled/split APKs",
|
||||||
|
"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.",
|
||||||
|
"url": "https://developer.android.com/reference/android/content/Intent#setDataAndType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"title": "Root installer",
|
||||||
|
"subtitle": "Installer for background installations",
|
||||||
|
"description": "Requires root, supports all Android versions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"title": "Aurora Service",
|
||||||
|
"subtitle": "Installer for background installations",
|
||||||
|
"description": "Requires Aurora Services to be installed as system app."
|
||||||
|
}
|
||||||
|
]
|
||||||
32
app/src/main/assets/themes.json
Executable file
32
app/src/main/assets/themes.json
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "0",
|
||||||
|
"title": "System",
|
||||||
|
"subtitle": "Follow system themes."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"title": "Light",
|
||||||
|
"subtitle": "White UI must die, may slap on your face at night."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"title": "Dark",
|
||||||
|
"subtitle": "As dark as your humour, suitable for night-owls."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"title": "Pitch Black",
|
||||||
|
"subtitle": "The black, that matters."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"title": "Dark-X",
|
||||||
|
"subtitle": "The dark, that looks cool?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"title": "Disskord",
|
||||||
|
"subtitle": "Kanged from discord"
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
51
app/src/main/java/com/aurora/Constants.kt
Normal file
51
app/src/main/java/com/aurora/Constants.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
const val INT_EXTRA = "INT_EXTRA"
|
||||||
|
const val FLOAT_EXTRA = "FLOAT_EXTRA"
|
||||||
|
const val STRING_APP = "STRING_APP"
|
||||||
|
const val STRING_EXTRA = "STRING_EXTRA"
|
||||||
|
const val BROWSE_EXTRA = "BROWSE_EXTRA"
|
||||||
|
|
||||||
|
const val FETCH_GROUP_ID = "FETCH_GROUP_ID"
|
||||||
|
|
||||||
|
const val CONNECTIVITY_CHECK_URL = "https://connectivitycheck.android.com/generate_204"
|
||||||
|
const val EXODUS_BASE_URL = "https://reports.exodus-privacy.eu.org/api/search/"
|
||||||
|
const val EXODUS_REPORT_URL = "https://reports.exodus-privacy.eu.org/reports/"
|
||||||
|
const val SHARE_URL = "http://play.google.com/store/apps/details?id="
|
||||||
|
|
||||||
|
const val NOTIFICATION_CHANNEL_ALERT = "NOTIFICATION_CHANNEL_ALERT"
|
||||||
|
const val NOTIFICATION_CHANNEL_GENERAL = "NOTIFICATION_CHANNEL_GENERAL"
|
||||||
|
|
||||||
|
const val URL_DISPENSER = "http://goolag.store:1337/api/auth"
|
||||||
|
|
||||||
|
//ACCOUNTS
|
||||||
|
const val ACCOUNT_SIGNED_IN = "ACCOUNT_SIGNED_IN"
|
||||||
|
const val ACCOUNT_SIGNED_TIMESTAMP = "ACCOUNT_SIGNED_TIMESTAMP"
|
||||||
|
const val ACCOUNT_TYPE = "ACCOUNT_TYPE"
|
||||||
|
const val ACCOUNT_EMAIL_PLAIN = "ACCOUNT_EMAIL_PLAIN"
|
||||||
|
const val ACCOUNT_AAS_PLAIN = "ACCOUNT_AAS_PLAIN"
|
||||||
|
|
||||||
|
const val PAGE_TYPE = "PAGE_TYPE"
|
||||||
|
const val TOP_CHART_TYPE = "TOP_CHART_TYPE"
|
||||||
|
const val TOP_CHART_CATEGORY = "TOP_CHART_CATEGORY"
|
||||||
|
}
|
||||||
78
app/src/main/java/com/aurora/store/AuroraApplication.kt
Normal file
78
app/src/main/java/com/aurora/store/AuroraApplication.kt
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.multidex.MultiDexApplication
|
||||||
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
import com.aurora.store.data.providers.NetworkProvider
|
||||||
|
import com.aurora.store.data.receiver.PackageManagerReceiver
|
||||||
|
import com.aurora.store.data.service.NotificationService
|
||||||
|
import com.aurora.store.util.CommonUtil
|
||||||
|
import com.aurora.store.util.PackageUtil
|
||||||
|
import com.tonyodev.fetch2.Fetch
|
||||||
|
import nl.komponents.kovenant.android.startKovenant
|
||||||
|
import nl.komponents.kovenant.android.stopKovenant
|
||||||
|
|
||||||
|
class AuroraApplication : MultiDexApplication() {
|
||||||
|
|
||||||
|
private lateinit var fetch: Fetch
|
||||||
|
private lateinit var packageManagerReceiver: PackageManagerReceiver
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
|
|
||||||
|
NotificationService.startService(this)
|
||||||
|
|
||||||
|
fetch = DownloadManager.with(this).fetch
|
||||||
|
|
||||||
|
packageManagerReceiver = object : PackageManagerReceiver() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Register broadcast receiver for package install/uninstall
|
||||||
|
registerReceiver(packageManagerReceiver, PackageUtil.getFilter())
|
||||||
|
|
||||||
|
NetworkProvider
|
||||||
|
.with(this)
|
||||||
|
.bind()
|
||||||
|
|
||||||
|
startKovenant()
|
||||||
|
|
||||||
|
CommonUtil.cleanupInstallationSessions(applicationContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTerminate() {
|
||||||
|
NetworkProvider
|
||||||
|
.with(this)
|
||||||
|
.unbind()
|
||||||
|
stopKovenant()
|
||||||
|
super.onTerminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLowMemory() {
|
||||||
|
NetworkProvider
|
||||||
|
.with(this)
|
||||||
|
.unbind()
|
||||||
|
super.onLowMemory()
|
||||||
|
}
|
||||||
|
}
|
||||||
53
app/src/main/java/com/aurora/store/AuroraGlide.kt
Normal file
53
app/src/main/java/com/aurora/store/AuroraGlide.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import com.bumptech.glide.GlideBuilder
|
||||||
|
import com.bumptech.glide.annotation.GlideModule
|
||||||
|
import com.bumptech.glide.load.DecodeFormat
|
||||||
|
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
|
||||||
|
import com.bumptech.glide.load.engine.cache.LruResourceCache
|
||||||
|
import com.bumptech.glide.module.AppGlideModule
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.signature.ObjectKey
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
class AuroraGlide : AppGlideModule() {
|
||||||
|
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
||||||
|
val memoryCacheSizeBytes = 1024 * 1024 * 50
|
||||||
|
builder.setMemoryCache(LruResourceCache(memoryCacheSizeBytes.toLong()))
|
||||||
|
builder.setDiskCache(InternalCacheDiskCacheFactory(context, memoryCacheSizeBytes.toLong()))
|
||||||
|
builder.setDefaultRequestOptions(requestOptions(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun requestOptions(context: Context): RequestOptions {
|
||||||
|
return RequestOptions()
|
||||||
|
.signature(ObjectKey(System.currentTimeMillis() / (24 * 60 * 60 * 1000)))
|
||||||
|
.centerCrop()
|
||||||
|
.encodeFormat(Bitmap.CompressFormat.PNG)
|
||||||
|
.encodeQuality(60)
|
||||||
|
.format(DecodeFormat.PREFER_ARGB_8888)
|
||||||
|
.skipMemoryCache(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/src/main/java/com/aurora/store/Enumerations.kt
Normal file
25
app/src/main/java/com/aurora/store/Enumerations.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
enum class AccountType {
|
||||||
|
ANONYMOUS,
|
||||||
|
GOOGLE
|
||||||
|
}
|
||||||
243
app/src/main/java/com/aurora/store/MainActivity.kt
Normal file
243
app/src/main/java/com/aurora/store/MainActivity.kt
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.view.GravityCompat
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavDestination
|
||||||
|
import androidx.navigation.Navigation
|
||||||
|
import androidx.navigation.ui.NavigationUI
|
||||||
|
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
|
||||||
|
import com.aurora.store.view.ui.commons.BaseActivity
|
||||||
|
import com.aurora.store.view.ui.commons.BlacklistActivity
|
||||||
|
import com.aurora.store.view.ui.downloads.DownloadActivity
|
||||||
|
import com.aurora.store.view.ui.preferences.SettingsActivity
|
||||||
|
import com.aurora.store.view.ui.sale.AppSalesActivity
|
||||||
|
import com.aurora.store.view.ui.search.SearchSuggestionActivity
|
||||||
|
import com.aurora.store.view.ui.spoof.SpoofActivity
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
import com.livinglifetechway.quickpermissions_kotlin.runWithPermissions
|
||||||
|
|
||||||
|
|
||||||
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
|
private lateinit var B: ActivityMainBinding
|
||||||
|
private lateinit var navController: NavController
|
||||||
|
private lateinit var authData: AuthData
|
||||||
|
|
||||||
|
private var lastBackPressed = 0L
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private fun matchDestination(
|
||||||
|
@NonNull destination: NavDestination?,
|
||||||
|
@IdRes destId: Int
|
||||||
|
): Boolean {
|
||||||
|
var currentDestination = destination
|
||||||
|
while (currentDestination?.id != destId && currentDestination?.parent != null) {
|
||||||
|
currentDestination = currentDestination.parent
|
||||||
|
}
|
||||||
|
return currentDestination?.id == destId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
B = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(B.root)
|
||||||
|
|
||||||
|
authData = AuthProvider.with(this).getAuthData()
|
||||||
|
|
||||||
|
attachToolbar()
|
||||||
|
attachNavigation()
|
||||||
|
attachDrawer()
|
||||||
|
attachSearch()
|
||||||
|
|
||||||
|
checkPermission()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkPermission() = runWithPermissions(
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) {
|
||||||
|
Log.i("External Storage Access Available")
|
||||||
|
|
||||||
|
if (isQAndAbove()) {
|
||||||
|
//pickExternalFileDir()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachToolbar() {
|
||||||
|
B.viewToolbar.imgActionPrimary.setOnClickListener {
|
||||||
|
B.drawerLayout.openDrawer(GravityCompat.START, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
B.viewToolbar.imgActionSecondary.setOnClickListener {
|
||||||
|
val userAppIntent = Intent(this, DownloadActivity::class.java)
|
||||||
|
startActivity(userAppIntent, ViewUtil.getEmptyActivityBundle(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachSearch() {
|
||||||
|
B.searchFab.setOnClickListener {
|
||||||
|
startActivity(
|
||||||
|
Intent(this, SearchSuggestionActivity::class.java),
|
||||||
|
ViewUtil.getEmptyActivityBundle(this)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachNavigation() {
|
||||||
|
val bottomNavigationView: BottomNavigationView = B.navView
|
||||||
|
navController = Navigation.findNavController(this, R.id.nav_host_fragment)
|
||||||
|
|
||||||
|
val backGroundColor = getStyledAttribute(this, android.R.attr.colorBackground)
|
||||||
|
bottomNavigationView.setBackgroundColor(ColorUtils.setAlphaComponent(backGroundColor, 245))
|
||||||
|
|
||||||
|
|
||||||
|
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
|
||||||
|
if (item.itemId == bottomNavigationView.selectedItemId)
|
||||||
|
return@setOnNavigationItemSelectedListener false
|
||||||
|
NavigationUI.onNavDestinationSelected(item, navController)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
navController.addOnDestinationChangedListener { _: NavController?, destination: NavDestination?, _: Bundle? ->
|
||||||
|
val menu: Menu = bottomNavigationView.menu
|
||||||
|
val size: Int = menu.size()
|
||||||
|
for (i in 0 until size) {
|
||||||
|
val item: MenuItem = menu.getItem(i)
|
||||||
|
if (matchDestination(destination, item.itemId)) {
|
||||||
|
item.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachDrawer() {
|
||||||
|
val headerView: View = B.navigation.getHeaderView(0)
|
||||||
|
|
||||||
|
headerView.let {
|
||||||
|
it.findViewById<ImageView>(R.id.img)?.load(R.mipmap.ic_launcher) {
|
||||||
|
transform(RoundedCorners(8))
|
||||||
|
}
|
||||||
|
it.findViewById<TextView>(R.id.txt_name)?.text = getString(R.string.app_name)
|
||||||
|
it.findViewById<TextView>(R.id.txt_email)?.text =
|
||||||
|
("v${BuildConfig.VERSION_NAME}.${BuildConfig.VERSION_CODE}")
|
||||||
|
}
|
||||||
|
|
||||||
|
B.navigation.setNavigationItemSelectedListener { item: MenuItem ->
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.menu_apps_games -> {
|
||||||
|
open(AppsGamesActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_apps_sale -> {
|
||||||
|
open(AppSalesActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_blacklist_manager -> {
|
||||||
|
open(BlacklistActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_download_manager -> {
|
||||||
|
open(DownloadActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_spoof_manager -> {
|
||||||
|
open(SpoofActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_account_manager -> {
|
||||||
|
open(AccountActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_settings -> {
|
||||||
|
open(SettingsActivity::class.java)
|
||||||
|
}
|
||||||
|
R.id.menu_about -> {
|
||||||
|
open(AboutActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
return navController.navigateUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (!navController.navigateUp()) {
|
||||||
|
if (lastBackPressed + 1000 > System.currentTimeMillis()) {
|
||||||
|
super.onBackPressed()
|
||||||
|
} else {
|
||||||
|
lastBackPressed = System.currentTimeMillis()
|
||||||
|
Toast.makeText(this, "Click twice to exit", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConnected() {
|
||||||
|
hideNetworkConnectivitySheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisconnected() {
|
||||||
|
showNetworkConnectivitySheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReconnected() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pickExternalFileDir() {
|
||||||
|
val getContentIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
getContentIntent.type = "*/*"
|
||||||
|
getContentIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||||
|
getContentIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true)
|
||||||
|
getContentIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
startActivityForResult(
|
||||||
|
Intent.createChooser(
|
||||||
|
getContentIntent,
|
||||||
|
"Aurora Store - External Storage Access"
|
||||||
|
), 1337
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/src/main/java/com/aurora/store/data/SingletonHolder.kt
Normal file
36
app/src/main/java/com/aurora/store/data/SingletonHolder.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data
|
||||||
|
|
||||||
|
open class SingletonHolder<out T, in A>(private val constructor: (A) -> T) {
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var instance: T? = null
|
||||||
|
|
||||||
|
fun with(arg: A): T {
|
||||||
|
return when {
|
||||||
|
instance != null -> instance!!
|
||||||
|
else -> synchronized(this) {
|
||||||
|
if (instance == null) instance = constructor(arg)
|
||||||
|
instance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/src/main/java/com/aurora/store/data/State.kt
Normal file
44
app/src/main/java/com/aurora/store/data/State.kt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data
|
||||||
|
|
||||||
|
|
||||||
|
sealed class ViewState {
|
||||||
|
object Loading : ViewState()
|
||||||
|
object Empty : ViewState()
|
||||||
|
data class Error(val error: String?) : ViewState()
|
||||||
|
data class Status(val status: String?) : ViewState()
|
||||||
|
data class Success<T>(val data: T) : ViewState()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class RequestState {
|
||||||
|
object Init : RequestState()
|
||||||
|
object Pending : RequestState()
|
||||||
|
object Complete : RequestState()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AuthState {
|
||||||
|
object Available : AuthState()
|
||||||
|
object Unavailable : AuthState()
|
||||||
|
object SignedIn : AuthState()
|
||||||
|
object SignedOut : AuthState()
|
||||||
|
object Valid : AuthState()
|
||||||
|
data class Status(val status: String?) : AuthState()
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.downloader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import com.aurora.store.util.Preferences
|
||||||
|
import com.tonyodev.fetch2.*
|
||||||
|
import com.tonyodev.fetch2core.DefaultStorageResolver
|
||||||
|
import com.tonyodev.fetch2core.getFileTempDir
|
||||||
|
|
||||||
|
class DownloadManager private constructor(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<DownloadManager, Context>(::DownloadManager)
|
||||||
|
|
||||||
|
var fetch: Fetch
|
||||||
|
|
||||||
|
init {
|
||||||
|
fetch = Fetch.getInstance(getFetchConfiguration(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFetchInstance(): Fetch {
|
||||||
|
return fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFetchConfiguration(context: Context): FetchConfiguration {
|
||||||
|
var maxActive = Preferences.getInteger(context, Preferences.PREFERENCE_DOWNLOAD_ACTIVE)
|
||||||
|
if (maxActive == 0)
|
||||||
|
maxActive = 1
|
||||||
|
return FetchConfiguration.Builder(context)
|
||||||
|
.setDownloadConcurrentLimit(maxActive)
|
||||||
|
.enableLogging(BuildConfig.DEBUG)
|
||||||
|
.enableHashCheck(true)
|
||||||
|
.enableFileExistChecks(true)
|
||||||
|
.enableRetryOnNetworkGain(true)
|
||||||
|
.enableAutoStart(true)
|
||||||
|
.setInternetAccessUrlCheck(Constants.CONNECTIVITY_CHECK_URL)
|
||||||
|
.setAutoRetryMaxAttempts(3)
|
||||||
|
.setProgressReportingInterval(3000)
|
||||||
|
.setNamespace(BuildConfig.APPLICATION_ID)
|
||||||
|
.setStorageResolver(DefaultStorageResolver(context, getFileTempDir(context)))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isDownloading(fetchGroup: FetchGroup): Boolean {
|
||||||
|
return fetchGroup.downloadingDownloads.isNotEmpty()
|
||||||
|
|| fetchGroup.queuedDownloads.isNotEmpty()
|
||||||
|
|| fetchGroup.addedDownloads.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCanceled(fetchGroup: FetchGroup): Boolean {
|
||||||
|
return fetchGroup.cancelledDownloads.isNotEmpty()
|
||||||
|
|| fetchGroup.removedDownloads.isNotEmpty()
|
||||||
|
|| fetchGroup.deletedDownloads.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateOngoingDownloads(
|
||||||
|
fetch: Fetch, packageList: MutableList<String?>, download: Download,
|
||||||
|
fetchListener: FetchListener?
|
||||||
|
) {
|
||||||
|
if (packageList.contains(download.tag)) {
|
||||||
|
val packageName = download.tag
|
||||||
|
if (packageName != null) {
|
||||||
|
fetch.deleteGroup(packageName.hashCode())
|
||||||
|
packageList.remove(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (packageList.size == 0) {
|
||||||
|
fetch.removeListener(fetchListener!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.downloader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
|
import com.aurora.store.util.PathUtil
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.tonyodev.fetch2.EnqueueAction
|
||||||
|
import com.tonyodev.fetch2.NetworkType
|
||||||
|
import com.tonyodev.fetch2.Request
|
||||||
|
import com.tonyodev.fetch2core.Extras
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
|
private inline fun Request.attachMetaData(app: App) {
|
||||||
|
apply {
|
||||||
|
groupId = app.id
|
||||||
|
tag = app.packageName
|
||||||
|
enqueueAction = EnqueueAction.UPDATE_ACCORDINGLY
|
||||||
|
networkType = NetworkType.ALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun Request.attachExtra(app: App) {
|
||||||
|
val stringMap: MutableMap<String, String> = mutableMapOf()
|
||||||
|
val gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
stringMap[Constants.STRING_EXTRA] = gson.toJson(app)
|
||||||
|
apply {
|
||||||
|
extras = Extras(stringMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object RequestBuilder {
|
||||||
|
|
||||||
|
fun buildRequest(context: Context, app: App, file: File): Request {
|
||||||
|
val fileName = when (file.type) {
|
||||||
|
File.FileType.BASE,
|
||||||
|
File.FileType.SPLIT -> PathUtil.getApkDownloadFile(context, app, file)
|
||||||
|
File.FileType.OBB,
|
||||||
|
File.FileType.PATCH -> PathUtil.getObbDownloadFile(context, app, file)
|
||||||
|
}
|
||||||
|
return Request(file.url, fileName).apply {
|
||||||
|
attachMetaData(app)
|
||||||
|
attachExtra(app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/src/main/java/com/aurora/store/data/event/BusEvent.kt
Normal file
31
app/src/main/java/com/aurora/store/data/event/BusEvent.kt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 GoogleAAS(
|
||||||
|
var success: Boolean,
|
||||||
|
var email: String = String(),
|
||||||
|
var aasToken: String = String()
|
||||||
|
) : BusEvent()
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
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, Context>(::AppInstaller)
|
||||||
|
|
||||||
|
fun getPreferredInstaller(): IInstaller {
|
||||||
|
val prefValue = Preferences.getInteger(
|
||||||
|
context,
|
||||||
|
PREFERENCE_INSTALLER_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
return when (prefValue) {
|
||||||
|
1 -> NativeInstaller(context)
|
||||||
|
2 -> RootInstaller(context)
|
||||||
|
3 -> ServiceInstaller(context)
|
||||||
|
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
SessionInstaller(context)
|
||||||
|
} else {
|
||||||
|
NativeInstaller(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
interface IInstaller {
|
||||||
|
fun install(packageName: String, files: List<Any>)
|
||||||
|
fun uninstall(packageName: String)
|
||||||
|
|
||||||
|
fun clearQueue()
|
||||||
|
fun isAlreadyQueued(packageName: String): Boolean
|
||||||
|
fun removeFromInstallQueue(packageName: String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class InstallerBase(protected var context: Context) : IInstaller {
|
||||||
|
|
||||||
|
private val enqueuedInstalls: MutableSet<String> = mutableSetOf()
|
||||||
|
|
||||||
|
override fun clearQueue() {
|
||||||
|
enqueuedInstalls.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAlreadyQueued(packageName: String): Boolean {
|
||||||
|
return enqueuedInstalls.contains(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeFromInstallQueue(packageName: String) {
|
||||||
|
enqueuedInstalls.remove(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uninstall(packageName: String) {
|
||||||
|
val uri = Uri.fromParts("package", packageName, null)
|
||||||
|
val intent = Intent().apply {
|
||||||
|
data = uri
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||||
|
intent.action = Intent.ACTION_DELETE
|
||||||
|
} else {
|
||||||
|
intent.action = Intent.ACTION_UNINSTALL_PACKAGE
|
||||||
|
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getUri(file: File): Uri {
|
||||||
|
return FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileProvider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
|
||||||
|
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 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>(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ACTION_SESSION_INSTALLER = "ACTION_SESSION_INSTALLER"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class NativeInstaller(context: Context) : InstallerBase(context) {
|
||||||
|
|
||||||
|
override fun install(packageName: String, files: List<Any>) {
|
||||||
|
if (isAlreadyQueued(packageName)) {
|
||||||
|
Log.i("$packageName already queued")
|
||||||
|
} else {
|
||||||
|
files.map {
|
||||||
|
when (it) {
|
||||||
|
is File -> it
|
||||||
|
is String -> File(it)
|
||||||
|
else -> {
|
||||||
|
throw Exception("Invalid data, expecting listOf() File or String")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
xInstall(packageName, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun xInstall(packageName: String, file: File) {
|
||||||
|
val intent: Intent
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||||
|
intent.data = getUri(file)
|
||||||
|
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
} else {
|
||||||
|
intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
}
|
||||||
|
|
||||||
|
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
|
||||||
|
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, "com.android.vending")
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.store.R
|
||||||
|
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 java.io.File
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
class RootInstaller(context: Context) : InstallerBase(context) {
|
||||||
|
|
||||||
|
override fun install(packageName: String, files: List<Any>) {
|
||||||
|
if (isAlreadyQueued(packageName)) {
|
||||||
|
Log.i("$packageName already queued")
|
||||||
|
} else {
|
||||||
|
if (Shell.getShell().isRoot) {
|
||||||
|
files.map {
|
||||||
|
when (it) {
|
||||||
|
is File -> it
|
||||||
|
is String -> File(it)
|
||||||
|
else -> {
|
||||||
|
throw Exception("Invalid data, expecting listOf() File or String")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
if (isLAndAbove())
|
||||||
|
xInstall(packageName, it)
|
||||||
|
else
|
||||||
|
xInstallLegacy(packageName, it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.toast(context.getString(R.string.installer_root_unavailable))
|
||||||
|
Log.e(" >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun xInstall(packageName: String, files: List<File>) {
|
||||||
|
var totalSize = 0
|
||||||
|
|
||||||
|
for (file in files)
|
||||||
|
totalSize += file.length().toInt()
|
||||||
|
|
||||||
|
val result: Shell.Result =
|
||||||
|
Shell.su("pm install-create -i com.android.vending --user 0 -r -S $totalSize")
|
||||||
|
.exec()
|
||||||
|
|
||||||
|
val response = result.out
|
||||||
|
|
||||||
|
val sessionIdPattern = Pattern.compile("(\\d+)")
|
||||||
|
val sessionIdMatcher = sessionIdPattern.matcher(response[0])
|
||||||
|
val found = sessionIdMatcher.find()
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
val sessionId = sessionIdMatcher.group(1).toInt()
|
||||||
|
if (Shell.getShell().isRoot) {
|
||||||
|
for (file in files) {
|
||||||
|
Shell.su("cat \"${file.absoluteFile}\" | pm install-write -S ${file.length()} $sessionId \"${file.name}\"")
|
||||||
|
.exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
Shell.su("pm install-commit $sessionId").exec()
|
||||||
|
} else {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun xInstallLegacy(packageName: String, files: List<File>) {
|
||||||
|
if (Shell.getShell().isRoot) {
|
||||||
|
Shell.su("pm install -i com.android.vending --user 0 \"${files[0].name}\"").exec()
|
||||||
|
} else {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.RemoteException
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.aurora.services.IPrivilegedCallback
|
||||||
|
import com.aurora.services.IPrivilegedService
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ServiceInstaller(context: Context) : InstallerBase(context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTION_INSTALL_REPLACE_EXISTING = 2
|
||||||
|
const val PRIVILEGED_EXTENSION_PACKAGE_NAME = "com.aurora.services"
|
||||||
|
const val PRIVILEGED_EXTENSION_SERVICE_INTENT = "com.aurora.services.IPrivilegedService"
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
override fun install(packageName: String, files: List<Any>) {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xInstall(packageName, uriList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
||||||
|
|
||||||
|
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(
|
||||||
|
packageName,
|
||||||
|
uriList,
|
||||||
|
ACTION_INSTALL_REPLACE_EXISTING,
|
||||||
|
BuildConfig.APPLICATION_ID,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
Log.e("Failed to connect Aurora Services")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(name: ComponentName) {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
Log.e("Disconnected from Aurora Services")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT)
|
||||||
|
intent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME)
|
||||||
|
|
||||||
|
context.bindService(
|
||||||
|
intent,
|
||||||
|
serviceConnection,
|
||||||
|
Context.BIND_AUTO_CREATE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageInstaller.SessionParams
|
||||||
|
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.Log
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class SessionInstaller(context: Context) : InstallerBase(context) {
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
override fun install(packageName: String, files: List<Any>) {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xInstall(packageName, uriList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
||||||
|
val packageInstaller = context.packageManager.packageInstaller
|
||||||
|
val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL)
|
||||||
|
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++}",
|
||||||
|
0,
|
||||||
|
-1
|
||||||
|
)
|
||||||
|
|
||||||
|
IOUtils.copy(inputStream, outputStream)
|
||||||
|
|
||||||
|
session.fsync(outputStream)
|
||||||
|
|
||||||
|
IOUtils.close(inputStream)
|
||||||
|
IOUtils.close(outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(context, InstallerService::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getService(
|
||||||
|
context,
|
||||||
|
sessionId,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.i("Starting install session for $packageName")
|
||||||
|
session.commit(pendingIntent.intentSender)
|
||||||
|
session.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
session.abandon()
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
Log.e("Failed to install $packageName : %s", e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUri(file: File): Uri {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileProvider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
|
||||||
|
uri.apply {
|
||||||
|
context.grantUriPermission(
|
||||||
|
BuildConfig.APPLICATION_ID,
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/src/main/java/com/aurora/store/data/model/Accent.kt
Normal file
36
app/src/main/java/com/aurora/store/data/model/Accent.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
data class Accent(
|
||||||
|
var id: Int,
|
||||||
|
var accent: String
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Accent -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/src/main/java/com/aurora/store/data/model/Black.kt
Normal file
40
app/src/main/java/com/aurora/store/data/model/Black.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
|
||||||
|
data class Black(val packageName: String) {
|
||||||
|
var displayName: String = String()
|
||||||
|
var drawable: Drawable? = null
|
||||||
|
var versionName: String = String()
|
||||||
|
var versionCode: Int = 0
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return packageName.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Black -> other.packageName == packageName
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/src/main/java/com/aurora/store/data/model/Dash.kt
Normal file
39
app/src/main/java/com/aurora/store/data/model/Dash.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
data class Dash(
|
||||||
|
var id: Int,
|
||||||
|
var title: String,
|
||||||
|
var subtitle: String,
|
||||||
|
var icon: String,
|
||||||
|
var url: String
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Dash -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/src/main/java/com/aurora/store/data/model/Download.kt
Normal file
36
app/src/main/java/com/aurora/store/data/model/Download.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import com.tonyodev.fetch2.Download
|
||||||
|
|
||||||
|
data class DownloadFile(val download: Download) {
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return download.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is DownloadFile -> other.download.status == download.status && other.download.progress == download.progress
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
app/src/main/java/com/aurora/store/data/model/Exodus.kt
Normal file
72
app/src/main/java/com/aurora/store/data/model/Exodus.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ExodusReport {
|
||||||
|
val creator: String = String()
|
||||||
|
val name: String = String()
|
||||||
|
val reports: List<Report> = listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Report {
|
||||||
|
val id: Int = 0
|
||||||
|
val downloads: String = String()
|
||||||
|
val version: String = String()
|
||||||
|
val creationDate: String = String()
|
||||||
|
val updatedAt: String = String()
|
||||||
|
val versionCode: String = String()
|
||||||
|
val trackers: List<Int> = listOf()
|
||||||
|
|
||||||
|
fun getFormattedCreationDate(): String {
|
||||||
|
return try {
|
||||||
|
val simpleDateFormat: DateFormat = SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
simpleDateFormat.parse(creationDate).toString()
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExodusTracker {
|
||||||
|
var id: Int = 0
|
||||||
|
var name: String = String()
|
||||||
|
var url: String = String()
|
||||||
|
var signature: String = String()
|
||||||
|
var date: String = String()
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is ExodusTracker -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/src/main/java/com/aurora/store/data/model/Installer.kt
Normal file
39
app/src/main/java/com/aurora/store/data/model/Installer.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
data class Installer(
|
||||||
|
var id: Int,
|
||||||
|
var title: String,
|
||||||
|
var subtitle: String,
|
||||||
|
var description: String,
|
||||||
|
var url: String
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Installer -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/src/main/java/com/aurora/store/data/model/Link.kt
Normal file
39
app/src/main/java/com/aurora/store/data/model/Link.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
data class Link(
|
||||||
|
var id: Int,
|
||||||
|
var title: String,
|
||||||
|
var subtitle: String,
|
||||||
|
var url: String,
|
||||||
|
var icon: Int,
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Link -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/src/main/java/com/aurora/store/data/model/Theme.kt
Normal file
37
app/src/main/java/com/aurora/store/data/model/Theme.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
data class Theme(
|
||||||
|
var id: Int,
|
||||||
|
var title: String,
|
||||||
|
var subtitle: String
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return when (other) {
|
||||||
|
is Theme -> other.id == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return id.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
126
app/src/main/java/com/aurora/store/data/network/FuelClient.kt
Normal file
126
app/src/main/java/com/aurora/store/data/network/FuelClient.kt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.network
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.GooglePlayApi
|
||||||
|
import com.aurora.gplayapi.data.models.PlayResponse
|
||||||
|
import com.aurora.gplayapi.network.IHttpClient
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import com.github.kittinunf.fuel.Fuel
|
||||||
|
import com.github.kittinunf.fuel.core.*
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
object FuelClient : IHttpClient {
|
||||||
|
|
||||||
|
override fun get(url: String, headers: Map<String, String>): PlayResponse {
|
||||||
|
return get(url, headers, hashMapOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
params: Map<String, String>
|
||||||
|
): PlayResponse {
|
||||||
|
val parameters = params
|
||||||
|
.map { it.key to it.value }
|
||||||
|
.toList()
|
||||||
|
val (request, response, result) = Fuel.get(url, parameters)
|
||||||
|
.header(headers)
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAuth(url: String): PlayResponse {
|
||||||
|
val (request, response, result) = Fuel.get(url)
|
||||||
|
.appendHeader(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}-${BuildConfig.VERSION_NAME}-${BuildConfig.VERSION_CODE}"
|
||||||
|
)
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
paramString: String
|
||||||
|
): PlayResponse {
|
||||||
|
val (request, response, result) = Fuel.get(url + paramString)
|
||||||
|
.header(headers)
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse {
|
||||||
|
val (request, response, result) = Fuel.post(url)
|
||||||
|
.header(headers)
|
||||||
|
.appendHeader(Headers.CONTENT_TYPE, "application/x-protobuf")
|
||||||
|
.body(body, Charset.defaultCharset())
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun post(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
params: Map<String, String>
|
||||||
|
): PlayResponse {
|
||||||
|
val parameters = params
|
||||||
|
.map { it.key to it.value }
|
||||||
|
.toList()
|
||||||
|
val (request, response, result) = Fuel.post(url, parameters)
|
||||||
|
.header(headers)
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun postAuth(url: String, body: ByteArray): PlayResponse {
|
||||||
|
val (request, response, result) = Fuel.post(url)
|
||||||
|
.appendHeader(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}-${BuildConfig.VERSION_NAME}-${BuildConfig.VERSION_CODE}"
|
||||||
|
)
|
||||||
|
.body(body)
|
||||||
|
.response()
|
||||||
|
return buildPlayResponse(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private fun buildPlayResponse(response: Response, request: Request): PlayResponse {
|
||||||
|
return PlayResponse().apply {
|
||||||
|
isSuccessful = response.isSuccessful
|
||||||
|
code = response.statusCode
|
||||||
|
|
||||||
|
GooglePlayApi
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
responseBytes = response.body().toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isClientError || response.isServerError) {
|
||||||
|
errorBytes = response.responseMessage.toByteArray()
|
||||||
|
errorString = String(errorBytes)
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
Log.i("FUEL [${request.method}:${response.statusCode}] ${response.url}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.network
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import com.aurora.gplayapi.network.IHttpClient
|
||||||
|
|
||||||
|
object HttpClient {
|
||||||
|
|
||||||
|
fun getPreferredClient(): IHttpClient {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
OkHttpClient
|
||||||
|
} else {
|
||||||
|
FuelClient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
app/src/main/java/com/aurora/store/data/network/OkHttpClient.kt
Normal file
169
app/src/main/java/com/aurora/store/data/network/OkHttpClient.kt
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.network
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.PlayResponse
|
||||||
|
import com.aurora.gplayapi.network.IHttpClient
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import okhttp3.*
|
||||||
|
import okhttp3.Headers.Companion.toHeaders
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object OkHttpClient : IHttpClient {
|
||||||
|
|
||||||
|
private const val POST = "POST"
|
||||||
|
private const val GET = "GET"
|
||||||
|
|
||||||
|
private val okHttpClient = OkHttpClient().newBuilder()
|
||||||
|
.connectTimeout(20, TimeUnit.SECONDS)
|
||||||
|
.retryOnConnectionFailure(true)
|
||||||
|
.followRedirects(true)
|
||||||
|
.followSslRedirects(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun post(url: String, headers: Map<String, String>, requestBody: RequestBody): PlayResponse {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.headers(headers.toHeaders())
|
||||||
|
.method(POST, requestBody)
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun post(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
params: Map<String, String>
|
||||||
|
): PlayResponse {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(buildUrl(url, params))
|
||||||
|
.headers(headers.toHeaders())
|
||||||
|
.method(POST, "".toRequestBody(null))
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun postAuth(url: String, body: ByteArray): PlayResponse {
|
||||||
|
val requestBody = body.toRequestBody("application/json".toMediaType(), 0, body.size)
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}-${BuildConfig.VERSION_NAME}-${BuildConfig.VERSION_CODE}"
|
||||||
|
)
|
||||||
|
.method(POST, requestBody)
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse {
|
||||||
|
val requestBody = body.toRequestBody(
|
||||||
|
"application/x-protobuf".toMediaType(),
|
||||||
|
0,
|
||||||
|
body.size
|
||||||
|
)
|
||||||
|
return post(url, headers, requestBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun get(url: String, headers: Map<String, String>): PlayResponse {
|
||||||
|
return get(url, headers, mapOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun get(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
params: Map<String, String>
|
||||||
|
): PlayResponse {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(buildUrl(url, params))
|
||||||
|
.headers(headers.toHeaders())
|
||||||
|
.method(GET, null)
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAuth(url: String): PlayResponse {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}-${BuildConfig.VERSION_NAME}-${BuildConfig.VERSION_CODE}"
|
||||||
|
)
|
||||||
|
.method(GET, null)
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun get(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
paramString: String
|
||||||
|
): PlayResponse {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url + paramString)
|
||||||
|
.headers(headers.toHeaders())
|
||||||
|
.method(GET, null)
|
||||||
|
.build()
|
||||||
|
return processRequest(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processRequest(request: Request): PlayResponse {
|
||||||
|
val call = okHttpClient.newCall(request)
|
||||||
|
return buildPlayResponse(call.execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildUrl(url: String, params: Map<String, String>): HttpUrl {
|
||||||
|
val urlBuilder = url.toHttpUrl().newBuilder()
|
||||||
|
params.forEach {
|
||||||
|
urlBuilder.addQueryParameter(it.key, it.value)
|
||||||
|
}
|
||||||
|
return urlBuilder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildPlayResponse(response: Response): PlayResponse {
|
||||||
|
return PlayResponse().apply {
|
||||||
|
isSuccessful = response.isSuccessful
|
||||||
|
code = response.code
|
||||||
|
|
||||||
|
if (response.body != null) {
|
||||||
|
responseBytes = response.body!!.bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSuccessful) {
|
||||||
|
errorString = response.message
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
Log.i("OKHTTP [${response.code}] ${response.request.url}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.store.AccountType
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import com.aurora.store.util.Preferences
|
||||||
|
|
||||||
|
class AccountProvider private constructor(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<AccountProvider, Context>(::AccountProvider)
|
||||||
|
|
||||||
|
fun isSignedIn(): Boolean {
|
||||||
|
return Preferences.getBoolean(context, Constants.ACCOUNT_SIGNED_IN)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSignInTimeStamp(): Long {
|
||||||
|
return Preferences.getLong(context, Constants.ACCOUNT_SIGNED_TIMESTAMP)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAccountType(): AccountType {
|
||||||
|
val rawType = Preferences.getString(context, Constants.ACCOUNT_TYPE)
|
||||||
|
return when (rawType) {
|
||||||
|
"GOOGLE" -> AccountType.GOOGLE
|
||||||
|
else -> AccountType.ANONYMOUS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
Preferences.putBoolean(context, Constants.ACCOUNT_SIGNED_IN, false)
|
||||||
|
Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, "")
|
||||||
|
Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
|
||||||
|
class ApkProvider : FileProvider() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.gplayapi.data.models.AuthData
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import com.aurora.store.util.Preferences
|
||||||
|
import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
|
class AuthProvider private constructor(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<AuthProvider, Context>(::AuthProvider)
|
||||||
|
|
||||||
|
private var gson: Gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun getAuthData(): AuthData {
|
||||||
|
return getSavedAuthData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSavedAuthData(): AuthData {
|
||||||
|
Log.i("Loading saved AuthData")
|
||||||
|
|
||||||
|
val rawAuth: String = Preferences.getString(context, PREFERENCE_AUTH_DATA)
|
||||||
|
return if (rawAuth.isNotEmpty())
|
||||||
|
gson.fromJson(rawAuth, AuthData::class.java)
|
||||||
|
else
|
||||||
|
AuthData("", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 com.google.gson.reflect.TypeToken
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
|
class BlacklistProvider private constructor(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<BlacklistProvider, Context>(::BlacklistProvider) {
|
||||||
|
const val PREFERENCE_BLACKLIST = "PREFERENCE_BLACKLIST"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var gson: Gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
fun getBlackList(): MutableSet<String> {
|
||||||
|
val rawBlacklist = Preferences.getString(context, PREFERENCE_BLACKLIST)
|
||||||
|
return try {
|
||||||
|
if (rawBlacklist.isEmpty())
|
||||||
|
mutableSetOf()
|
||||||
|
else
|
||||||
|
gson.fromJson(rawBlacklist, object : TypeToken<Set<String?>?>() {}.type)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
mutableSetOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isBlacklisted(packageName: String): Boolean {
|
||||||
|
return getBlackList().contains(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blacklist(packageName: String) {
|
||||||
|
val oldBlackList: MutableSet<String> = getBlackList()
|
||||||
|
oldBlackList.add(packageName)
|
||||||
|
save(oldBlackList)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whitelist(packageName: String) {
|
||||||
|
val oldBlackList: MutableSet<String> = getBlackList()
|
||||||
|
oldBlackList.remove(packageName)
|
||||||
|
save(oldBlackList)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blacklist(packageNames: Set<String>) {
|
||||||
|
val oldBlackList: MutableSet<String> = getBlackList()
|
||||||
|
oldBlackList.addAll(packageNames)
|
||||||
|
save(oldBlackList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun save(blacklist: Set<String>) {
|
||||||
|
Preferences.putString(context, PREFERENCE_BLACKLIST, gson.toJson(blacklist))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.opengl.GLES10
|
||||||
|
import android.text.TextUtils
|
||||||
|
import java.util.*
|
||||||
|
import javax.microedition.khronos.egl.EGL10
|
||||||
|
import javax.microedition.khronos.egl.EGLConfig
|
||||||
|
import javax.microedition.khronos.egl.EGLContext
|
||||||
|
import javax.microedition.khronos.egl.EGLDisplay
|
||||||
|
|
||||||
|
object EglExtensionProvider {
|
||||||
|
@JvmStatic
|
||||||
|
val eglExtensions: List<String>
|
||||||
|
get() {
|
||||||
|
val glExtensions: MutableSet<String> = HashSet()
|
||||||
|
val egl10 = EGLContext.getEGL() as EGL10
|
||||||
|
val display = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
||||||
|
egl10.eglInitialize(display, IntArray(2))
|
||||||
|
val cf = IntArray(1)
|
||||||
|
if (egl10.eglGetConfigs(display, null, 0, cf)) {
|
||||||
|
val configs = arrayOfNulls<EGLConfig>(cf[0])
|
||||||
|
if (egl10.eglGetConfigs(display, configs, cf[0], cf)) {
|
||||||
|
val a1 = intArrayOf(
|
||||||
|
EGL10.EGL_WIDTH,
|
||||||
|
EGL10.EGL_PBUFFER_BIT,
|
||||||
|
EGL10.EGL_HEIGHT,
|
||||||
|
EGL10.EGL_PBUFFER_BIT,
|
||||||
|
EGL10.EGL_NONE
|
||||||
|
)
|
||||||
|
val a2 = intArrayOf(12440, EGL10.EGL_PIXMAP_BIT, EGL10.EGL_NONE)
|
||||||
|
val a3 = IntArray(1)
|
||||||
|
for (i in 0 until cf[0]) {
|
||||||
|
egl10.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_CAVEAT, a3)
|
||||||
|
if (a3[0] != EGL10.EGL_SLOW_CONFIG) {
|
||||||
|
egl10.eglGetConfigAttrib(
|
||||||
|
display,
|
||||||
|
configs[i],
|
||||||
|
EGL10.EGL_SURFACE_TYPE,
|
||||||
|
a3
|
||||||
|
)
|
||||||
|
if (1 and a3[0] != 0) {
|
||||||
|
egl10.eglGetConfigAttrib(
|
||||||
|
display,
|
||||||
|
configs[i],
|
||||||
|
EGL10.EGL_RENDERABLE_TYPE,
|
||||||
|
a3
|
||||||
|
)
|
||||||
|
if (1 and a3[0] != 0) {
|
||||||
|
addExtensionsForConfig(
|
||||||
|
egl10,
|
||||||
|
display,
|
||||||
|
configs[i],
|
||||||
|
a1,
|
||||||
|
null,
|
||||||
|
glExtensions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (4 and a3[0] != 0) {
|
||||||
|
addExtensionsForConfig(
|
||||||
|
egl10,
|
||||||
|
display,
|
||||||
|
configs[i],
|
||||||
|
a1,
|
||||||
|
a2,
|
||||||
|
glExtensions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
egl10.eglTerminate(display)
|
||||||
|
val sorted: List<String> = ArrayList(glExtensions)
|
||||||
|
Collections.sort(sorted)
|
||||||
|
return sorted
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addExtensionsForConfig(
|
||||||
|
egl10: EGL10,
|
||||||
|
eglDisplay: EGLDisplay,
|
||||||
|
eglConfig: EGLConfig?,
|
||||||
|
ai: IntArray,
|
||||||
|
ai1: IntArray?,
|
||||||
|
set: MutableSet<String>
|
||||||
|
) {
|
||||||
|
val eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, ai1)
|
||||||
|
if (eglContext === EGL10.EGL_NO_CONTEXT) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val eglSurface = egl10.eglCreatePbufferSurface(eglDisplay, eglConfig, ai)
|
||||||
|
if (eglSurface === EGL10.EGL_NO_SURFACE) {
|
||||||
|
egl10.eglDestroyContext(eglDisplay, eglContext)
|
||||||
|
} else {
|
||||||
|
egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)
|
||||||
|
val s = GLES10.glGetString(7939)
|
||||||
|
if (!TextUtils.isEmpty(s)) {
|
||||||
|
val `as` = s.split(" ".toRegex()).toTypedArray()
|
||||||
|
val i = `as`.size
|
||||||
|
set.addAll(listOf(*`as`).subList(0, i))
|
||||||
|
}
|
||||||
|
egl10.eglMakeCurrent(
|
||||||
|
eglDisplay,
|
||||||
|
EGL10.EGL_NO_SURFACE,
|
||||||
|
EGL10.EGL_NO_SURFACE,
|
||||||
|
EGL10.EGL_NO_CONTEXT
|
||||||
|
)
|
||||||
|
egl10.eglDestroySurface(eglDisplay, eglSurface)
|
||||||
|
egl10.eglDestroyContext(eglDisplay, eglContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
class ExodusDataProvider private constructor(val context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<ExodusDataProvider, Context>(::ExodusDataProvider)
|
||||||
|
|
||||||
|
private val exodusTrackers: JSONObject
|
||||||
|
|
||||||
|
init {
|
||||||
|
exodusTrackers = loadLocalTrackers()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLocalTrackers(): JSONObject {
|
||||||
|
return exodusTrackers
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilteredTrackers(trackerIds: List<Int>): List<JSONObject> {
|
||||||
|
return trackerIds.map {
|
||||||
|
exodusTrackers.getJSONObject(
|
||||||
|
it.toString()
|
||||||
|
)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLocalTrackers(): JSONObject {
|
||||||
|
val inputStream = context.assets.open("exodus_trackers.json")
|
||||||
|
val bytes = ByteArray(inputStream.available())
|
||||||
|
inputStream.read(bytes)
|
||||||
|
inputStream.close()
|
||||||
|
|
||||||
|
val json = String(bytes, StandardCharsets.UTF_8)
|
||||||
|
val jsonArray = JSONArray(json)
|
||||||
|
return jsonArray.getJSONObject(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.TextUtils
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class NativeDeviceInfoProvider(context: Context) : ContextWrapper(context) {
|
||||||
|
|
||||||
|
fun getNativeDeviceProperties(): Properties {
|
||||||
|
return Properties().apply {
|
||||||
|
//Build Props
|
||||||
|
setProperty("UserReadableName", Build.DEVICE)
|
||||||
|
setProperty("Build.HARDWARE", Build.HARDWARE)
|
||||||
|
setProperty(
|
||||||
|
"Build.RADIO",
|
||||||
|
if (Build.getRadioVersion() != null)
|
||||||
|
Build.getRadioVersion()
|
||||||
|
else
|
||||||
|
"unknown"
|
||||||
|
)
|
||||||
|
setProperty("Build.FINGERPRINT", Build.FINGERPRINT)
|
||||||
|
setProperty("Build.BRAND", Build.BRAND)
|
||||||
|
setProperty("Build.DEVICE", Build.DEVICE)
|
||||||
|
setProperty("Build.VERSION.SDK_INT", "${Build.VERSION.SDK_INT}")
|
||||||
|
setProperty("Build.VERSION.RELEASE", Build.VERSION.RELEASE)
|
||||||
|
setProperty("Build.MODEL", Build.MODEL)
|
||||||
|
setProperty("Build.MANUFACTURER", Build.MANUFACTURER)
|
||||||
|
setProperty("Build.PRODUCT", Build.PRODUCT)
|
||||||
|
setProperty("Build.ID", Build.ID)
|
||||||
|
setProperty("Build.BOOTLOADER", Build.BOOTLOADER)
|
||||||
|
|
||||||
|
val config = applicationContext.resources.configuration
|
||||||
|
setProperty("TouchScreen", "${config.touchscreen}")
|
||||||
|
setProperty("Keyboard", "${config.keyboard}")
|
||||||
|
setProperty("Navigation", "${config.navigation}")
|
||||||
|
setProperty("ScreenLayout", "${config.screenLayout and 15}")
|
||||||
|
setProperty("HasHardKeyboard", "${config.keyboard == Configuration.KEYBOARD_QWERTY}")
|
||||||
|
setProperty(
|
||||||
|
"HasFiveWayNavigation",
|
||||||
|
"${config.navigation == Configuration.NAVIGATIONHIDDEN_YES}"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Display Metrics
|
||||||
|
val metrics = applicationContext.resources.displayMetrics
|
||||||
|
setProperty("Screen.Density", "${metrics.densityDpi}")
|
||||||
|
setProperty("Screen.Width", "${metrics.widthPixels}")
|
||||||
|
setProperty("Screen.Height", "${metrics.heightPixels}")
|
||||||
|
|
||||||
|
|
||||||
|
//Supported Platforms
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ","))
|
||||||
|
} else {
|
||||||
|
val platform = mutableListOf<String>()
|
||||||
|
if (!TextUtils.isEmpty(Build.CPU_ABI)) {
|
||||||
|
platform.add(Build.CPU_ABI)
|
||||||
|
}
|
||||||
|
if (!TextUtils.isEmpty(Build.CPU_ABI2)) {
|
||||||
|
platform.add(Build.CPU_ABI2)
|
||||||
|
}
|
||||||
|
|
||||||
|
setProperty("Platforms", platform.joinToString(separator = ","))
|
||||||
|
}
|
||||||
|
//Supported Features
|
||||||
|
setProperty("Features", getFeatures().joinToString(separator = ","))
|
||||||
|
//Shared Locales
|
||||||
|
setProperty("Locales", getLocales().joinToString(separator = ","))
|
||||||
|
//Shared Libraries
|
||||||
|
setProperty("SharedLibraries", getSharedLibraries().joinToString(separator = ","))
|
||||||
|
//GL Extensions
|
||||||
|
val activityManager =
|
||||||
|
applicationContext.getSystemService(ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
setProperty(
|
||||||
|
"GL.Version",
|
||||||
|
activityManager.deviceConfigurationInfo.reqGlEsVersion.toString()
|
||||||
|
)
|
||||||
|
setProperty(
|
||||||
|
"GL.Extensions",
|
||||||
|
EglExtensionProvider.eglExtensions.joinToString(separator = ",")
|
||||||
|
)
|
||||||
|
|
||||||
|
//Google Related Props
|
||||||
|
val gsfVersionProvider = NativeGsfVersionProvider(applicationContext)
|
||||||
|
setProperty("Client", "android-google")
|
||||||
|
setProperty("GSF.version", "${gsfVersionProvider.getGsfVersionCode(true)}")
|
||||||
|
setProperty("Vending.version", "${gsfVersionProvider.getVendingVersionCode(true)}")
|
||||||
|
setProperty("Vending.versionString", gsfVersionProvider.getVendingVersionString(true))
|
||||||
|
|
||||||
|
//MISC
|
||||||
|
setProperty("Roaming", "mobile-notroaming")
|
||||||
|
setProperty("TimeZone", "UTC-10")
|
||||||
|
|
||||||
|
//Telephony (USA 3650 AT&T)
|
||||||
|
setProperty("CellOperator", "310")
|
||||||
|
setProperty("SimOperator", "38")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFeatures(): List<String> {
|
||||||
|
val featureStringList: MutableList<String> = ArrayList()
|
||||||
|
try {
|
||||||
|
val availableFeatures = applicationContext.packageManager.systemAvailableFeatures
|
||||||
|
for (feature in availableFeatures) {
|
||||||
|
if (feature.name.isNotEmpty()) {
|
||||||
|
featureStringList.add(feature.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return featureStringList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLocales(): List<String> {
|
||||||
|
val localeList: MutableList<String> = ArrayList()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
localeList.addAll(listOf(*applicationContext.assets.locales))
|
||||||
|
} else {
|
||||||
|
for (locale in Locale.getAvailableLocales()) {
|
||||||
|
localeList.add(locale.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val locales: MutableList<String> = ArrayList()
|
||||||
|
for (locale in localeList) {
|
||||||
|
if (TextUtils.isEmpty(locale)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
locales.add(locale.replace("-", "_"))
|
||||||
|
}
|
||||||
|
return locales
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSharedLibraries(): List<String> {
|
||||||
|
val systemSharedLibraryNames = applicationContext.packageManager.systemSharedLibraryNames
|
||||||
|
val libraries: MutableList<String> = ArrayList()
|
||||||
|
try {
|
||||||
|
if (systemSharedLibraryNames != null) {
|
||||||
|
libraries.addAll(listOf(*systemSharedLibraryNames))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return libraries
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
|
||||||
|
class NativeGsfVersionProvider(context: Context) {
|
||||||
|
private var gsfVersionCode = 0
|
||||||
|
private var vendingVersionCode = 0
|
||||||
|
private var vendingVersionString = ""
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
gsfVersionCode =
|
||||||
|
context.packageManager.getPackageInfo(GOOGLE_SERVICES_PACKAGE_ID, 0).versionCode
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
// com.google.android.gms not found
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val packageInfo = context.packageManager.getPackageInfo(GOOGLE_VENDING_PACKAGE_ID, 0)
|
||||||
|
vendingVersionCode = packageInfo.versionCode
|
||||||
|
vendingVersionString = packageInfo.versionName
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
// com.android.vending not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
gsfVersionCode =
|
||||||
|
context.packageManager.getPackageInfo(GOOGLE_SERVICES_PACKAGE_ID, 0).versionCode
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
// com.google.android.gms not found
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val packageInfo = context.packageManager.getPackageInfo(GOOGLE_VENDING_PACKAGE_ID, 0)
|
||||||
|
vendingVersionCode = packageInfo.versionCode
|
||||||
|
vendingVersionString = packageInfo.versionName
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
// com.android.vending not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGsfVersionCode(defaultIfNotFound: Boolean): Int {
|
||||||
|
return if (defaultIfNotFound && gsfVersionCode < GOOGLE_SERVICES_VERSION_CODE)
|
||||||
|
GOOGLE_SERVICES_VERSION_CODE
|
||||||
|
else
|
||||||
|
gsfVersionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVendingVersionCode(defaultIfNotFound: Boolean): Int {
|
||||||
|
return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE)
|
||||||
|
GOOGLE_VENDING_VERSION_CODE
|
||||||
|
else
|
||||||
|
vendingVersionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVendingVersionString(defaultIfNotFound: Boolean): String {
|
||||||
|
return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE)
|
||||||
|
GOOGLE_VENDING_VERSION_STRING
|
||||||
|
else
|
||||||
|
vendingVersionString
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val GOOGLE_SERVICES_PACKAGE_ID = "com.google.android.gms"
|
||||||
|
private const val GOOGLE_VENDING_PACKAGE_ID = "com.android.vending"
|
||||||
|
private const val GOOGLE_SERVICES_VERSION_CODE = 203019037
|
||||||
|
private const val GOOGLE_VENDING_VERSION_CODE = 82151710
|
||||||
|
private const val GOOGLE_VENDING_VERSION_STRING = "21.5.17-21 [0] [PR] 326734551"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import com.novoda.merlin.Merlin
|
||||||
|
|
||||||
|
class NetworkProvider(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<NetworkProvider, Context>(::NetworkProvider) {
|
||||||
|
|
||||||
|
private var networkListeners: MutableList<NetworkListener> = mutableListOf()
|
||||||
|
|
||||||
|
fun addListener(networkListener: NetworkListener) {
|
||||||
|
Log.i("Network-Provider added to ${networkListener.javaClass.simpleName}")
|
||||||
|
networkListeners.add(networkListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeListener(networkListener: NetworkListener) {
|
||||||
|
Log.i("Network-Provider removed from ${networkListener.javaClass.simpleName}")
|
||||||
|
networkListeners.remove(networkListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var merlin: Merlin = Merlin.Builder()
|
||||||
|
.withAllCallbacks()
|
||||||
|
.build(context)
|
||||||
|
|
||||||
|
private var isDisconnected = true
|
||||||
|
|
||||||
|
fun bind() {
|
||||||
|
merlin.bind()
|
||||||
|
|
||||||
|
merlin.registerConnectable {
|
||||||
|
if (isDisconnected) {
|
||||||
|
isDisconnected = false
|
||||||
|
onReConnected()
|
||||||
|
} else {
|
||||||
|
onConnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merlin.registerDisconnectable {
|
||||||
|
isDisconnected = true
|
||||||
|
onDisconnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unbind() {
|
||||||
|
networkListeners.clear()
|
||||||
|
merlin.unbind()
|
||||||
|
Log.i("Network-Provider destroyed")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onConnected() {
|
||||||
|
Log.i("Network-Provider connected")
|
||||||
|
isDisconnected = false
|
||||||
|
networkListeners.forEach {
|
||||||
|
it.onConnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onReConnected() {
|
||||||
|
Log.i("Network-Provider reconnected")
|
||||||
|
networkListeners.forEach {
|
||||||
|
it.onReconnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDisconnected() {
|
||||||
|
Log.e("Network-Provider disconnected")
|
||||||
|
networkListeners.forEach {
|
||||||
|
it.onDisconnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NetworkListener {
|
||||||
|
fun onConnected()
|
||||||
|
fun onDisconnected()
|
||||||
|
fun onReconnected()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.providers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.data.SingletonHolder
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import com.aurora.store.util.PathUtil
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
import java.util.jar.JarEntry
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
|
class SpoofDeviceProvider private constructor(var context: Context) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<SpoofDeviceProvider, Context>(::SpoofDeviceProvider) {
|
||||||
|
private const val SUFFIX = ".properties"
|
||||||
|
|
||||||
|
fun filenameValid(filename: String): Boolean {
|
||||||
|
return filename.endsWith(SUFFIX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val availableDevice: List<Properties>
|
||||||
|
get() {
|
||||||
|
val propertiesList: MutableList<Properties> = ArrayList()
|
||||||
|
propertiesList.add(0, NativeDeviceInfoProvider(context).getNativeDeviceProperties())
|
||||||
|
propertiesList.addAll(spoofDevicesFromApk)
|
||||||
|
propertiesList.addAll(spoofDevicesFromUser)
|
||||||
|
return propertiesList
|
||||||
|
}
|
||||||
|
|
||||||
|
private val spoofDevicesFromApk: List<Properties>
|
||||||
|
get() {
|
||||||
|
val jarFile = apkAsJar
|
||||||
|
val propertiesList: MutableList<Properties> = ArrayList()
|
||||||
|
if (null == jarFile) {
|
||||||
|
return propertiesList
|
||||||
|
}
|
||||||
|
val entries = jarFile.entries()
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
val entry = entries.nextElement()
|
||||||
|
if (!filenameValid(entry.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
propertiesList.add(getProperties(jarFile, entry))
|
||||||
|
}
|
||||||
|
return propertiesList
|
||||||
|
}
|
||||||
|
|
||||||
|
private val spoofDevicesFromUser: List<Properties>
|
||||||
|
get() {
|
||||||
|
val deviceNames: MutableList<Properties> = ArrayList()
|
||||||
|
val defaultDir = File(PathUtil.getExternalPath())
|
||||||
|
val files = defaultDir.listFiles()
|
||||||
|
if (defaultDir.exists() && files != null) {
|
||||||
|
for (file in files) {
|
||||||
|
if (!file.isFile || !filenameValid(file.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deviceNames.add(getProperties(file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deviceNames
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProperties(jarFile: JarFile, entry: JarEntry): Properties {
|
||||||
|
val properties = Properties()
|
||||||
|
try {
|
||||||
|
properties.load(jarFile.getInputStream(entry))
|
||||||
|
properties.setProperty("CONFIG_NAME", entry.name)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e("Could not read %s", entry.name)
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProperties(file: File): Properties {
|
||||||
|
val properties = Properties()
|
||||||
|
try {
|
||||||
|
properties.load(BufferedInputStream(FileInputStream(file)))
|
||||||
|
properties.setProperty("CONFIG_NAME", file.name)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e("Could not read %s", file.name)
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
private val devicesFromApk: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val deviceNames: MutableMap<String, String> = HashMap()
|
||||||
|
val jarFile = apkAsJar ?: return deviceNames
|
||||||
|
|
||||||
|
val entries = jarFile.entries()
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
val entry = entries.nextElement()
|
||||||
|
if (!filenameValid(entry.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceNames[entry.name] =
|
||||||
|
getProperties(jarFile, entry).getProperty("UserReadableName")
|
||||||
|
}
|
||||||
|
return deviceNames
|
||||||
|
}
|
||||||
|
|
||||||
|
private val apkAsJar: JarFile?
|
||||||
|
get() {
|
||||||
|
val file = apkFile
|
||||||
|
try {
|
||||||
|
if (file != null && file.exists()) {
|
||||||
|
return JarFile(file)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e("Could not open Aurora Store apk as a jar file")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val apkFile: File?
|
||||||
|
get() {
|
||||||
|
try {
|
||||||
|
val sourceDir: String = context.packageManager.getApplicationInfo(
|
||||||
|
BuildConfig.APPLICATION_ID,
|
||||||
|
0
|
||||||
|
).sourceDir
|
||||||
|
|
||||||
|
if (sourceDir.isNotEmpty()) {
|
||||||
|
return File(sourceDir)
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val devicesFromDownloadDirectory: Map<String, String>
|
||||||
|
get() {
|
||||||
|
val deviceNames: MutableMap<String, String> = HashMap()
|
||||||
|
val defaultDir = File(PathUtil.getExternalPath())
|
||||||
|
if (!defaultDir.exists() || null == defaultDir.listFiles()) {
|
||||||
|
return deviceNames
|
||||||
|
}
|
||||||
|
for (file in defaultDir.listFiles()) {
|
||||||
|
if (!file.isFile || !filenameValid(file.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val name = getProperties(file).getProperty("UserReadableName")
|
||||||
|
if (name != null) {
|
||||||
|
deviceNames[file.name] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deviceNames
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
companion object : SingletonHolder<SpoofProvider, Context>(::SpoofProvider) {
|
||||||
|
const val LOCALE_SPOOF_ENABLED = "LOCALE_SPOOF_ENABLED"
|
||||||
|
const val LOCALE_SPOOF_LANG = "LOCALE_SPOOF_LANG"
|
||||||
|
const val LOCALE_SPOOF_COUNTRY = "LOCALE_SPOOF_COUNTRY"
|
||||||
|
|
||||||
|
const val DEVICE_SPOOF_ENABLED = "DEVICE_SPOOF_ENABLED"
|
||||||
|
const val DEVICE_SPOOF_PROPERTIES = "DEVICE_SPOOF_PROPERTIES"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isLocaleSpoofEnabled(): Boolean {
|
||||||
|
return Preferences.getBoolean(context, LOCALE_SPOOF_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isDeviceSpoofEnabled(): Boolean {
|
||||||
|
return Preferences.getBoolean(context, DEVICE_SPOOF_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSpoofLocale(): Locale {
|
||||||
|
return if (isLocaleSpoofEnabled()) {
|
||||||
|
Locale(
|
||||||
|
Preferences.getString(context, LOCALE_SPOOF_LANG),
|
||||||
|
Preferences.getString(context, LOCALE_SPOOF_COUNTRY)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Locale.getDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSpoofDeviceProperties(): Properties {
|
||||||
|
return if (isDeviceSpoofEnabled()) {
|
||||||
|
val gson: Gson =
|
||||||
|
GsonBuilder().excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
return gson.fromJson(
|
||||||
|
Preferences.getString(context, DEVICE_SPOOF_PROPERTIES),
|
||||||
|
Properties::class.java
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Properties()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSpoofLocale(locale: Locale) {
|
||||||
|
Preferences.putBoolean(context, LOCALE_SPOOF_ENABLED, true)
|
||||||
|
Preferences.putString(context, LOCALE_SPOOF_LANG, locale.language)
|
||||||
|
Preferences.putString(context, LOCALE_SPOOF_COUNTRY, locale.country)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSpoofDeviceProperties(properties: Properties) {
|
||||||
|
val gson: Gson =
|
||||||
|
GsonBuilder().excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
Preferences.putBoolean(context, DEVICE_SPOOF_ENABLED, true)
|
||||||
|
Preferences.putString(context, DEVICE_SPOOF_PROPERTIES, gson.toJson(properties))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
class BootReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.aurora.Constants.FETCH_GROUP_ID
|
||||||
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
|
||||||
|
class DownloadCancelReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val extras = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
val groupId = extras.getInt(FETCH_GROUP_ID, -1)
|
||||||
|
DownloadManager
|
||||||
|
.with(context)
|
||||||
|
.getFetchInstance()
|
||||||
|
.cancelGroup(groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.aurora.Constants.FETCH_GROUP_ID
|
||||||
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
|
||||||
|
class DownloadPauseReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val extras = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
val groupId: Int = extras.getInt(FETCH_GROUP_ID, -1)
|
||||||
|
DownloadManager
|
||||||
|
.with(context)
|
||||||
|
.getFetchInstance()
|
||||||
|
.pauseGroup(groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.aurora.Constants.FETCH_GROUP_ID
|
||||||
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
|
||||||
|
class DownloadResumeReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val extras = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
val groupId: Int = extras.getInt(FETCH_GROUP_ID, -1)
|
||||||
|
DownloadManager
|
||||||
|
.with(context)
|
||||||
|
.getFetchInstance()
|
||||||
|
.resumeGroup(groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
class InstallReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val extras = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
/*val packageName = extras.getString(Constants.INTENT_PACKAGE_NAME, "")
|
||||||
|
val versionString = extras.getString(Constants.DOWNLOAD_VERSION_CODE)
|
||||||
|
if (!packageName.isEmpty() && versionString != null) {
|
||||||
|
AuroraApplication.getInstaller().installSplit(packageName, versionString.toInt())
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.aurora.store.data.event.BusEvent.InstallEvent
|
||||||
|
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 org.greenrobot.eventbus.EventBus
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
open class PackageManagerReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action != null && intent.data != null) {
|
||||||
|
val packageName = intent.data!!.encodedSchemeSpecificPart
|
||||||
|
|
||||||
|
when (intent.action) {
|
||||||
|
Intent.ACTION_PACKAGE_ADDED -> {
|
||||||
|
EventBus.getDefault()
|
||||||
|
.post(InstallEvent(packageName, ""))
|
||||||
|
|
||||||
|
//Clear installation queue
|
||||||
|
AppInstaller.with(context)
|
||||||
|
.getPreferredInstaller()
|
||||||
|
.removeFromInstallQueue(packageName)
|
||||||
|
}
|
||||||
|
Intent.ACTION_PACKAGE_REMOVED -> EventBus.getDefault()
|
||||||
|
.post(UninstallEvent(packageName, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
clearNotification(context, packageName)
|
||||||
|
|
||||||
|
val isAutoDeleteAPKEnabled = Preferences.getBoolean(
|
||||||
|
context,
|
||||||
|
Preferences.PREFERENCE_AUTO_DELETE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isAutoDeleteAPKEnabled)
|
||||||
|
clearDownloads(context, packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearNotification(context: Context, packageName: String) {
|
||||||
|
val notificationManager = context.applicationContext
|
||||||
|
.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.cancel(packageName, packageName.hashCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearDownloads(context: Context, packageName: String) {
|
||||||
|
try {
|
||||||
|
val rootDirPath = PathUtil.getPackageDirectory(context, packageName)
|
||||||
|
val rootDir = File(rootDirPath)
|
||||||
|
if (rootDir.exists())
|
||||||
|
rootDir.deleteRecursively()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.receiver
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
class UpdatesReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.data.service
|
||||||
|
|
||||||
|
import android.app.*
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.ArrayMap
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
|
import com.aurora.store.data.receiver.DownloadCancelReceiver
|
||||||
|
import com.aurora.store.data.receiver.DownloadPauseReceiver
|
||||||
|
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 java.lang.reflect.Modifier
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class NotificationService : Service() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun startService(context: Context) {
|
||||||
|
try {
|
||||||
|
context.startService(Intent(context, NotificationService::class.java))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Failed to start notification service : %s", e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var fetch: Fetch
|
||||||
|
private lateinit var fetchListener: AbstractFetchGroupListener
|
||||||
|
private lateinit var notificationManager: NotificationManager
|
||||||
|
|
||||||
|
private val appMap = ArrayMap<Int, App>()
|
||||||
|
private val gson: Gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
|
return START_NOT_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
Log.i("Notification Service Started")
|
||||||
|
|
||||||
|
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
|
//Create Notification Channels : General & Alert
|
||||||
|
createNotificationChannel()
|
||||||
|
|
||||||
|
fetch = DownloadManager.with(this).fetch
|
||||||
|
|
||||||
|
fetchListener = object : AbstractFetchGroupListener() {
|
||||||
|
override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
||||||
|
showNotification(groupId, download, fetchGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
groupId: Int,
|
||||||
|
download: Download,
|
||||||
|
error: Error,
|
||||||
|
throwable: Throwable?,
|
||||||
|
fetchGroup: FetchGroup
|
||||||
|
) {
|
||||||
|
showNotification(groupId, download, fetchGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProgress(
|
||||||
|
groupId: Int,
|
||||||
|
download: Download,
|
||||||
|
etaInMilliSeconds: Long,
|
||||||
|
downloadedBytesPerSecond: Long,
|
||||||
|
fetchGroup: FetchGroup
|
||||||
|
) {
|
||||||
|
showNotification(groupId, download, fetchGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueued(
|
||||||
|
groupId: Int,
|
||||||
|
download: Download,
|
||||||
|
waitingNetwork: Boolean,
|
||||||
|
fetchGroup: FetchGroup
|
||||||
|
) {
|
||||||
|
showNotification(groupId, download, fetchGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
||||||
|
showNotification(groupId, download, fetchGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch.addListener(fetchListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val channels = ArrayList<NotificationChannel>()
|
||||||
|
channels.add(
|
||||||
|
NotificationChannel(
|
||||||
|
Constants.NOTIFICATION_CHANNEL_ALERT,
|
||||||
|
getString(R.string.notification_channel_alert),
|
||||||
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
|
)
|
||||||
|
)
|
||||||
|
channels.add(
|
||||||
|
NotificationChannel(
|
||||||
|
Constants.NOTIFICATION_CHANNEL_GENERAL,
|
||||||
|
getString(R.string.notification_channel_general),
|
||||||
|
NotificationManager.IMPORTANCE_MIN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
notificationManager.createNotificationChannels(channels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showNotification(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
||||||
|
val status = download.status
|
||||||
|
|
||||||
|
//Ignore notifications for completion of sub-parts of a bundled apk
|
||||||
|
if (status == Status.COMPLETED && fetchGroup.groupDownloadProgress < 100)
|
||||||
|
return
|
||||||
|
|
||||||
|
//synchronized(appMap) {
|
||||||
|
var app: App? = appMap[groupId]
|
||||||
|
|
||||||
|
if (app == null) {
|
||||||
|
app = gson.fromJson(
|
||||||
|
download.extras.getString(Constants.STRING_EXTRA, "{}"),
|
||||||
|
App::class.java
|
||||||
|
)
|
||||||
|
appMap[groupId] = app
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
val builder = NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_GENERAL)
|
||||||
|
builder.setContentTitle(app.displayName)
|
||||||
|
builder.setSmallIcon(R.drawable.ic_notification_outlined)
|
||||||
|
builder.color = ContextCompat.getColor(this, R.color.colorAccent)
|
||||||
|
builder.setWhen(download.created)
|
||||||
|
builder.setContentIntent(getContentIntentForDownloads())
|
||||||
|
|
||||||
|
when (status) {
|
||||||
|
Status.PAUSED -> {
|
||||||
|
builder.setSmallIcon(R.drawable.ic_download_pause)
|
||||||
|
builder.setContentText(getString(R.string.download_paused))
|
||||||
|
}
|
||||||
|
Status.CANCELLED -> {
|
||||||
|
builder.setSmallIcon(R.drawable.ic_download_cancel)
|
||||||
|
builder.setContentText(getString(R.string.download_canceled))
|
||||||
|
builder.color = Color.RED
|
||||||
|
}
|
||||||
|
Status.FAILED -> {
|
||||||
|
builder.setSmallIcon(R.drawable.ic_download_fail)
|
||||||
|
builder.setContentText(getString(R.string.download_failed))
|
||||||
|
builder.color = Color.RED
|
||||||
|
}
|
||||||
|
Status.COMPLETED -> if (fetchGroup.groupDownloadProgress == 100) {
|
||||||
|
builder.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
builder.setContentText(getString(R.string.download_completed))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
builder.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
builder.setContentText(getString(R.string.download_metadata))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val progress = fetchGroup.groupDownloadProgress
|
||||||
|
val progressBigText = NotificationCompat.BigTextStyle()
|
||||||
|
when (status) {
|
||||||
|
|
||||||
|
Status.QUEUED -> {
|
||||||
|
builder.setProgress(100, 0, true)
|
||||||
|
progressBigText.bigText(getString(R.string.download_queued))
|
||||||
|
builder.setStyle(progressBigText)
|
||||||
|
}
|
||||||
|
|
||||||
|
Status.DOWNLOADING -> {
|
||||||
|
val contentString = getString(R.string.download_progress)
|
||||||
|
val partString = StringUtils.joinWith(
|
||||||
|
"/",
|
||||||
|
fetchGroup.completedDownloads.size + 1,
|
||||||
|
fetchGroup.downloads.size
|
||||||
|
)
|
||||||
|
val speedString: String =
|
||||||
|
CommonUtil.humanReadableByteSpeed(download.downloadedBytesPerSecond, true)
|
||||||
|
progressBigText.bigText(
|
||||||
|
StringUtils.joinWith(
|
||||||
|
" \u2022 ",
|
||||||
|
contentString,
|
||||||
|
partString,
|
||||||
|
speedString
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builder.setStyle(progressBigText)
|
||||||
|
builder.addAction(
|
||||||
|
NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.ic_download_pause,
|
||||||
|
getString(R.string.action_pause),
|
||||||
|
getPauseIntent(groupId)
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
builder.addAction(
|
||||||
|
NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.ic_download_cancel,
|
||||||
|
getString(R.string.action_cancel),
|
||||||
|
getCancelIntent(groupId)
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
if (progress < 0) builder.setProgress(
|
||||||
|
100,
|
||||||
|
0,
|
||||||
|
true
|
||||||
|
) else builder.setProgress(100, progress, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Status.PAUSED -> {
|
||||||
|
val pauseString = getString(R.string.download_paused)
|
||||||
|
val filesString = StringUtils.joinWith(
|
||||||
|
"/",
|
||||||
|
fetchGroup.completedDownloads.size,
|
||||||
|
fetchGroup.downloads.size
|
||||||
|
)
|
||||||
|
progressBigText.bigText(
|
||||||
|
StringUtils.joinWith(
|
||||||
|
" \u2022 ",
|
||||||
|
pauseString,
|
||||||
|
filesString
|
||||||
|
)
|
||||||
|
)
|
||||||
|
builder.setStyle(progressBigText)
|
||||||
|
builder.addAction(
|
||||||
|
NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.ic_download_pause,
|
||||||
|
getString(R.string.action_resume),
|
||||||
|
getResumeIntent(groupId)
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Status.COMPLETED -> if (fetchGroup.groupDownloadProgress == 100) {
|
||||||
|
builder.setAutoCancel(true)
|
||||||
|
builder.setContentIntent(getContentIntentForDetails(app))
|
||||||
|
builder.setStyle(progressBigText)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLAndAbove()) {
|
||||||
|
when (status) {
|
||||||
|
Status.DOWNLOADING -> builder.setCategory(Notification.CATEGORY_PROGRESS)
|
||||||
|
Status.FAILED, Status.CANCELLED -> builder.setCategory(Notification.CATEGORY_ERROR)
|
||||||
|
else -> builder.setCategory(Notification.CATEGORY_STATUS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager.notify(
|
||||||
|
app.packageName,
|
||||||
|
app.id,
|
||||||
|
builder.build()
|
||||||
|
)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPauseIntent(groupId: Int): PendingIntent {
|
||||||
|
val intent = Intent(this, DownloadPauseReceiver::class.java)
|
||||||
|
intent.putExtra(Constants.FETCH_GROUP_ID, groupId)
|
||||||
|
return PendingIntent.getBroadcast(this, groupId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getResumeIntent(groupId: Int): PendingIntent {
|
||||||
|
val intent = Intent(this, DownloadResumeReceiver::class.java)
|
||||||
|
intent.putExtra(Constants.FETCH_GROUP_ID, groupId)
|
||||||
|
return PendingIntent.getBroadcast(this, groupId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCancelIntent(groupId: Int): PendingIntent {
|
||||||
|
val intent = Intent(this, DownloadCancelReceiver::class.java)
|
||||||
|
intent.putExtra(Constants.FETCH_GROUP_ID, groupId)
|
||||||
|
return PendingIntent.getBroadcast(this, groupId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContentIntentForDetails(app: App?): PendingIntent {
|
||||||
|
val intent = Intent(this, AppDetailsActivity::class.java)
|
||||||
|
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
packageName.hashCode(),
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContentIntentForDownloads(): PendingIntent {
|
||||||
|
val intent = Intent(this, DownloadActivity::class.java)
|
||||||
|
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInstallIntent(packageName: String, versionCode: String): PendingIntent {
|
||||||
|
val intent = Intent(this, InstallReceiver::class.java)
|
||||||
|
intent.putExtra(Constants.STRING_EXTRA, packageName)
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
this,
|
||||||
|
packageName.hashCode(),
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun install(packageName: String, files: List<Download>) {
|
||||||
|
AppInstaller.with(this)
|
||||||
|
.getPreferredInstaller()
|
||||||
|
.install(
|
||||||
|
packageName,
|
||||||
|
files
|
||||||
|
.filter { it.file.endsWith(".apk") }
|
||||||
|
.map {
|
||||||
|
it.file
|
||||||
|
}.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
Log.i("Notification Service Stopped")
|
||||||
|
fetch.removeListener(fetchListener)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/src/main/java/com/aurora/store/util/AC2DMTask.kt
Normal file
66
app/src/main/java/com/aurora/store/util/AC2DMTask.kt
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import com.github.kittinunf.fuel.Fuel
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class AC2DMTask {
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getAC2DMResponse(email: String?, oAuthToken: String?): Map<String, String> {
|
||||||
|
if (email == null || oAuthToken == null)
|
||||||
|
return mapOf()
|
||||||
|
|
||||||
|
val params: MutableMap<String, Any> = hashMapOf()
|
||||||
|
params["lang"] = Locale.getDefault().toString().replace("_", "-")
|
||||||
|
params["google_play_services_version"] = PLAY_SERVICES_VERSION_CODE
|
||||||
|
params["sdk_version"] = BUILD_VERSION_SDK
|
||||||
|
params["device_country"] = Locale.getDefault().country.toLowerCase(Locale.US)
|
||||||
|
params["Email"] = email
|
||||||
|
params["service"] = "ac2dm"
|
||||||
|
params["get_accountid"] = 1
|
||||||
|
params["ACCESS_TOKEN"] = 1
|
||||||
|
params["callerPkg"] = "com.google.android.gms"
|
||||||
|
params["add_account"] = 1
|
||||||
|
params["Token"] = oAuthToken
|
||||||
|
params["callerSig"] = "38918a453d07199354f8b19af05ec6562ced5788"
|
||||||
|
|
||||||
|
val body = params.map { "${it.key}=${it.value}" }.joinToString(separator = "&")
|
||||||
|
|
||||||
|
val response = Fuel.post(TOKEN_AUTH_URL)
|
||||||
|
.body(body)
|
||||||
|
.header("app" to "com.google.android.gms")
|
||||||
|
.header("User-Agent" to "")
|
||||||
|
.header("Content-Type" to "application/x-www-form-urlencoded")
|
||||||
|
.response()
|
||||||
|
|
||||||
|
return response.third.fold(success = {
|
||||||
|
Util.parseResponse(String(it))
|
||||||
|
}, failure = {
|
||||||
|
mapOf()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TOKEN_AUTH_URL = "https://android.clients.google.com/auth"
|
||||||
|
private const val BUILD_VERSION_SDK = 28
|
||||||
|
private const val PLAY_SERVICES_VERSION_CODE = 19629032
|
||||||
|
}
|
||||||
|
}
|
||||||
122
app/src/main/java/com/aurora/store/util/ApkCopier.kt
Normal file
122
app/src/main/java/com/aurora/store/util/ApkCopier.kt
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 org.apache.commons.io.IOUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.*
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
|
class ApkCopier(private val context: Context, private val packageName: String) {
|
||||||
|
|
||||||
|
fun copy() {
|
||||||
|
val destination = File(PathUtil.getBaseCopyDirectory())
|
||||||
|
|
||||||
|
destination.let {
|
||||||
|
if (it.exists()) {
|
||||||
|
Log.i("Base copy directory is available")
|
||||||
|
} else {
|
||||||
|
it.mkdirs()
|
||||||
|
Log.e("Base copy directory is created : ${it.path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val packageInfo: PackageInfo = context.packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.GET_META_DATA
|
||||||
|
)
|
||||||
|
|
||||||
|
val baseApk = getBaseApk(packageInfo)
|
||||||
|
val fileList: MutableList<File?> = mutableListOf()
|
||||||
|
|
||||||
|
/*Add base APK*/
|
||||||
|
fileList.add(baseApk)
|
||||||
|
|
||||||
|
if (isLAndAbove()) {
|
||||||
|
val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs
|
||||||
|
if (splitSourceDirs != null && splitSourceDirs.isNotEmpty()) {
|
||||||
|
/*Add Split APKs*/
|
||||||
|
fileList.addAll(getSplitAPKs(packageInfo))
|
||||||
|
}
|
||||||
|
bundleAllAPKs(fileList)
|
||||||
|
} else {
|
||||||
|
bundleAllAPKs(fileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBaseApk(packageInfo: PackageInfo?): File? {
|
||||||
|
return if (packageInfo?.applicationInfo != null) {
|
||||||
|
File(packageInfo.applicationInfo.sourceDir)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
private fun getSplitAPKs(packageInfo: PackageInfo): MutableList<File> {
|
||||||
|
val fileList: MutableList<File> = ArrayList()
|
||||||
|
val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs
|
||||||
|
if (splitSourceDirs != null) {
|
||||||
|
for (fileName in splitSourceDirs) fileList.add(File(fileName))
|
||||||
|
}
|
||||||
|
return fileList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bundleAllAPKs(fileList: List<File?>) {
|
||||||
|
try {
|
||||||
|
val fileOutputStream =
|
||||||
|
FileOutputStream(PathUtil.getBaseCopyDirectory() + "$packageName.zip")
|
||||||
|
val zipOutputStream = ZipOutputStream(fileOutputStream)
|
||||||
|
|
||||||
|
for (file in fileList) {
|
||||||
|
file?.let {
|
||||||
|
val zipEntry = ZipEntry(file.name)
|
||||||
|
zipOutputStream.putNextEntry(zipEntry)
|
||||||
|
IOUtils.copy(it.inputStream(), zipOutputStream)
|
||||||
|
zipOutputStream.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zipOutputStream.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
Log.e("ApkCopier : %s", e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUri(file: File): Uri {
|
||||||
|
return FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileProvider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
100
app/src/main/java/com/aurora/store/util/CertUtil.kt
Normal file
100
app/src/main/java/com/aurora/store/util/CertUtil.kt
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import com.aurora.store.util.extensions.isPAndAbove
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object CertUtil {
|
||||||
|
private const val FDROID = "FDROID"
|
||||||
|
private const val GUARDIAN = "GUARDIANPROJECT.INFO"
|
||||||
|
|
||||||
|
private fun getX509Certificates(
|
||||||
|
context: Context,
|
||||||
|
packageName: String
|
||||||
|
): List<X509Certificate?> {
|
||||||
|
val certificates: MutableList<X509Certificate?> = mutableListOf()
|
||||||
|
val packageManager = context.applicationContext.packageManager
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
val packageInfo = if (isPAndAbove())
|
||||||
|
packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.GET_SIGNING_CERTIFICATES
|
||||||
|
)
|
||||||
|
else
|
||||||
|
packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.GET_SIGNATURES
|
||||||
|
)
|
||||||
|
|
||||||
|
val certificateFactory = CertificateFactory.getInstance("X509")
|
||||||
|
|
||||||
|
if (isPAndAbove()) {
|
||||||
|
packageInfo.signingInfo.apkContentsSigners.forEach {
|
||||||
|
val bytes = it.toByteArray()
|
||||||
|
val inputStream: InputStream = ByteArrayInputStream(bytes)
|
||||||
|
certificates.add(
|
||||||
|
certificateFactory!!.generateCertificate(inputStream) as X509Certificate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in 0..packageInfo.signatures.size) {
|
||||||
|
val bytes = packageInfo.signatures[i].toByteArray()
|
||||||
|
val inStream: InputStream = ByteArrayInputStream(bytes)
|
||||||
|
certificates.add(
|
||||||
|
certificateFactory!!.generateCertificate(inStream) as X509Certificate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isFDroidApp(context: Context, packageName: String): Boolean {
|
||||||
|
val certificates = getX509Certificates(context, packageName)
|
||||||
|
|
||||||
|
return if (certificates.isEmpty())
|
||||||
|
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
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
189
app/src/main/java/com/aurora/store/util/CommonUtil.kt
Normal file
189
app/src/main/java/com/aurora/store/util/CommonUtil.kt
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.*
|
||||||
|
import kotlin.math.ln
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
object CommonUtil {
|
||||||
|
|
||||||
|
private val siPrefixes: Map<Int, String> = hashMapOf(
|
||||||
|
Pair(1, ""),
|
||||||
|
Pair(3, " KB"),
|
||||||
|
Pair(6, " MB"),
|
||||||
|
Pair(9, " GB")
|
||||||
|
)
|
||||||
|
private val diPrefixes: Map<Int, String> = hashMapOf(
|
||||||
|
Pair(1, ""),
|
||||||
|
Pair(3, " K"),
|
||||||
|
Pair(6, " Million"),
|
||||||
|
Pair(9, " Billion")
|
||||||
|
)
|
||||||
|
|
||||||
|
fun addSiPrefix(value: Long): String {
|
||||||
|
if (value <= 0L)
|
||||||
|
return "NA"
|
||||||
|
var tempValue = value
|
||||||
|
var order = 0
|
||||||
|
while (tempValue >= 1000.0) {
|
||||||
|
tempValue /= 1000.toLong()
|
||||||
|
order += 3
|
||||||
|
}
|
||||||
|
return tempValue.toString() + siPrefixes[order]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDiPrefix(value: Long): String {
|
||||||
|
if (value <= 0L)
|
||||||
|
return "NA"
|
||||||
|
var tempValue = value
|
||||||
|
var order = 0
|
||||||
|
while (tempValue >= 1000.0) {
|
||||||
|
tempValue /= 1000.0.toLong()
|
||||||
|
order += 3
|
||||||
|
}
|
||||||
|
return tempValue.toString() + diPrefixes[order]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getETAString(context: Context, etaInMilliSeconds: Long): String {
|
||||||
|
if (etaInMilliSeconds < 0) {
|
||||||
|
return context.getString(R.string.download_eta_calculating)
|
||||||
|
}
|
||||||
|
var seconds = (etaInMilliSeconds / 1000).toInt()
|
||||||
|
val hours = (seconds / 3600).toLong()
|
||||||
|
seconds -= (hours * 3600).toInt()
|
||||||
|
val minutes = (seconds / 60).toLong()
|
||||||
|
seconds -= (minutes * 60).toInt()
|
||||||
|
return when {
|
||||||
|
hours > 0 -> {
|
||||||
|
context.getString(R.string.download_eta_hrs, hours, minutes, seconds)
|
||||||
|
}
|
||||||
|
minutes > 0 -> {
|
||||||
|
context.getString(R.string.download_eta_min, minutes, seconds)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
context.getString(R.string.download_eta_sec, seconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun humanReadableByteSpeed(bytes: Long, si: Boolean): String {
|
||||||
|
val unit = if (si) 1000 else 1024
|
||||||
|
if (bytes < unit) return "$bytes B"
|
||||||
|
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||||
|
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1].toString() + if (si) "" else "i"
|
||||||
|
return String.format(
|
||||||
|
Locale.getDefault(), "%.1f %sB/s",
|
||||||
|
bytes / unit.toDouble().pow(exp.toDouble()),
|
||||||
|
pre
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun humanReadableByteValue(bytes: Long, si: Boolean): String {
|
||||||
|
val unit = if (si) 1000 else 1024
|
||||||
|
if (bytes < unit) return "$bytes B"
|
||||||
|
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||||
|
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1].toString() + if (si) "" else "i"
|
||||||
|
return String.format(
|
||||||
|
Locale.getDefault(), "%.1f %sB",
|
||||||
|
bytes / unit.toDouble().pow(exp.toDouble()),
|
||||||
|
pre
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDownloadSpeedString(context: Context, downloadedBytesPerSecond: Long): String {
|
||||||
|
if (downloadedBytesPerSecond < 0) {
|
||||||
|
return context.getString(R.string.download_speed_estimating)
|
||||||
|
}
|
||||||
|
val kb = downloadedBytesPerSecond.toDouble() / 1000.toDouble()
|
||||||
|
val mb = kb / 1000.toDouble()
|
||||||
|
val decimalFormat = DecimalFormat(".##")
|
||||||
|
return when {
|
||||||
|
mb >= 1 -> {
|
||||||
|
context.getString(R.string.download_speed_mb, decimalFormat.format(mb))
|
||||||
|
}
|
||||||
|
kb >= 1 -> {
|
||||||
|
context.getString(R.string.download_speed_kb, decimalFormat.format(kb))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
context.getString(R.string.download_speed_bytes, downloadedBytesPerSecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
for (sessionInfo in packageInstaller.mySessions) {
|
||||||
|
try {
|
||||||
|
val sessionId = sessionInfo.sessionId
|
||||||
|
packageInstaller.abandonSession(sessionInfo.sessionId)
|
||||||
|
Log.i("Abandoned session id -> %d", sessionId)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getThemeStyleById(themeId: Int): Int {
|
||||||
|
return when (themeId) {
|
||||||
|
0 -> R.style.AppTheme
|
||||||
|
1 -> R.style.AppTheme_Light
|
||||||
|
2 -> R.style.AppTheme_Dark
|
||||||
|
3 -> R.style.AppTheme_Black
|
||||||
|
4 -> R.style.AppTheme_DarkX
|
||||||
|
5 -> R.style.AppTheme_Darkord
|
||||||
|
else -> R.style.AppTheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAccentStyleById(accentId: Int): Int {
|
||||||
|
return when (accentId) {
|
||||||
|
1 -> R.style.Accent01
|
||||||
|
2 -> R.style.Accent02
|
||||||
|
3 -> R.style.Accent03
|
||||||
|
4 -> R.style.Accent04
|
||||||
|
5 -> R.style.Accent05
|
||||||
|
6 -> R.style.Accent06
|
||||||
|
7 -> R.style.Accent07
|
||||||
|
8 -> R.style.Accent08
|
||||||
|
9 -> R.style.Accent09
|
||||||
|
10 -> R.style.Accent10
|
||||||
|
11 -> R.style.Accent11
|
||||||
|
12 -> R.style.Accent12
|
||||||
|
else -> R.style.Accent01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
app/src/main/java/com/aurora/store/util/Log.kt
Normal file
73
app/src/main/java/com/aurora/store/util/Log.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
object Log {
|
||||||
|
|
||||||
|
const val TAG = "¯\\_(ツ)_/¯ "
|
||||||
|
|
||||||
|
fun e(message: String?, vararg args: Any?) {
|
||||||
|
e(String.format(message!!, *args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun e(message: String?) {
|
||||||
|
Log.e(TAG, message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun i(message: String?, vararg args: Any?) {
|
||||||
|
i(String.format(message!!, *args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun i(message: String?) {
|
||||||
|
Log.i(TAG, message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun d(message: String?, vararg args: Any?) {
|
||||||
|
d(String.format(message!!, *args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun d(message: String?) {
|
||||||
|
Log.d(TAG, message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun w(message: String?, vararg args: Any?) {
|
||||||
|
w(String.format(message!!, *args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun w(message: String?) {
|
||||||
|
Log.w(TAG, message!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeToFile(context: Context, obj: Any) {
|
||||||
|
try {
|
||||||
|
val out = FileWriter(File(context.filesDir, "AuroraLogs.txt"))
|
||||||
|
out.write(obj.toString())
|
||||||
|
out.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
app/src/main/java/com/aurora/store/util/NavigationUtil.kt
Normal file
99
app/src/main/java/com/aurora/store/util/NavigationUtil.kt
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.app.ActivityOptions
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.store.data.model.Report
|
||||||
|
import com.aurora.store.view.ui.details.AppDetailsActivity
|
||||||
|
import com.aurora.store.view.ui.details.DetailsExodusActivity
|
||||||
|
import com.aurora.store.view.ui.details.DevAppsActivity
|
||||||
|
import com.aurora.store.view.ui.search.SearchResultsActivity
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
|
object NavigationUtil {
|
||||||
|
val gson: Gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
|
||||||
|
|
||||||
|
fun openDetailsActivity(context: Context, app: App) {
|
||||||
|
val intent = Intent(
|
||||||
|
context,
|
||||||
|
AppDetailsActivity::class.java
|
||||||
|
).apply {
|
||||||
|
putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
val options = ActivityOptions.makeSceneTransitionAnimation(context as AppCompatActivity)
|
||||||
|
context.startActivity(intent, options.toBundle())
|
||||||
|
} else {
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openDevAppsActivity(context: Context, app: App) {
|
||||||
|
val intent = Intent(
|
||||||
|
context,
|
||||||
|
DevAppsActivity::class.java
|
||||||
|
).apply {
|
||||||
|
putExtra(Constants.STRING_APP, gson.toJson(app))
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
val options = ActivityOptions.makeSceneTransitionAnimation(context as AppCompatActivity)
|
||||||
|
context.startActivity(intent, options.toBundle())
|
||||||
|
} else {
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openExodusActivity(context: Context, app: App, report: Report) {
|
||||||
|
val intent = Intent(
|
||||||
|
context,
|
||||||
|
DetailsExodusActivity::class.java
|
||||||
|
).apply {
|
||||||
|
putExtra(Constants.STRING_APP, gson.toJson(app))
|
||||||
|
putExtra(Constants.STRING_EXTRA, gson.toJson(report))
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
val options = ActivityOptions.makeSceneTransitionAnimation(context as AppCompatActivity)
|
||||||
|
context.startActivity(intent, options.toBundle())
|
||||||
|
} else {
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openSearchActivity(context: Context) {
|
||||||
|
val intent = Intent(
|
||||||
|
context,
|
||||||
|
SearchResultsActivity::class.java
|
||||||
|
)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
val options = ActivityOptions.makeSceneTransitionAnimation(context as AppCompatActivity)
|
||||||
|
context.startActivity(intent, options.toBundle())
|
||||||
|
} else {
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
155
app/src/main/java/com/aurora/store/util/PackageUtil.kt
Normal file
155
app/src/main/java/com/aurora/store/util/PackageUtil.kt
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
|
||||||
|
object PackageUtil {
|
||||||
|
|
||||||
|
fun isInstalled(context: Context, packageName: String): Boolean {
|
||||||
|
return try {
|
||||||
|
context.packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
|
||||||
|
true
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isUpdatable(context: Context, packageName: String, versionCode: Long): Boolean {
|
||||||
|
return try {
|
||||||
|
val packageInfo = getPackageInfo(context, packageName)
|
||||||
|
if (packageInfo != null) {
|
||||||
|
return versionCode > packageInfo.versionCode
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isTv(context: Context): Boolean {
|
||||||
|
val uiMode = context.resources.configuration.uiMode
|
||||||
|
return uiMode and Configuration.UI_MODE_TYPE_MASK == Configuration.UI_MODE_TYPE_TELEVISION
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLaunchIntent(context: Context, packageName: String?): Intent? {
|
||||||
|
val isTv = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTv(context)
|
||||||
|
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
if (isTv) {
|
||||||
|
context.packageManager.getLeanbackLaunchIntentForPackage(packageName!!)
|
||||||
|
} else {
|
||||||
|
context.packageManager.getLaunchIntentForPackage(packageName!!)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.packageManager.getLaunchIntentForPackage(packageName!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (intent == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
intent.addCategory(if (isTv) Intent.CATEGORY_LEANBACK_LAUNCHER else Intent.CATEGORY_LAUNCHER)
|
||||||
|
intent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPackageInfo(context: Context, packageName: String?): PackageInfo? {
|
||||||
|
return context.packageManager.getPackageInfo(packageName!!, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllPackages(context: Context): List<PackageInfo> {
|
||||||
|
val packageInfoSet: MutableList<PackageInfo> = mutableListOf()
|
||||||
|
val packageManager: PackageManager = context.packageManager
|
||||||
|
val flags: Int = getAllFlags()
|
||||||
|
val packageInfoList: List<PackageInfo> = packageManager.getInstalledPackages(flags)
|
||||||
|
for (packageInfo in packageInfoList) {
|
||||||
|
if (packageInfo.packageName != null && packageInfo.applicationInfo != null) {
|
||||||
|
packageInfoSet.add(packageInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packageInfoSet
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPackageInfoMap(context: Context): MutableMap<String, PackageInfo> {
|
||||||
|
val packageInfoSet: MutableMap<String, PackageInfo> = mutableMapOf()
|
||||||
|
val packageManager: PackageManager = context.packageManager
|
||||||
|
val flags: Int = PackageManager.GET_META_DATA
|
||||||
|
var packageInfoList: List<PackageInfo> = packageManager.getInstalledPackages(flags)
|
||||||
|
|
||||||
|
val isFdroidFilterEnabled = Preferences.getBoolean(
|
||||||
|
context,
|
||||||
|
Preferences.PREFERENCE_FILTER_FDROID
|
||||||
|
)
|
||||||
|
|
||||||
|
packageInfoList = packageInfoList
|
||||||
|
.filter {
|
||||||
|
it.packageName != null && it.applicationInfo != null && it.applicationInfo.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFdroidFilterEnabled) {
|
||||||
|
packageInfoList
|
||||||
|
.filter {
|
||||||
|
val packageInstaller = packageManager.getInstallerPackageName(it.packageName)
|
||||||
|
packageInstaller != "org.fdroid.fdroid.privileged"
|
||||||
|
}.filter {
|
||||||
|
!CertUtil.isFDroidApp(context, it.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageInfoList.forEach {
|
||||||
|
packageInfoSet[it.packageName] = it
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageInfoSet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getFilter(): IntentFilter {
|
||||||
|
val filter = IntentFilter()
|
||||||
|
filter.addDataScheme("package")
|
||||||
|
filter.addAction(Intent.ACTION_PACKAGE_INSTALL)
|
||||||
|
filter.addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||||
|
filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllFlags(): Int {
|
||||||
|
var flags = (PackageManager.GET_META_DATA
|
||||||
|
or PackageManager.GET_ACTIVITIES
|
||||||
|
or PackageManager.GET_SERVICES
|
||||||
|
or PackageManager.GET_PROVIDERS
|
||||||
|
or PackageManager.GET_RECEIVERS)
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
flags = flags or PackageManager.GET_DISABLED_COMPONENTS
|
||||||
|
flags = flags or PackageManager.GET_UNINSTALLED_PACKAGES
|
||||||
|
} else {
|
||||||
|
flags = flags or PackageManager.MATCH_DISABLED_COMPONENTS
|
||||||
|
flags = flags or PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/src/main/java/com/aurora/store/util/PathUtil.kt
Normal file
78
app/src/main/java/com/aurora/store/util/PathUtil.kt
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
object PathUtil {
|
||||||
|
|
||||||
|
private fun getDownloadDirectory(context: Context): String {
|
||||||
|
return if (isLAndAbove())
|
||||||
|
context.getInternalBaseDirectory() + "/Downloads"
|
||||||
|
else
|
||||||
|
getExternalPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPackageDirectory(context: Context, packageName: String): String {
|
||||||
|
return getDownloadDirectory(context) + "/$packageName"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getVersionDirectory(
|
||||||
|
context: Context,
|
||||||
|
packageName: String,
|
||||||
|
versionCode: Int
|
||||||
|
): String {
|
||||||
|
return getPackageDirectory(context, packageName) + "/$versionCode"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getApkDownloadFile(context: Context, app: App, file: File): String {
|
||||||
|
return getVersionDirectory(context, app.packageName, app.versionCode) + "/${file.name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getApkDownloadFile(context: Context, packageName: String, versionCode: Int): String {
|
||||||
|
return getVersionDirectory(context, packageName, versionCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getExternalPath(): String {
|
||||||
|
return Environment.getExternalStorageDirectory().toString() + "/Aurora/"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBaseCopyDirectory(): String {
|
||||||
|
return "${getExternalPath()}/files/export/"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getObbDownloadPath(context: Context, app: App): String {
|
||||||
|
return Environment.getExternalStorageDirectory()
|
||||||
|
.toString() + "/Android/obb/" + app.packageName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getObbDownloadFile(context: Context, app: App, file: File): String {
|
||||||
|
val obbDir = getObbDownloadPath(context, app)
|
||||||
|
return "$obbDir/${file.name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
114
app/src/main/java/com/aurora/store/util/Preferences.kt
Normal file
114
app/src/main/java/com/aurora/store/util/Preferences.kt
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
|
||||||
|
object Preferences {
|
||||||
|
|
||||||
|
const val PREFERENCE_AUTH_DATA = "PREFERENCE_AUTH_DATA"
|
||||||
|
const val PREFERENCE_INSTALLER_ID = "PREFERENCE_INSTALLER_ID"
|
||||||
|
const val PREFERENCE_THEME_TYPE = "PREFERENCE_THEME_TYPE"
|
||||||
|
const val PREFERENCE_THEME_ACCENT = "PREFERENCE_THEME_ACCENT"
|
||||||
|
const val PREFERENCE_INTRO = "PREFERENCE_INTRO"
|
||||||
|
|
||||||
|
const val PREFERENCE_FILTER_GOOGLE = "PREFERENCE_FILTER_GOOGLE"
|
||||||
|
const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID"
|
||||||
|
|
||||||
|
const val PREFERENCE_AUTO_INSTALL = "PREFERENCE_AUTO_INSTALL"
|
||||||
|
const val PREFERENCE_AUTO_DELETE = "PREFERENCE_AUTO_DELETE"
|
||||||
|
|
||||||
|
const val INSTALLATION_ABANDON_SESSION = "INSTALLATION_ABANDON_SESSION"
|
||||||
|
|
||||||
|
const val PREFERENCE_DOWNLOAD_ACTIVE = "PREFERENCE_DOWNLOAD_ACTIVE"
|
||||||
|
const val PREFERENCE_DOWNLOAD_WIFI = "PREFERENCE_DOWNLOAD_WIFI"
|
||||||
|
|
||||||
|
|
||||||
|
private fun getPrefs(context: Context): SharedPreferences {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putString(context: Context, key: String, value: String) {
|
||||||
|
getPrefs(context).edit().putString(key, value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putInteger(context: Context, key: String, value: Int) {
|
||||||
|
getPrefs(context).edit().putInt(key, value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putFloat(context: Context, key: String, value: Float) {
|
||||||
|
getPrefs(context).edit().putFloat(key, value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putLong(context: Context, key: String, value: Long) {
|
||||||
|
getPrefs(context).edit().putLong(key, value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putBoolean(context: Context, key: String, value: Boolean) {
|
||||||
|
getPrefs(context).edit().putBoolean(key, value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getString(context: Context, key: String): String {
|
||||||
|
return getPrefs(context).getString(key, "").toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInteger(context: Context, key: String): Int {
|
||||||
|
return getPrefs(context).getInt(key, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFloat(context: Context, key: String): Float {
|
||||||
|
return getPrefs(context).getFloat(key, 0.0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLong(context: Context, key: String): Long {
|
||||||
|
return getPrefs(context).getLong(key, 0L)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBoolean(context: Context, key: String): Boolean {
|
||||||
|
return getPrefs(context).getBoolean(key, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.save(key: String, value: Int) {
|
||||||
|
Preferences.putInteger(this, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.save(key: String, value: Int) {
|
||||||
|
Preferences.putInteger(requireContext(), 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 Fragment.save(key: String, value: String) {
|
||||||
|
Preferences.putString(requireContext(), key, value)
|
||||||
|
}
|
||||||
53
app/src/main/java/com/aurora/store/util/Util.java
Normal file
53
app/src/main/java/com/aurora/store/util/Util.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Util {
|
||||||
|
|
||||||
|
public static Map<String, String> parseResponse(String response) {
|
||||||
|
Map<String, String> keyValueMap = new HashMap<String, String>();
|
||||||
|
StringTokenizer st = new StringTokenizer(response, "\n\r");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
String[] keyValue = st.nextToken().split("=", 2);
|
||||||
|
if (keyValue.length >= 2) {
|
||||||
|
keyValueMap.put(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> parseCookieString(String cookies) {
|
||||||
|
Map<String, String> cookieList = new HashMap<>();
|
||||||
|
Pattern cookiePattern = Pattern.compile("([^=]+)=([^;]*);?\\s?");
|
||||||
|
Matcher matcher = cookiePattern.matcher(cookies);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String cookieKey = matcher.group(1);
|
||||||
|
String cookieValue = matcher.group(2);
|
||||||
|
cookieList.put(cookieKey, cookieValue);
|
||||||
|
}
|
||||||
|
return cookieList;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/src/main/java/com/aurora/store/util/ViewUtil.kt
Normal file
71
app/src/main/java/com/aurora/store/util/ViewUtil.kt
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
fun <T> MutableList<T>.flushAndAdd(list: List<T>) {
|
||||||
|
clear()
|
||||||
|
addAll(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> MutableSet<T>.flushAndAdd(list: Set<T>) {
|
||||||
|
clear()
|
||||||
|
addAll(list)
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
fun Long.toDate(): String {
|
||||||
|
val calendar = Calendar.getInstance(Locale.getDefault())
|
||||||
|
calendar.timeInMillis = this
|
||||||
|
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
|
||||||
|
}
|
||||||
114
app/src/main/java/com/aurora/store/util/extensions/Context.kt
Normal file
114
app/src/main/java/com/aurora/store/util/extensions/Context.kt
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.browse(url: String) {
|
||||||
|
try {
|
||||||
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(url)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.share(app: App) {
|
||||||
|
try {
|
||||||
|
ShareCompat.IntentBuilder.from(this as AppCompatActivity)
|
||||||
|
.setType("text/plain")
|
||||||
|
.setChooserTitle(getString(R.string.action_share))
|
||||||
|
.setSubject(app.displayName)
|
||||||
|
.setText(Constants.SHARE_URL + app.packageName)
|
||||||
|
.startChooser()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.openInfo(packageName: String) {
|
||||||
|
try {
|
||||||
|
val intent = Intent(
|
||||||
|
"android.settings.APPLICATION_DETAILS_SETTINGS",
|
||||||
|
Uri.parse("package:$packageName")
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Context.open(className: Class<T>, newTask: Boolean = false) {
|
||||||
|
val intent = Intent(this, className)
|
||||||
|
if (newTask)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
startActivity(
|
||||||
|
intent,
|
||||||
|
ViewUtil.getEmptyActivityBundle(this)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.close() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
finishAfterTransition()
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.restartApp() {
|
||||||
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
1337,
|
||||||
|
Intent(this, MainActivity::class.java),
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
|
)
|
||||||
|
|
||||||
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
|
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent)
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.copyToClipBoard(data: String?) {
|
||||||
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
val clip = ClipData.newPlainText("Download Url", data)
|
||||||
|
clipboard.setPrimaryClip(clip)
|
||||||
|
}
|
||||||
165
app/src/main/java/com/aurora/store/util/extensions/Glide.kt
Normal file
165
app/src/main/java/com/aurora/store/util/extensions/Glide.kt
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.RawRes
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.TransitionOptions
|
||||||
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.request.target.ViewTarget
|
||||||
|
import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun ImageView.load(
|
||||||
|
bitmap: Bitmap?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(bitmap, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
byteArray: ByteArray?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(byteArray, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
drawable: Drawable?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(drawable, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
@RawRes @DrawableRes resourceId: Int?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(resourceId, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
uri: Uri?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(uri, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
string: String?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(string, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.load(
|
||||||
|
file: File?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(file, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
bitmap: Bitmap?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(bitmap, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
byteArray: ByteArray?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(byteArray, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
drawable: Drawable?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(drawable, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
@RawRes @DrawableRes resourceId: Int?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(resourceId, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
uri: Uri?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(uri, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
string: String?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(string, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.load(
|
||||||
|
file: File?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> = loadAny(file, transitionOptions, requestOptions)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.loadAny(
|
||||||
|
data: Any?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions? = null
|
||||||
|
): ViewTarget<ImageView, Drawable> {
|
||||||
|
return Glide.with(this)
|
||||||
|
.load(data)
|
||||||
|
.apply {
|
||||||
|
transitionOptions?.let { transition(it) }
|
||||||
|
requestOptions?.let { apply(it) }
|
||||||
|
}
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun ImageView.loadAny(
|
||||||
|
data: Any?,
|
||||||
|
transitionOptions: TransitionOptions<*, Drawable>? = null,
|
||||||
|
requestOptions: RequestOptions.() -> Unit
|
||||||
|
): ViewTarget<ImageView, Drawable> {
|
||||||
|
val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()
|
||||||
|
return Glide.with(this)
|
||||||
|
.load(data)
|
||||||
|
.transition(withCrossFade(factory))
|
||||||
|
.apply(RequestOptions().apply(requestOptions))
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
fun ImageView.clear() {
|
||||||
|
//Glide.with(this).clear(this)
|
||||||
|
}
|
||||||
28
app/src/main/java/com/aurora/store/util/extensions/Number.kt
Normal file
28
app/src/main/java/com/aurora/store/util/extensions/Number.kt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
|
||||||
|
val Number.dp: Number
|
||||||
|
get() = (this.toFloat() / Resources.getSystem().displayMetrics.density).toInt()
|
||||||
|
|
||||||
|
val Number.px: Number
|
||||||
|
get() = (this.toFloat() * Resources.getSystem().displayMetrics.density).toInt()
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowInsetsController
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
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,
|
||||||
|
accentId: Int = 1,
|
||||||
|
shouldApplyTransition: Boolean = true,
|
||||||
|
position: Int = 2
|
||||||
|
) {
|
||||||
|
val themeStyle = CommonUtil.getThemeStyleById(themeId)
|
||||||
|
|
||||||
|
if (themeStyle == 0) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
(requireActivity() as AppCompatActivity).applyDayNightMask()
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Apply Theme*/
|
||||||
|
requireContext().theme.applyStyle(themeStyle, true)
|
||||||
|
|
||||||
|
/*Apply transition only on AppCompatActivity*/
|
||||||
|
if (shouldApplyTransition)
|
||||||
|
(requireActivity() as AppCompatActivity).transitionRecreate(position)
|
||||||
|
else
|
||||||
|
(requireActivity() as AppCompatActivity).recreate()
|
||||||
|
|
||||||
|
if (themeId == 1) {
|
||||||
|
(requireActivity() as AppCompatActivity).setLightConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.transitionRecreate(position: Int = 2) {
|
||||||
|
val intent = Intent(this, javaClass)
|
||||||
|
intent.putExtra(Constants.INT_EXTRA, position)
|
||||||
|
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
startActivity(intent)
|
||||||
|
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.applyTheme(themeId: Int, accentId: Int = 1) {
|
||||||
|
val themeStyle = CommonUtil.getThemeStyleById(themeId)
|
||||||
|
val accentStyle = CommonUtil.getAccentStyleById(accentId)
|
||||||
|
|
||||||
|
if (themeStyle == 0) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
applyDayNightMask()
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Apply Theme*/
|
||||||
|
setTheme(themeStyle)
|
||||||
|
|
||||||
|
/*Apply Accent*/
|
||||||
|
theme.applyStyle(accentStyle, true)
|
||||||
|
|
||||||
|
/*Apply Light Configuration*/
|
||||||
|
if (themeId == 1) {
|
||||||
|
setLightConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.applyDayNightMask() {
|
||||||
|
val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||||
|
if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
|
||||||
|
setLightConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.setLightConfiguration() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
setLightConfigurationO()
|
||||||
|
} else {
|
||||||
|
setLightConfigurationO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
private fun AppCompatActivity.setLightConfigurationR() {
|
||||||
|
window?.insetsController?.setSystemBarsAppearance(
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
||||||
|
or WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AppCompatActivity.setLightConfigurationO() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
setLightStatusBar()
|
||||||
|
setLightNavigationBar()
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
window.statusBarColor = ColorUtils.setAlphaComponent(Color.BLACK, 120)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AppCompatActivity.setLightStatusBar() {
|
||||||
|
var flags = window.decorView.systemUiVisibility
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
}
|
||||||
|
window.decorView.systemUiVisibility = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||||
|
}
|
||||||
|
window.decorView.systemUiVisibility = flags
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
36
app/src/main/java/com/aurora/store/util/extensions/Toast.kt
Normal file
36
app/src/main/java/com/aurora/store/util/extensions/Toast.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
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 =
|
||||||
|
Toast.makeText(this, resId, Toast.LENGTH_SHORT).apply { show() }
|
||||||
|
|
||||||
|
fun Context.longToast(@StringRes resId: Int): Toast =
|
||||||
|
Toast.makeText(this, resId, Toast.LENGTH_LONG).apply { show() }
|
||||||
81
app/src/main/java/com/aurora/store/util/extensions/View.kt
Normal file
81
app/src/main/java/com/aurora/store/util/extensions/View.kt
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.util.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
|
||||||
|
fun View.isVisible() = visibility == View.VISIBLE
|
||||||
|
|
||||||
|
fun View.isGone() = visibility == View.GONE
|
||||||
|
|
||||||
|
fun View.isInvisible() = visibility == View.INVISIBLE
|
||||||
|
|
||||||
|
fun View.show() {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.hide() {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.showKeyboard() {
|
||||||
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
this.requestFocus()
|
||||||
|
imm.showSoftInput(this, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.hideKeyboard(): Boolean {
|
||||||
|
try {
|
||||||
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
return imm.hideSoftInputFromWindow(windowToken, 0)
|
||||||
|
} catch (ignored: RuntimeException) {
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.setOnSingleClickListener(tolerance: Long = 500, onClick: (v: View) -> Unit) {
|
||||||
|
var lastClicked = 0L
|
||||||
|
val currentTimeMillis = System.currentTimeMillis()
|
||||||
|
setOnClickListener {
|
||||||
|
if (currentTimeMillis - lastClicked > tolerance) {
|
||||||
|
onClick(it)
|
||||||
|
lastClicked = currentTimeMillis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.rotate(resetToZero: Boolean = true, duration: Long = 400) {
|
||||||
|
if (resetToZero)
|
||||||
|
rotation = 0f
|
||||||
|
animate().rotation(360f).setDuration(duration).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.flip(resetToZero: Boolean = true, duration: Long = 400) {
|
||||||
|
if (resetToZero)
|
||||||
|
rotation = 0f
|
||||||
|
animate().rotation(180f).setDuration(duration).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.getString(resourceId: Int): String {
|
||||||
|
return context.getString(resourceId)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.ViewActionButtonBinding
|
||||||
|
|
||||||
|
class ActionButton : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewActionButtonBinding
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
init(context, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context, attrs: AttributeSet?) {
|
||||||
|
val view = inflate(context, R.layout.view_action_button, this)
|
||||||
|
B = ViewActionButtonBinding.bind(view)
|
||||||
|
|
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ActionButton)
|
||||||
|
val btnTxt = typedArray.getString(R.styleable.ActionButton_btnActionText)
|
||||||
|
B.btn.text = btnTxt
|
||||||
|
typedArray.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setText(text: String) {
|
||||||
|
B.btn.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProgress(isVisible: Boolean) {
|
||||||
|
if (isVisible)
|
||||||
|
B.progress.visibility = View.VISIBLE
|
||||||
|
else
|
||||||
|
|
||||||
|
B.progress.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addOnClickListener(onClickListener: OnClickListener) {
|
||||||
|
B.btn.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom;
|
||||||
|
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.view.animation.Interpolator;
|
||||||
|
|
||||||
|
public class CubicBezierInterpolator implements Interpolator {
|
||||||
|
|
||||||
|
public static final CubicBezierInterpolator DEFAULT = new CubicBezierInterpolator(0.25, 0.1, 0.25, 1);
|
||||||
|
public static final CubicBezierInterpolator EASE_OUT = new CubicBezierInterpolator(0, 0, .58, 1);
|
||||||
|
public static final CubicBezierInterpolator EASE_OUT_QUINT = new CubicBezierInterpolator(.23, 1, .32, 1);
|
||||||
|
public static final CubicBezierInterpolator EASE_IN = new CubicBezierInterpolator(.42, 0, 1, 1);
|
||||||
|
public static final CubicBezierInterpolator EASE_BOTH = new CubicBezierInterpolator(.42, 0, .58, 1);
|
||||||
|
public static final CubicBezierInterpolator EASE_IN_OUT_QUAD = new CubicBezierInterpolator(0.455, 0.03, 0.515, 0.955);
|
||||||
|
|
||||||
|
protected PointF start;
|
||||||
|
protected PointF end;
|
||||||
|
protected PointF a = new PointF();
|
||||||
|
protected PointF b = new PointF();
|
||||||
|
protected PointF c = new PointF();
|
||||||
|
|
||||||
|
public CubicBezierInterpolator(PointF start, PointF end) throws IllegalArgumentException {
|
||||||
|
if (start.x < 0 || start.x > 1) {
|
||||||
|
throw new IllegalArgumentException("startX value must be in the range [0, 1]");
|
||||||
|
}
|
||||||
|
if (end.x < 0 || end.x > 1) {
|
||||||
|
throw new IllegalArgumentException("endX value must be in the range [0, 1]");
|
||||||
|
}
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubicBezierInterpolator(float startX, float startY, float endX, float endY) {
|
||||||
|
this(new PointF(startX, startY), new PointF(endX, endY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubicBezierInterpolator(double startX, double startY, double endX, double endY) {
|
||||||
|
this((float) startX, (float) startY, (float) endX, (float) endY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getInterpolation(float time) {
|
||||||
|
return getBezierCoordinateY(getXForTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float getBezierCoordinateY(float time) {
|
||||||
|
c.y = 3 * start.y;
|
||||||
|
b.y = 3 * (end.y - start.y) - c.y;
|
||||||
|
a.y = 1 - c.y - b.y;
|
||||||
|
return time * (c.y + time * (b.y + time * a.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float getXForTime(float time) {
|
||||||
|
float x = time;
|
||||||
|
float z;
|
||||||
|
for (int i = 1; i < 14; i++) {
|
||||||
|
z = getBezierCoordinateX(x) - time;
|
||||||
|
if (Math.abs(z) < 1e-3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
x -= z / getXDerivate(x);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getXDerivate(float t) {
|
||||||
|
return c.x + t * (2 * b.x + 3 * a.x * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getBezierCoordinateX(float time) {
|
||||||
|
c.x = 3 * start.x;
|
||||||
|
b.x = 3 * (end.x - start.x) - c.x;
|
||||||
|
a.x = 1 - c.x - b.x;
|
||||||
|
return time * (c.x + time * (b.x + time * a.x));
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/src/main/java/com/aurora/store/view/custom/RatingView.kt
Normal file
71
app/src/main/java/com/aurora/store/view/custom/RatingView.kt
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.ViewRatingBinding
|
||||||
|
|
||||||
|
class RatingView : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewRatingBinding
|
||||||
|
|
||||||
|
var number = 0
|
||||||
|
var max = 0
|
||||||
|
var rating = 0
|
||||||
|
|
||||||
|
constructor(context: Context, number: Int, max: Int, rating: Int) : super(context) {
|
||||||
|
this.number = number
|
||||||
|
this.max = max
|
||||||
|
this.rating = rating
|
||||||
|
init(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context?) : super(context) {}
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||||
|
context,
|
||||||
|
attrs,
|
||||||
|
defStyleAttr
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
constructor(
|
||||||
|
context: Context?,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context) {
|
||||||
|
val view = inflate(context, R.layout.view_rating, this)
|
||||||
|
B = ViewRatingBinding.bind(view)
|
||||||
|
|
||||||
|
B.avgNum.text = number.toString()
|
||||||
|
B.avgRating.max = max
|
||||||
|
B.avgRating.progress = rating
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
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.store.R
|
||||||
|
import com.aurora.store.databinding.ViewStateButtonBinding
|
||||||
|
|
||||||
|
class StateButton : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewStateButtonBinding
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
init(context, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context, attrs: AttributeSet?) {
|
||||||
|
val view = inflate(context, R.layout.view_state_button, this)
|
||||||
|
B = ViewStateButtonBinding.bind(view)
|
||||||
|
|
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.StateButton)
|
||||||
|
val btnTxt = typedArray.getString(R.styleable.StateButton_btnStateText)
|
||||||
|
val btnIcon = typedArray.getResourceId(
|
||||||
|
R.styleable.StateButton_btnStateIcon,
|
||||||
|
R.drawable.ic_arrow_right
|
||||||
|
)
|
||||||
|
|
||||||
|
B.btn.text = btnTxt
|
||||||
|
B.btn.icon = ContextCompat.getDrawable(context, btnIcon)
|
||||||
|
typedArray.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProgress(isVisible: Boolean) {
|
||||||
|
if (isVisible)
|
||||||
|
B.progress.visibility = View.VISIBLE
|
||||||
|
else
|
||||||
|
|
||||||
|
B.progress.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addOnClickListener(onClickListener: OnClickListener) {
|
||||||
|
B.btn.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom.layouts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.ViewActionHeaderBinding
|
||||||
|
|
||||||
|
class ActionHeaderLayout : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewActionHeaderBinding
|
||||||
|
|
||||||
|
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_action_header, this)
|
||||||
|
B = ViewActionHeaderBinding.bind(view)
|
||||||
|
|
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ActionHeaderLayout)
|
||||||
|
val textPrimary = typedArray.getString(R.styleable.ActionHeaderLayout_headerTitle)
|
||||||
|
|
||||||
|
textPrimary?.let {
|
||||||
|
B.txtTitle.text = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHeader(header: String?) {
|
||||||
|
B.txtTitle.text = header
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addClickListener(onclickListener: OnClickListener?) {
|
||||||
|
B.imgAction.visibility = View.VISIBLE
|
||||||
|
B.imgAction.setOnClickListener(onclickListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom.layouts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.ViewDevInfoBinding
|
||||||
|
|
||||||
|
class ViewDevInfo : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewDevInfoBinding
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
init(context, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context, attrs: AttributeSet?) {
|
||||||
|
val view = inflate(context, R.layout.view_dev_info, this)
|
||||||
|
B = ViewDevInfoBinding.bind(view)
|
||||||
|
|
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.DevInfoLayout)
|
||||||
|
val icon = typedArray.getResourceId(
|
||||||
|
R.styleable.DevInfoLayout_imgIcon,
|
||||||
|
R.drawable.ic_map_marker
|
||||||
|
)
|
||||||
|
|
||||||
|
val textPrimary = typedArray.getString(R.styleable.DevInfoLayout_txtTitle)
|
||||||
|
val textSecondary = typedArray.getString(R.styleable.DevInfoLayout_txtSubtitle)
|
||||||
|
|
||||||
|
B.img.setImageResource(icon)
|
||||||
|
B.txtTitle.text = textPrimary
|
||||||
|
B.txtSubtitle.text = textSecondary
|
||||||
|
typedArray.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTxtSubtitle(text: String?) {
|
||||||
|
B.txtSubtitle.text = text
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
|
||||||
|
class AuroraListPreference : ListPreference {
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||||
|
context,
|
||||||
|
attrs,
|
||||||
|
defStyleAttr
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPersistedString(defaultReturnValue: String?): String? {
|
||||||
|
return getPersistedInt(-1).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun persistString(value: String?): Boolean {
|
||||||
|
return persistInt(Integer.valueOf(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.custom.recycler
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.OrientationHelper
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
abstract class EndlessRecyclerOnScrollListener : RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
|
private var enabled = true
|
||||||
|
private var previousTotal = 0
|
||||||
|
private var isLoading = true
|
||||||
|
private var visibleThreshold = RecyclerView.NO_POSITION
|
||||||
|
|
||||||
|
var firstVisibleItem: Int = 0
|
||||||
|
private set
|
||||||
|
var visibleItemCount: Int = 0
|
||||||
|
private set
|
||||||
|
var totalItemCount: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var isOrientationHelperVertical: Boolean = false
|
||||||
|
private var orientationHelper: OrientationHelper? = null
|
||||||
|
|
||||||
|
var currentPage = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
lateinit var layoutManager: RecyclerView.LayoutManager
|
||||||
|
private set
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
constructor(layoutManager: RecyclerView.LayoutManager) {
|
||||||
|
this.layoutManager = layoutManager
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(visibleThreshold: Int) {
|
||||||
|
this.visibleThreshold = visibleThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(layoutManager: RecyclerView.LayoutManager, visibleThreshold: Int) {
|
||||||
|
this.layoutManager = layoutManager
|
||||||
|
this.visibleThreshold = visibleThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findFirstVisibleItemPosition(recyclerView: RecyclerView): Int {
|
||||||
|
val child = findOneVisibleChild(0, layoutManager.childCount, false, true)
|
||||||
|
return if (child == null) RecyclerView.NO_POSITION else recyclerView.getChildAdapterPosition(
|
||||||
|
child
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findLastVisibleItemPosition(recyclerView: RecyclerView): Int {
|
||||||
|
val child = findOneVisibleChild(recyclerView.childCount - 1, -1, false, true)
|
||||||
|
return if (child == null) RecyclerView.NO_POSITION else recyclerView.getChildAdapterPosition(
|
||||||
|
child
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOneVisibleChild(
|
||||||
|
fromIndex: Int,
|
||||||
|
toIndex: Int,
|
||||||
|
completelyVisible: Boolean,
|
||||||
|
acceptPartiallyVisible: Boolean
|
||||||
|
): View? {
|
||||||
|
if (layoutManager.canScrollVertically() != isOrientationHelperVertical || orientationHelper == null) {
|
||||||
|
isOrientationHelperVertical = layoutManager.canScrollVertically()
|
||||||
|
orientationHelper = if (isOrientationHelperVertical)
|
||||||
|
OrientationHelper.createVerticalHelper(layoutManager)
|
||||||
|
else
|
||||||
|
OrientationHelper.createHorizontalHelper(layoutManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mOrientationHelper = this.orientationHelper ?: return null
|
||||||
|
|
||||||
|
val start = mOrientationHelper.startAfterPadding
|
||||||
|
val end = mOrientationHelper.endAfterPadding
|
||||||
|
val next = if (toIndex > fromIndex) 1 else -1
|
||||||
|
var partiallyVisible: View? = null
|
||||||
|
var i = fromIndex
|
||||||
|
while (i != toIndex) {
|
||||||
|
val child = layoutManager.getChildAt(i)
|
||||||
|
if (child != null) {
|
||||||
|
val childStart = mOrientationHelper.getDecoratedStart(child)
|
||||||
|
val childEnd = mOrientationHelper.getDecoratedEnd(child)
|
||||||
|
if (childStart < end && childEnd > start) {
|
||||||
|
if (completelyVisible) {
|
||||||
|
if (childStart >= start && childEnd <= end) {
|
||||||
|
return child
|
||||||
|
} else if (acceptPartiallyVisible && partiallyVisible == null) {
|
||||||
|
partiallyVisible = child
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += next
|
||||||
|
}
|
||||||
|
return partiallyVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
if (!::layoutManager.isInitialized) {
|
||||||
|
layoutManager = recyclerView.layoutManager
|
||||||
|
?: throw RuntimeException("A LayoutManager is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (visibleThreshold == RecyclerView.NO_POSITION) {
|
||||||
|
visibleThreshold =
|
||||||
|
findLastVisibleItemPosition(recyclerView) - findFirstVisibleItemPosition(
|
||||||
|
recyclerView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleItemCount = recyclerView.childCount
|
||||||
|
totalItemCount = layoutManager.itemCount
|
||||||
|
firstVisibleItem = findFirstVisibleItemPosition(recyclerView)
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
if (totalItemCount > previousTotal) {
|
||||||
|
isLoading = false
|
||||||
|
previousTotal = totalItemCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoading && totalItemCount - visibleItemCount <= firstVisibleItem + visibleThreshold) {
|
||||||
|
currentPage++
|
||||||
|
onLoadMore(currentPage)
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enable(): EndlessRecyclerOnScrollListener {
|
||||||
|
enabled = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disable(): EndlessRecyclerOnScrollListener {
|
||||||
|
enabled = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun resetPageCount(page: Int = 0) {
|
||||||
|
previousTotal = 0
|
||||||
|
isLoading = true
|
||||||
|
currentPage = page
|
||||||
|
onLoadMore(currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun onLoadMore(currentPage: Int)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.controller
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
|
||||||
|
class CategoryCarouselController(callbacks: Callbacks) : GenericCarouselController(callbacks) {
|
||||||
|
|
||||||
|
override fun applyFilter(streamBundle: StreamCluster): Boolean {
|
||||||
|
return streamBundle.clusterAppList.isNotEmpty() //Filter empty clusters
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.controller
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
|
||||||
|
class EarlyAccessCarouselController(callbacks: Callbacks) : GenericCarouselController(callbacks) {
|
||||||
|
|
||||||
|
override fun applyFilter(streamBundle: StreamCluster): Boolean {
|
||||||
|
return streamBundle.clusterTitle.isNotBlank() //Filter noisy cluster
|
||||||
|
&& streamBundle.clusterAppList.isNotEmpty() //Filter empty clusters
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.controller
|
||||||
|
|
||||||
|
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_
|
||||||
|
|
||||||
|
class EditorChoiceController(private val callbacks: Callbacks) :
|
||||||
|
TypedEpoxyController<List<EditorChoiceBundle>>() {
|
||||||
|
|
||||||
|
interface Callbacks {
|
||||||
|
fun onClick(editorChoiceCluster: EditorChoiceCluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels(editorChoiceBundles: List<EditorChoiceBundle>) {
|
||||||
|
editorChoiceBundles.forEach { editorChoiceBundle ->
|
||||||
|
val idPrefix = editorChoiceBundle.id
|
||||||
|
|
||||||
|
add(
|
||||||
|
EditorHeadViewModel_()
|
||||||
|
.id("header_${idPrefix}")
|
||||||
|
.title(editorChoiceBundle.bundleTitle)
|
||||||
|
)
|
||||||
|
|
||||||
|
editorChoiceBundle.bundleChoiceClusters.forEach {
|
||||||
|
add(EditorChoiceModelGroup(it, callbacks))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.gara.store.view.epoxy.controller
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.flexbox.FlexboxLayoutManager
|
||||||
|
|
||||||
|
class FlexLayoutManager : FlexboxLayoutManager {
|
||||||
|
constructor(context: Context?) : super(context)
|
||||||
|
constructor(context: Context?, flexDirection: Int) : super(context, flexDirection)
|
||||||
|
constructor(context: Context?, flexDirection: Int, flexWrap: Int) : super(
|
||||||
|
context,
|
||||||
|
flexDirection,
|
||||||
|
flexWrap
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: Context?,
|
||||||
|
attrs: AttributeSet?,
|
||||||
|
defStyleAttr: Int,
|
||||||
|
defStyleRes: Int
|
||||||
|
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||||
|
|
||||||
|
override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams {
|
||||||
|
return LayoutParams(lp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.controller
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
|
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.AppProgressViewModel_
|
||||||
|
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
|
||||||
|
|
||||||
|
open class GenericCarouselController(private val callbacks: Callbacks) :
|
||||||
|
|
||||||
|
TypedEpoxyController<StreamBundle?>() {
|
||||||
|
|
||||||
|
interface Callbacks {
|
||||||
|
fun onHeaderClicked(streamCluster: StreamCluster)
|
||||||
|
fun onClusterScrolled(streamCluster: StreamCluster)
|
||||||
|
fun onAppClick(app: App)
|
||||||
|
fun onAppLongClick(app: App)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun applyFilter(streamBundle: StreamCluster): Boolean {
|
||||||
|
return streamBundle.clusterTitle.isNotBlank() //Filter noisy cluster
|
||||||
|
&& streamBundle.clusterAppList.isNotEmpty() //Filter empty clusters
|
||||||
|
&& streamBundle.clusterAppList.count() > 1 //Filter clusters with single apps (mostly promotions)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels(streamBundle: StreamBundle?) {
|
||||||
|
setFilterDuplicates(true)
|
||||||
|
if (streamBundle == null) {
|
||||||
|
for (i in 1..2) {
|
||||||
|
add(
|
||||||
|
CarouselShimmerGroup()
|
||||||
|
.id(i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (streamBundle.streamClusters.isEmpty()) {
|
||||||
|
add(
|
||||||
|
NoAppViewModel_()
|
||||||
|
.id("no_app")
|
||||||
|
.icon(R.drawable.ic_apps)
|
||||||
|
.message("No apps available")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (streamBundle.hasNext())
|
||||||
|
add(
|
||||||
|
AppProgressViewModel_()
|
||||||
|
.id("progress")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.groups
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.epoxy.Carousel
|
||||||
|
import com.airbnb.epoxy.ModelView
|
||||||
|
|
||||||
|
@ModelView(saveViewState = true, autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
|
||||||
|
class CarouselHorizontal(context: Context?) : Carousel(context) {
|
||||||
|
|
||||||
|
override fun createLayoutManager(): LayoutManager {
|
||||||
|
return LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSnapHelperFactory(): Nothing? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.AppViewModel_
|
||||||
|
import com.aurora.store.view.epoxy.views.HeaderViewModel_
|
||||||
|
|
||||||
|
class CarouselModelGroup(
|
||||||
|
streamCluster: StreamCluster,
|
||||||
|
callbacks: GenericCarouselController.Callbacks
|
||||||
|
) :
|
||||||
|
EpoxyModelGroup(
|
||||||
|
R.layout.model_carousel_group, buildModels(
|
||||||
|
streamCluster,
|
||||||
|
callbacks
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private fun buildModels(
|
||||||
|
streamCluster: StreamCluster,
|
||||||
|
callbacks: GenericCarouselController.Callbacks
|
||||||
|
): List<EpoxyModel<*>> {
|
||||||
|
val models = ArrayList<EpoxyModel<*>>()
|
||||||
|
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
||||||
|
|
||||||
|
val idPrefix = streamCluster.id
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
HeaderViewModel_()
|
||||||
|
.id("${idPrefix}_header")
|
||||||
|
.title(streamCluster.clusterTitle)
|
||||||
|
.browseUrl(streamCluster.clusterBrowseUrl)
|
||||||
|
.click { _ ->
|
||||||
|
callbacks.onHeaderClicked(streamCluster)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (streamCluster.hasNext()) {
|
||||||
|
clusterViewModels.add(
|
||||||
|
AppProgressViewModel_()
|
||||||
|
.id("${idPrefix}_progress")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
CarouselHorizontalModel_()
|
||||||
|
.id("${idPrefix}_cluster")
|
||||||
|
.models(clusterViewModels)
|
||||||
|
)
|
||||||
|
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.groups
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.EpoxyModelGroup
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.view.epoxy.views.shimmer.AppViewShimmerModel_
|
||||||
|
import com.aurora.store.view.epoxy.views.shimmer.HeaderViewShimmerModel_
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
class CarouselShimmerGroup() :
|
||||||
|
EpoxyModelGroup(
|
||||||
|
R.layout.model_carousel_group, buildModels()
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private fun buildModels(): List<EpoxyModel<*>> {
|
||||||
|
val models = ArrayList<EpoxyModel<*>>()
|
||||||
|
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
||||||
|
val idPrefix = UUID.randomUUID()
|
||||||
|
|
||||||
|
for (i in 1..8) {
|
||||||
|
clusterViewModels.add(
|
||||||
|
AppViewShimmerModel_()
|
||||||
|
.id(i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
HeaderViewShimmerModel_()
|
||||||
|
.id("shimmer_header")
|
||||||
|
)
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
CarouselHorizontalModel_()
|
||||||
|
.id("cluster_${idPrefix}")
|
||||||
|
.models(clusterViewModels)
|
||||||
|
)
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.groups
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
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.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
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private fun buildModels(
|
||||||
|
editorChoiceCluster: EditorChoiceCluster,
|
||||||
|
callbacks: EditorChoiceController.Callbacks
|
||||||
|
): List<EpoxyModel<*>> {
|
||||||
|
|
||||||
|
val models = ArrayList<EpoxyModel<*>>()
|
||||||
|
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
||||||
|
|
||||||
|
val idPrefix = editorChoiceCluster.id
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
HeaderViewModel_()
|
||||||
|
.id("header_${idPrefix}")
|
||||||
|
.title(editorChoiceCluster.clusterTitle)
|
||||||
|
.browseUrl(editorChoiceCluster.clusterBrowseUrl)
|
||||||
|
.click { _ -> callbacks.onClick(editorChoiceCluster) }
|
||||||
|
)
|
||||||
|
|
||||||
|
editorChoiceCluster.clusterArtwork.forEach {
|
||||||
|
clusterViewModels.add(
|
||||||
|
EditorImageViewModel_()
|
||||||
|
.id("artwork_${idPrefix}")
|
||||||
|
.artwork(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
models.add(
|
||||||
|
CarouselHorizontalModel_()
|
||||||
|
.id("cluster_${idPrefix}")
|
||||||
|
.models(clusterViewModels)
|
||||||
|
)
|
||||||
|
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.epoxy.CallbackProp
|
||||||
|
import com.airbnb.epoxy.ModelProp
|
||||||
|
import com.airbnb.epoxy.ModelView
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.model.Accent
|
||||||
|
import com.aurora.store.databinding.ViewAccentBinding
|
||||||
|
|
||||||
|
@ModelView(
|
||||||
|
autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT,
|
||||||
|
baseModelClass = BaseView::class
|
||||||
|
)
|
||||||
|
class AccentView : RelativeLayout {
|
||||||
|
|
||||||
|
private lateinit var B: ViewAccentBinding
|
||||||
|
|
||||||
|
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_accent, this)
|
||||||
|
B = ViewAccentBinding.bind(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelProp
|
||||||
|
fun accent(accent: Accent) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
B.img.backgroundTintList = ColorStateList.valueOf(Color.parseColor(accent.accent))
|
||||||
|
} else {
|
||||||
|
B.img.setBackgroundColor(Color.parseColor(accent.accent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelProp
|
||||||
|
fun markChecked(isChecked: Boolean) {
|
||||||
|
B.tick.isVisible = isChecked
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallbackProp
|
||||||
|
fun click(onClickListener: OnClickListener?) {
|
||||||
|
B.root.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user