Skip to content
Commits on Source (95)
......@@ -28,11 +28,12 @@ android {
applicationId "com.aurora.store"
minSdkVersion 21
targetSdkVersion 29
versionCode 13
versionName "3.1.3"
versionCode 14
versionName "3.1.4"
resConfigs "ar-rSA", "b+ast", "bh-rIN", "bg-rBG", "ca-rES", "de-rDE", "es-rES", "el-rGR", "fr-rFR", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "in-rID", "it-rIT", "iw-rIL", "ja-rJP", "ko-rKR", "lt-rLT",
"pl-rPL", "pt-rPT", "pt-rBR", "ro-rRO", "ru-rRU", "sc-rIT", "sk-rSK", "sr-rSR", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW", "nl-rNL", "be-rBY", "nb-rNO", "pa-rIN"
"pl-rPL", "pt-rPT", "pt-rBR", "ro-rRO", "ru-rRU", "sc-rIT", "sk-rSK", "sr-rSR", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW", "nl-rNL", "be-rBY", "nb-rNO", "pa-rIN",
"cy-rGB", "et-rEE"
vectorDrawables.useSupportLibrary = true
}
......@@ -70,7 +71,7 @@ configurations.all {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'commons-io:commons-io:2.6'
implementation 'org.apache.maven:maven-artifact:3.2.1'
implementation 'org.apache.maven:maven-artifact:3.6.2'
//UI-Libs
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
......@@ -79,27 +80,27 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0'
//Utils
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'org.apache.commons:commons-text:1.4'
implementation 'org.apache.commons:commons-text:1.8'
implementation 'com.scottyab:rootbeer-lib:0.0.7'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.google.code.gson:gson:2.8.6'
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
//PlayStoreApi
implementation 'com.github.whyorean:playstore-api-v2:2.3'
implementation 'com.github.whyorean:playstore-api-v2:2.4'
//OkHTTP3
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.2.1'
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.2.2'
//RX-Java2
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.14'
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.1'
//ButterKnife
implementation 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
//Glide
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation "com.github.bumptech.glide:okhttp3-integration:4.9.0"
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation 'com.github.bumptech.glide:glide:4.10.0'
implementation "com.github.bumptech.glide:okhttp3-integration:4.10.0"
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//Fetch - Downloader
implementation 'androidx.tonyodev.fetch2:xfetch2:3.1.4'
......
......@@ -142,6 +142,9 @@
<activity
android:name=".activity.DeviceInfoActivity"
android:configChanges="keyboardHidden|locale|orientation|fontScale" />
<activity
android:name=".activity.GoogleLoginActivity"
android:theme="@style/LoginTheme" />
<provider
android:name=".provider.AuroraSuggestionProvider"
......@@ -175,7 +178,6 @@
<service android:name=".installer.InstallerService" />
<service android:name=".SelfUpdateService" />
<service android:name=".AnonymousLoginService" />
<service android:name=".AnonymousRefreshService" />
<meta-data
android:name="preloaded_fonts"
......
......@@ -58,10 +58,10 @@ public class AnonymousLoginService extends Service {
.login(this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((success) -> {
if (success) {
.subscribe((api) -> {
if (api != null) {
Log.i("Anonymous Login Successful");
Accountant.saveDummy(this);
Accountant.setAnonymous(this, true);
RxBus.publish(new Event(Events.LOGGED_IN));
} else
Log.e("Anonymous Login Failed Permanently");
......@@ -79,7 +79,7 @@ public class AnonymousLoginService extends Service {
}
private void destroyService() {
Log.e("Self-update service destroyed");
Log.e("Anonymous login service destroyed");
stopSelf();
}
}
}
\ No newline at end of file
package com.aurora.store;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.aurora.store.api.PlayStoreApiAuthenticator;
import com.aurora.store.events.Event;
import com.aurora.store.events.Events;
import com.aurora.store.events.RxBus;
import com.aurora.store.utility.Log;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
public class AnonymousRefreshService extends Service {
public static AnonymousRefreshService instance = null;
private CompositeDisposable disposable = new CompositeDisposable();
public static boolean isServiceRunning() {
try {
return instance != null && instance.isRunning();
} catch (NullPointerException e) {
return false;
}
}
private boolean isRunning() {
return true;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
refreshToken();
}
private void refreshToken() {
disposable.add(Flowable.fromCallable(() -> PlayStoreApiAuthenticator
.refreshToken(this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(subscription -> {
RxBus.publish(new Event(Events.TOKEN_EXPIRED));
})
.subscribe((success) -> {
if (success) {
Log.i("Token Refreshed");
RxBus.publish(new Event(Events.TOKEN_REFRESHED));
} else {
Log.e("Token Refresh Failed Permanently");
RxBus.publish(new Event(Events.NET_DISCONNECTED));
}
destroyService();
}, err -> {
Log.e("Token Refresh Login failed %s", err.getMessage());
RxBus.publish(new Event(Events.PERMANENT_FAIL));
destroyService();
}));
}
@Override
public void onDestroy() {
instance = null;
super.onDestroy();
}
private void destroyService() {
Log.e("Self-update service destroyed");
stopSelf();
}
}
......@@ -26,6 +26,7 @@ public class Constants {
public static final String APP_DETAIL_URL = "https://play.google.com/store/apps/details?id=";
public static final String APP_ICON_URL = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png";
public static final String UPDATE_URL = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/updates.json";
public static final String DISPENSER_PRIMARY = "http://auroraoss.com:8080";
public static final String INTENT_DEVICE_NAME = "INTENT_DEVICE_NAME";
public static final String INTENT_DEVICE_INDEX = "INTENT_DEVICE_INDEX";
......@@ -97,13 +98,16 @@ public class Constants {
public static final String PREFERENCE_LOCALE_LIST = "PREFERENCE_LOCALE_LIST";
public static final String PREFERENCE_LOCALE_COUNTRY = "PREFERENCE_LOCALE_COUNTRY";
public static final String PREFERENCE_UPDATES_INTERVAL = "PREFERENCE_UPDATES_INTERVAL";
public static final String PREFERENCE_UPDATES_AUTO = "PREFERENCE_UPDATES_AUTO";
public static final String PREFERENCE_UPDATES_EXTENDED = "PREFERENCE_UPDATES_EXTENDED";
public static final String PREFERENCE_ENABLE_CUSTOM_TOKENIZER = "PREFERENCE_ENABLE_CUSTOM_TOKENIZER";
public static final String PREFERENCE_CUSTOM_TOKENIZER = "PREFERENCE_CUSTOM_TOKENIZER";
public static final String PREFERENCE_CUSTOM_TOKENIZER_URL = "PREFERENCE_CUSTOM_TOKENIZER_URL";
public static final String PREFERENCE_TOKENIZER_URL = "PREFERENCE_TOKENIZER_URL";
public static final String PREFERENCE_CACHE_STRATEGY = "PREFERENCE_CACHE_STRATEGY";
public static final String PREFERENCE_CLEAN_JUNK = "PREFERENCE_CLEAN_JUNK";
public static final String PREFERENCE_INSTALLATION_ABANDON_SESSION = "PREFERENCE_INSTALLATION_ABANDON_SESSION";
public static final String PREFERENCE_INSTALLATION_PROFILE = "PREFERENCE_INSTALLATION_PROFILE";
public static final String PREFERENCE_ACCOUNTS_PASSWORD_SWITCH = "PREFERENCE_ACCOUNTS_PASSWORD_SWITCH";
public static final String PREFERENCE_TOP_APPS = "PREFERENCE_TOP_APPS";
public static final String PREFERENCE_TOP_GAMES = "PREFERENCE_TOP_GAMES";
......
......@@ -27,6 +27,7 @@ public enum ErrorType {
NO_SEARCH,
NO_DOWNLOADS,
UNKNOWN,
SESSION_EXPIRED,
APP_NOT_FOUND,
LOGOUT_ERR,
IMPORT
......
......@@ -25,22 +25,31 @@ package com.aurora.store;
import android.content.Context;
import com.aurora.store.utility.PrefUtil;
import com.aurora.store.utility.Util;
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
public class TokenDispenserMirrors {
static private String[] mirrors = new String[]{
"http://www.auroraoss.com:8080",
"http://www.auroraoss.com:8880",
"http://www.auroraoss.com:2095"
};
private static List<String> dispenserList = new ArrayList<>();
public String get(Context context) {
static {
dispenserList.add("http://auroraoss.com:8080");
dispenserList.add("http://auroraoss.in:8080");
//dispenserList.add("http://92.42.46.11:8080");
//dispenserList.add("https://token-dispenser.calyxinstitute.org");
}
public static String get(Context context) {
if (Util.isCustomTokenizerEnabled(context))
return Util.getCustomTokenizerURL(context);
else
return mirrors[new Random().nextInt(mirrors.length)];
return Util.getTokenizerURL(context);
}
public static void setNextDispenser(Context context, int dispenserNum) {
PrefUtil.putString(context, Constants.PREFERENCE_TOKENIZER_URL, dispenserList.get(dispenserNum % dispenserList.size()));
}
}
......@@ -21,6 +21,7 @@
package com.aurora.store.activity;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
......@@ -51,6 +52,16 @@ public class AccountsActivity extends AppCompatActivity {
init();
}
@Override
public boolean onOptionsItemSelected(final MenuItem menuItem) {
if (menuItem.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return false;
}
@Override
protected void onResume() {
super.onResume();
......
......@@ -132,10 +132,10 @@ public class AuroraActivity extends AppCompatActivity {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Bundle mBundle = intent.getExtras();
if (mBundle != null)
fragmentCur = mBundle.getInt(Constants.INTENT_FRAGMENT_POSITION);
if (intent.getScheme() != null && intent.getScheme().equals("market")) {
Bundle bundle = intent.getExtras();
if (bundle != null)
fragmentCur = bundle.getInt(Constants.INTENT_FRAGMENT_POSITION);
else if (intent.getScheme() != null && intent.getScheme().equals("market")) {
fragmentCur = 2;
isSearchIntent = true;
if (intent.getData() != null)
......
package com.aurora.store.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.aurora.store.R;
import com.aurora.store.task.AuthTask;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.Util;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
public class GoogleLoginActivity extends Activity {
public static final String EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup";
public static final String OAUTH_TOKEN = "oauth_token";
@BindView(R.id.webview)
WebView webview;
private CookieManager cookieManager = CookieManager.getInstance();
private CompositeDisposable disposable = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
setupWebView();
}
@SuppressLint("SetJavaScriptEnabled")
private void setupWebView() {
cookieManager.removeAllCookies(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
webview.getSettings().setSafeBrowsingEnabled(false);
}
webview.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
String cookies = CookieManager.getInstance().getCookie(url);
Map<String, String> cookieMap = Util.parseCookieString(cookies);
if (!cookieMap.isEmpty() && cookieMap.get(OAUTH_TOKEN) != null) {
String oauth_token = cookieMap.get(OAUTH_TOKEN);
webview.evaluateJavascript("(function() { return document.getElementById('profileIdentifier').innerHTML; })();",
email -> {
generateTokens(email, oauth_token);
});
}
}
});
webview.getSettings().setAllowContentAccess(true);
webview.getSettings().setDatabaseEnabled(true);
webview.getSettings().setDomStorageEnabled(true);
webview.getSettings().setJavaScriptEnabled(true);
cookieManager.acceptThirdPartyCookies(webview);
cookieManager.setAcceptThirdPartyCookies(webview, true);
webview.loadUrl(EMBEDDED_SETUP_URL);
}
private void generateTokens(String email, String token) {
disposable.add(Observable.fromCallable(() -> new AuthTask(this).getAASToken(email, token))
.map(aas_token -> new AuthTask(this).getAuthToken(email, aas_token))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(success -> {
if (success) {
Toast.makeText(this, getText(R.string.toast_login_success), Toast.LENGTH_SHORT).show();
Accountant.setAnonymous(this, false);
Intent intent = new Intent(this, AuroraActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(this);
startActivity(intent, activityOptions.toBundle());
finish();
} else {
Toast.makeText(this, getText(R.string.toast_login_failed), Toast.LENGTH_LONG).show();
}
}, err -> {
err.printStackTrace();
Toast.makeText(this, getText(R.string.toast_login_failed), Toast.LENGTH_LONG).show();
}));
}
}
......@@ -32,6 +32,7 @@ import androidx.appcompat.widget.Toolbar;
import com.aurora.store.R;
import com.aurora.store.adapter.ViewPagerAdapter;
import com.aurora.store.fragment.AccountsFragment;
import com.aurora.store.fragment.intro.LoginFragment;
import com.aurora.store.fragment.intro.PermissionFragment;
import com.aurora.store.fragment.intro.WelcomeFragment;
import com.aurora.store.utility.Accountant;
......@@ -101,7 +102,7 @@ public class IntroActivity extends AppCompatActivity {
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPagerAdapter.addFragment(0, new WelcomeFragment());
viewPagerAdapter.addFragment(1, new PermissionFragment());
viewPagerAdapter.addFragment(2, new AccountsFragment());
viewPagerAdapter.addFragment(2, new LoginFragment());
viewPager.setAdapter(viewPagerAdapter);
viewPager.setScroll(false);
}
......
/*
* Aurora Store
* Copyright (C) 2019, 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.api;
import com.dragons.aurora.playstoreapiv2.DocV2;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import com.dragons.aurora.playstoreapiv2.IteratorGooglePlayException;
import com.dragons.aurora.playstoreapiv2.ListResponse;
import com.dragons.aurora.playstoreapiv2.Payload;
import com.dragons.aurora.playstoreapiv2.SearchResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
abstract public class AppListIterator2 implements Iterator {
protected GooglePlayAPI googlePlayApi;
protected boolean firstQuery = true;
protected String firstPageUrl;
protected String nextPageUrl;
public AppListIterator2(GooglePlayAPI googlePlayApi) {
setGooglePlayApi(googlePlayApi);
}
public void setGooglePlayApi(GooglePlayAPI googlePlayApi) {
this.googlePlayApi = googlePlayApi;
}
public List<DocV2> next() {
Payload payload;
DocV2 rootDoc;
try {
payload = getPayload();
rootDoc = getRootDoc(payload);
this.firstQuery = false;
} catch (IOException e) {
throw new IteratorGooglePlayException(e);
}
nextPageUrl = findNextPageUrl(payload);
if (null == nextPageUrl && null != rootDoc) {
nextPageUrl = findNextPageUrl(rootDoc);
}
if (nextPageStartsFromZero()) {
return next();
}
if (null != rootDoc) {
return rootDoc.getChildList();
} else {
return new ArrayList<DocV2>();
}
}
public boolean hasNext() {
return this.firstQuery || (null != this.nextPageUrl && this.nextPageUrl.length() > 0);
}
public void remove() {
throw new UnsupportedOperationException();
}
protected Payload getPayload() throws IOException {
String url;
if (firstQuery && null != firstPageUrl) {
url = firstPageUrl;
} else if (null != nextPageUrl && nextPageUrl.length() > 0) {
url = nextPageUrl;
} else {
throw new NoSuchElementException();
}
return googlePlayApi.genericGet(url, null);
}
protected String findNextPageUrl(Payload payload) {
if (null == payload) {
return null;
}
if (payload.hasSearchResponse()) {
return findNextPageUrl(payload.getSearchResponse());
} else if (payload.hasListResponse()) {
return findNextPageUrl(payload.getListResponse());
}
return null;
}
protected String findNextPageUrl(SearchResponse searchResponse) {
if (searchResponse.hasNextPageUrl()) {
return GooglePlayAPI.FDFE_URL + searchResponse.getNextPageUrl();
} else if (searchResponse.getDocCount() > 0) {
return findNextPageUrl(searchResponse.getDoc(0));
}
return null;
}
protected String findNextPageUrl(ListResponse listResponse) {
if (listResponse.getDocCount() > 0) {
return findNextPageUrl(listResponse.getDoc(0));
}
return null;
}
protected String findNextPageUrl(DocV2 rootDoc) {
if (rootDoc.hasContainerMetadata() && rootDoc.getContainerMetadata().hasNextPageUrl()) {
return GooglePlayAPI.FDFE_URL + rootDoc.getContainerMetadata().getNextPageUrl();
}
if (rootDoc.hasRelatedLinks()
&& rootDoc.getRelatedLinks().hasUnknown1()
&& rootDoc.getRelatedLinks().getUnknown1().hasUnknown2()
&& rootDoc.getRelatedLinks().getUnknown1().getUnknown2().hasNextPageUrl()
) {
return GooglePlayAPI.FDFE_URL + rootDoc.getRelatedLinks().getUnknown1().getUnknown2().getNextPageUrl();
}
for (DocV2 child : rootDoc.getChildList()) {
if (!isRootDoc(child)) {
continue;
}
String nextPageUrl = findNextPageUrl(child);
if (null != nextPageUrl) {
return nextPageUrl;
}
}
return null;
}
/**
* Sometimes not a list of apps is returned by search, but a list of content types (music and apps, for example)
* each of them having a list of items
* In this case we have to find the apps list and return it
*/
protected DocV2 getRootDoc(Payload payload) {
if (null == payload) {
return null;
}
if (payload.hasSearchResponse() && payload.getSearchResponse().getDocCount() > 0) {
return getRootDoc(payload.getSearchResponse().getDoc(0));
} else if (payload.hasListResponse() && payload.getListResponse().getDocCount() > 0) {
return getRootDoc(payload.getListResponse().getDoc(0));
}
return null;
}
protected DocV2 getRootDoc(DocV2 doc) {
if (isRootDoc(doc)) {
return doc;
}
for (DocV2 child : doc.getChildList()) {
DocV2 root = getRootDoc(child);
if (null != root) {
return root;
}
}
return null;
}
protected boolean isRootDoc(DocV2 doc) {
return doc.getChildCount() > 0 && doc.getChild(0).getBackendId() == 3 && doc.getChild(0).getDocType() == 1;
}
private boolean nextPageStartsFromZero() {
if (null == nextPageUrl) {
return false;
}
try {
return new URI(nextPageUrl).getQuery().contains("o=0");
} catch (URISyntaxException e) {
return false;
}
}
}
/*
* Aurora Store
* Copyright (C) 2019, 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.api;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import java.util.HashMap;
import java.util.Map;
public class CategoryAppsIterator2 extends AppListIterator2 {
private String categoryId;
public CategoryAppsIterator2(GooglePlayAPI googlePlayApi, String categoryId, GooglePlayAPI.SUBCATEGORY subcategory) {
super(googlePlayApi);
this.categoryId = categoryId;
String url = GooglePlayAPI.LIST_URL;
Map<String, String> params = new HashMap<String, String>();
params.put("cat", categoryId);
params.put("ctr", subcategory.value);
params.put("c", "3");
firstPageUrl = googlePlayApi.getClient().buildUrl(url, params);
}
public String getCategoryId() {
return categoryId;
}
}
\ No newline at end of file
......@@ -24,15 +24,10 @@
package com.aurora.store.api;
import android.content.Context;
import android.text.TextUtils;
import com.aurora.store.exception.CredentialsEmptyException;
import com.aurora.store.TokenDispenserMirrors;
import com.aurora.store.model.LoginInfo;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.ApiBuilderUtil;
import com.aurora.store.utility.Log;
import com.aurora.store.utility.PrefUtil;
import com.aurora.store.utility.Util;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import java.io.IOException;
......@@ -40,7 +35,7 @@ import java.io.IOException;
public class PlayStoreApiAuthenticator {
private static volatile PlayStoreApiAuthenticator instance;
private static volatile GooglePlayAPI api;
private static GooglePlayAPI api;
public PlayStoreApiAuthenticator() {
if (instance != null) {
......@@ -48,72 +43,46 @@ public class PlayStoreApiAuthenticator {
}
}
public static GooglePlayAPI getApi(Context context) {
public static GooglePlayAPI getApi() {
return api;
}
public static GooglePlayAPI getInstance(Context context) throws Exception {
if (instance == null) {
synchronized (PlayStoreApiAuthenticator.class) {
if (instance == null) {
instance = new PlayStoreApiAuthenticator();
api = instance.buildApi(context);
api = instance.getApi(context);
}
}
}
return api;
}
public static boolean login(Context context) throws IOException {
LoginInfo loginInfo = new LoginInfo();
GooglePlayAPI api = ApiBuilderUtil.build(context, loginInfo);
Util
.getPrefs(context.getApplicationContext()).edit()
.putBoolean(Accountant.DUMMY_ACCOUNT, true)
.putString(Accountant.ACCOUNT_EMAIL, loginInfo.getEmail())
.putString(Accountant.LAST_USED_TOKEN_DISPENSER, loginInfo.getTokenDispenserUrl())
.apply();
return api != null;
}
public static boolean login(Context context, String email, String password) throws IOException {
LoginInfo loginInfo = new LoginInfo();
loginInfo.setEmail(email);
loginInfo.setPassword(password);
GooglePlayAPI api = ApiBuilderUtil.build(context, loginInfo);
PrefUtil.remove(context, Accountant.DUMMY_ACCOUNT);
loginInfo.setAasToken(password);
GooglePlayAPI api = ApiBuilderUtil.buildApi(context, loginInfo);
return api != null;
}
public static boolean refreshToken(Context context) throws IOException {
PrefUtil.remove(context, Accountant.AUTH_TOKEN);
String email = PrefUtil.getString(context, Accountant.ACCOUNT_EMAIL);
if (TextUtils.isEmpty(email)) {
throw new CredentialsEmptyException();
}
public static GooglePlayAPI login(Context context) throws IOException {
LoginInfo loginInfo = new LoginInfo();
loginInfo.setEmail(email);
loginInfo.setTokenDispenserUrl(PrefUtil.getString(context, Accountant.LAST_USED_TOKEN_DISPENSER));
GooglePlayAPI api = ApiBuilderUtil.build(context, loginInfo);
PrefUtil.putBoolean(context, Accountant.DUMMY_ACCOUNT, true);
PrefUtil.putString(context, Accountant.LAST_USED_TOKEN_DISPENSER, loginInfo.getTokenDispenserUrl());
return api != null;
loginInfo.setTokenDispenserUrl(TokenDispenserMirrors.get(context));
GooglePlayAPI api = ApiBuilderUtil.buildAnonymousApi(context, loginInfo);
return api;
}
public static void logout(Context context) {
PrefUtil.remove(context, Accountant.ACCOUNT_EMAIL);
PrefUtil.remove(context, Accountant.GSF_ID);
PrefUtil.remove(context, Accountant.AUTH_TOKEN);
PrefUtil.remove(context, (Accountant.LAST_USED_TOKEN_DISPENSER));
PrefUtil.remove(context, Accountant.DUMMY_ACCOUNT);
public static void destroyInstance() {
api = null;
instance = null;
}
private synchronized GooglePlayAPI buildApi(Context context) {
private synchronized GooglePlayAPI getApi(Context context) throws Exception {
if (api == null) {
try {
api = ApiBuilderUtil.buildFromPreferences(context);
} catch (Exception e) {
Log.e("Error building API -> %s", e.getMessage());
}
api = ApiBuilderUtil.buildFromPreferences(context);
}
return api;
}
}
}
\ No newline at end of file
/*
* Aurora Store
* Copyright (C) 2019, Rahul Kumar Patel <whyorean@gmail.com>
*
* Raccoon 4
* Copyright 2019 Patrick Ahlbrecht
*
* 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.api;
import com.dragons.aurora.playstoreapiv2.DocV2;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import com.dragons.aurora.playstoreapiv2.Payload;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SearchIterator2 extends AppListIterator2 {
public SearchIterator2(GooglePlayAPI googlePlayApi, String query) {
super(googlePlayApi);
String url = GooglePlayAPI.SEARCH_URL;
Map<String, String> params = new HashMap<>();
params.put("c", "3");
params.put("q", query);
firstPageUrl = googlePlayApi.getClient().buildUrl(url, params);
}
@Override
public List<DocV2> next() {
try {
Payload payload = getPayload();
DocV2 rootDoc = getRootDoc(payload);
SearchResultParser searchEngineResultPage = new SearchResultParser(SearchResultParser.SEARCH);
searchEngineResultPage.append(rootDoc);
nextPageUrl = searchEngineResultPage.getNextPageUrl();
firstQuery = false;
return searchEngineResultPage.getDocList();
} catch (IOException e) {
return new ArrayList<>();
}
}
@Override
protected boolean isRootDoc(DocV2 doc) {
return doc != null && doc.getBackendId() == 3;
}
}
/*
* Aurora Store
* Copyright (C) 2019, Rahul Kumar Patel <whyorean@gmail.com>
*
* Raccoon 4
* Copyright 2019 Patrick Ahlbrecht
*
* 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.api;
import com.aurora.store.utility.ResponseUtil;
import com.dragons.aurora.playstoreapiv2.DocV2;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import com.dragons.aurora.playstoreapiv2.PreFetch;
import com.dragons.aurora.playstoreapiv2.ResponseWrapper;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.ArrayList;
import java.util.List;
public class SearchResultParser {
public static final int ALL = 0;
public static final int SEARCH = 1;
public static final int SIMILAR = 2;
public static final int RELATED = 3;
private ArrayList<DocV2> items;
private String nextPageUrl;
private String title;
private int type;
public SearchResultParser(int type) {
this.items = new ArrayList<>();
this.nextPageUrl = null;
this.type = type;
}
public String getNextPageUrl() {
return GooglePlayAPI.FDFE_URL + nextPageUrl;
}
public void append(ResponseWrapper rw) {
append(ResponseUtil.searchResponse(rw).getDocList());
append(ResponseUtil.listResponse(rw).getDocList());
for (PreFetch pf : rw.getPreFetchList()) {
try {
append(ResponseWrapper.parseFrom(pf.getResponse().toByteString()));
} catch (InvalidProtocolBufferException ignored) {
}
}
}
private void append(List<DocV2> list) {
for (DocV2 doc : list) {
append(doc);
}
}
public void append(DocV2 doc) {
switch (doc.getDocType()) {
case 46: {
for (DocV2 child : doc.getChildList()) {
if (accept(child)) {
append(child);
}
}
break;
}
case 45: {
items.addAll(doc.getChildList());
nextPageUrl = null;
if (doc.hasContainerMetadata()) {
nextPageUrl = doc.getContainerMetadata().getNextPageUrl();
}
if (title == null && doc.hasTitle()) {
title = doc.getTitle();
}
break;
}
default: {
for (DocV2 child : doc.getChildList()) {
append(child);
}
break;
}
}
}
private boolean accept(DocV2 doc) {
String pattern = doc.getBackendDocid();
switch (type) {
case ALL: {
return true;
}
case SEARCH: {
return (pattern != null && pattern.matches(".*search.*"));
}
case SIMILAR: {
return (pattern != null && pattern.matches("similar_apps"));
}
case RELATED: {
return (pattern != null && pattern
.matches("pre_install_users_also_installed"));
}
default: {
return false;
}
}
}
public List<DocV2> getDocList() {
return items;
}
}
/*
* Aurora Store
* Copyright (C) 2019, 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.api;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
public class UrlIterator2 extends AppListIterator2 {
public UrlIterator2(GooglePlayAPI googlePlayApi) {
super(googlePlayApi);
}
public UrlIterator2(GooglePlayAPI googlePlayApi, String firstPageUrl) {
this(googlePlayApi);
if (!firstPageUrl.startsWith(GooglePlayAPI.FDFE_URL)) {
firstPageUrl = GooglePlayAPI.FDFE_URL + firstPageUrl;
}
this.firstPageUrl = firstPageUrl;
}
}
......@@ -22,7 +22,6 @@ package com.aurora.store.fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
......@@ -34,60 +33,48 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.aurora.store.GlideApp;
import com.aurora.store.R;
import com.aurora.store.activity.AuroraActivity;
import com.aurora.store.activity.IntroActivity;
import com.aurora.store.activity.GoogleLoginActivity;
import com.aurora.store.activity.SettingsActivity;
import com.aurora.store.api.PlayStoreApiAuthenticator;
import com.aurora.store.events.Event;
import com.aurora.store.events.Events;
import com.aurora.store.events.RxBus;
import com.aurora.store.exception.TokenizerException;
import com.aurora.store.exception.TwoFactorAuthException;
import com.aurora.store.exception.UnknownException;
import com.aurora.store.task.UserProfiler;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.ContextUtil;
import com.aurora.store.utility.Log;
import com.aurora.store.utility.PrefUtil;
import com.aurora.store.utility.ViewUtil;
import com.dragons.aurora.playstoreapiv2.ApiBuilderException;
import com.dragons.aurora.playstoreapiv2.GooglePlayAPI;
import com.dragons.aurora.playstoreapiv2.Image;
import com.dragons.aurora.playstoreapiv2.UserProfile;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.chip.Chip;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText;
import org.jetbrains.annotations.NotNull;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import static com.aurora.store.utility.ContextUtil.runOnUiThread;
public class AccountsFragment extends Fragment {
private static final String URL_TOS = "https://www.google.com/mobile/android/market-tos.html";
private static final String URL_LICENSE = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/LICENSE";
private static final String URL_DISCLAIMER = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/DISCLAIMER";
private static final String URL_APP_PASS = "https://myaccount.google.com/apppasswords";
@BindView(R.id.view_switcher_top)
ViewSwitcher mViewSwitcherTop;
@BindView(R.id.view_switcher_bottom)
ViewSwitcher mViewSwitcherBottom;
@BindView(R.id.view_switcher_login)
ViewSwitcher mViewSwitcherLogin;
@BindView(R.id.init)
LinearLayout initLayout;
@BindView(R.id.info)
......@@ -98,8 +85,6 @@ public class AccountsFragment extends Fragment {
LinearLayout logoutLayout;
@BindView(R.id.login_google)
RelativeLayout loginGoogle;
@BindView(R.id.login_dummy)
RelativeLayout loginDummy;
@BindView(R.id.avatar)
ImageView imgAvatar;
@BindView(R.id.user_name)
......@@ -110,30 +95,25 @@ public class AccountsFragment extends Fragment {
TextInputEditText txtInputEmail;
@BindView(R.id.txt_input_password)
TextInputEditText txtInputPassword;
@BindView(R.id.chip_google)
Chip chipGoogle;
@BindView(R.id.chip_dummy)
Chip chipDummy;
@BindView(R.id.progress_bar)
ProgressBar progressBar;
@BindView(R.id.btn_positive)
Button btnPositive;
@BindView(R.id.btn_positive_alt)
Button btnPositiveAlt;
@BindView(R.id.btn_negative)
Button btnNegative;
@BindView(R.id.btn_anonymous)
Button btnAnonymous;
@BindView(R.id.chip_tos)
Chip chipTos;
@BindView(R.id.chip_disclaimer)
Chip chipDisclaimer;
@BindView(R.id.chip_license)
Chip chipLicense;
int colorAccent;
int colorPrimary;
@BindView(R.id.check_save_password)
MaterialCheckBox materialCheckBox;
private Context context;
private CompositeDisposable disposable = new CompositeDisposable();
private boolean isDummy = true;
private boolean isLoggedIn = false;
@Override
public void onAttach(@NotNull Context context) {
......@@ -145,15 +125,8 @@ public class AccountsFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_accounts, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
colorAccent = ViewUtil.getStyledAttribute(context, R.attr.colorAccent);
colorPrimary = ViewUtil.getStyledAttribute(context, android.R.attr.colorForeground);
return view;
}
@Override
......@@ -162,27 +135,49 @@ public class AccountsFragment extends Fragment {
disposable.clear();
}
private void init() {
isLoggedIn = Accountant.isLoggedIn(context);
isDummy = Accountant.isDummy(context);
progressBar.setVisibility(View.INVISIBLE);
setupView();
setupChips();
setupAccountType();
setupActions();
@OnClick(R.id.btn_positive)
public void openLoginActivity() {
context.startActivity(new Intent(context, GoogleLoginActivity.class));
if (getActivity() instanceof SettingsActivity)
((SettingsActivity) getActivity()).finish();
}
private void setupView() {
if (isLoggedIn) {
if (isDummy)
loadDummyData();
else
loadGoogleData();
}
@OnClick(R.id.btn_negative)
public void clearAccountantData() {
Accountant.completeCheckout(context);
init();
}
@OnClick(R.id.btn_anonymous)
public void loginAnonymous() {
disposable.add(Observable.fromCallable(() -> PlayStoreApiAuthenticator
.login(context))
.map(api -> api.userProfile().getUserProfile())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> {
btnAnonymous.setText(getText(R.string.action_logging_in));
btnAnonymous.setEnabled(false);
progressBar.setVisibility(View.VISIBLE);
})
.doOnComplete(() -> ContextUtil.runOnUiThread(() -> resetAnonymousLogin()))
.subscribe(userProfile -> {
Toast.makeText(context, "Successfully logged in", Toast.LENGTH_LONG).show();
Accountant.setAnonymous(context, true);
updateUI(userProfile);
}, err -> {
Toast.makeText(context, err.getMessage(), Toast.LENGTH_LONG).show();
ContextUtil.runOnUiThread(() -> resetAnonymousLogin());
}));
}
private void init() {
boolean isLoggedIn = Accountant.isLoggedIn(context);
progressBar.setVisibility(View.INVISIBLE);
switchTopViews(isLoggedIn);
switchLoginViews(!isDummy);
switchBottomViews(isLoggedIn);
switchButtonState(isLoggedIn);
setupChips();
setupProfile();
}
private void setupChips() {
......@@ -197,48 +192,6 @@ public class AccountsFragment extends Fragment {
});
}
private void setupAccountType() {
chipGoogle.setEnabled(!isLoggedIn);
chipDummy.setEnabled(!isLoggedIn);
}
private void setupActions() {
btnPositive.setOnClickListener(loginListener());
btnPositiveAlt.setOnClickListener(loginListener());
btnNegative.setOnClickListener(logoutListener());
chipGoogle.setOnClickListener(v -> {
isDummy = false;
chipGoogle.setChipStrokeColor(ColorStateList.valueOf(colorAccent));
chipDummy.setChipStrokeColor(ColorStateList.valueOf(colorPrimary));
if (!Accountant.isLoggedIn(context))
switchLoginViews(true);
});
chipDummy.setOnClickListener(v -> {
isDummy = true;
chipDummy.setChipStrokeColor(ColorStateList.valueOf(colorAccent));
chipGoogle.setChipStrokeColor(ColorStateList.valueOf(colorPrimary));
if (!Accountant.isLoggedIn(context))
switchLoginViews(false);
});
}
private void loadDummyData() {
imgAvatar.setImageDrawable(context.getDrawable(R.drawable.ic_avatar_boy));
txtName.setText(Accountant.getUserName(context));
txtMail.setText(Accountant.getEmail(context));
}
private void loadGoogleData() {
GlideApp
.with(this)
.load(Accountant.getImageURL(context))
.circleCrop()
.into(imgAvatar);
txtName.setText(Accountant.getUserName(context));
txtMail.setText(Accountant.getEmail(context));
}
private void switchTopViews(boolean showInfo) {
if (mViewSwitcherTop.getCurrentView() == initLayout && showInfo)
mViewSwitcherTop.showNext();
......@@ -253,133 +206,32 @@ public class AccountsFragment extends Fragment {
mViewSwitcherBottom.showPrevious();
}
private void switchLoginViews(boolean showGoogle) {
if (mViewSwitcherLogin.getCurrentView() == loginGoogle && !showGoogle)
mViewSwitcherLogin.showNext();
else if (mViewSwitcherLogin.getCurrentView() == loginDummy && showGoogle)
mViewSwitcherLogin.showPrevious();
}
private void switchButtonState(boolean logging) {
btnPositive.setText(logging ? R.string.action_logging_in : R.string.action_login);
btnPositiveAlt.setText(logging ? R.string.action_logging_in : R.string.action_login);
btnPositive.setEnabled(!logging);
btnPositiveAlt.setEnabled(!logging);
}
private View.OnClickListener logoutListener() {
return v -> {
Accountant.completeCheckout(context);
init();
};
}
private View.OnClickListener loginListener() {
return v -> {
if (isDummy) {
logInWithDummy();
} else {
final String email = txtInputEmail.getText().toString();
final String password = txtInputPassword.getText().toString();
if (email.isEmpty())
txtInputEmail.setError("?");
if (password.isEmpty())
txtInputPassword.setError("?");
if (!email.isEmpty() && !password.isEmpty())
logInWithGoogle(email, password);
private void updateUI(UserProfile userProfile) {
PrefUtil.putString(context, Accountant.PROFILE_NAME, userProfile.getName());
for (Image image : userProfile.getImageList()) {
if (image.getImageType() == GooglePlayAPI.IMAGE_TYPE_APP_ICON) {
PrefUtil.putString(context, Accountant.PROFILE_AVATAR, image.getImageUrl());
}
};
}
private void logInWithDummy() {
switchButtonState(true);
disposable.add(Observable.fromCallable(() ->
PlayStoreApiAuthenticator.login(context))
.subscribeOn(Schedulers.io())
.doOnSubscribe(sub -> progressBar.setVisibility(View.VISIBLE))
.observeOn(Schedulers.computation())
.subscribe((success) -> {
if (success) {
Log.i("Dummy Login Successful");
RxBus.publish(new Event(Events.LOGGED_IN));
runOnUiThread(() -> {
Accountant.saveDummy(context);
init();
finishIntro();
});
} else {
Log.e("Dummy Login Failed Permanently");
switchButtonState(false);
}
}, err -> {
String error = err.getMessage();
if (err instanceof ApiBuilderException || err instanceof NullPointerException) {
error = "Invalid token dispenser url is provided";
} else if (err instanceof TokenizerException) {
error = "Anonymous login failed,try again later!";
}
ContextUtil.toastLong(context, error);
ContextUtil.runOnUiThread(this::init);
Log.e(err.getMessage());
}));
}
private void logInWithGoogle(String email, String password) {
switchButtonState(true);
disposable.add(Observable.fromCallable(() -> PlayStoreApiAuthenticator
.login(context, email, password))
.subscribeOn(Schedulers.io())
.doOnSubscribe(sub -> progressBar.setVisibility(View.VISIBLE))
.observeOn(AndroidSchedulers.mainThread())
.doOnTerminate(() -> progressBar.setVisibility(View.INVISIBLE))
.subscribe((success) -> {
if (success) {
Log.i("Google Login Successful");
RxBus.publish(new Event(Events.LOGGED_IN));
runOnUiThread(() -> {
Accountant.saveGoogle(context);
getUserInfo();
finishIntro();
});
} else {
Log.e("Google Login Failed Permanently");
switchButtonState(false);
}
}, err -> {
Log.e("Google Login failed : %s", err.getMessage());
ContextUtil.runOnUiThread(() -> switchButtonState(false));
if (err instanceof TwoFactorAuthException) {
show2FADialog();
} else if (err instanceof UnknownException) {
ContextUtil.toastLong(context, getString(R.string.toast_unknown_reason));
} else
txtInputPassword.setError("Check your password");
}));
}
setupProfile();
init();
}
private void getUserInfo() {
disposable.add(Observable.fromCallable(() ->
new UserProfiler(context).getUserProfile())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((profile) -> {
if (profile != null) {
PrefUtil.putString(context, Accountant.GOOGLE_NAME, profile.getName());
for (Image image : profile.getImageList()) {
if (image.getImageType() == GooglePlayAPI.IMAGE_TYPE_APP_ICON) {
PrefUtil.putString(context, Accountant.GOOGLE_URL, image.getImageUrl());
}
}
runOnUiThread(this::init);
}
}, err -> Log.e("Google Login failed : %s", err.getMessage())));
private void setupProfile() {
GlideApp
.with(this)
.load(Accountant.getImageURL(context))
.placeholder(R.drawable.ic_avatar_boy)
.circleCrop()
.into(imgAvatar);
txtName.setText(Accountant.isAnonymous(context) ? getText(R.string.account_dummy) : Accountant.getUserName(context));
txtMail.setText(Accountant.isAnonymous(context) ? "auroraoss@gmail.com" : Accountant.getEmail(context));
}
private void finishIntro() {
if (getActivity() instanceof IntroActivity) {
getActivity().startActivity(new Intent(context, AuroraActivity.class));
getActivity().finish();
}
private void resetAnonymousLogin() {
btnAnonymous.setEnabled(true);
btnAnonymous.setText(getText(R.string.account_dummy));
progressBar.setVisibility(View.INVISIBLE);
}
private void openWebView(String URL) {
......@@ -389,18 +241,4 @@ public class AccountsFragment extends Fragment {
Log.e("No WebView found !");
}
}
private void show2FADialog() {
MaterialAlertDialogBuilder mBuilder = new MaterialAlertDialogBuilder(context)
.setTitle(getString(R.string.dialog_2FA_title))
.setMessage(getString(R.string.dialog_2FA_desc))
.setPositiveButton(getString(R.string.dialog_2FA_positive), (dialog, which) -> {
openWebView(URL_APP_PASS);
})
.setNegativeButton(getString(R.string.action_later), (dialog, which) -> {
dialog.dismiss();
});
mBuilder.create();
mBuilder.show();
}
}
......@@ -22,7 +22,6 @@ package com.aurora.store.fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
......@@ -30,17 +29,13 @@ import android.widget.Button;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import com.aurora.store.AnonymousLoginService;
import com.aurora.store.AnonymousRefreshService;
import com.aurora.store.ErrorType;
import com.aurora.store.R;
import com.aurora.store.activity.AccountsActivity;
import com.aurora.store.api.PlayStoreApiAuthenticator;
import com.aurora.store.api.SearchIterator2;
import com.aurora.store.events.Event;
import com.aurora.store.events.Events;
import com.aurora.store.events.RxBus;
......@@ -48,7 +43,6 @@ import com.aurora.store.exception.CredentialsEmptyException;
import com.aurora.store.exception.InvalidApiException;
import com.aurora.store.exception.MalformedRequestException;
import com.aurora.store.exception.TooManyRequestsException;
import com.aurora.store.iterator.CustomAppListIterator;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.ContextUtil;
import com.aurora.store.utility.Log;
......@@ -68,7 +62,6 @@ import static com.aurora.store.utility.Util.noNetwork;
public abstract class BaseFragment extends Fragment {
protected CustomAppListIterator iterator;
protected CompositeDisposable disposable = new CompositeDisposable();
@BindView(R.id.coordinator)
......@@ -80,21 +73,9 @@ public abstract class BaseFragment extends Fragment {
@BindView(R.id.err_view)
ViewGroup layoutError;
private SearchIterator2 searchIterator;
private CompositeDisposable disposableBus = new CompositeDisposable();
private Context context;
CustomAppListIterator getIterator(String query) {
try {
searchIterator = new SearchIterator2(PlayStoreApiAuthenticator.getApi(context), query);
iterator = new CustomAppListIterator(searchIterator);
return iterator;
} catch (Exception e) {
processException(e);
return null;
}
}
protected abstract View.OnClickListener errRetry();
protected abstract void fetchData();
......@@ -126,6 +107,8 @@ public abstract class BaseFragment extends Fragment {
}
protected void notifyTokenExpired() {
setErrorView(ErrorType.SESSION_EXPIRED);
switchViews(true);
notifyStatus(coordinatorLayout, context.getString(R.string.action_token_expired));
}
......@@ -140,14 +123,12 @@ public abstract class BaseFragment extends Fragment {
Events eventEnum = ((Event) event).getEvent();
switch (eventEnum) {
case LOGGED_IN:
case TOKEN_REFRESHED:
notifyLoggedIn();
break;
case LOGGED_OUT:
notifyLoggedOut();
break;
case TOKEN_REFRESHED:
notifyLoggedIn();
break;
case TOKEN_EXPIRED:
notifyTokenExpired();
break;
......@@ -168,19 +149,6 @@ public abstract class BaseFragment extends Fragment {
this.context = context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDestroy() {
super.onDestroy();
searchIterator = null;
iterator = null;
disposable.clear();
}
@Override
public void onStop() {
super.onStop();
......@@ -214,10 +182,7 @@ public abstract class BaseFragment extends Fragment {
RxBus.publish(new Event(Events.LOGGED_IN));
return;
}
if (Accountant.isGoogle(context))
context.startActivity(new Intent(context, AccountsActivity.class));
else
logInWithDummy();
context.startActivity(new Intent(context, AccountsActivity.class));
};
}
......@@ -241,7 +206,6 @@ public abstract class BaseFragment extends Fragment {
/*Exception handling methods*/
protected void processException(Throwable e) {
disposable.clear();
if (e instanceof AuthException) {
processAuthException((AuthException) e);
} else if (e instanceof IteratorGooglePlayException) {
......@@ -260,53 +224,35 @@ public abstract class BaseFragment extends Fragment {
private void processIOException(IOException e) {
String message;
if (context != null) {
if (noNetwork(e)) {
message = context.getString(R.string.error_no_network);
Log.i(message);
RxBus.publish(new Event(Events.NET_DISCONNECTED));
} else {
message = TextUtils.isEmpty(e.getMessage())
? context.getString(R.string.error_network_other)
: e.getMessage();
Log.i(message);
}
} else Log.i("No Network Connection");
if (noNetwork(e)) {
message = context.getString(R.string.error_no_network);
Log.i(message);
RxBus.publish(new Event(Events.NET_DISCONNECTED));
} else {
message = TextUtils.isEmpty(e.getMessage())
? context.getString(R.string.error_network_other)
: e.getMessage();
Log.i(message);
}
}
private void processAuthException(AuthException e) {
if (e instanceof CredentialsEmptyException || e instanceof InvalidApiException) {
Log.i("Logged out");
Accountant.completeCheckout(context);
RxBus.publish(new Event(Events.LOGGED_OUT));
} else if (e.getCode() == 401 && Accountant.isDummy(context)) {
Log.i("Token is stale");
refreshToken();
} else if (e.getCode() == 429 && Accountant.isDummy(context)) {
Log.i("Too many requests from current session, requesting new login session");
} else if (e.getCode() >= 400 && Accountant.isAnonymous(context)) {
RxBus.publish(new Event(Events.TOKEN_EXPIRED));
Accountant.completeCheckout(context);
logInWithDummy();
} else {
ContextUtil.toast(context, R.string.error_incorrect_password);
PlayStoreApiAuthenticator.logout(context);
ContextUtil.toast(context, R.string.error_session_expired);
Accountant.completeCheckout(context);
}
}
/*
* Anonymous accounts handling methods
*
*/
private void logInWithDummy() {
Intent intent = new Intent(context, AnonymousLoginService.class);
if (!AnonymousLoginService.isServiceRunning())
context.startService(intent);
}
private void refreshToken() {
Intent intent = new Intent(context, AnonymousRefreshService.class);
if (!AnonymousRefreshService.isServiceRunning())
context.startService(intent);
}
}
......@@ -172,7 +172,8 @@ public class DetailsFragment extends BaseFragment {
public void drawButtons() {
if (PackageUtil.isInstalled(context, app))
app.setInstalled(true);
actionButton.draw();
if (actionButton != null)
actionButton.draw();
}
@Override
......