diff --git a/.gitmodules b/.gitmodules index b78fa9d2b4..e100021dc2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "apps/sasquatch/src/main/cpp/google-breakpad"] path = apps/sasquatch/src/main/cpp/google-breakpad - url = https://github.com/Microsoft/AppCenter-SDK-Android-Breakpad.git + url = https://github.com/microsoft/appcenter-sdk-android-breakpad.git diff --git a/CHANGELOG.md b/CHANGELOG.md index ad7a9564e4..dc0f1b4d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # App Center SDK for Android Change Log +## Version 2.1.0 + +### App Center + +* **[Fix]** Handle incorrect usage of `AppCenter.setLogUrl` API to provide readable error message. +* **[Fix]** Fix decrypting values that have been stored for more than a year (such as the in-app update token). + +### App Center Analytics + +* **[Feature]** Support setting latency of sending events via `Analytics.setTransmissionInterval`. + +### App Center Auth + +* **[Feature]** Expose the ID Token and Access Token (as raw JWT format) in the `UserInformation` object returned from the sign-in method. +* **[Fix]** Fix missing proguard rules so that the app does not have to specify them. +* **[Fix]** Fix crash on silently refreshing token if initialization of MSAL fails. +* **[Fix]** Fix sign-in before start auth service never ends and blocks every next try. +* **[Breaking change]** The `UserInformation` class has been moved from the `appcenter` module to the `appcenter-auth` module and must now be imported as `import com.microsoft.appcenter.auth.UserInformation`. + +### App Center Data + +* **[Fix]** Fix an issue where invalid characters in the document ID are accepted at creation time but causing errors while trying to read or delete the document. The characters are `#`, `\`, `/`, `?`, and all whitespaces. + +### App Center Crashes + +* **[Fix]** Fix a crash that could sometimes occur while processing native crash reports. + +### App Center Distribute + +* **[Feature]** Add `Distribute.setEnabledForDebuggableBuild(boolean)` method to allow in-app updates in debuggable builds. +* **[Fix]** Fix duplicate in-app update dialog when restarting (or switching) activity quickly after clicking download. Also fixes a crash when choosing "Ask me in a day" in the duplicate dialog. +* **[Fix]** Fix a crash that could occur when downloading the update with a customized dialog and then calling `Distribute.notifyUserConfirmation(UpdateAction.POSTPONE)` right after calling `Distribute.notifyUserConfirmation(UpdateAction.UPDATE)`. +* **[Fix]** Fix a crash that could occur while trying to open the browser on some devices. + +### App Center Push + +* **[Fix]** Update Firebase dependency and AppCenter push logic to avoid a runtime issue with the latest Firebase messaging version 18.0.0. + ## Version 2.0.0 Version 2 of the App Center SDK includes two new modules: Auth and Data. @@ -14,7 +52,7 @@ The App Center Data service provides functionality enabling developers to persis ### AppCenterCrashes -* **[Feature]** After calling `Auth.signIn`, the next crashes are associated with an `accountId` corresponding to the signed in user. This is a different field than the `userId` set by `AppCenter.setUserId`. Calling `Auth.signOut` stops the `accountId` association for the next crashes. +* **[Feature]** After calling `Auth.signIn`, the next crashes are associated with an `accountId` corresponding to the signed in user. This is a different field than the `userId` set by `AppCenter.setUserId`. Calling `Auth.signOut` stops the `accountId` association for the next crashes. ### AppCenterDistribute diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 80606e04bb..b43f9d9269 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ Thanks for your interest in using the App Center SDK for Android. If your issue is not related to using our Android SDK but rather about the product experience like the portal or CI, please create a ticket using the blue chat button on any page of the https://appcenter.ms portal instead. - If you are using Xamarin, please report the issue on https://github.com/Microsoft/AppCenter-SDK-DotNet instead. + If you are using Xamarin, please report the issue on https://github.com/microsoft/appcenter-sdk-dotnet instead. --> ### **Description** diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index a78011a91c..d7046d9dc7 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ Cheers! The App Center team --> -Please have a look at our [guidelines for contributions](https://github.com/Microsoft/AppCenter-SDK-Android/blob/develop/CONTRIBUTING.md) and consider the following before you submit the PR: +Please have a look at our [guidelines for contributions](https://github.com/microsoft/appcenter-sdk-android/blob/develop/CONTRIBUTING.md) and consider the following before you submit the PR: * [ ] Has `CHANGELOG.md` been updated? * [ ] Are tests passing locally? diff --git a/README.md b/README.md index 99a2a8331e..987a328698 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -[![Coverage Status](https://coveralls.io/repos/github/Microsoft/AppCenter-SDK-Android/badge.svg?branch=develop)](https://coveralls.io/github/Microsoft/AppCenter-SDK-Android?branch=develop) -[![GitHub Release](https://img.shields.io/github/release/Microsoft/AppCenter-SDK-Android.svg)](https://github.com/Microsoft/AppCenter-SDK-Android/releases/latest) +[![Coverage Status](https://coveralls.io/repos/github/microsoft/appcenter-sdk-android/badge.svg?branch=develop)](https://coveralls.io/github/microsoft/appcenter-sdk-android?branch=develop) +[![GitHub Release](https://img.shields.io/github/release/microsoft/appcenter-sdk-android.svg)](https://github.com/microsoft/appcenter-sdk-android/releases/latest) [![Bintray](https://api.bintray.com/packages/vsappcenter/appcenter/appcenter/images/download.svg)](https://bintray.com/vsappcenter/appcenter) -[![license](https://img.shields.io/badge/license-MIT%20License-00AAAA.svg)](https://github.com/Microsoft/AppCenter-SDK-Android/blob/master/license.txt) +[![license](https://img.shields.io/badge/license-MIT%20License-00AAAA.svg)](https://github.com/microsoft/appcenter-sdk-android/blob/master/license.txt) # Visual Studio App Center SDK for Android diff --git a/apps/sasquatch/build.gradle b/apps/sasquatch/build.gradle index 54298fb4fa..12bb99a998 100644 --- a/apps/sasquatch/build.gradle +++ b/apps/sasquatch/build.gradle @@ -91,4 +91,14 @@ dependencies { def taskRequests = getGradle().getStartParameter().getTaskRequests().toString() if (!taskRequests.contains("sasquatch") || taskRequests.contains("Firebase")) { apply plugin: 'com.google.gms.google-services' -} \ No newline at end of file +} + +/* + * Project and jcenter conflict since they are not using same version of Push and + * somehow affects the plugin when building. + * If we build project alone (commenting jcenter dependencies), the problem is solved. + * So until we release the SDK with the push update, disable version check. + * TODO remove this after SDK release. + */ +//noinspection UnnecessaryQualifiedReference +com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true diff --git a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java index a27ffe2edc..ea0a3492b6 100644 --- a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java +++ b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java @@ -71,7 +71,6 @@ public class CrashesTest { private Context mContext; - @SuppressWarnings("rawtypes") private static Matcher withCrashTitle(@StringRes final int titleId) { return new BoundedMatcher(CrashActivity.Crash.class) { @@ -160,7 +159,6 @@ public void variableMessageTest() throws InterruptedException { * @param titleId Title string resource to find list item. * @throws InterruptedException If the current thread is interrupted. */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") private void crashTest(@StringRes int titleId) throws InterruptedException { /* Crash. */ diff --git a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivityTest.java b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivityTest.java index c863016caf..329240c2bd 100644 --- a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivityTest.java +++ b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivityTest.java @@ -17,10 +17,10 @@ import static android.support.test.espresso.Espresso.onData; import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anything; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.allOf; import static org.junit.Assert.assertNotNull; @SuppressWarnings("unused") @@ -58,7 +58,6 @@ private static Matcher withInfoTitle(String expectedTitle) { return withInfoTitle(equalTo(expectedTitle)); } - @SuppressWarnings("rawtypes") private static Matcher withInfoTitle(final Matcher itemTitleMatcher) { assertNotNull(itemTitleMatcher); return new BoundedMatcher(DeviceInfoActivity.DeviceInfoDisplayModel.class) { diff --git a/apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java b/apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java new file mode 100644 index 0000000000..88771b76bf --- /dev/null +++ b/apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.sasquatch.activities; + +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.ListView; + +import com.microsoft.appcenter.UserInformation; +import com.microsoft.appcenter.analytics.AuthenticationProvider; +import com.microsoft.appcenter.auth.Auth; +import com.microsoft.appcenter.auth.SignInResult; +import com.microsoft.appcenter.sasquatch.R; +import com.microsoft.appcenter.sasquatch.features.TestFeatures; +import com.microsoft.appcenter.sasquatch.features.TestFeaturesListAdapter; +import com.microsoft.appcenter.utils.async.AppCenterConsumer; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.ACCOUNT_ID; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ACCESS_TOKEN; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ID; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ID_TOKEN; +import static com.microsoft.appcenter.sasquatch.activities.MainActivity.LOG_TAG; + +public class AuthenticationProviderActivity extends AppCompatActivity { + + private boolean mUserLeaving; + + private static UserInformation sUserInformation; + + private TestFeatures.TestFeature mAuthInfoTestFeature; + + private List mFeatureList; + + private ListView mListView; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_auth_provider_list); + + /* Populate UI. */ + mFeatureList = new ArrayList<>(); + mFeatureList.add(new TestFeatures.TestFeatureTitle(R.string.msa_title)); + mFeatureList.add(new TestFeatures.TestFeature(R.string.msa_compact_title, R.string.msa_compact_description, new View.OnClickListener() { + + @Override + public void onClick(View v) { + startMSALoginActivity(AuthenticationProvider.Type.MSA_COMPACT); + } + })); + mFeatureList.add(new TestFeatures.TestFeature(R.string.msa_delegate_title, R.string.msa_delegate_description, new View.OnClickListener() { + + @Override + public void onClick(View v) { + startMSALoginActivity(AuthenticationProvider.Type.MSA_DELEGATE); + } + })); + mFeatureList.add(new TestFeatures.TestFeature(R.string.b2c_sign_in_title, R.string.b2c_sign_in_description, new View.OnClickListener() { + + @Override + public void onClick(View v) { + Auth.signIn().thenAccept(new AppCenterConsumer() { + + @Override + public void accept(SignInResult signInResult) { + try { + Exception exception = signInResult.getException(); + if (exception != null) { + throw exception; + } + sUserInformation = signInResult.getUserInformation(); + loadAuthStatus(false); + String accountId = sUserInformation.getAccountId(); + SharedPreferences.Editor edit = MainActivity.sSharedPreferences.edit(); + edit.putString("accountId", accountId); + edit.apply(); + Log.i(LOG_TAG, "Auth.signIn succeeded, accountId=" + accountId); + } catch (Exception e) { + sUserInformation = null; + loadAuthStatus(false); + Log.e(LOG_TAG, "Auth.signIn failed", e); + } + } + }); + } + })); + mFeatureList.add(new TestFeatures.TestFeature(R.string.b2c_sign_out_title, R.string.b2c_sign_out_description, new View.OnClickListener() { + + @Override + public void onClick(View v) { + try { + Auth.signOut(); + sUserInformation = null; + loadAuthStatus(false); + SharedPreferences.Editor edit = MainActivity.sSharedPreferences.edit(); + edit.putString(ACCOUNT_ID, null); + edit.apply(); + } catch (Exception e) { + Log.e(LOG_TAG, "Auth.signOut failed", e); + } + } + })); + mListView = findViewById(R.id.list); + loadAuthStatus(sUserInformation == null); + mListView.setOnItemClickListener(TestFeatures.getOnItemClickListener()); + } + + private static boolean isAuthenticated() { + return sUserInformation != null; + } + + private void loadAuthStatus(boolean loadDefaultStatus) { + if (mAuthInfoTestFeature != null) { + mFeatureList.remove(mAuthInfoTestFeature); + } + mAuthInfoTestFeature = getAuthenticationDefaultTestFeature(); + if (!loadDefaultStatus) { + mAuthInfoTestFeature = isAuthenticated() ? getAuthenticatedTestFeature() : getNotAuthenticatedTestFeature(); + } + mFeatureList.add(mAuthInfoTestFeature); + mListView.setAdapter(new TestFeaturesListAdapter(mFeatureList)); + } + + private TestFeatures.TestFeature getAuthenticationDefaultTestFeature() { + return getAuthenticationTestFeature(R.string.b2c_authentication_status_description); + } + + private TestFeatures.TestFeature getAuthenticatedTestFeature() { + return getAuthenticationTestFeature(R.string.b2c_authentication_status_authenticated); + } + + private TestFeatures.TestFeature getNotAuthenticatedTestFeature() { + return getAuthenticationTestFeature(R.string.b2c_authentication_status_not_authenticated); + } + + private TestFeatures.TestFeature getAuthenticationTestFeature(int valueStringId) { + return new TestFeatures.TestFeature(R.string.b2c_authentication_status_title, valueStringId, new View.OnClickListener() { + + @Override + public void onClick(View v) { + if (isAuthenticated()) { + startUserInfoActivity(sUserInformation); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(AuthenticationProviderActivity.this); + builder.setTitle(R.string.b2c_authentication_status_dialog_unavailable_title) + .setMessage(R.string.b2c_authentication_status_dialog_unavailable_description) + .setPositiveButton(R.string.alert_ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.dismiss(); + } + }); + builder.create().show(); + } + } + }); + } + + private void startMSALoginActivity(AuthenticationProvider.Type type) { + Intent intent = new Intent(getApplication(), MSALoginActivity.class); + intent.putExtra(AuthenticationProvider.Type.class.getName(), type); + startActivity(intent); + } + + private void startUserInfoActivity(UserInformation userInformation) { + Intent intent = new Intent(getApplication(), UserInformationActivity.class); + intent.putExtra(USER_INFORMATION_ID, userInformation.getAccountId()); + startActivity(intent); + } + + @Override + protected void onUserLeaveHint() { + mUserLeaving = true; + } + + @Override + protected void onRestart() { + + /* When coming back from browser, finish this intermediate menu screen too. */ + super.onRestart(); + if (mUserLeaving) { + finish(); + } + } +} diff --git a/apps/sasquatch/src/main/AndroidManifest.xml b/apps/sasquatch/src/main/AndroidManifest.xml index 9d1124b1fa..463a858ea8 100644 --- a/apps/sasquatch/src/main/AndroidManifest.xml +++ b/apps/sasquatch/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnpackedNativeCode" tools:targetApi="n"> + diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/SasquatchConstants.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/SasquatchConstants.java index bc435aaea7..b5692e411a 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/SasquatchConstants.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/SasquatchConstants.java @@ -12,4 +12,20 @@ public class SasquatchConstants { public static final String DOCUMENT_PARTITION = "documentPartition"; public static final String DOCUMENT_ID = "documentId"; + + public static final String DOCUMENT_CONTENT = "documentContent"; + + public static final String DOCUMENT_DATE = "documentDate"; + + public static final String DOCUMENT_STATE = "documentState"; + + public static final String DOCUMENT_ERROR = "documentError"; + + public static final String DOCUMENT_ERROR_NULL_STATUS = "documentErrorNullStatus"; + + public static final String USER_INFORMATION_ID = "userInfoId"; + + public static final String USER_INFORMATION_ID_TOKEN = "userInfoIdToken"; + + public static final String USER_INFORMATION_ACCESS_TOKEN = "userInfoAccessToken"; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/ActivityConstants.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/ActivityConstants.java index 603891cfa3..e6fdcc7f97 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/ActivityConstants.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/ActivityConstants.java @@ -7,5 +7,18 @@ final class ActivityConstants { - final static String EXTRA_TARGET_SELECTED = "TARGET_SELECTED"; + /** + * A name of extra for target position in intent that is used when configuring target property. + */ + static final String EXTRA_TARGET_SELECTED = "TARGET_SELECTED"; + + /** + * Shared preference key to store Analytics transmission interval. + */ + static final String ANALYTICS_TRANSMISSION_INTERVAL_KEY = "analyticsTransmissionInterval"; + + /** + * Default Analytics transmission interval in seconds. + */ + static final int DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS = 3; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java deleted file mode 100644 index 24a7951561..0000000000 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/AuthenticationProviderActivity.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.sasquatch.activities; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.View; -import android.widget.ListView; - -import com.microsoft.appcenter.analytics.AuthenticationProvider; -import com.microsoft.appcenter.auth.Auth; -import com.microsoft.appcenter.auth.SignInResult; -import com.microsoft.appcenter.sasquatch.R; -import com.microsoft.appcenter.sasquatch.features.TestFeatures; -import com.microsoft.appcenter.sasquatch.features.TestFeaturesListAdapter; -import com.microsoft.appcenter.utils.async.AppCenterConsumer; - -import java.util.ArrayList; -import java.util.List; - -import static com.microsoft.appcenter.sasquatch.SasquatchConstants.ACCOUNT_ID; -import static com.microsoft.appcenter.sasquatch.activities.MainActivity.LOG_TAG; - -public class AuthenticationProviderActivity extends AppCompatActivity { - - private boolean mUserLeaving; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_auth_provider_list); - - /* Populate UI. */ - List featureList = new ArrayList<>(); - featureList.add(new TestFeatures.TestFeatureTitle(R.string.msa_title)); - featureList.add(new TestFeatures.TestFeature(R.string.msa_compact_title, R.string.msa_compact_description, new View.OnClickListener() { - - @Override - public void onClick(View v) { - startMSALoginActivity(AuthenticationProvider.Type.MSA_COMPACT); - } - })); - featureList.add(new TestFeatures.TestFeature(R.string.msa_delegate_title, R.string.msa_delegate_description, new View.OnClickListener() { - - @Override - public void onClick(View v) { - startMSALoginActivity(AuthenticationProvider.Type.MSA_DELEGATE); - } - })); - featureList.add(new TestFeatures.TestFeature(R.string.b2c_sign_in_title, R.string.b2c_sign_in_description, new View.OnClickListener() { - - @Override - public void onClick(View v) { - Auth.signIn().thenAccept(new AppCenterConsumer() { - - @Override - public void accept(SignInResult signInResult) { - try { - Exception exception = signInResult.getException(); - if (exception != null) { - throw exception; - } - String accountId = signInResult.getUserInformation().getAccountId(); - SharedPreferences.Editor edit = MainActivity.sSharedPreferences.edit(); - edit.putString("accountId", accountId); - edit.apply(); - Log.i(LOG_TAG, "Auth.signIn succeeded, accountId=" + accountId); - } catch (Exception e) { - Log.e(LOG_TAG, "Auth.signIn failed", e); - } - } - }); - } - })); - featureList.add(new TestFeatures.TestFeature(R.string.b2c_sign_out_title, R.string.b2c_sign_out_description, new View.OnClickListener() { - - @Override - public void onClick(View v) { - try { - Auth.signOut(); - SharedPreferences.Editor edit = MainActivity.sSharedPreferences.edit(); - edit.putString(ACCOUNT_ID, null); - edit.apply(); - } catch (Exception e) { - Log.e(LOG_TAG, "Auth.signOut failed", e); - } - } - })); - ListView listView = findViewById(R.id.list); - listView.setAdapter(new TestFeaturesListAdapter(featureList)); - listView.setOnItemClickListener(TestFeatures.getOnItemClickListener()); - } - - private void startMSALoginActivity(AuthenticationProvider.Type type) { - Intent intent = new Intent(getApplication(), MSALoginActivity.class); - intent.putExtra(AuthenticationProvider.Type.class.getName(), type); - startActivity(intent); - } - - @Override - protected void onUserLeaveHint() { - mUserLeaving = true; - } - - @Override - protected void onRestart() { - - /* When coming back from browser, finish this intermediate menu screen too. */ - super.onRestart(); - if (mUserLeaving) { - finish(); - } - } -} diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java index 7b65cca95b..18669e5a0d 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CrashActivity.java @@ -54,7 +54,6 @@ public void run() { new Crash(R.string.title_test_ui_crash, R.string.description_test_ui_crash, new Runnable() { @Override - @SuppressWarnings("ResultOfMethodCallIgnored") public void run() { ListView view = findViewById(R.id.list); view.setAdapter(new ArrayAdapter<>(CrashActivity.this, android.R.layout.simple_list_item_2, sCrashes)); @@ -149,7 +148,6 @@ public void run() { new Crash(R.string.title_native_stack_overflow_crash, R.string.description_native_stack_overflow_crash, new Runnable() { @Override - @SuppressWarnings("InfiniteRecursion") public void run() { nativeStackOverflowCrash(); } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CustomPropertiesActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CustomPropertiesActivity.java index 597ff05d5d..4bb223012b 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CustomPropertiesActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/CustomPropertiesActivity.java @@ -39,10 +39,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - addProperty(); - break; + if (item.getItemId() == R.id.action_add) { + addProperty(); } return true; } @@ -54,7 +52,6 @@ private void addProperty() { mProperties.add(fragment); } - @SuppressWarnings({"unused", "unchecked"}) public void send(@SuppressWarnings("UnusedParameters") View view) { CustomProperties customProperties = new CustomProperties(); for (CustomPropertyFragment property : mProperties) { diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DataActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DataActivity.java index 3fc2f7dbec..6d8e3b385d 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DataActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DataActivity.java @@ -26,13 +26,13 @@ import com.microsoft.appcenter.data.Data; import com.microsoft.appcenter.data.DefaultPartitions; +import com.microsoft.appcenter.data.Utils; import com.microsoft.appcenter.data.models.DocumentWrapper; import com.microsoft.appcenter.data.models.Page; import com.microsoft.appcenter.data.models.PaginatedDocuments; import com.microsoft.appcenter.sasquatch.R; import com.microsoft.appcenter.sasquatch.activities.data.AppDocumentListAdapter; import com.microsoft.appcenter.sasquatch.activities.data.CustomItemAdapter; -import com.microsoft.appcenter.sasquatch.activities.data.TestDocument; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import java.util.ArrayList; @@ -40,11 +40,18 @@ import java.util.Map; import static com.microsoft.appcenter.sasquatch.SasquatchConstants.ACCOUNT_ID; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_CONTENT; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_DATE; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ERROR; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ERROR_NULL_STATUS; import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ID; import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_PARTITION; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_STATE; public class DataActivity extends AppCompatActivity { + private static final int MAX_CONTENT_LENGTH = 50; + private RecyclerView mListView; private boolean mLoading; @@ -61,7 +68,7 @@ public class DataActivity extends AppCompatActivity { private DocumentType mDocumentType = DocumentType.READONLY; - private PaginatedDocuments mCurrentAppDocuments; + private PaginatedDocuments mCurrentAppDocuments; private PaginatedDocuments mCurrentUserDocuments; @@ -71,10 +78,10 @@ public class DataActivity extends AppCompatActivity { private boolean mAppDocumentsLoading; - private AppCenterConsumer> mUploadApp = new AppCenterConsumer>() { + private final AppCenterConsumer> mUploadApp = new AppCenterConsumer>() { @Override - public void accept(PaginatedDocuments documents) { + public void accept(PaginatedDocuments documents) { mAppDocumentsLoading = false; if (!mUserDocumentsLoading) { hideProgress(); @@ -86,7 +93,7 @@ public void accept(PaginatedDocuments documents) { } }; - private AppCenterConsumer> mUploadUser = new AppCenterConsumer>() { + private final AppCenterConsumer> mUploadUser = new AppCenterConsumer>() { @Override public void accept(PaginatedDocuments documents) { @@ -111,17 +118,17 @@ private void showProgress() { mDocumentTypeSpinner.setEnabled(false); } - private RecyclerView.OnScrollListener mScrollAppListener = new RecyclerView.OnScrollListener() { + private final RecyclerView.OnScrollListener mScrollAppListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (mCurrentAppDocuments != null && mCurrentAppDocuments.hasNextPage() && !mLoading) { mLoading = true; - mCurrentAppDocuments.getNextPage().thenAccept(new AppCenterConsumer>() { + mCurrentAppDocuments.getNextPage().thenAccept(new AppCenterConsumer>() { @Override - public void accept(Page testDocumentPage) { + public void accept(Page testDocumentPage) { mLoading = false; updateAppDocument(testDocumentPage.getItems()); } @@ -130,7 +137,7 @@ public void accept(Page testDocumentPage) { } }; - private RecyclerView.OnScrollListener mScrollUserListener = new RecyclerView.OnScrollListener() { + private final RecyclerView.OnScrollListener mScrollUserListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { @@ -155,7 +162,7 @@ private void updateUserDocuments(List> documents) { mAdapterUser.notifyDataSetChanged(); } - private void updateAppDocument(List> list) { + private void updateAppDocument(List> list) { mAppDocumentListAdapter.upload(list); mAppDocumentListAdapter.notifyDataSetChanged(); } @@ -171,20 +178,20 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mMessageText = findViewById(R.id.data_message); /* List the app read-only documents. */ - mAppDocumentListAdapter = new AppDocumentListAdapter(this, new ArrayList>()); + mAppDocumentListAdapter = new AppDocumentListAdapter(this, new ArrayList>()); mAppDocumentListAdapter.setOnItemClickListener(new AppDocumentListAdapter.OnItemClickListener() { @Override public void onItemClick(int position) { Intent intent = new Intent(DataActivity.this, DocumentDetailActivity.class); - intent.putExtra(DOCUMENT_PARTITION, DefaultPartitions.APP_DOCUMENTS); - intent.putExtra(DOCUMENT_ID, mAppDocumentListAdapter.getDocumentByPosition(position)); + DocumentWrapper document = mAppDocumentListAdapter.getDocument(position); + fillIntentWithDocDetails(intent, document, DefaultPartitions.APP_DOCUMENTS); startActivity(intent); } }); showProgress(); mAppDocumentsLoading = true; - Data.list(TestDocument.class, DefaultPartitions.APP_DOCUMENTS).thenAccept(mUploadApp); + Data.list(Map.class, DefaultPartitions.APP_DOCUMENTS).thenAccept(mUploadApp); /* List the user documents. */ mAdapterUser = new CustomItemAdapter(new ArrayList>(), this); @@ -193,8 +200,8 @@ public void onItemClick(int position) { @Override public void onItemClick(int position) { Intent intent = new Intent(DataActivity.this, DocumentDetailActivity.class); - intent.putExtra(DOCUMENT_PARTITION, DefaultPartitions.USER_DOCUMENTS); - intent.putExtra(DOCUMENT_ID, mAdapterUser.getDocumentByPosition(position)); + DocumentWrapper document = mAdapterUser.getDocument(position); + fillIntentWithDocDetails(intent, document, DefaultPartitions.USER_DOCUMENTS); startActivity(intent); } @@ -233,6 +240,28 @@ public void onNothingSelected(AdapterView parent) { }); } + private void fillIntentWithDocDetails(Intent intent, DocumentWrapper document, String partition) { + intent.putExtra(DOCUMENT_PARTITION, partition); + intent.putExtra(DOCUMENT_ID, document.getId()); + if (document.getError() != null) { + String message = document.getError().getMessage(); + if (message.length() > MAX_CONTENT_LENGTH) { + message = message.substring(0, MAX_CONTENT_LENGTH) + "..."; + intent.putExtra(DOCUMENT_ERROR, message); + intent.putExtra(DOCUMENT_ERROR_NULL_STATUS, true); + } + } else { + Object doc = document.getDeserializedValue(); + String docContents = doc == null ? "{}" : Utils.getGson().toJson(doc); + intent.putExtra(DOCUMENT_CONTENT, docContents); + intent.putExtra(DOCUMENT_ERROR_NULL_STATUS, false); + } + if (document.getLastUpdatedDate() != null) { + intent.putExtra(DOCUMENT_DATE, document.getLastUpdatedDate().toString()); + } + intent.putExtra(DOCUMENT_STATE, document.isFromDeviceCache()); + } + private void loadUserDocuments() { /* List the user documents. */ @@ -247,32 +276,30 @@ private void loadUserDocuments() { @Override public boolean onOptionsItemSelected(MenuItem item) { mLoading = false; - switch (item.getItemId()) { - case R.id.action_add: - switch (mDocumentType) { - case USER: - String accountId = MainActivity.sSharedPreferences.getString(ACCOUNT_ID, null); - if (accountId != null) { - Intent intent = new Intent(DataActivity.this, NewUserDocumentActivity.class); - startActivity(intent); + if (item.getItemId() == R.id.action_add) { + switch (mDocumentType) { + case USER: + String accountId = MainActivity.sSharedPreferences.getString(ACCOUNT_ID, null); + if (accountId != null) { + Intent intent = new Intent(DataActivity.this, NewUserDocumentActivity.class); + startActivity(intent); + } + break; + + case READONLY: + final AlertDialog.Builder builder = new AlertDialog.Builder(DataActivity.this); + builder.setIcon(R.drawable.ic_appcenter_logo); + builder.setTitle(getApplicationContext().getResources().getString(R.string.document_type_reminder)); + builder.setPositiveButton(getApplicationContext().getResources().getString(R.string.alert_ok), new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + builder.setCancelable(true); } - break; - - case READONLY: - final AlertDialog.Builder builder = new AlertDialog.Builder(DataActivity.this); - builder.setIcon(R.drawable.ic_appcenter_logo); - builder.setTitle(getApplicationContext().getResources().getString(R.string.document_type_reminder)); - builder.setPositiveButton(getApplicationContext().getResources().getString(R.string.alert_ok), new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - builder.setCancelable(true); - } - }); - builder.show(); - break; - } - break; + }); + builder.show(); + break; + } } return true; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivity.java index 54ca80f60e..f0430c403a 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DeviceInfoActivity.java @@ -58,8 +58,7 @@ protected void onCreate(Bundle savedInstanceState) { final List list = getDeviceInfoDisplayModelList(log); - @SuppressLint("HardwareIds") - final String deviceId = Settings.Secure.getString(getApplication().getContentResolver(), Settings.Secure.ANDROID_ID); + @SuppressLint("HardwareIds") final String deviceId = Settings.Secure.getString(getApplication().getContentResolver(), Settings.Secure.ANDROID_ID); list.add(new DeviceInfoDisplayModel() {{ title = "Device ID"; value = deviceId; @@ -126,7 +125,7 @@ public void onNetworkStateUpdated(boolean connected) { } @VisibleForTesting - class DeviceInfoDisplayModel { + static class DeviceInfoDisplayModel { String title; String value; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DocumentDetailActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DocumentDetailActivity.java index 2b7b8055c1..7e4f2bc8b9 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DocumentDetailActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/DocumentDetailActivity.java @@ -11,6 +11,8 @@ import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -24,7 +26,6 @@ import com.microsoft.appcenter.data.Utils; import com.microsoft.appcenter.data.models.DocumentWrapper; import com.microsoft.appcenter.sasquatch.R; -import com.microsoft.appcenter.sasquatch.activities.data.TestDocument; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import org.json.JSONException; @@ -35,8 +36,13 @@ import java.util.Map; import static android.view.View.GONE; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_CONTENT; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_DATE; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ERROR; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ERROR_NULL_STATUS; import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_ID; import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_PARTITION; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.DOCUMENT_STATE; public class DocumentDetailActivity extends AppCompatActivity { @@ -50,24 +56,34 @@ public class DocumentDetailActivity extends AppCompatActivity { private String mFullErrorContents; + private String mDocumentContent; + + private String mDocumentDate; + + private boolean mDocumentState; + + private String mDocumentError; + + private boolean mDocumentNullStatus; + private ProgressBar mDetailProgress; private ListView mListView; - private AppCenterConsumer> getAppDocument = new AppCenterConsumer>() { + private final AppCenterConsumer> getAppDocument = new AppCenterConsumer>() { @Override - public void accept(DocumentWrapper document) { + public void accept(DocumentWrapper document) { if (document.hasFailed()) { Toast.makeText(DocumentDetailActivity.this, String.format(getResources().getString(R.string.get_document_failed), mDocumentId), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(DocumentDetailActivity.this, String.format(getResources().getString(R.string.get_document_success), mDocumentId), Toast.LENGTH_SHORT).show(); } - fillInfo(document); + refreshDocumentWithRead(document); } }; - private AppCenterConsumer> getUserDocument = new AppCenterConsumer>() { + private final AppCenterConsumer> getUserDocument = new AppCenterConsumer>() { @Override public void accept(DocumentWrapper document) { @@ -76,7 +92,7 @@ public void accept(DocumentWrapper document) { } else { Toast.makeText(DocumentDetailActivity.this, String.format(getResources().getString(R.string.get_document_success), mDocumentId), Toast.LENGTH_SHORT).show(); } - fillInfo(document); + refreshDocumentWithRead(document); } }; @@ -90,17 +106,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mDetailProgress.setVisibility(View.VISIBLE); mDocumentPartition = intent.getStringExtra(DOCUMENT_PARTITION); mDocumentId = intent.getStringExtra(DOCUMENT_ID); - if (mDocumentPartition.equals(DefaultPartitions.USER_DOCUMENTS)) { - Data.read(mDocumentId, Map.class, mDocumentPartition).thenAccept(getUserDocument); - } else { - Data.read(mDocumentId, TestDocument.class, mDocumentPartition).thenAccept(getAppDocument); - } + mDocumentContent = intent.getStringExtra(DOCUMENT_CONTENT); + mDocumentDate = intent.getStringExtra(DOCUMENT_DATE); + mDocumentState = intent.getBooleanExtra(DOCUMENT_STATE, false); + mDocumentError = intent.getStringExtra(DOCUMENT_ERROR); + mDocumentNullStatus = intent.getBooleanExtra(DOCUMENT_ERROR_NULL_STATUS, false); + fillInfo(mDocumentNullStatus, mDocumentError, mDocumentDate, mDocumentState, mDocumentContent); } - private void fillInfo(DocumentWrapper document) { + private void fillInfo(boolean documentErrorNullStatus, String errorMessage, String date, boolean documentState, String docContents) { mDetailProgress.setVisibility(GONE); mListView.setVisibility(View.VISIBLE); - final List list = getDocumentInfoDisplayModelList(document); + final List list = getDocumentInfoDisplayModelList(documentErrorNullStatus, errorMessage, date, documentState, docContents); ArrayAdapter adapter = new ArrayAdapter(this, R.layout.info_list_item, R.id.info_title, list) { @NonNull @@ -135,23 +152,17 @@ public void onClick(View view) { mListView.setAdapter(adapter); } - private List getDocumentInfoDisplayModelList(DocumentWrapper document) { + private List getDocumentInfoDisplayModelList(boolean documentErrorNullStatus, String errorMessage, String date, boolean documentState, String docContents) { List list = new ArrayList<>(); list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_id_title), mDocumentId)); list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_partition_title), mDocumentPartition)); - if (document.getError() != null) { - String message = document.getError().getMessage(); - mFullErrorContents = message; - if (message.length() > MAX_CONTENT_LENGTH) { - message = message.substring(0, MAX_CONTENT_LENGTH) + "..."; - } - list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_error_title), message)); + if (documentErrorNullStatus) { + mFullErrorContents = errorMessage; + list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_error_title), errorMessage)); return list; } - list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_date_title), document.getLastUpdatedDate().toString())); - list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_state_title), document.isFromDeviceCache() ? getString(R.string.document_info_cached_state) : getString(R.string.document_info_remote_state))); - Object doc = document.getDeserializedValue(); - String docContents = doc == null ? "{}" : Utils.getGson().toJson(doc); + list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_date_title), date)); + list.add(new DocumentInfoDisplayModel(getString(R.string.document_info_state_title), documentState ? getString(R.string.document_info_cached_state) : getString(R.string.document_info_remote_state))); try { JSONObject docContentsJSON = new JSONObject(docContents); mFullDocContents = docContentsJSON.toString(4); @@ -165,12 +176,50 @@ private List getDocumentInfoDisplayModelList(DocumentW return list; } + private void refreshDocumentWithRead(DocumentWrapper document) { + if (document.getError() != null) { + String message = document.getError().getMessage(); + if (message.length() > MAX_CONTENT_LENGTH) { + message = message.substring(0, MAX_CONTENT_LENGTH) + "..."; + mDocumentError = message; + mDocumentNullStatus = true; + } + } else { + Object doc = document.getDeserializedValue(); + mDocumentContent = doc == null ? "{}" : Utils.getGson().toJson(doc); + mDocumentNullStatus = false; + } + if (document.getLastUpdatedDate() != null) { + mDocumentDate = document.getLastUpdatedDate().toString(); + } + mDocumentState = document.isFromDeviceCache(); + fillInfo(mDocumentNullStatus, mDocumentError, mDocumentDate, mDocumentState, mDocumentContent); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_refresh) { + if (mDocumentPartition.equals(DefaultPartitions.USER_DOCUMENTS)) { + Data.read(mDocumentId, Map.class, mDocumentPartition).thenAccept(getUserDocument); + } else { + Data.read(mDocumentId, Map.class, mDocumentPartition).thenAccept(getAppDocument); + } + } + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.refresh, menu); + return true; + } + @VisibleForTesting - class DocumentInfoDisplayModel { + static class DocumentInfoDisplayModel { - String mTitle; + final String mTitle; - String mValue; + final String mValue; DocumentInfoDisplayModel(String title, String value) { mTitle = title; diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java index 74660d78e7..29e556ffda 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java @@ -36,7 +36,6 @@ import java.util.Map; import java.util.Set; - public class EventActivity extends AppCompatActivity { /** @@ -140,7 +139,6 @@ public void onClick(View v) { mPauseTransmissionButton.setOnClickListener(new View.OnClickListener() { @Override - @SuppressWarnings("ConstantConditions") public void onClick(View v) { getSelectedTarget().pause(); } @@ -149,7 +147,6 @@ public void onClick(View v) { mResumeTransmissionButton.setOnClickListener(new View.OnClickListener() { @Override - @SuppressWarnings("ConstantConditions") public void onClick(View v) { getSelectedTarget().resume(); } @@ -189,10 +186,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - addProperty(); - break; + if (item.getItemId() == R.id.action_add) { + addProperty(); } return true; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventPropertiesActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventPropertiesActivity.java index 689543511f..da3455657c 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventPropertiesActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventPropertiesActivity.java @@ -106,27 +106,25 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - mTypedPropertyFragment.reset(); - mAddPropertyLayout.setVisibility(View.VISIBLE); - mAddPropertyLayout.findViewById(R.id.add_button).setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - mTypedPropertyFragment.set(getSelectedTarget().getPropertyConfigurator()); - updatePropertyList(); - mAddPropertyLayout.setVisibility(View.GONE); - } - }); - mAddPropertyLayout.findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() { + if (item.getItemId() == R.id.action_add) { + mTypedPropertyFragment.reset(); + mAddPropertyLayout.setVisibility(View.VISIBLE); + mAddPropertyLayout.findViewById(R.id.add_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAddPropertyLayout.setVisibility(View.GONE); - } - }); - break; + @Override + public void onClick(View v) { + mTypedPropertyFragment.set(getSelectedTarget().getPropertyConfigurator()); + updatePropertyList(); + mAddPropertyLayout.setVisibility(View.GONE); + } + }); + mAddPropertyLayout.findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + mAddPropertyLayout.setVisibility(View.GONE); + } + }); } return true; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MSALoginActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MSALoginActivity.java index a9bf7f476b..98688ee8aa 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MSALoginActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MSALoginActivity.java @@ -153,7 +153,6 @@ public void onPageFinished(WebView view, String url) { } @Override - @SuppressWarnings("deprecation") public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { failSignIn(errorCode, description); } @@ -190,7 +189,6 @@ public void onPageFinished(WebView view, String url) { } @Override - @SuppressWarnings("deprecation") public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { clearCookies(); failSignOut(errorCode, description); @@ -206,7 +204,6 @@ public void onReceivedError(WebView view, WebResourceRequest request, WebResourc mWebView.loadUrl(SIGN_OUT_URL + REDIRECT_URI_PARAM + CLIENT_ID_PARAM); } - @SuppressWarnings("deprecation") private void clearCookies() { CookieManager cookieManager = CookieManager.getInstance(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { @@ -320,13 +317,7 @@ public void onBeforeCalling(URL url, Map headers) { }, new ServiceCallback() { - /* TODO remove this method once Auth published to jCenter. */ - public void onCallSucceeded(String payload) { - onCallSucceeded(payload, null); - } - - /* TODO add @Override once Auth published to jCenter. Remove also suppress warnings. */ - @SuppressWarnings("WeakerAccess") + @Override public void onCallSucceeded(String payload, @SuppressWarnings("unused") Map headers) { try { JSONObject response = new JSONObject(payload); @@ -370,13 +361,7 @@ public void onBeforeCalling(URL url, Map headers) { }, new ServiceCallback() { - /* TODO remove this method once Auth published to jCenter. */ - public void onCallSucceeded(String payload) { - onCallSucceeded(payload, null); - } - - /* TODO add @Override once Auth published to jCenter. Remove also suppress warnings. */ - @SuppressWarnings("WeakerAccess") + @Override public void onCallSucceeded(String payload, @SuppressWarnings("unused") Map headers) { try { JSONObject response = new JSONObject(payload); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MainActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MainActivity.java index 112563de34..3716bf111e 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MainActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/MainActivity.java @@ -48,8 +48,12 @@ import com.microsoft.appcenter.sasquatch.listeners.SasquatchPushListener; import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import java.lang.reflect.Method; import java.util.UUID; +import static com.microsoft.appcenter.sasquatch.activities.ActivityConstants.ANALYTICS_TRANSMISSION_INTERVAL_KEY; +import static com.microsoft.appcenter.sasquatch.activities.ActivityConstants.DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS; + public class MainActivity extends AppCompatActivity { public static final String LOG_TAG = "AppCenterSasquatch"; @@ -68,14 +72,14 @@ public class MainActivity extends AppCompatActivity { static final String MAX_STORAGE_SIZE_KEY = "maxStorageSize"; - private final int DATABASE_SIZE_MULTIPLE = 4096; - private static final String SENDER_ID = "177539951155"; private static final String TEXT_ATTACHMENT_KEY = "textAttachment"; private static final String FILE_ATTACHMENT_KEY = "fileAttachment"; + private static final int DATABASE_SIZE_MULTIPLE = 4096; + static SharedPreferences sSharedPreferences; @SuppressLint("StaticFieldLeak") @@ -114,7 +118,32 @@ public static void setFileAttachment(Uri fileAttachment) { native void setupNativeCrashesListener(String path); + static String getLogUrl(Context context, String startType) { + switch (StartType.valueOf(startType)) { + case TARGET: + case NO_SECRET: + return context.getString(R.string.log_url_one_collector); + } + return context.getString(R.string.log_url); + } + static void startAppCenter(Application application, String startTypeString) { + if (MainActivity.sSharedPreferences.contains(ANALYTICS_TRANSMISSION_INTERVAL_KEY)) { + int latency = MainActivity.sSharedPreferences.getInt(ANALYTICS_TRANSMISSION_INTERVAL_KEY, DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS); + try { + + /* TODO remove reflection and catch block after API available to jCenter. */ + boolean result = (boolean) Analytics.class.getMethod("setTransmissionInterval", int.class).invoke(null, latency); + if (result) { + Toast.makeText(application, String.format(application.getString(R.string.analytics_transmission_interval_change_success), latency), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(application, application.getString(R.string.analytics_transmission_interval_change_failed), Toast.LENGTH_SHORT).show(); + } + } catch (Exception ignored) { + + /* Nothing to handle; this is reached if Analytics isn't being used. */ + } + } StartType startType = StartType.valueOf(startTypeString); if (startType == StartType.SKIP_START) { return; @@ -188,10 +217,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_settings: - startActivity(new Intent(this, SettingsActivity.class)); - break; + if (item.getItemId() == R.id.action_settings) { + startActivity(new Intent(this, SettingsActivity.class)); } return true; } @@ -247,7 +274,8 @@ protected void onCreate(Bundle savedInstanceState) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().build()); /* Set custom log URL if one was configured in settings. */ - String logUrl = sSharedPreferences.getString(LOG_URL_KEY, getString(R.string.log_url)); + String startType = sSharedPreferences.getString(APPCENTER_START_TYPE, StartType.APP_SECRET.toString()); + String logUrl = sSharedPreferences.getString(LOG_URL_KEY, getLogUrl(this, startType)); if (!TextUtils.isEmpty(logUrl)) { AppCenter.setLogUrl(logUrl); } @@ -299,8 +327,10 @@ protected void onCreate(Bundle savedInstanceState) { /* Set max storage size. */ setMaxStorageSize(); + /* Set debug enabled for distribute. */ + setDistributeEnabledForDebuggableBuild(); + /* Start App Center. */ - String startType = sSharedPreferences.getString(APPCENTER_START_TYPE, StartType.APP_SECRET.toString()); startAppCenter(getApplication(), startType); /* Set user id. */ @@ -357,6 +387,18 @@ public void accept(ErrorReport data) { listView.setOnItemClickListener(TestFeatures.getOnItemClickListener()); } + private void setDistributeEnabledForDebuggableBuild() { + + /* TODO Call method directly / remove reflection once SDK being released. */ + try { + Method method = Distribute.class.getMethod("setEnabledForDebuggableBuild", boolean.class); + method.invoke(null, sSharedPreferences.getBoolean(getString(R.string.appcenter_distribute_debug_state_key), false)); + } catch (NoSuchMethodException ignore) { + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public enum StartType { APP_SECRET, TARGET, diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/NewUserDocumentActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/NewUserDocumentActivity.java index 36610acfd7..735af95aad 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/NewUserDocumentActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/NewUserDocumentActivity.java @@ -88,10 +88,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - addProperty(); - break; + if (item.getItemId() == R.id.action_add) { + addProperty(); } return true; } @@ -109,7 +107,6 @@ public void save(View view) { property.setGenericProperty(document); } String documentId = mEditDocumentId.getText().toString(); - documentId = documentId.replace(" ", "-"); Data.replace(documentId, document, Map.class, DefaultPartitions.USER_DOCUMENTS, mWriteOptions).thenAccept(new AppCenterConsumer>() { @Override diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/PageActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/PageActivity.java index 6d6d905fa8..ed7dc2bb2b 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/PageActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/PageActivity.java @@ -47,10 +47,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - addProperty(); - break; + if (item.getItemId() == R.id.action_add) { + addProperty(); } return true; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java index 0d5bdda72e..c584dab88f 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java @@ -18,6 +18,7 @@ import android.os.FileObserver; import android.preference.CheckBoxPreference; import android.preference.Preference; +import android.preference.PreferenceCategory; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.text.InputType; @@ -46,9 +47,16 @@ import java.io.File; import java.lang.reflect.Method; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Locale; +import java.util.TimeZone; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import static com.microsoft.appcenter.sasquatch.activities.ActivityConstants.ANALYTICS_TRANSMISSION_INTERVAL_KEY; +import static com.microsoft.appcenter.sasquatch.activities.ActivityConstants.DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS; import static com.microsoft.appcenter.sasquatch.activities.MainActivity.APPCENTER_START_TYPE; import static com.microsoft.appcenter.sasquatch.activities.MainActivity.APP_SECRET_KEY; import static com.microsoft.appcenter.sasquatch.activities.MainActivity.FIREBASE_ENABLED_KEY; @@ -73,6 +81,8 @@ public class SettingsActivity extends AppCompatActivity { private static boolean sAnalyticsPaused; + private static String sInitialStartType; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -180,6 +190,64 @@ public void setEnabled(boolean enabled) { sAnalyticsPaused = enabled; } }); + try { + + /* TODO remove reflection and catch block after API available to jCenter. */ + final Method setTransmissionInterval = Analytics.class.getMethod("setTransmissionInterval", int.class); + int interval = MainActivity.sSharedPreferences.getInt(ANALYTICS_TRANSMISSION_INTERVAL_KEY, DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS); + initClickableSetting(R.string.appcenter_analytics_transmission_interval_key, getTransmissionInterval(interval), new Preference.OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(final Preference preference) { + + /* Initialize views for dialog. */ + final EditText input = new EditText(getActivity()); + input.setInputType(InputType.TYPE_NUMBER_FLAG_SIGNED); + input.setHint(R.string.time_interval_in_seconds); + input.setText(String.format(Locale.ENGLISH, "%d", MainActivity.sSharedPreferences.getInt(ANALYTICS_TRANSMISSION_INTERVAL_KEY, DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS))); + input.setSelection(input.getText().length()); + + /* Display dialog. */ + new AlertDialog.Builder(getActivity()).setTitle(R.string.appcenter_analytics_transmission_interval_title).setView(input) + .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + int newInterval; + try { + newInterval = Integer.parseInt(input.getText().toString()); + } catch (NumberFormatException ignored) { + Toast.makeText(getActivity(), getActivity().getString(R.string.analytics_transmission_interval_invalid_value), Toast.LENGTH_SHORT).show(); + return; + } + if (newInterval == ActivityConstants.DEFAULT_TRANSMISSION_INTERVAL_IN_SECONDS) { + MainActivity.sSharedPreferences.edit().remove(ANALYTICS_TRANSMISSION_INTERVAL_KEY).apply(); + } else { + MainActivity.sSharedPreferences.edit().putInt(ANALYTICS_TRANSMISSION_INTERVAL_KEY, newInterval).apply(); + } + String intervalString = getTransmissionInterval(newInterval); + preference.setSummary(intervalString); + Toast.makeText(getActivity(), intervalString, Toast.LENGTH_SHORT).show(); + + /* TODO remove reflection and catch block after API available to jCenter. */ + try { + + /* Setting interval without restarting works if we used SKIP_START and has not started yet by changing startType. */ + setTransmissionInterval.invoke(null, newInterval); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }) + .setNegativeButton(R.string.cancel, null) + .create().show(); + return true; + } + }); + } catch (NoSuchMethodException e) { + PreferenceCategory preference = (PreferenceCategory) findPreference(getString(R.string.analytics_key)); + preference.removePreference(preference.findPreference(getString(R.string.appcenter_analytics_transmission_interval_key))); + } initCheckBoxSetting(R.string.appcenter_auto_page_tracking_key, R.string.appcenter_auto_page_tracking_enabled, R.string.appcenter_auto_page_tracking_disabled, new HasEnabled() { @Override @@ -246,6 +314,30 @@ public boolean isEnabled() { } }); + /* TODO Call method directly / remove reflection once SDK being released. */ + try { + final Method method = Distribute.class.getMethod("setEnabledForDebuggableBuild", boolean.class); + initCheckBoxSetting(R.string.appcenter_distribute_debug_state_key, R.string.appcenter_distribute_debug_summary_enabled, R.string.appcenter_distribute_debug_summary_disabled, new HasEnabled() { + + @Override + public boolean isEnabled() { + return MainActivity.sSharedPreferences.getBoolean(getString(R.string.appcenter_distribute_debug_state_key), false); + } + + @Override + public void setEnabled(boolean enabled) { + MainActivity.sSharedPreferences.edit().putBoolean(getString(R.string.appcenter_distribute_debug_state_key), enabled).apply(); + try { + method.invoke(null, enabled); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } catch (NoSuchMethodException e) { + getPreferenceScreen().removePreference(findPreference(getString(R.string.appcenter_distribute_debug_state_key))); + } + /* Push. */ initCheckBoxSetting(R.string.appcenter_push_state_key, R.string.appcenter_push_state_summary_enabled, R.string.appcenter_push_state_summary_disabled, new HasEnabled() { @@ -272,8 +364,6 @@ public boolean isEnabled() { public void setEnabled(boolean enabled) { Auth.setEnabled(enabled); } - - }); /* Data. */ @@ -288,8 +378,6 @@ public boolean isEnabled() { public void setEnabled(boolean enabled) { Data.setEnabled(enabled); } - - }); /* Push. */ @@ -453,8 +541,11 @@ public void onReset() { }); /* Miscellaneous. */ - String initialStartType = MainActivity.sSharedPreferences.getString(APPCENTER_START_TYPE, StartType.APP_SECRET.toString()); - initChangeableSetting(R.string.appcenter_start_type_key, initialStartType, new Preference.OnPreferenceChangeListener() { + final String startType = MainActivity.sSharedPreferences.getString(APPCENTER_START_TYPE, StartType.APP_SECRET.toString()); + if (sInitialStartType == null) { + sInitialStartType = startType; + } + initChangeableSetting(R.string.appcenter_start_type_key, startType, new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -466,12 +557,17 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary(MainActivity.sSharedPreferences.getString(APPCENTER_START_TYPE, null)); /* Try to start now, this tests double calls log an error as well as valid call if previous type was none. */ + String logUrl = MainActivity.sSharedPreferences.getString(LOG_URL_KEY, MainActivity.getLogUrl(getActivity(), startValue)); + if (!TextUtils.isEmpty(logUrl)) { + AppCenter.setLogUrl(logUrl); + } MainActivity.startAppCenter(getActivity().getApplication(), startValue); /* Invite to restart app to take effect. */ if (sNeedRestartOnStartTypeUpdate) { Toast.makeText(getActivity(), R.string.appcenter_start_type_changed, Toast.LENGTH_SHORT).show(); } else { + sInitialStartType = startValue; sNeedRestartOnStartTypeUpdate = true; } return true; @@ -521,7 +617,7 @@ public boolean onPreferenceClick(Preference preference) { return true; } }); - String defaultLogUrl = getString(R.string.log_url); + String defaultLogUrl = MainActivity.getLogUrl(getActivity(), sInitialStartType); final String defaultLogUrlDisplay = TextUtils.isEmpty(defaultLogUrl) ? getString(R.string.log_url_set_to_production) : defaultLogUrl; initClickableSetting(R.string.log_url_key, MainActivity.sSharedPreferences.getString(LOG_URL_KEY, defaultLogUrlDisplay), new Preference.OnPreferenceClickListener() { @@ -556,9 +652,9 @@ public void onClick(DialogInterface dialog, int which) { @Override public void onClick(DialogInterface dialog, int which) { - setDefaultUrl(); - if (!TextUtils.isEmpty(getString(R.string.log_url))) { - AppCenter.setLogUrl(getString(R.string.log_url)); + String defaultUrl = setDefaultUrl(); + if (!TextUtils.isEmpty(defaultUrl)) { + AppCenter.setLogUrl(defaultUrl); } preference.setSummary(MainActivity.sSharedPreferences.getString(LOG_URL_KEY, defaultLogUrlDisplay)); } @@ -568,9 +664,11 @@ public void onClick(DialogInterface dialog, int which) { return true; } - private void setDefaultUrl() { + private String setDefaultUrl() { setKeyValue(LOG_URL_KEY, null); - toastUrlChange(getString(R.string.log_url)); + String logUrl = MainActivity.getLogUrl(getActivity(), sInitialStartType); + toastUrlChange(logUrl); + return logUrl; } private void toastUrlChange(String url) { @@ -761,6 +859,18 @@ private void setKeyValue(String key, String value) { editor.apply(); } + private String getTransmissionInterval(int interval) { + Date date = new Date(TimeUnit.SECONDS.toMillis(interval)); + DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + String formattedInterval = dateFormat.format(date); + long days = TimeUnit.SECONDS.toDays(interval); + if (days > 0) { + formattedInterval = days + "." + formattedInterval; + } + return interval + getString(R.string.appcenter_analytics_transmission_interval_summary_format) + formattedInterval; + } + private boolean isFirebaseEnabled() { return MainActivity.sSharedPreferences.getBoolean(FIREBASE_ENABLED_KEY, false); } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/UserInformationActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/UserInformationActivity.java new file mode 100644 index 0000000000..cf00544d25 --- /dev/null +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/UserInformationActivity.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.sasquatch.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.microsoft.appcenter.sasquatch.R; +import com.microsoft.appcenter.utils.AppCenterLog; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ACCESS_TOKEN; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ID; +import static com.microsoft.appcenter.sasquatch.SasquatchConstants.USER_INFORMATION_ID_TOKEN; + +public class UserInformationActivity extends AppCompatActivity { + + private static final int MAX_CONTENT_LENGTH = 50; + + private String mFullIdToken; + + private String mFullAccessToken; + + private ListView mListView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_information); + Intent intent = getIntent(); + String accountId = intent.getStringExtra(USER_INFORMATION_ID); + String idToken = intent.getStringExtra(USER_INFORMATION_ID_TOKEN); + String accessToken = intent.getStringExtra(USER_INFORMATION_ACCESS_TOKEN); + mListView = findViewById(R.id.user_info_list_view); + fillInfo(accountId, idToken, accessToken); + } + + private JSONObject getParsedToken(String rawToken) { + try { + JWT parsedIdToken = JWTParser.parse(rawToken); + Map claims = parsedIdToken.getJWTClaimsSet().getClaims(); + return new JSONObject(claims); + } catch (ParseException ex) { + AppCenterLog.error(AppCenterLog.LOG_TAG, getString(R.string.b2c_jwt_parse_error)); + } + return null; + } + + private void fillInfo(String accountId, String idToken, String accessToken) { + final List list = (idToken == null || accessToken == null) ? + getOnlyAccountIdDisplayModel(accountId) : + getUserInfoDisplayModelList(accountId, getParsedToken(idToken), getParsedToken(accessToken)); + ArrayAdapter adapter = new ArrayAdapter(this, R.layout.info_list_item, R.id.info_title, list) { + + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + View view = super.getView(position, convertView, parent); + TextView titleView = view.findViewById(R.id.info_title); + final TextView valueView = view.findViewById(R.id.info_content); + titleView.setText(list.get(position).mTitle); + valueView.setText(list.get(position).mValue); + if (list.get(position).mTitle.equals(getString(R.string.b2c_user_info_id_token_title))) { + view.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + valueView.setText(mFullIdToken); + } + }); + } + if (list.get(position).mTitle.equals(getString(R.string.b2c_user_info_access_token_title))) { + view.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + valueView.setText(mFullAccessToken); + } + }); + } + return view; + } + }; + mListView.setAdapter(adapter); + } + + private List getOnlyAccountIdDisplayModel(String accountId) { + List list = new ArrayList<>(); + list.add(new UserInfoDisplayModel(getString(R.string.b2c_user_info_id_title), accountId)); + return list; + } + + private List getUserInfoDisplayModelList(String accountId, JSONObject idTokenJSON, JSONObject accessTokenJSON) { + List list = new ArrayList<>(); + list.add(new UserInfoDisplayModel(getString(R.string.b2c_user_info_id_title), accountId)); + mFullIdToken = parseAndAddTokenToList(getString(R.string.b2c_user_info_id_token_title), idTokenJSON, list); + mFullAccessToken = parseAndAddTokenToList(getString(R.string.b2c_user_info_access_token_title), accessTokenJSON, list); + return list; + } + + private String parseAndAddTokenToList(String title, JSONObject tokenJSON, List list) { + String fullToken; + String tokenPreview; + try { + if (tokenJSON == null) { + fullToken = tokenPreview = getString(R.string.b2c_jwt_parse_error); + } else { + fullToken = tokenJSON.toString(4).replace("\\", ""); + tokenPreview = tokenJSON.toString(); + } + } catch (JSONException e) { + fullToken = tokenPreview = getString(R.string.b2c_jwt_parse_json_error); + } + if (tokenPreview.length() > MAX_CONTENT_LENGTH) { + tokenPreview = tokenPreview.substring(0, MAX_CONTENT_LENGTH) + "..."; + } + list.add(new UserInfoDisplayModel(title, tokenPreview)); + return fullToken; + } + + @VisibleForTesting + static class UserInfoDisplayModel { + + final String mTitle; + + final String mValue; + + UserInfoDisplayModel(String title, String value) { + mTitle = title; + mValue = value; + } + } +} diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/AppDocumentListAdapter.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/AppDocumentListAdapter.java index 96de0e0e41..bc66a47dd8 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/AppDocumentListAdapter.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/AppDocumentListAdapter.java @@ -18,16 +18,17 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; public class AppDocumentListAdapter extends RecyclerView.Adapter { - private Context mContext; + private final Context mContext; - private List> mList; + private final List> mList; private OnItemClickListener mListener; - public AppDocumentListAdapter(Context context, List> list) { + public AppDocumentListAdapter(Context context, List> list) { mContext = context; mList = new ArrayList<>(list); } @@ -56,7 +57,7 @@ public int getItemCount() { return mList.size(); } - public void upload(List> list) { + public void upload(List> list) { if (list != null) { mList.addAll(list); } @@ -71,13 +72,13 @@ public void setOnItemClickListener(OnItemClickListener listener) { mListener = listener; } - public String getDocumentByPosition(int position) { - return mList.get(position).getId(); + public DocumentWrapper getDocument(int position) { + return mList.get(position); } class AppDocumentListHolder extends RecyclerView.ViewHolder { - TextView titleFile; + final TextView titleFile; AppDocumentListHolder(@NonNull View itemView) { super(itemView); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/CustomItemAdapter.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/CustomItemAdapter.java index 3c56858b65..c19ce2c196 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/CustomItemAdapter.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/CustomItemAdapter.java @@ -24,7 +24,7 @@ public class CustomItemAdapter extends RecyclerView.Adapter> mList; - private Context mContext; + private final Context mContext; private CustomItemAdapter.OnItemClickListener mListener; @@ -88,8 +88,8 @@ public String getItem(int position) { return mList.get(position).getId(); } - public String getDocumentByPosition(int position) { - return mList.get(position).getId(); + public DocumentWrapper getDocument(int position) { + return mList.get(position); } public interface OnItemClickListener { @@ -101,9 +101,9 @@ public interface OnItemClickListener { class CustomItemAdapterHolder extends RecyclerView.ViewHolder { - TextView documentIdTextView; + final TextView documentIdTextView; - ImageButton deleteButton; + final ImageButton deleteButton; CustomItemAdapterHolder(@NonNull View itemView) { super(itemView); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/TestDocument.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/TestDocument.java deleted file mode 100644 index 0f62e8c011..0000000000 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/data/TestDocument.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.sasquatch.activities.data; - -public class TestDocument { - - @SuppressWarnings("unused") - String key; -} diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/features/TestFeaturesListAdapter.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/features/TestFeaturesListAdapter.java index 4b2ee24b0d..167c3f9f40 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/features/TestFeaturesListAdapter.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/features/TestFeaturesListAdapter.java @@ -82,7 +82,7 @@ private static class ViewHolder { private final TextView mTextView2; - private ViewHolder(Class clazz, TextView view) { + private ViewHolder(@SuppressWarnings("SameParameterValue") Class clazz, TextView view) { this(clazz, view, null); } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/CustomPropertyFragment.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/CustomPropertyFragment.java index b067528ea2..ef99b7a1d8 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/CustomPropertyFragment.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/CustomPropertyFragment.java @@ -69,7 +69,7 @@ private void updateValueType() { mValueLabel.setVisibility(type != CustomPropertyType.CLEAR ? View.VISIBLE : View.GONE); } - public CustomPropertyType getType() { + private CustomPropertyType getType() { return CustomPropertyType.values()[mEditType.getSelectedItemPosition()]; } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/DatePickerFragment.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/DatePickerFragment.java index a8cdab09aa..edba2028bb 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/DatePickerFragment.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/DatePickerFragment.java @@ -20,7 +20,7 @@ public class DatePickerFragment extends DialogFragment { private DatePickerDialog.OnDateSetListener mListener; - public Date getInitialDate() { + private Date getInitialDate() { Bundle arguments = getArguments(); if (arguments != null) { return (Date) arguments.getSerializable(INITIAL_DATE); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/EditDateTimeFragment.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/EditDateTimeFragment.java index 0477861e11..5d3373b559 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/EditDateTimeFragment.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/EditDateTimeFragment.java @@ -29,12 +29,12 @@ public abstract class EditDateTimeFragment extends Fragment private EditText mEditTime; - protected View mDateTime; + View mDateTime; /** * Date value, with default being current time. */ - protected Date mDate = new Date(); + Date mDate = new Date(); @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { @@ -90,7 +90,7 @@ private void showTime() { fragment.show(getActivity().getSupportFragmentManager(), "timePicker"); } - protected void setDate(Date date) { + void setDate(Date date) { mDate = date; /* If UI ready update now, otherwise do it in onCreateView. */ diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TimePickerFragment.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TimePickerFragment.java index cc1611de2e..24cf06d166 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TimePickerFragment.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TimePickerFragment.java @@ -20,7 +20,7 @@ public class TimePickerFragment extends DialogFragment { private TimePickerDialog.OnTimeSetListener mListener; - public Date getInitialTime() { + private Date getInitialTime() { Bundle arguments = getArguments(); if (arguments != null) { return (Date) arguments.getSerializable(INITIAL_TIME); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TypedPropertyFragment.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TypedPropertyFragment.java index 462b698d1d..4fcbc82ab9 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TypedPropertyFragment.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/fragments/TypedPropertyFragment.java @@ -82,10 +82,8 @@ public EventPropertyType getType() { public void set(Map eventProperties) { EventPropertyType type = getType(); String key = mEditKey.getText().toString(); - switch (type) { - case STRING: - eventProperties.put(key, mEditString.getText().toString()); - break; + if (type == EventPropertyType.STRING) { + eventProperties.put(key, mEditString.getText().toString()); } } diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/listeners/SasquatchCrashesListener.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/listeners/SasquatchCrashesListener.java index 0a8570d85e..bfc50e0cac 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/listeners/SasquatchCrashesListener.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/listeners/SasquatchCrashesListener.java @@ -143,7 +143,6 @@ public void onSendingFailed(ErrorReport report, Exception e) { notifySending(mContext.getString(R.string.crash_sent_failed)); } - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Override public void onSendingSucceeded(ErrorReport report) { String message = String.format("%s\nCrash ID: %s", mContext.getString(R.string.crash_sent_succeeded), report.getId()); diff --git a/apps/sasquatch/src/main/res/layout/activity_common_schema_properties.xml b/apps/sasquatch/src/main/res/layout/activity_common_schema_properties.xml index c2aa8608af..e8cdc9c4ec 100644 --- a/apps/sasquatch/src/main/res/layout/activity_common_schema_properties.xml +++ b/apps/sasquatch/src/main/res/layout/activity_common_schema_properties.xml @@ -1,5 +1,4 @@ - - @@ -12,7 +11,6 @@ tools:context=".activities.CommonSchemaPropertiesActivity"> @@ -24,7 +22,6 @@ android:background="@android:drawable/btn_dropdown" /> @@ -35,7 +32,9 @@ android:layout_height="wrap_content" android:ems="10" android:hint="@string/value" - android:inputType="text" /> + android:importantForAutofill="no" + android:inputType="text" + tools:targetApi="o" />