Instant verification (#974)
* add instant verification options and Google Play Billing bumpversion 0.15.16 --> 0.15.17 restore account_undergo_review default string twitter sign-in flow fix build error final changes update api key and secret * tweak build script
This commit is contained in:
parent
4d024c06cc
commit
6931dbe79c
29 changed files with 1042 additions and 72 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -60,6 +60,7 @@ buck-out/
|
||||||
|
|
||||||
# Other Files
|
# Other Files
|
||||||
app/google-services.json
|
app/google-services.json
|
||||||
|
app/twitter.properties
|
||||||
*.log
|
*.log
|
||||||
.vagrant
|
.vagrant
|
||||||
*.hprof
|
*.hprof
|
||||||
|
|
|
@ -11,7 +11,7 @@ build apk:
|
||||||
- apt-get -y update && apt-get -y install build-essential ca-certificates curl git gpg-agent openjdk-8-jdk software-properties-common wget zipalign
|
- apt-get -y update && apt-get -y install build-essential ca-certificates curl git gpg-agent openjdk-8-jdk software-properties-common wget zipalign
|
||||||
- chmod u+x $CI_PROJECT_DIR/gradlew
|
- chmod u+x $CI_PROJECT_DIR/gradlew
|
||||||
- export ANDROID_SDK_ROOT=~/.buildozer/android/platform/android-sdk-23
|
- export ANDROID_SDK_ROOT=~/.buildozer/android/platform/android-sdk-23
|
||||||
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1)
|
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- bin/browser-*-release__arm.apk
|
- bin/browser-*-release__arm.apk
|
||||||
|
@ -27,8 +27,8 @@ build apk:
|
||||||
- yarn
|
- yarn
|
||||||
- chmod u+x ./release.sh
|
- chmod u+x ./release.sh
|
||||||
- ./release.sh
|
- ./release.sh
|
||||||
- cp bin/browser-$BUILD_VERSION-release__arm.apk /dev/null
|
- cp bin/browser-$BUILD_VERSION-release__arm.apk $CI_PROJECT_DIR
|
||||||
- cp bin/browser-$BUILD_VERSION-release__arm64.apk /dev/null
|
- cp bin/browser-$BUILD_VERSION-release__arm64.apk $CI_PROJECT_DIR
|
||||||
|
|
||||||
deploy build.lbry.io:
|
deploy build.lbry.io:
|
||||||
image: python:stretch
|
image: python:stretch
|
||||||
|
@ -39,7 +39,7 @@ deploy build.lbry.io:
|
||||||
- apt-get -y update && apt-get -y install openjdk-8-jdk
|
- apt-get -y update && apt-get -y install openjdk-8-jdk
|
||||||
- pip install awscli
|
- pip install awscli
|
||||||
- chmod u+x $CI_PROJECT_DIR/gradlew
|
- chmod u+x $CI_PROJECT_DIR/gradlew
|
||||||
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1)
|
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
|
||||||
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
||||||
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
||||||
script:
|
script:
|
||||||
|
@ -58,7 +58,7 @@ release apk:
|
||||||
- apt-get -y update && apt-get -y install openjdk-8-jdk
|
- apt-get -y update && apt-get -y install openjdk-8-jdk
|
||||||
- pip install awscli githubrelease
|
- pip install awscli githubrelease
|
||||||
- chmod u+x $CI_PROJECT_DIR/gradlew
|
- chmod u+x $CI_PROJECT_DIR/gradlew
|
||||||
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -q printVersionName --console=plain | tail -1)
|
- export BUILD_VERSION=$($CI_PROJECT_DIR/gradlew -p $CI_PROJECT_DIR -q printVersionName --console=plain | tail -1)
|
||||||
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
||||||
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
||||||
script:
|
script:
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,2 +1,3 @@
|
||||||
lbry-android.keystore:0d958c531870694624cc877ea98ca1c583485f8ebbb3a5acca58b1930c190d65
|
lbry-android.keystore:0d958c531870694624cc877ea98ca1c583485f8ebbb3a5acca58b1930c190d65
|
||||||
app/google-services.json:896a0bee8294a36d061f10fa926129d8a780528b34d0a2f03113400c4246d67c
|
app/google-services.json:896a0bee8294a36d061f10fa926129d8a780528b34d0a2f03113400c4246d67c
|
||||||
|
app/twitter.properties:01212d70712f2041efb5c814bf30ecbf6f72e1ca5179c7647c4f8cbd995dd033
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import com.google.gms.googleservices.GoogleServicesPlugin
|
import com.google.gms.googleservices.GoogleServicesPlugin
|
||||||
|
|
||||||
|
Properties twitterProps = new Properties()
|
||||||
|
try {
|
||||||
|
twitterProps.load(project.file('twitter.properties').newDataInputStream())
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new GradleException("Missing twitter.properties.")
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -22,6 +29,10 @@ android {
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'META-INF/DEPENDENCIES'
|
||||||
|
}
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
__32bit {
|
__32bit {
|
||||||
versionCode android.defaultConfig.versionCode * 10 + 1
|
versionCode android.defaultConfig.versionCode * 10 + 1
|
||||||
|
@ -38,7 +49,13 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
resValue "string", "TWITTER_CONSUMER_KEY", "\"${twitterProps.getProperty("twitterConsumerKey")}\""
|
||||||
|
resValue "string", "TWITTER_CONSUMER_SECRET", "\"${twitterProps.getProperty("twitterConsumerSecret")}\""
|
||||||
|
}
|
||||||
release {
|
release {
|
||||||
|
resValue "string", "TWITTER_CONSUMER_KEY", "\"${twitterProps.getProperty("twitterConsumerKey")}\""
|
||||||
|
resValue "string", "TWITTER_CONSUMER_SECRET", "\"${twitterProps.getProperty("twitterConsumerSecret")}\""
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
|
@ -51,6 +68,13 @@ task printVersionName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
all {
|
||||||
|
exclude module: 'httpclient'
|
||||||
|
exclude module: 'commons-logging'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
@ -76,6 +100,9 @@ dependencies {
|
||||||
implementation 'com.google.firebase:firebase-analytics:17.4.2'
|
implementation 'com.google.firebase:firebase-analytics:17.4.2'
|
||||||
implementation 'com.google.android.gms:play-services-base:17.2.1'
|
implementation 'com.google.android.gms:play-services-base:17.2.1'
|
||||||
implementation 'com.google.firebase:firebase-messaging:20.2.0'
|
implementation 'com.google.firebase:firebase-messaging:20.2.0'
|
||||||
|
implementation 'com.google.oauth-client:google-oauth-client:1.30.4'
|
||||||
|
|
||||||
|
implementation 'com.android.billingclient:billing:3.0.0'
|
||||||
|
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.11.4'
|
implementation 'com.google.android.exoplayer:exoplayer-core:2.11.4'
|
||||||
|
|
Binary file not shown.
|
@ -10,6 +10,7 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|
|
@ -47,6 +47,13 @@ import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.android.billingclient.api.BillingClient;
|
||||||
|
import com.android.billingclient.api.BillingClientStateListener;
|
||||||
|
import com.android.billingclient.api.BillingResult;
|
||||||
|
import com.android.billingclient.api.ConsumeParams;
|
||||||
|
import com.android.billingclient.api.ConsumeResponseListener;
|
||||||
|
import com.android.billingclient.api.Purchase;
|
||||||
|
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.request.target.CustomTarget;
|
import com.bumptech.glide.request.target.CustomTarget;
|
||||||
import com.bumptech.glide.request.transition.Transition;
|
import com.bumptech.glide.request.transition.Transition;
|
||||||
|
@ -140,10 +147,13 @@ import io.lbry.browser.model.WalletBalance;
|
||||||
import io.lbry.browser.model.WalletSync;
|
import io.lbry.browser.model.WalletSync;
|
||||||
import io.lbry.browser.model.lbryinc.LbryNotification;
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
import io.lbry.browser.model.lbryinc.Reward;
|
import io.lbry.browser.model.lbryinc.Reward;
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
import io.lbry.browser.model.lbryinc.Subscription;
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
import io.lbry.browser.tasks.GenericTaskHandler;
|
import io.lbry.browser.tasks.GenericTaskHandler;
|
||||||
|
import io.lbry.browser.tasks.RewardVerifiedHandler;
|
||||||
import io.lbry.browser.tasks.claim.ClaimListResultHandler;
|
import io.lbry.browser.tasks.claim.ClaimListResultHandler;
|
||||||
import io.lbry.browser.tasks.claim.ClaimListTask;
|
import io.lbry.browser.tasks.claim.ClaimListTask;
|
||||||
|
import io.lbry.browser.tasks.lbryinc.AndroidPurchaseTask;
|
||||||
import io.lbry.browser.tasks.lbryinc.ClaimRewardTask;
|
import io.lbry.browser.tasks.lbryinc.ClaimRewardTask;
|
||||||
import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
|
import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
|
||||||
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
||||||
|
@ -192,6 +202,7 @@ import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements SdkStatusListener {
|
public class MainActivity extends AppCompatActivity implements SdkStatusListener {
|
||||||
|
|
||||||
|
static final String SKU_SKIP = "lbryskip";
|
||||||
private Map<String, Class> specialRouteFragmentClassMap;
|
private Map<String, Class> specialRouteFragmentClassMap;
|
||||||
@Getter
|
@Getter
|
||||||
private boolean inPictureInPictureMode;
|
private boolean inPictureInPictureMode;
|
||||||
|
@ -213,7 +224,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
public static boolean startingPermissionRequest = false;
|
public static boolean startingPermissionRequest = false;
|
||||||
public static boolean startingSignInFlowActivity = false;
|
public static boolean startingSignInFlowActivity = false;
|
||||||
|
|
||||||
|
private BillingClient billingClient;
|
||||||
@Getter
|
@Getter
|
||||||
private boolean enteringPIPMode = false;
|
private boolean enteringPIPMode = false;
|
||||||
private boolean fullSyncInProgress = false;
|
private boolean fullSyncInProgress = false;
|
||||||
|
@ -408,6 +419,24 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
// setup the billing client in main activity (to handle cases where the verification purchase flow may have been interrupted)
|
||||||
|
billingClient = BillingClient.newBuilder(this)
|
||||||
|
.setListener(new PurchasesUpdatedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {
|
||||||
|
int responseCode = billingResult.getResponseCode();
|
||||||
|
if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null)
|
||||||
|
{
|
||||||
|
for (Purchase purchase : purchases) {
|
||||||
|
handlePurchase(purchase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.enablePendingPurchases()
|
||||||
|
.build();
|
||||||
|
establishBillingClientConnection();
|
||||||
|
|
||||||
playerNotificationManager = new PlayerNotificationManager(
|
playerNotificationManager = new PlayerNotificationManager(
|
||||||
this, LbrynetService.NOTIFICATION_CHANNEL_ID, PLAYBACK_NOTIFICATION_ID, new PlayerNotificationDescriptionAdapter());
|
this, LbrynetService.NOTIFICATION_CHANNEL_ID, PLAYBACK_NOTIFICATION_ID, new PlayerNotificationDescriptionAdapter());
|
||||||
|
|
||||||
|
@ -1024,6 +1053,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
checkPurchases();
|
||||||
enteringPIPMode = false;
|
enteringPIPMode = false;
|
||||||
|
|
||||||
applyNavbarSigninPadding();
|
applyNavbarSigninPadding();
|
||||||
|
@ -1046,6 +1076,33 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkPurchases() {
|
||||||
|
if (billingClient != null) {
|
||||||
|
Purchase.PurchasesResult result = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
|
||||||
|
if (result.getPurchasesList() != null) {
|
||||||
|
for (Purchase purchase : result.getPurchasesList()) {
|
||||||
|
handlePurchase(purchase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePurchase(Purchase purchase) {
|
||||||
|
handleBillingPurchase(purchase, billingClient, MainActivity.this, null, new RewardVerifiedHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(RewardVerified rewardVerified) {
|
||||||
|
if (Lbryio.currentUser != null) {
|
||||||
|
Lbryio.currentUser.setRewardApproved(rewardVerified.isRewardApproved());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void checkPendingOpens() {
|
private void checkPendingOpens() {
|
||||||
if (pendingFollowingReload) {
|
if (pendingFollowingReload) {
|
||||||
loadFollowingContent();
|
loadFollowingContent();
|
||||||
|
@ -2536,8 +2593,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true);
|
startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// nope
|
// nopecd
|
||||||
android.util.Log.e(TAG, String.format("App startup failed: %s", ex.getMessage()), ex);
|
Log.e(TAG, String.format("App startup failed: %s", ex.getMessage()), ex);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
Helper.closeCloseable(reader);
|
Helper.closeCloseable(reader);
|
||||||
|
@ -3269,6 +3326,53 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
return (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
return (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void establishBillingClientConnection() {
|
||||||
|
if (billingClient != null) {
|
||||||
|
billingClient.startConnection(new BillingClientStateListener() {
|
||||||
|
@Override
|
||||||
|
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
|
||||||
|
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
|
||||||
|
// no need to do anything here. purchases are always checked server-side
|
||||||
|
checkPurchases();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBillingServiceDisconnected() {
|
||||||
|
establishBillingClientConnection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleBillingPurchase(
|
||||||
|
Purchase purchase,
|
||||||
|
BillingClient billingClient,
|
||||||
|
Context context,
|
||||||
|
View progressView,
|
||||||
|
RewardVerifiedHandler handler) {
|
||||||
|
String sku = purchase.getSku();
|
||||||
|
if (SKU_SKIP.equalsIgnoreCase(sku)) {
|
||||||
|
// send purchase token for verification
|
||||||
|
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
|
||||||
|
/*&& isSignatureValid(purchase)*/) {
|
||||||
|
// consume the purchase
|
||||||
|
String purchaseToken = purchase.getPurchaseToken();
|
||||||
|
ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchaseToken).build();
|
||||||
|
billingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
|
||||||
|
@Override
|
||||||
|
public void onConsumeResponse(@NonNull BillingResult billingResult, @NonNull String s) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// send the purchase token to the backend to complete verification
|
||||||
|
AndroidPurchaseTask task = new AndroidPurchaseTask(purchaseToken, progressView, context, handler);
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface BackPressInterceptor {
|
public interface BackPressInterceptor {
|
||||||
boolean onBackPressed();
|
boolean onBackPressed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,35 @@ import android.content.IntentFilter;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.android.billingclient.api.BillingClient;
|
||||||
|
import com.android.billingclient.api.BillingClientStateListener;
|
||||||
|
import com.android.billingclient.api.BillingFlowParams;
|
||||||
|
import com.android.billingclient.api.BillingResult;
|
||||||
|
import com.android.billingclient.api.Purchase;
|
||||||
|
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||||
|
import com.android.billingclient.api.SkuDetails;
|
||||||
|
import com.android.billingclient.api.SkuDetailsParams;
|
||||||
|
import com.android.billingclient.api.SkuDetailsResponseListener;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import io.lbry.browser.adapter.VerificationPagerAdapter;
|
import io.lbry.browser.adapter.VerificationPagerAdapter;
|
||||||
import io.lbry.browser.listener.SignInListener;
|
import io.lbry.browser.listener.SignInListener;
|
||||||
import io.lbry.browser.listener.WalletSyncListener;
|
import io.lbry.browser.listener.WalletSyncListener;
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
import io.lbry.browser.model.lbryinc.User;
|
import io.lbry.browser.model.lbryinc.User;
|
||||||
|
import io.lbry.browser.tasks.RewardVerifiedHandler;
|
||||||
import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask;
|
import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask;
|
||||||
import io.lbry.browser.utils.Helper;
|
import io.lbry.browser.utils.Helper;
|
||||||
import io.lbry.browser.utils.LbryAnalytics;
|
import io.lbry.browser.utils.LbryAnalytics;
|
||||||
|
@ -32,11 +48,59 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
public static final int VERIFICATION_FLOW_REWARDS = 2;
|
public static final int VERIFICATION_FLOW_REWARDS = 2;
|
||||||
public static final int VERIFICATION_FLOW_WALLET = 3;
|
public static final int VERIFICATION_FLOW_WALLET = 3;
|
||||||
|
|
||||||
|
private BillingClient billingClient;
|
||||||
private BroadcastReceiver sdkReceiver;
|
private BroadcastReceiver sdkReceiver;
|
||||||
private String email;
|
private String email;
|
||||||
private boolean signedIn;
|
private boolean signedIn;
|
||||||
private int flow;
|
private int flow;
|
||||||
|
|
||||||
|
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {
|
||||||
|
int responseCode = billingResult.getResponseCode();
|
||||||
|
if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null)
|
||||||
|
{
|
||||||
|
for (Purchase purchase : purchases) {
|
||||||
|
if (MainActivity.SKU_SKIP.equalsIgnoreCase(purchase.getSku())) {
|
||||||
|
showLoading();
|
||||||
|
MainActivity.handleBillingPurchase(
|
||||||
|
purchase,
|
||||||
|
billingClient,
|
||||||
|
VerificationActivity.this, null, new RewardVerifiedHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(RewardVerified rewardVerified) {
|
||||||
|
if (Lbryio.currentUser != null) {
|
||||||
|
Lbryio.currentUser.setRewardApproved(rewardVerified.isRewardApproved());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rewardVerified.isRewardApproved()) {
|
||||||
|
// show pending purchase message (possible slow card tx)
|
||||||
|
Snackbar.make(findViewById(R.id.verification_pager), R.string.purchase_request_pending, Snackbar.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Snackbar.make(findViewById(R.id.verification_pager), R.string.reward_verification_successful, Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
showFetchUserError(getString(R.string.purchase_request_failed_error));
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -73,6 +137,12 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
};
|
};
|
||||||
registerReceiver(sdkReceiver, filter);
|
registerReceiver(sdkReceiver, filter);
|
||||||
|
|
||||||
|
billingClient = BillingClient.newBuilder(this)
|
||||||
|
.setListener(purchasesUpdatedListener)
|
||||||
|
.enablePendingPurchases()
|
||||||
|
.build();
|
||||||
|
establishBillingClientConnection();
|
||||||
|
|
||||||
setContentView(R.layout.activity_verification);
|
setContentView(R.layout.activity_verification);
|
||||||
ViewPager2 viewPager = findViewById(R.id.verification_pager);
|
ViewPager2 viewPager = findViewById(R.id.verification_pager);
|
||||||
viewPager.setUserInputEnabled(false);
|
viewPager.setUserInputEnabled(false);
|
||||||
|
@ -89,6 +159,24 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void establishBillingClientConnection() {
|
||||||
|
if (billingClient != null) {
|
||||||
|
billingClient.startConnection(new BillingClientStateListener() {
|
||||||
|
@Override
|
||||||
|
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
|
||||||
|
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
|
||||||
|
// no need to do anything here. purchases are always checked server-side
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBillingServiceDisconnected() {
|
||||||
|
establishBillingClientConnection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
LbryAnalytics.setCurrentScreen(this, "Verification", "Verification");
|
LbryAnalytics.setCurrentScreen(this, "Verification", "Verification");
|
||||||
|
@ -104,11 +192,13 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
flowHandled = true;
|
flowHandled = true;
|
||||||
} else if (flow == VERIFICATION_FLOW_REWARDS) {
|
} else if (flow == VERIFICATION_FLOW_REWARDS) {
|
||||||
User user = Lbryio.currentUser;
|
User user = Lbryio.currentUser;
|
||||||
if (!user.isIdentityVerified()) {
|
// disable phone verification for now
|
||||||
|
/*if (!user.isIdentityVerified()) {
|
||||||
// phone number verification required
|
// phone number verification required
|
||||||
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
|
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
|
||||||
flowHandled = true;
|
flowHandled = true;
|
||||||
} else if (!user.isRewardApproved()) {
|
} else */
|
||||||
|
if (!user.isRewardApproved()) {
|
||||||
// manual verification required
|
// manual verification required
|
||||||
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
|
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
|
||||||
flowHandled = true;
|
flowHandled = true;
|
||||||
|
@ -195,10 +285,14 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
ViewPager2 viewPager = findViewById(R.id.verification_pager);
|
ViewPager2 viewPager = findViewById(R.id.verification_pager);
|
||||||
// for rewards, (show phone verification if not done, or manual verification if required)
|
// for rewards, (show phone verification if not done, or manual verification if required)
|
||||||
if (flow == VERIFICATION_FLOW_REWARDS) {
|
if (flow == VERIFICATION_FLOW_REWARDS) {
|
||||||
if (!user.isIdentityVerified()) {
|
// skipping phone verification
|
||||||
|
/*if (!user.isIdentityVerified()) {
|
||||||
// phone number verification required
|
// phone number verification required
|
||||||
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
|
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_PHONE, false);
|
||||||
} else if (!user.isRewardApproved()) {
|
} else
|
||||||
|
*/
|
||||||
|
if (!user.isRewardApproved()) {
|
||||||
|
|
||||||
// manual verification required
|
// manual verification required
|
||||||
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
|
viewPager.setCurrentItem(VerificationPagerAdapter.PAGE_VERIFICATION_MANUAL, false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -289,6 +383,54 @@ public class VerificationActivity extends FragmentActivity implements SignInList
|
||||||
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
|
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipQueueAction() {
|
||||||
|
if (billingClient != null) {
|
||||||
|
List<String> skuList = new ArrayList<>();
|
||||||
|
skuList.add(MainActivity.SKU_SKIP);
|
||||||
|
|
||||||
|
SkuDetailsParams detailsParams = SkuDetailsParams.newBuilder().
|
||||||
|
setType(BillingClient.SkuType.INAPP).
|
||||||
|
setSkusList(skuList).build();
|
||||||
|
billingClient.querySkuDetailsAsync(detailsParams, new SkuDetailsResponseListener() {
|
||||||
|
@Override
|
||||||
|
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
|
||||||
|
if (list != null && list.size() > 0) {
|
||||||
|
// we only queried one product, so it should be the first item in the list
|
||||||
|
SkuDetails skuDetails = list.get(0);
|
||||||
|
|
||||||
|
// launch the billing flow for skip queue
|
||||||
|
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().
|
||||||
|
setSkuDetails(skuDetails).build();
|
||||||
|
billingClient.launchBillingFlow(VerificationActivity.this, billingFlowParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTwitterVerified() {
|
||||||
|
Snackbar.make(findViewById(R.id.verification_pager), R.string.reward_verification_successful, Snackbar.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onManualProgress(boolean progress) {
|
||||||
|
if (progress) {
|
||||||
|
findViewById(R.id.verification_close_button).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
Helper.unregisterReceiver(sdkReceiver, this);
|
Helper.unregisterReceiver(sdkReceiver, this);
|
||||||
|
|
|
@ -7,4 +7,7 @@ public interface SignInListener {
|
||||||
void onPhoneAdded(String countryCode, String phoneNumber);
|
void onPhoneAdded(String countryCode, String phoneNumber);
|
||||||
void onPhoneVerified();
|
void onPhoneVerified();
|
||||||
void onManualVerifyContinue();
|
void onManualVerifyContinue();
|
||||||
|
void onSkipQueueAction();
|
||||||
|
void onTwitterVerified();
|
||||||
|
void onManualProgress(boolean progress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.lbry.browser.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TwitterOauth {
|
||||||
|
private String oauthToken;
|
||||||
|
private String oauthTokenSecret;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.lbry.browser.model.lbryinc;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RewardVerified {
|
||||||
|
private long userId;
|
||||||
|
private boolean isRewardApproved;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.lbry.browser.tasks;
|
||||||
|
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
|
|
||||||
|
public interface RewardVerifiedHandler {
|
||||||
|
void onSuccess(RewardVerified rewardVerified);
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.lbry.browser.tasks;
|
||||||
|
|
||||||
|
import io.lbry.browser.model.TwitterOauth;
|
||||||
|
|
||||||
|
public interface TwitterOauthHandler {
|
||||||
|
void onSuccess(TwitterOauth twitterOauth);
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package io.lbry.browser.tasks.lbryinc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.gson.FieldNamingPolicy;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.lbry.browser.MainActivity;
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
|
import io.lbry.browser.tasks.RewardVerifiedHandler;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class AndroidPurchaseTask extends AsyncTask<Void, Void, RewardVerified> {
|
||||||
|
private Context context;
|
||||||
|
private View progressView;
|
||||||
|
private String purchaseToken;
|
||||||
|
private RewardVerifiedHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public AndroidPurchaseTask(String purchaseToken, View progressView, Context context, RewardVerifiedHandler handler) {
|
||||||
|
this.purchaseToken = purchaseToken;
|
||||||
|
this.progressView = progressView;
|
||||||
|
this.context = context;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Helper.setViewVisibility(progressView, View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RewardVerified doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
Map<String, String> options = new HashMap<>();
|
||||||
|
options.put("purchase_token", purchaseToken);
|
||||||
|
|
||||||
|
JSONObject object = (JSONObject) Lbryio.parseResponse(Lbryio.call("verification", "android_purchase", options, context));
|
||||||
|
Type type = new TypeToken<RewardVerified>(){}.getType();
|
||||||
|
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||||
|
return gson.fromJson(object.toString(), type);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(RewardVerified result) {
|
||||||
|
Helper.setViewVisibility(progressView, View.GONE);
|
||||||
|
if (handler != null) {
|
||||||
|
if (result != null) {
|
||||||
|
handler.onSuccess(result);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package io.lbry.browser.tasks.lbryinc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.gson.FieldNamingPolicy;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.lbry.browser.model.TwitterOauth;
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
|
import io.lbry.browser.tasks.RewardVerifiedHandler;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
|
|
||||||
|
public class TwitterVerifyTask extends AsyncTask<Void, Void, RewardVerified> {
|
||||||
|
private Context context;
|
||||||
|
private View progressView;
|
||||||
|
private TwitterOauth twitterOauth;
|
||||||
|
private RewardVerifiedHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public TwitterVerifyTask(TwitterOauth twitterOauth, View progressView, Context context, RewardVerifiedHandler handler) {
|
||||||
|
this.twitterOauth = twitterOauth;
|
||||||
|
this.progressView = progressView;
|
||||||
|
this.context = context;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Helper.setViewVisibility(progressView, View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RewardVerified doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
Map<String, String> options = new HashMap<>();
|
||||||
|
options.put("oauth_token", twitterOauth.getOauthToken());
|
||||||
|
options.put("oauth_token_secret", twitterOauth.getOauthTokenSecret());
|
||||||
|
|
||||||
|
JSONObject object = (JSONObject) Lbryio.parseResponse(Lbryio.call("verification", "twitter_verify", options, context));
|
||||||
|
Type type = new TypeToken<RewardVerified>(){}.getType();
|
||||||
|
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||||
|
return gson.fromJson(object.toString(), type);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(RewardVerified result) {
|
||||||
|
Helper.setViewVisibility(progressView, View.GONE);
|
||||||
|
if (handler != null) {
|
||||||
|
if (result != null) {
|
||||||
|
handler.onSuccess(result);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package io.lbry.browser.tasks.verification;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.google.api.client.auth.oauth.OAuthHmacSigner;
|
||||||
|
import com.google.api.client.auth.oauth.OAuthParameters;
|
||||||
|
import com.google.api.client.http.GenericUrl;
|
||||||
|
|
||||||
|
import io.lbry.browser.VerificationActivity;
|
||||||
|
import io.lbry.browser.model.TwitterOauth;
|
||||||
|
import io.lbry.browser.tasks.TwitterOauthHandler;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class TwitterAccessTokenTask extends AsyncTask<Void, Void, String> {
|
||||||
|
private static final String ENDPOINT = "https://api.twitter.com/oauth/access_token";
|
||||||
|
|
||||||
|
private Exception error;
|
||||||
|
private String oauthParams;
|
||||||
|
private TwitterOauthHandler handler;
|
||||||
|
|
||||||
|
public TwitterAccessTokenTask(String oauthParams, TwitterOauthHandler handler) {
|
||||||
|
this.oauthParams = oauthParams;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
String url = String.format("%s?%s", ENDPOINT, oauthParams);
|
||||||
|
RequestBody body = RequestBody.create(new byte[0]);
|
||||||
|
Request request = new Request.Builder().url(url).post(body).build();
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
|
return response.body().string();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(String response) {
|
||||||
|
if (!Helper.isNullOrEmpty(response)) {
|
||||||
|
String[] pairs = response.split("&");
|
||||||
|
TwitterOauth twitterOauth = new TwitterOauth();
|
||||||
|
for (String pair : pairs) {
|
||||||
|
String[] parts = pair.split("=");
|
||||||
|
if (parts.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String key = parts[0];
|
||||||
|
String value = parts[1];
|
||||||
|
if ("oauth_token".equalsIgnoreCase(key)) {
|
||||||
|
twitterOauth.setOauthToken(value);
|
||||||
|
} else if ("oauth_token_secret".equalsIgnoreCase(key)) {
|
||||||
|
twitterOauth.setOauthTokenSecret(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.onSuccess(twitterOauth);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package io.lbry.browser.tasks.verification;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import com.google.api.client.auth.oauth.OAuthHmacSigner;
|
||||||
|
import com.google.api.client.auth.oauth.OAuthParameters;
|
||||||
|
import com.google.api.client.http.GenericUrl;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import io.lbry.browser.VerificationActivity;
|
||||||
|
import io.lbry.browser.model.TwitterOauth;
|
||||||
|
import io.lbry.browser.tasks.TwitterOauthHandler;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class TwitterRequestTokenTask extends AsyncTask<Void, Void, String> {
|
||||||
|
private static final String ENDPOINT = "https://api.twitter.com/oauth/request_token";
|
||||||
|
|
||||||
|
private String consumerKey;
|
||||||
|
private String consumerSecret;
|
||||||
|
private Exception error;
|
||||||
|
private TwitterOauthHandler handler;
|
||||||
|
|
||||||
|
public TwitterRequestTokenTask(String consumerKey, String consumerSecret, TwitterOauthHandler handler) {
|
||||||
|
this.consumerKey = consumerKey;
|
||||||
|
this.consumerSecret = consumerSecret;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
OAuthHmacSigner signer = new OAuthHmacSigner();
|
||||||
|
signer.clientSharedSecret = new String(
|
||||||
|
Base64.decode(consumerSecret, Base64.NO_WRAP), StandardCharsets.UTF_8.name());
|
||||||
|
|
||||||
|
OAuthParameters oauthParams = new OAuthParameters();
|
||||||
|
oauthParams.callback = "https://lbry.tv";
|
||||||
|
oauthParams.consumerKey = new String(
|
||||||
|
Base64.decode(consumerKey, Base64.NO_WRAP), StandardCharsets.UTF_8.name());;
|
||||||
|
oauthParams.signatureMethod = "HMAC-SHA-1";
|
||||||
|
oauthParams.signer = signer;
|
||||||
|
oauthParams.computeNonce();
|
||||||
|
oauthParams.computeTimestamp();
|
||||||
|
oauthParams.computeSignature("POST", new GenericUrl(ENDPOINT));
|
||||||
|
|
||||||
|
RequestBody body = RequestBody.create(new byte[0]);
|
||||||
|
Request request = new Request.Builder().url(ENDPOINT).addHeader(
|
||||||
|
"Authorization", oauthParams.getAuthorizationHeader()).post(body).build();
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
|
return response.body().string();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(String response) {
|
||||||
|
if (!Helper.isNullOrEmpty(response)) {
|
||||||
|
String[] pairs = response.split("&");
|
||||||
|
TwitterOauth twitterOauth = new TwitterOauth();
|
||||||
|
for (String pair : pairs) {
|
||||||
|
String[] parts = pair.split("=");
|
||||||
|
if (parts.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String key = parts[0];
|
||||||
|
String value = parts[1];
|
||||||
|
if ("oauth_token".equalsIgnoreCase(key)) {
|
||||||
|
twitterOauth.setOauthToken(value);
|
||||||
|
} else if ("oauth_token_secret".equalsIgnoreCase(key)) {
|
||||||
|
twitterOauth.setOauthTokenSecret(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.onSuccess(twitterOauth);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,75 @@
|
||||||
package io.lbry.browser.ui.verification;
|
package io.lbry.browser.ui.verification;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import io.lbry.browser.R;
|
import io.lbry.browser.R;
|
||||||
import io.lbry.browser.listener.SignInListener;
|
import io.lbry.browser.listener.SignInListener;
|
||||||
|
import io.lbry.browser.model.TwitterOauth;
|
||||||
|
import io.lbry.browser.model.lbryinc.RewardVerified;
|
||||||
|
import io.lbry.browser.tasks.RewardVerifiedHandler;
|
||||||
|
import io.lbry.browser.tasks.TwitterOauthHandler;
|
||||||
|
import io.lbry.browser.tasks.lbryinc.TwitterVerifyTask;
|
||||||
|
import io.lbry.browser.tasks.verification.TwitterAccessTokenTask;
|
||||||
|
import io.lbry.browser.tasks.verification.TwitterRequestTokenTask;
|
||||||
import io.lbry.browser.utils.Helper;
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class ManualVerificationFragment extends Fragment {
|
public class ManualVerificationFragment extends Fragment {
|
||||||
@Setter
|
@Setter
|
||||||
private SignInListener listener;
|
private SignInListener listener;
|
||||||
|
private PopupWindow popup;
|
||||||
|
private View mainView;
|
||||||
|
private View loadingView;
|
||||||
|
|
||||||
|
private TwitterOauth currentOauth;
|
||||||
|
private boolean twitterOauthInProgress = false;
|
||||||
|
|
||||||
|
private static final double SKIP_QUEUE_PRICE = 4.99;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = inflater.inflate(R.layout.fragment_verification_manual, container, false);
|
View root = inflater.inflate(R.layout.fragment_verification_manual, container, false);
|
||||||
|
|
||||||
Helper.applyHtmlForTextView((TextView) root.findViewById(R.id.verification_manual_discord_verify));
|
mainView = root.findViewById(R.id.verification_manual_main);
|
||||||
|
loadingView = root.findViewById(R.id.verification_manual_loading);
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
MaterialButton buttonSkipQueue = root.findViewById(R.id.verification_manual_skip_queue);
|
||||||
|
buttonSkipQueue.setText(context.getString(R.string.skip_queue_button_text, String.valueOf(SKIP_QUEUE_PRICE)));
|
||||||
|
|
||||||
|
Helper.applyHtmlForTextView(root.findViewById(R.id.verification_manual_discord_verify));
|
||||||
|
root.findViewById(R.id.verification_manual_twitter_button).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
// start twitter verification
|
||||||
|
if (currentOauth != null) {
|
||||||
|
// Twitter three-legged oauth already completed, verify directly
|
||||||
|
twitterVerify(currentOauth);
|
||||||
|
} else {
|
||||||
|
// show twitter sign-in flow
|
||||||
|
twitterVerificationFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
root.findViewById(R.id.verification_manual_continue_button).setOnClickListener(new View.OnClickListener() {
|
root.findViewById(R.id.verification_manual_continue_button).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
@ -31,6 +79,205 @@ public class ManualVerificationFragment extends Fragment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
root.findViewById(R.id.verification_manual_skip_queue).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onSkipQueueAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void twitterVerificationFlow() {
|
||||||
|
twitterOauthInProgress = true;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
showLoading();
|
||||||
|
String consumerKey = getResources().getString(R.string.TWITTER_CONSUMER_KEY);
|
||||||
|
String consumerSecret = getResources().getString(R.string.TWITTER_CONSUMER_SECRET);
|
||||||
|
TwitterRequestTokenTask task = new TwitterRequestTokenTask(consumerKey, consumerSecret, new TwitterOauthHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TwitterOauth twitterOauth) {
|
||||||
|
twitterOauthInProgress = false;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
showTwitterAuthenticateWithToken(twitterOauth.getOauthToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
handleFlowError(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showLoading() {
|
||||||
|
Helper.setViewVisibility(mainView, View.INVISIBLE);
|
||||||
|
Helper.setViewVisibility(loadingView, View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideLoading() {
|
||||||
|
Helper.setViewVisibility(mainView, View.VISIBLE);
|
||||||
|
Helper.setViewVisibility(loadingView, View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showTwitterAuthenticateWithToken(String oauthToken) {
|
||||||
|
Context context = getContext();
|
||||||
|
if (context != null) {
|
||||||
|
WebView webView = new WebView(context);
|
||||||
|
webView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
||||||
|
webView.loadUrl(String.format("https://api.twitter.com/oauth/authorize?oauth_token=%s", oauthToken));
|
||||||
|
webView.setWebViewClient(new WebViewClient() {
|
||||||
|
@Override
|
||||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
if (url.startsWith("https://lbry.tv") || url.equalsIgnoreCase("https://twitter.com/home") /* Return to Twitter */) {
|
||||||
|
if (url.startsWith("https://lbry.tv") && url.contains("oauth_token") && url.contains("oauth_verifier")) {
|
||||||
|
// finish 3-legged oauth
|
||||||
|
twitterOauthInProgress = true;
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
finishTwitterOauth(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popup != null) {
|
||||||
|
popup.dismiss();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.loadUrl(url);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
View popupView = LayoutInflater.from(context).inflate(R.layout.popup_webview, null);
|
||||||
|
((LinearLayout) popupView.findViewById(R.id.popup_webivew_container)).addView(webView);
|
||||||
|
popupView.findViewById(R.id.popup_cancel_button).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (!twitterOauthInProgress && popup != null) {
|
||||||
|
popup.dismiss();
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
float scale = getResources().getDisplayMetrics().density;
|
||||||
|
popup = new PopupWindow(context);
|
||||||
|
popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
|
||||||
|
@Override
|
||||||
|
public void onDismiss() {
|
||||||
|
if (!twitterOauthInProgress) {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
popup = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
popup.setWidth(Helper.getScaledValue(340, scale));
|
||||||
|
popup.setHeight(Helper.getScaledValue(480, scale));
|
||||||
|
popup.setContentView(popupView);
|
||||||
|
|
||||||
|
View parent = getView();
|
||||||
|
popup.setFocusable(true);
|
||||||
|
popup.showAtLocation(parent, Gravity.CENTER, 0, 0);
|
||||||
|
popup.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishTwitterOauth(String callbackUrl) {
|
||||||
|
String params = callbackUrl.substring(callbackUrl.indexOf('?') + 1);
|
||||||
|
TwitterAccessTokenTask task = new TwitterAccessTokenTask(params, new TwitterOauthHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TwitterOauth twitterOauth) {
|
||||||
|
// send request to finish verifying
|
||||||
|
currentOauth = twitterOauth;
|
||||||
|
twitterVerify(twitterOauth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
handleFlowError(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void twitterVerify(TwitterOauth twitterOauth) {
|
||||||
|
Context context = getContext();
|
||||||
|
if (context != null) {
|
||||||
|
showLoading();
|
||||||
|
twitterOauthInProgress = true;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterVerifyTask task = new TwitterVerifyTask(twitterOauth, null, context, new RewardVerifiedHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(RewardVerified rewardVerified) {
|
||||||
|
twitterOauthInProgress = false;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lbryio.currentUser != null) {
|
||||||
|
Lbryio.currentUser.setRewardApproved(rewardVerified.isRewardApproved());
|
||||||
|
}
|
||||||
|
if (rewardVerified.isRewardApproved()) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onTwitterVerified();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
View root = getView();
|
||||||
|
if (root != null) {
|
||||||
|
// reward approved wasn't set to true
|
||||||
|
Snackbar.make(root, getString(R.string.twitter_verification_not_approved), Snackbar.LENGTH_LONG).
|
||||||
|
setTextColor(Color.WHITE).
|
||||||
|
setBackgroundTint(Color.RED).show();
|
||||||
|
}
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
handleFlowError(error != null ? error.getMessage() : null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
} else {
|
||||||
|
twitterOauthInProgress = false;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFlowError(String extra) {
|
||||||
|
hideLoading();
|
||||||
|
twitterOauthInProgress = false;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onManualProgress(twitterOauthInProgress);
|
||||||
|
}
|
||||||
|
showFlowError(extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFlowError(String extra) {
|
||||||
|
Context context = getContext();
|
||||||
|
View root = getView();
|
||||||
|
if (context != null && root != null) {
|
||||||
|
String message = !Helper.isNullOrEmpty(extra) ?
|
||||||
|
getString(R.string.twitter_account_ineligible, extra) :
|
||||||
|
getString(R.string.twitter_verification_failed);
|
||||||
|
|
||||||
|
Snackbar.make(root, message, Snackbar.LENGTH_LONG).
|
||||||
|
setTextColor(Color.WHITE).
|
||||||
|
setBackgroundTint(Color.RED).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.lbry.browser.utils;
|
package io.lbry.browser.utils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
@ -128,7 +130,7 @@ public final class Lbry {
|
||||||
IS_STATUS_PARSED = true;
|
IS_STATUS_PARSED = true;
|
||||||
} catch (JSONException | LbryResponseException ex) {
|
} catch (JSONException | LbryResponseException ex) {
|
||||||
// pass
|
// pass
|
||||||
android.util.Log.e(TAG, "Could not parse status response.", ex);
|
Log.e(TAG, "Could not parse status response.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@ public final class Lbryio {
|
||||||
}
|
}
|
||||||
url = uriBuilder.build().toString();
|
url = uriBuilder.build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (BuildConfig.DEBUG) {
|
/*if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, String.format("Request Method: %s, Sending request to URL: %s", method, url));
|
Log.d(TAG, String.format("Request Method: %s, Sending request to URL: %s", method, url));
|
||||||
}*/
|
}*/
|
||||||
|
@ -200,6 +201,7 @@ public final class Lbryio {
|
||||||
throw new LbryioResponseException("Unknown API error signature.", response.code());
|
throw new LbryioResponseException("Unknown API error signature.", response.code());
|
||||||
}
|
}
|
||||||
} catch (JSONException | IOException ex) {
|
} catch (JSONException | IOException ex) {
|
||||||
|
|
||||||
throw new LbryioResponseException(String.format("Could not parse response: %s", responseString), ex);
|
throw new LbryioResponseException(String.format("Could not parse response: %s", responseString), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +232,7 @@ public final class Lbryio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
android.util.Log.e(TAG, "Could not retrieve the current user", ex);
|
Log.e(TAG, "Could not retrieve the current user", ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +310,7 @@ public final class Lbryio {
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
android.util.Log.e(TAG, "Error sending encrypted auth token action broadcast", ex);
|
Log.e(TAG, "Error sending encrypted auth token action broadcast", ex);
|
||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/verification_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/verification_close_button"
|
android:id="@+id/verification_close_button"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
@ -29,10 +34,6 @@
|
||||||
android:tint="@color/white" />
|
android:tint="@color/white" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
|
||||||
android:id="@+id/verification_pager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/verification_loading_progress"
|
android:id="@+id/verification_loading_progress"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
@ -4,57 +4,134 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
<ProgressBar
|
||||||
|
android:id="@+id/verification_manual_loading"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/verification_manual_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent">
|
||||||
android:layout_centerVertical="true"
|
<LinearLayout
|
||||||
android:visibility="visible"
|
android:orientation="vertical"
|
||||||
android:layout_margin="36dp">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:layout_margin="36dp">
|
||||||
|
<TextView
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/reward_verification"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<TextView
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/get_instantly_verified"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fontFamily="@font/inter"
|
|
||||||
android:text="@string/manual_reward_verification"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textFontWeight="300" />
|
|
||||||
<TextView
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:fontFamily="@font/inter"
|
|
||||||
android:text="@string/account_undergo_review"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textFontWeight="300" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/verification_manual_discord_verify"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:fontFamily="@font/inter"
|
|
||||||
android:text="@string/request_to_be_verified"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textFontWeight="300" />
|
|
||||||
<TextView
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:fontFamily="@font/inter"
|
|
||||||
android:text="@string/enjoy_free_content"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textFontWeight="300" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<TextView
|
||||||
android:id="@+id/verification_manual_continue_button"
|
android:textSize="20sp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="36dp"
|
||||||
android:fontFamily="@font/inter"
|
android:fontFamily="@font/inter"
|
||||||
android:text="@string/continue_text" />
|
android:text="@string/twitter_verification"
|
||||||
</LinearLayout>
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<TextView
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/twitter_verification_desc"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/verification_manual_twitter_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/twitter_verify" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="36dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/skip_queue_verification"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<TextView
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/skip_queue_verification_desc"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/verification_manual_skip_queue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/skip_queue_button_text" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="36dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/manual_reward_verification"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/verification_manual_discord_verify"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/request_to_be_verified"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/enjoy_free_content"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textFontWeight="300" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/verification_manual_continue_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:text="@string/continue_text" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
20
app/src/main/res/layout/popup_webview.xml
Normal file
20
app/src/main/res/layout/popup_webview.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="340dp"
|
||||||
|
android:layout_height="480dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/popup_webivew_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_above="@id/popup_cancel_button"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/popup_cancel_button"
|
||||||
|
android:text="@string/cancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"/>
|
||||||
|
</RelativeLayout>
|
|
@ -422,8 +422,18 @@
|
||||||
<string name="enter_phone_number">Please enter your phone number.</string>
|
<string name="enter_phone_number">Please enter your phone number.</string>
|
||||||
<string name="not_interested">Not interested</string>
|
<string name="not_interested">Not interested</string>
|
||||||
<string name="manual_reward_verification">Manual Reward Verification</string>
|
<string name="manual_reward_verification">Manual Reward Verification</string>
|
||||||
|
<string name="reward_verification">Reward Verification</string>
|
||||||
|
<string name="social_media_verification">Social Media Verification</string>
|
||||||
|
<string name="skip_the_queue">Skip the Queue</string>
|
||||||
|
<string name="skip_queue_button_text">Skip for $%1$s</string>
|
||||||
<string name="account_undergo_review">This account must undergo review before you can participate in the rewards program. This can take anywhere from several minutes to several days.</string>
|
<string name="account_undergo_review">This account must undergo review before you can participate in the rewards program. This can take anywhere from several minutes to several days.</string>
|
||||||
<string name="request_to_be_verified">If you continue to see this message, please request to be verified on the <a href="https://discordapp.com/invite/Z3bERWA">LBRY Discord server</a>.</string>
|
<string name="get_instantly_verified">You can get instantly verified to be able to participate in the rewards program using your Twitter account or skipping the manual verification queue.</string>
|
||||||
|
<string name="twitter_verification">Twitter Verification</string>
|
||||||
|
<string name="twitter_verification_desc">Get instantly verified using your Twitter account. Your Twitter email address must match the email that you provided and your account should be active.</string>
|
||||||
|
<string name="twitter_verify">Verify with Twitter</string>
|
||||||
|
<string name="skip_queue_verification">Skip the Queue</string>
|
||||||
|
<string name="skip_queue_verification_desc">Skip the manual verification queue by paying a fee in order to start participating in the rewards program immediately.</string>
|
||||||
|
<string name="request_to_be_verified">Please request to be verified on the <a href="https://discordapp.com/invite/Z3bERWA">LBRY Discord server</a>. A manual review can take anywhere from several minutes to several days.</string>
|
||||||
<string name="enjoy_free_content">Please enjoy free content in the meantime!</string>
|
<string name="enjoy_free_content">Please enjoy free content in the meantime!</string>
|
||||||
<string name="verify_phone_number">Verify Phone Number</string>
|
<string name="verify_phone_number">Verify Phone Number</string>
|
||||||
<string name="enter_phone_verify_code">Please enter the verification code sent to %1$s</string>
|
<string name="enter_phone_verify_code">Please enter the verification code sent to %1$s</string>
|
||||||
|
@ -432,6 +442,12 @@
|
||||||
<string name="please_enter_valid_phone">Please enter a valid phone number.</string>
|
<string name="please_enter_valid_phone">Please enter a valid phone number.</string>
|
||||||
<string name="please_enter_verification_code">Please enter the verification code sent to your phone number.</string>
|
<string name="please_enter_verification_code">Please enter the verification code sent to your phone number.</string>
|
||||||
<string name="fetch_current_user_error">User account could not be retrieved at this time. Please try again later.</string>
|
<string name="fetch_current_user_error">User account could not be retrieved at this time. Please try again later.</string>
|
||||||
|
<string name="purchase_request_pending">Your purchase request is still being processed. Please send an email to support@lbry.com.</string>
|
||||||
|
<string name="purchase_request_failed_error">Your purchase request could not be completed at this time. Please send an email to support@lbry.com.</string>
|
||||||
|
<string name="twitter_account_ineligible">Your Twitter account is not eligible at this time: %1$s</string>
|
||||||
|
<string name="twitter_verification_not_approved">Your account was not approved for the rewards program. Please try again later.</string>
|
||||||
|
<string name="twitter_verification_failed">Twitter verification failed. Please try again later.</string>
|
||||||
|
<string name="reward_verification_successful">You are now eligible to participate in the rewards program!</string>
|
||||||
|
|
||||||
<!-- Forms -->
|
<!-- Forms -->
|
||||||
<string name="no_added_tags">You have not added any tags yet. Add tags to improve discovery.</string>
|
<string name="no_added_tags">You have not added any tags yet. Add tags to improve discovery.</string>
|
||||||
|
|
BIN
app/twitter.properties.secret
Normal file
BIN
app/twitter.properties.secret
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue