Переглянути джерело

Move first run and whats new to onboarding package

Signed-off-by: Chris Narkiewicz <hello@ezaquarii.com>
Chris Narkiewicz 5 роки тому
батько
коміт
b6fc62fb87

+ 21 - 5
build.gradle

@@ -26,16 +26,21 @@ buildscript {
         }
         classpath 'gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.6'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+        classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0-RC14"
     }
 }
 
 apply plugin: 'com.android.application'
+
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
 apply plugin: 'checkstyle'
 apply plugin: 'pmd'
 apply plugin: 'jacoco-android'
 apply plugin: "com.github.spotbugs"
+apply plugin: "io.gitlab.arturbosch.detekt"
 
 configurations {
     ktlint
@@ -257,7 +262,8 @@ dependencies {
     implementation 'commons-io:commons-io:2.6'
     implementation 'com.github.tobiaskaminsky:android-job:v1.2.6.1' // 'com.github.evernote:android-job:v1.2.5'
     implementation 'com.jakewharton:butterknife:10.1.0'
-    annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
+    kapt 'com.jakewharton:butterknife-compiler:10.1.0'
+
     implementation 'org.greenrobot:eventbus:3.1.1'
     implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5'
     implementation 'org.lukhnos:nnio:0.2'
@@ -280,12 +286,12 @@ dependencies {
 
     spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0'
     spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.5'
-
+    
     implementation 'com.google.dagger:dagger:2.23.1'
     implementation 'com.google.dagger:dagger-android:2.23.1'
     implementation 'com.google.dagger:dagger-android-support:2.23.1'
-    annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'
-    annotationProcessor 'com.google.dagger:dagger-android-processor:2.23.1'
+    kapt 'com.google.dagger:dagger-compiler:2.23.1'
+    kapt 'com.google.dagger:dagger-android-processor:2.23.1'
 
     compileOnly "org.projectlombok:lombok:1.18.8"
     annotationProcessor "org.projectlombok:lombok:1.18.8"
@@ -368,7 +374,7 @@ task ktlint(type: JavaExec, group: "verification") {
     description = "Check Kotlin code style."
     main = "com.pinterest.ktlint.Main"
     classpath = configurations.ktlint
-    args "--reporter=plain,output=${buildDir}/ktlint.txt,src/**/*.kt"
+    args "--reporter=plain", "--reporter=plain,output=${buildDir}/ktlint.txt,src/**/*.kt"
 }
 
 task ktlintFormat(type: JavaExec, group: "formatting") {
@@ -377,3 +383,13 @@ task ktlintFormat(type: JavaExec, group: "formatting") {
     classpath = configurations.ktlint
     args "-F", "src/**/*.kt"
 }
+
+detekt {
+    reports {
+        xml {
+            enabled = false
+        }
+    }
+    config = files("detekt.yml")
+    input = files("src/")
+}

+ 523 - 0
detekt.yml

@@ -0,0 +1,523 @@
+autoCorrect: true
+
+test-pattern: # Configure exclusions for test sources
+  active: true
+  patterns: # Test file regexes
+    - '.*/test/.*'
+    - '.*/androidTest/.*'
+    - '.*Test.kt'
+    - '.*Spec.kt'
+    - '.*Spek.kt'
+  exclude-rule-sets:
+    - 'comments'
+  exclude-rules:
+    - 'NamingRules'
+    - 'WildcardImport'
+    - 'MagicNumber'
+    - 'MaxLineLength'
+    - 'LateinitUsage'
+    - 'StringLiteralDuplication'
+    - 'SpreadOperator'
+    - 'TooManyFunctions'
+    - 'ForEachOnRange'
+    - 'FunctionMaxLength'
+    - 'TooGenericExceptionCaught'
+    - 'InstanceOfCheckForException'
+
+build:
+  maxIssues: 10
+  weights:
+    # complexity: 2
+    # LongParameterList: 1
+    # style: 1
+    # comments: 1
+
+processors:
+  active: true
+  exclude:
+  # - 'FunctionCountProcessor'
+  # - 'PropertyCountProcessor'
+  # - 'ClassCountProcessor'
+  # - 'PackageCountProcessor'
+  # - 'KtFileCountProcessor'
+
+console-reports:
+  active: true
+  exclude:
+  #  - 'ProjectStatisticsReport'
+  #  - 'ComplexityReport'
+  #  - 'NotificationReport'
+  #  - 'FindingsReport'
+  #  - 'BuildFailureReport'
+
+comments:
+  active: true
+  CommentOverPrivateFunction:
+    active: false
+  CommentOverPrivateProperty:
+    active: false
+  EndOfSentenceFormat:
+    active: false
+    endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$)
+  UndocumentedPublicClass:
+    active: false
+    searchInNestedClass: true
+    searchInInnerClass: true
+    searchInInnerObject: true
+    searchInInnerInterface: true
+  UndocumentedPublicFunction:
+    active: false
+
+complexity:
+  active: true
+  ComplexCondition:
+    active: true
+    threshold: 4
+  ComplexInterface:
+    active: false
+    threshold: 10
+    includeStaticDeclarations: false
+  ComplexMethod:
+    active: true
+    threshold: 10
+    ignoreSingleWhenExpression: false
+    ignoreSimpleWhenEntries: false
+  LabeledExpression:
+    active: false
+    ignoredLabels: ""
+  LargeClass:
+    active: true
+    threshold: 600
+  LongMethod:
+    active: true
+    threshold: 60
+  LongParameterList:
+    active: true
+    threshold: 6
+    ignoreDefaultParameters: false
+  MethodOverloading:
+    active: false
+    threshold: 6
+  NestedBlockDepth:
+    active: true
+    threshold: 4
+  StringLiteralDuplication:
+    active: false
+    threshold: 3
+    ignoreAnnotation: true
+    excludeStringsWithLessThan5Characters: true
+    ignoreStringsRegex: '$^'
+  TooManyFunctions:
+    active: true
+    thresholdInFiles: 11
+    thresholdInClasses: 11
+    thresholdInInterfaces: 11
+    thresholdInObjects: 11
+    thresholdInEnums: 11
+    ignoreDeprecated: false
+    ignorePrivate: false
+    ignoreOverridden: false
+
+empty-blocks:
+  active: true
+  EmptyCatchBlock:
+    active: true
+    allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
+  EmptyClassBlock:
+    active: true
+  EmptyDefaultConstructor:
+    active: true
+  EmptyDoWhileBlock:
+    active: true
+  EmptyElseBlock:
+    active: true
+  EmptyFinallyBlock:
+    active: true
+  EmptyForBlock:
+    active: true
+  EmptyFunctionBlock:
+    active: true
+    ignoreOverriddenFunctions: false
+  EmptyIfBlock:
+    active: true
+  EmptyInitBlock:
+    active: true
+  EmptyKtFile:
+    active: true
+  EmptySecondaryConstructor:
+    active: true
+  EmptyWhenBlock:
+    active: true
+  EmptyWhileBlock:
+    active: true
+
+exceptions:
+  active: true
+  ExceptionRaisedInUnexpectedLocation:
+    active: false
+    methodNames: 'toString,hashCode,equals,finalize'
+  InstanceOfCheckForException:
+    active: false
+  NotImplementedDeclaration:
+    active: false
+  PrintStackTrace:
+    active: false
+  RethrowCaughtException:
+    active: false
+  ReturnFromFinally:
+    active: false
+  SwallowedException:
+    active: false
+    ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
+  ThrowingExceptionFromFinally:
+    active: false
+  ThrowingExceptionInMain:
+    active: false
+  ThrowingExceptionsWithoutMessageOrCause:
+    active: false
+    exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
+  ThrowingNewInstanceOfSameException:
+    active: false
+  TooGenericExceptionCaught:
+    active: true
+    exceptionNames:
+     - ArrayIndexOutOfBoundsException
+     - Error
+     - Exception
+     - IllegalMonitorStateException
+     - NullPointerException
+     - IndexOutOfBoundsException
+     - RuntimeException
+     - Throwable
+    allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
+  TooGenericExceptionThrown:
+    active: true
+    exceptionNames:
+     - Error
+     - Exception
+     - Throwable
+     - RuntimeException
+
+formatting:
+  active: true
+  android: false
+  autoCorrect: true
+  ChainWrapping:
+    active: true
+    autoCorrect: true
+  CommentSpacing:
+    active: true
+    autoCorrect: true
+  Filename:
+    active: true
+  FinalNewline:
+    active: true
+    autoCorrect: true
+  ImportOrdering:
+    active: false
+  Indentation:
+    active: true
+    autoCorrect: true
+    indentSize: 4
+    continuationIndentSize: 4
+  MaximumLineLength:
+    active: true
+    maxLineLength: 120
+  ModifierOrdering:
+    active: true
+    autoCorrect: true
+  NoBlankLineBeforeRbrace:
+    active: true
+    autoCorrect: true
+  NoConsecutiveBlankLines:
+    active: true
+    autoCorrect: true
+  NoEmptyClassBody:
+    active: true
+    autoCorrect: true
+  NoItParamInMultilineLambda:
+    active: false
+  NoLineBreakAfterElse:
+    active: true
+    autoCorrect: true
+  NoLineBreakBeforeAssignment:
+    active: true
+    autoCorrect: true
+  NoMultipleSpaces:
+    active: true
+    autoCorrect: true
+  NoSemicolons:
+    active: true
+    autoCorrect: true
+  NoTrailingSpaces:
+    active: true
+    autoCorrect: true
+  NoUnitReturn:
+    active: true
+    autoCorrect: true
+  NoUnusedImports:
+    active: true
+    autoCorrect: true
+  NoWildcardImports:
+    active: true
+    autoCorrect: true
+  PackageName:
+    active: true
+    autoCorrect: true
+  ParameterListWrapping:
+    active: true
+    autoCorrect: true
+    indentSize: 4
+  SpacingAroundColon:
+    active: true
+    autoCorrect: true
+  SpacingAroundComma:
+    active: true
+    autoCorrect: true
+  SpacingAroundCurly:
+    active: true
+    autoCorrect: true
+  SpacingAroundKeyword:
+    active: true
+    autoCorrect: true
+  SpacingAroundOperators:
+    active: true
+    autoCorrect: true
+  SpacingAroundParens:
+    active: true
+    autoCorrect: true
+  SpacingAroundRangeOperator:
+    active: true
+    autoCorrect: true
+  StringTemplate:
+    active: true
+    autoCorrect: true
+
+naming:
+  active: true
+  ClassNaming:
+    active: true
+    classPattern: '[A-Z$][a-zA-Z0-9$]*'
+  ConstructorParameterNaming:
+    active: true
+    parameterPattern: '[a-z][A-Za-z0-9]*'
+    privateParameterPattern: '[a-z][A-Za-z0-9]*'
+    excludeClassPattern: '$^'
+  EnumNaming:
+    active: true
+    enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
+  ForbiddenClassName:
+    active: false
+    forbiddenName: ''
+  FunctionMaxLength:
+    active: false
+    maximumFunctionNameLength: 30
+  FunctionMinLength:
+    active: false
+    minimumFunctionNameLength: 3
+  FunctionNaming:
+    active: true
+    functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
+    excludeClassPattern: '$^'
+    ignoreOverridden: true
+  FunctionParameterNaming:
+    active: true
+    parameterPattern: '[a-z][A-Za-z0-9]*'
+    excludeClassPattern: '$^'
+    ignoreOverriddenFunctions: true
+  MatchingDeclarationName:
+    active: true
+  MemberNameEqualsClassName:
+    active: false
+    ignoreOverriddenFunction: true
+  ObjectPropertyNaming:
+    active: true
+    constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+    privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+  PackageNaming:
+    active: true
+    packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
+  TopLevelPropertyNaming:
+    active: true
+    constantPattern: '[A-Z][_A-Z0-9]*'
+    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+    privatePropertyPattern: '(_)?[A-Za-z][A-Za-z0-9]*'
+  VariableMaxLength:
+    active: false
+    maximumVariableNameLength: 64
+  VariableMinLength:
+    active: false
+    minimumVariableNameLength: 1
+  VariableNaming:
+    active: true
+    variablePattern: '[a-z][A-Za-z0-9]*'
+    privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+    excludeClassPattern: '$^'
+    ignoreOverridden: true
+
+performance:
+  active: true
+  ArrayPrimitive:
+    active: false
+  ForEachOnRange:
+    active: true
+  SpreadOperator:
+    active: true
+  UnnecessaryTemporaryInstantiation:
+    active: true
+
+potential-bugs:
+  active: true
+  DuplicateCaseInWhenExpression:
+    active: true
+  EqualsAlwaysReturnsTrueOrFalse:
+    active: false
+  EqualsWithHashCodeExist:
+    active: true
+  ExplicitGarbageCollectionCall:
+    active: true
+  InvalidRange:
+    active: false
+  IteratorHasNextCallsNextMethod:
+    active: false
+  IteratorNotThrowingNoSuchElementException:
+    active: false
+  LateinitUsage:
+    active: false
+    excludeAnnotatedProperties: ""
+    ignoreOnClassesPattern: ""
+  UnconditionalJumpStatementInLoop:
+    active: false
+  UnreachableCode:
+    active: true
+  UnsafeCallOnNullableType:
+    active: false
+  UnsafeCast:
+    active: false
+  UselessPostfixExpression:
+    active: false
+  WrongEqualsTypeParameter:
+    active: false
+
+style:
+  active: true
+  CollapsibleIfStatements:
+    active: false
+  DataClassContainsFunctions:
+    active: false
+    conversionFunctionPrefix: 'to'
+  EqualsNullCall:
+    active: false
+  EqualsOnSignatureLine:
+    active: false
+  ExplicitItLambdaParameter:
+    active: false
+  ExpressionBodySyntax:
+    active: false
+    includeLineWrapping: false
+  ForbiddenComment:
+    active: true
+    values: 'TODO:,FIXME:,STOPSHIP:'
+  ForbiddenImport:
+    active: false
+    imports: ''
+  ForbiddenVoid:
+    active: false
+  FunctionOnlyReturningConstant:
+    active: false
+    ignoreOverridableFunction: true
+    excludedFunctions: 'describeContents'
+  LoopWithTooManyJumpStatements:
+    active: false
+    maxJumpCount: 1
+  MagicNumber:
+    active: true
+    ignoreNumbers: '-1,0,1,2'
+    ignoreHashCodeFunction: true
+    ignorePropertyDeclaration: false
+    ignoreConstantDeclaration: true
+    ignoreCompanionObjectPropertyDeclaration: true
+    ignoreAnnotation: false
+    ignoreNamedArgument: true
+    ignoreEnums: false
+  MandatoryBracesIfStatements:
+    active: false
+  MaxLineLength:
+    active: true
+    maxLineLength: 120
+    excludePackageStatements: true
+    excludeImportStatements: true
+    excludeCommentStatements: false
+  MayBeConst:
+    active: false
+  ModifierOrder:
+    active: true
+  NestedClassesVisibility:
+    active: false
+  NewLineAtEndOfFile:
+    active: true
+  NoTabs:
+    active: false
+  OptionalAbstractKeyword:
+    active: true
+  OptionalUnit:
+    active: false
+  OptionalWhenBraces:
+    active: false
+  PreferToOverPairSyntax:
+    active: false
+  ProtectedMemberInFinalClass:
+    active: false
+  RedundantVisibilityModifierRule:
+    active: false
+  ReturnCount:
+    active: true
+    max: 2
+    excludedFunctions: "equals"
+    excludeLabeled: false
+    excludeReturnFromLambda: true
+  SafeCast:
+    active: true
+  SerialVersionUIDInSerializableClass:
+    active: false
+  SpacingBetweenPackageAndImports:
+    active: false
+  ThrowsCount:
+    active: true
+    max: 2
+  TrailingWhitespace:
+    active: false
+  UnderscoresInNumericLiterals:
+    active: false
+    acceptableDecimalLength: 5
+  UnnecessaryAbstractClass:
+    active: false
+    excludeAnnotatedClasses: "dagger.Module"
+  UnnecessaryApply:
+    active: false
+  UnnecessaryInheritance:
+    active: false
+  UnnecessaryLet:
+    active: false
+  UnnecessaryParentheses:
+    active: false
+  UntilInsteadOfRangeTo:
+    active: false
+  UnusedImports:
+    active: false
+  UnusedPrivateClass:
+    active: false
+  UnusedPrivateMember:
+    active: false
+    allowedNames: "(_|ignored|expected|serialVersionUID)"
+  UseDataClass:
+    active: false
+    excludeAnnotatedClasses: ""
+  UtilityClassWithPublicConstructor:
+    active: false
+  VarCouldBeVal:
+    active: false
+  WildcardImport:
+    active: true
+    excludeImports: 'java.util.*,kotlinx.android.synthetic.*'

+ 15 - 1
scripts/analysis/analysis-wrapper.sh

@@ -17,9 +17,13 @@ lintValue=$?
 ruby scripts/analysis/findbugs-up.rb $1 $2 $3
 findbugsValue=$?
 
+
 ./gradlew ktlint
 ktlintValue=$?
 
+./gradlew detekt
+detektValue=$?
+
 # exit codes:
 # 0: count was reduced
 # 1: count was increased
@@ -129,6 +133,11 @@ else
         ktlintMessage="<h1>Kotlin lint found errors</h1><a href='https://www.kaminsky.me/nc-dev/android-ktlint/$6.html'>Lint</a>"
     fi
 
+    if ( [ $detektValue -eq 1 ] ) ; then
+        curl -u $4:$5 -X PUT https://nextcloud.kaminsky.me/remote.php/webdav/android-detekt/$6.html --upload-file build/reports/detekt/detekt.html
+        detektMessage="<h1>Detekt errors found</h1><a href='https://www.kaminsky.me/nc-dev/android-detekt/$6.html'>Lint</a>"
+    fi
+
     # check gplay limitation: all changelog files must only have 500 chars
     gplayLimitation=$(scripts/checkGplayLimitation.sh)
 
@@ -136,7 +145,7 @@ else
         gplayLimitation="<h1>Following files are beyond 500 char limit:</h1><br><br>"$gplayLimitation
     fi
 
-    curl -u $1:$2 -X POST https://api.github.com/repos/nextcloud/android/issues/$7/comments -d "{ \"body\" : \"$codacyResult $lintResult $findbugsResultNew $findbugsResultOld $checkLibraryMessage $lintMessage $findbugsMessage $ktlintMessage $gplayLimitation \" }"
+    curl -u $1:$2 -X POST https://api.github.com/repos/nextcloud/android/issues/$7/comments -d "{ \"body\" : \"$codacyResult $lintResult $findbugsResultNew $findbugsResultOld $checkLibraryMessage $lintMessage $findbugsMessage $ktlintMessage $detektMessage $gplayLimitation \" }"
 
     if [ ! -z "$gplayLimitation" ]; then
         exit 1
@@ -150,10 +159,15 @@ else
         exit $lintValue
     fi
 
+
     if [ $ktlintValue -eq 1 ]; then
         exit 1
     fi
 
+    if [ $detektValue -eq 1 ]; then
+        exit 1
+    fi
+
     if [ $findbugsValue -eq 2 ]; then
         exit 0
     else

+ 1 - 1
src/androidTest/java/com/owncloud/android/ui/activity/FileDisplayActivityTest.java

@@ -2,7 +2,7 @@ package com.owncloud.android.ui.activity;
 
 import android.app.Activity;
 
-import com.nextcloud.client.whatsnew.WhatsNewActivity;
+import com.nextcloud.client.onboarding.WhatsNewActivity;
 import com.owncloud.android.AbstractIT;
 
 import org.junit.Rule;

+ 2 - 2
src/main/AndroidManifest.xml

@@ -314,10 +314,10 @@
         <activity
             android:name=".ui.trashbin.TrashbinActivity"
             android:configChanges="orientation|screenSize|keyboardHidden"/>
-        <activity android:name="com.nextcloud.client.whatsnew.WhatsNewActivity"
+        <activity android:name="com.nextcloud.client.onboarding.WhatsNewActivity"
                   android:theme="@style/Theme.ownCloud.noActionBar.Login" />
         <activity
-            android:name=".ui.activity.FirstRunActivity"
+            android:name="com.nextcloud.client.onboarding.FirstRunActivity"
             android:theme="@style/Theme.ownCloud.noActionBar.Login"
             android:configChanges="orientation|screenSize"/>
 

+ 5 - 0
src/main/java/com/nextcloud/client/account/UserAccountManager.java

@@ -33,6 +33,11 @@ public interface UserAccountManager extends CurrentAccountProvider {
     @Nullable
     OwnCloudAccount getCurrentOwnCloudAccount();
 
+    /**
+     * Remove all NextCloud accounts from OS account manager.
+     */
+    void removeAllAccounts();
+
     /**
      * Get configured NextCloud's user accounts.
      *

+ 7 - 0
src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java

@@ -69,6 +69,13 @@ public class UserAccountManagerImpl implements UserAccountManager {
         this.accountManager = accountManager;
     }
 
+    @Override
+    public void removeAllAccounts() {
+        for (Account account : accountManager.getAccounts()) {
+            accountManager.removeAccount(account, null, null);
+        }
+    }
+
     @Override
     @NonNull
     public Account[] getAccounts() {

+ 3 - 3
src/main/java/com/nextcloud/client/di/AppComponent.java

@@ -25,7 +25,7 @@ import android.app.Application;
 import com.nextcloud.client.appinfo.AppInfoModule;
 import com.nextcloud.client.device.DeviceModule;
 import com.nextcloud.client.network.NetworkModule;
-import com.nextcloud.client.whatsnew.WhatsNewModule;
+import com.nextcloud.client.onboarding.OnboardingModule;
 import com.owncloud.android.MainApp;
 
 import javax.inject.Singleton;
@@ -38,9 +38,9 @@ import dagger.android.support.AndroidSupportInjectionModule;
     AndroidSupportInjectionModule.class,
     AppModule.class,
     AppInfoModule.class,
-    WhatsNewModule.class,
     NetworkModule.class,
-    DeviceModule.class
+    DeviceModule.class,
+    OnboardingModule.class
 })
 @Singleton
 public interface AppComponent {

+ 2 - 2
src/main/java/com/nextcloud/client/di/ComponentsModule.java

@@ -20,7 +20,7 @@
 
 package com.nextcloud.client.di;
 
-import com.nextcloud.client.whatsnew.WhatsNewActivity;
+import com.nextcloud.client.onboarding.WhatsNewActivity;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.authentication.DeepLinkLoginActivity;
 import com.owncloud.android.files.BootupBroadcastReceiver;
@@ -42,7 +42,7 @@ import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
 import com.owncloud.android.ui.activity.ExternalSiteWebView;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FilePickerActivity;
-import com.owncloud.android.ui.activity.FirstRunActivity;
+import com.nextcloud.client.onboarding.FirstRunActivity;
 import com.owncloud.android.ui.activity.FolderPickerActivity;
 import com.owncloud.android.ui.activity.LogHistoryActivity;
 import com.owncloud.android.ui.activity.ManageAccountsActivity;

+ 7 - 36
src/main/java/com/owncloud/android/ui/activity/FirstRunActivity.java → src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java

@@ -22,7 +22,7 @@
  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package com.owncloud.android.ui.activity;
+package com.nextcloud.client.onboarding;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -42,12 +42,13 @@ import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.appinfo.AppInfo;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.preferences.AppPreferences;
-import com.nextcloud.client.whatsnew.WhatsNewService;
 import com.owncloud.android.BuildConfig;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.features.FeatureItem;
+import com.owncloud.android.ui.activity.BaseActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.adapter.FeaturesViewAdapter;
 import com.owncloud.android.ui.whatsnew.ProgressIndicator;
 import com.owncloud.android.utils.DisplayUtils;
@@ -70,7 +71,7 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
     @Inject UserAccountManager userAccountManager;
     @Inject AppPreferences preferences;
     @Inject AppInfo appInfo;
-    @Inject WhatsNewService whatsNew;
+    @Inject OnboardingService onboarding;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -119,13 +120,8 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
         ViewPager viewPager = findViewById(R.id.contentPanel);
 
         // Sometimes, accounts are not deleted when you uninstall the application so we'll do it now
-        if (whatsNew.isFirstRun()) {
-            AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-            if (am != null) {
-                for (Account account : userAccountManager.getAccounts()) {
-                    am.removeAccount(account, null, null);
-                }
-            }
+        if (onboarding.isFirstRun()) {
+            userAccountManager.removeAllAccounts();
         }
 
         FeaturesViewAdapter featuresViewAdapter = new FeaturesViewAdapter(getSupportFragmentManager(), getFirstRun());
@@ -190,31 +186,6 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
         super.onStop();
     }
 
-    private static boolean isFirstRun(Context context) {
-        return AccountUtils.getCurrentOwnCloudAccount(context) == null;
-    }
-
-    public static boolean runIfNeeded(Context context) {
-        boolean isProviderOrOwnInstallationVisible = context.getResources()
-                .getBoolean(R.bool.show_provider_or_own_installation);
-
-        if (!isProviderOrOwnInstallationVisible) {
-            return false;
-        }
-
-        if (context instanceof FirstRunActivity) {
-            return false;
-        }
-
-        if (isFirstRun(context) && context instanceof AuthenticatorActivity) {
-            ((AuthenticatorActivity) context).startActivityForResult(new Intent(context, FirstRunActivity.class),
-                                                                     AuthenticatorActivity.REQUEST_CODE_FIRST_RUN);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     @Override
     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
         // unused but to be implemented due to abstract parent
@@ -248,7 +219,7 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
             }
 
             setAccount(account);
-            accountManager.setCurrentOwnCloudAccount(account.name);
+            userAccountManager.setCurrentOwnCloudAccount(account.name);
             onAccountSet(false);
 
             Intent i = new Intent(this, FileDisplayActivity.class);

+ 14 - 15
src/main/java/com/nextcloud/client/whatsnew/WhatsNewModule.java → src/main/java/com/nextcloud/client/onboarding/OnboardingModule.kt

@@ -16,26 +16,25 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
-package com.nextcloud.client.whatsnew;
+package com.nextcloud.client.onboarding
 
-import android.content.res.Resources;
-
-import com.nextcloud.client.account.CurrentAccountProvider;
-import com.nextcloud.client.preferences.AppPreferences;
-
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
+import android.content.res.Resources
+import com.nextcloud.client.account.CurrentAccountProvider
+import com.nextcloud.client.preferences.AppPreferences
+import dagger.Module
+import dagger.Provides
+import javax.inject.Singleton
 
 @Module
-public class WhatsNewModule {
+class OnboardingModule {
 
     @Provides
     @Singleton
-    WhatsNewService whatsNewService(Resources resources,
-                                    AppPreferences preferences,
-                                    CurrentAccountProvider accountProvider) {
-        return new WhatsNewService(resources, preferences, accountProvider);
+    internal fun onboardingService(
+        resources: Resources,
+        preferences: AppPreferences,
+        accountProvider: CurrentAccountProvider
+    ): OnboardingService {
+        return OnboardingServiceImpl(resources, preferences, accountProvider)
     }
 }

+ 31 - 0
src/main/java/com/nextcloud/client/onboarding/OnboardingService.kt

@@ -0,0 +1,31 @@
+/* Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.onboarding
+
+import android.app.Activity
+import android.content.Context
+import com.owncloud.android.features.FeatureItem
+
+interface OnboardingService {
+    val whatsNew: Array<FeatureItem>
+    val isFirstRun: Boolean
+    fun launchActivityIfNeeded(activity: Activity)
+    fun launchFirstRunIfNeeded(activity: Activity): Boolean
+    fun shouldShowWhatsNew(callingContext: Context): Boolean
+}

+ 80 - 0
src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt

@@ -0,0 +1,80 @@
+/* Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.client.onboarding
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.res.Resources
+
+import com.nextcloud.client.account.CurrentAccountProvider
+import com.nextcloud.client.preferences.AppPreferences
+import com.owncloud.android.BuildConfig
+import com.owncloud.android.R
+import com.owncloud.android.authentication.AuthenticatorActivity
+import com.owncloud.android.features.FeatureItem
+import com.owncloud.android.ui.activity.PassCodeActivity
+
+internal class OnboardingServiceImpl constructor(
+    private val resources: Resources,
+    private val preferences: AppPreferences,
+    accountProvider: CurrentAccountProvider
+) : OnboardingService {
+
+    private companion object {
+        const val ITEM_VERSION_CODE = 99999999
+    }
+
+    private val notSeenYet: Boolean get() {
+        return BuildConfig.VERSION_CODE >= ITEM_VERSION_CODE && preferences.lastSeenVersionCode < ITEM_VERSION_CODE
+    }
+
+    override val whatsNew: Array<FeatureItem>
+        get() = if (!isFirstRun && notSeenYet) {
+            emptyArray()
+        } else {
+            emptyArray()
+        }
+
+    override val isFirstRun: Boolean = accountProvider.currentAccount == null
+
+    override fun shouldShowWhatsNew(callingContext: Context): Boolean {
+        return callingContext !is PassCodeActivity && whatsNew.size > 0
+    }
+
+    override fun launchActivityIfNeeded(activity: Activity) {
+        if (!resources.getBoolean(R.bool.show_whats_new) || activity is WhatsNewActivity) {
+            return
+        }
+
+        if (shouldShowWhatsNew(activity)) {
+            activity.startActivity(Intent(activity, WhatsNewActivity::class.java))
+        }
+    }
+
+    override fun launchFirstRunIfNeeded(activity: Activity): Boolean {
+        val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation)
+        val canLaunch = isProviderOrOwnInstallationVisible && isFirstRun && activity is AuthenticatorActivity
+        if (canLaunch) {
+            val intent = Intent(activity, FirstRunActivity::class.java)
+            activity.startActivityForResult(intent, AuthenticatorActivity.REQUEST_CODE_FIRST_RUN)
+        }
+        return canLaunch
+    }
+}

+ 3 - 3
src/main/java/com/nextcloud/client/whatsnew/WhatsNewActivity.java → src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.java

@@ -21,7 +21,7 @@
  * You should have received a copy of the GNU Affero General Public
  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package com.nextcloud.client.whatsnew;
+package com.nextcloud.client.onboarding;
 
 import android.os.Build;
 import android.os.Bundle;
@@ -56,7 +56,7 @@ public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPa
     private ViewPager mPager;
     @Inject AppPreferences preferences;
     @Inject AppInfo appInfo;
-    @Inject WhatsNewService whatsNew;
+    @Inject OnboardingService onboarding;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -78,7 +78,7 @@ public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPa
             mPager.setAdapter(featuresWebViewAdapter);
         } else {
             FeaturesViewAdapter featuresViewAdapter = new FeaturesViewAdapter(getSupportFragmentManager(),
-                                                                              whatsNew.getWhatsNew());
+                                                                              onboarding.getWhatsNew());
             mProgress.setNumberOfSteps(featuresViewAdapter.getCount());
             mPager.setAdapter(featuresViewAdapter);
         }

+ 0 - 75
src/main/java/com/nextcloud/client/whatsnew/WhatsNewService.java

@@ -1,75 +0,0 @@
-/* Nextcloud Android client application
- *
- * @author Chris Narkiewicz
- * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.nextcloud.client.whatsnew;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-
-import com.nextcloud.client.account.CurrentAccountProvider;
-import com.nextcloud.client.preferences.AppPreferences;
-import com.owncloud.android.BuildConfig;
-import com.owncloud.android.R;
-import com.owncloud.android.features.FeatureItem;
-import com.owncloud.android.ui.activity.PassCodeActivity;
-
-public class WhatsNewService {
-
-    private Resources resources;
-    private AppPreferences preferences;
-    private CurrentAccountProvider accountProvider;
-
-    WhatsNewService(Resources resources,
-                    AppPreferences preferences,
-                    CurrentAccountProvider accountProvider) {
-        this.resources = resources;
-        this.preferences = preferences;
-        this.accountProvider = accountProvider;
-    }
-
-    public void launchActivityIfNeeded(Activity activity) {
-        if (!resources.getBoolean(R.bool.show_whats_new) || activity instanceof WhatsNewActivity) {
-            return;
-        }
-
-        if (shouldShow(activity)) {
-            activity.startActivity(new Intent(activity, WhatsNewActivity.class));
-        }
-    }
-
-    FeatureItem[] getWhatsNew() {
-        int itemVersionCode = 99999999;
-
-        if (!isFirstRun() && BuildConfig.VERSION_CODE >= itemVersionCode
-            && preferences.getLastSeenVersionCode() < itemVersionCode) {
-            return new FeatureItem[0];
-        } else {
-            return new FeatureItem[0];
-        }
-    }
-
-    private boolean shouldShow(Context callingContext) {
-        return !(callingContext instanceof PassCodeActivity) && getWhatsNew().length > 0;
-    }
-
-    public boolean isFirstRun() {
-        return accountProvider.getCurrentAccount() == null;
-    }
-}

+ 3 - 3
src/main/java/com/owncloud/android/MainApp.java

@@ -49,9 +49,9 @@ import com.nextcloud.client.appinfo.AppInfo;
 import com.nextcloud.client.di.ActivityInjector;
 import com.nextcloud.client.di.DaggerAppComponent;
 import com.nextcloud.client.network.ConnectivityService;
+import com.nextcloud.client.onboarding.OnboardingService;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.nextcloud.client.preferences.AppPreferencesImpl;
-import com.nextcloud.client.whatsnew.WhatsNewService;
 import com.owncloud.android.authentication.PassCodeManager;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.MediaFolder;
@@ -156,7 +156,7 @@ public class MainApp extends MultiDexApplication implements
     protected AppInfo appInfo;
 
     @Inject
-    protected WhatsNewService whatsNew;
+    protected OnboardingService onboarding;
 
     @Inject
     ConnectivityService connectivityService;
@@ -258,7 +258,7 @@ public class MainApp extends MultiDexApplication implements
             @Override
             public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                 Log_OC.d(activity.getClass().getSimpleName(), "onCreate(Bundle) starting");
-                whatsNew.launchActivityIfNeeded(activity);
+                onboarding.launchActivityIfNeeded(activity);
             }
 
             @Override

+ 4 - 2
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -89,6 +89,7 @@ import com.google.android.material.snackbar.Snackbar;
 import com.google.android.material.textfield.TextInputLayout;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.di.Injectable;
+import com.nextcloud.client.onboarding.OnboardingService;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -113,7 +114,7 @@ import com.owncloud.android.operations.GetServerInfoOperation;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
-import com.owncloud.android.ui.activity.FirstRunActivity;
+import com.nextcloud.client.onboarding.FirstRunActivity;
 import com.owncloud.android.ui.components.CustomEditText;
 import com.owncloud.android.ui.dialog.CredentialsDialogFragment;
 import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
@@ -252,6 +253,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
     @Inject UserAccountManager accountManager;
     @Inject AppPreferences preferences;
+    @Inject OnboardingService onboarding;
 
     /**
      * {@inheritDoc}
@@ -266,7 +268,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         Uri data = getIntent().getData();
         boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme));
         if (savedInstanceState == null && !directLogin) {
-            FirstRunActivity.runIfNeeded(this);
+            onboarding.launchFirstRunIfNeeded(this);
         }
 
         // delete cookies for webView

+ 1 - 0
src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -57,6 +57,7 @@ import com.bumptech.glide.request.target.SimpleTarget;
 import com.google.android.material.navigation.NavigationView;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.di.Injectable;
+import com.nextcloud.client.onboarding.FirstRunActivity;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;

+ 1 - 1
src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java

@@ -42,9 +42,9 @@ import android.widget.ListView;
 import com.evernote.android.job.JobRequest;
 import com.evernote.android.job.util.support.PersistableBundleCompat;
 import com.nextcloud.client.account.UserAccountManager;
+import com.nextcloud.client.onboarding.FirstRunActivity;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.files.services.FileDownloader;