diff --git a/prebuilts/gradle/ActionBarCompat-Basic/Application/build.gradle b/prebuilts/gradle/ActionBarCompat-Basic/Application/build.gradle index e85cdbd0bb94e1e226ff635780fbb7b9b9ae49ad..12dcfb154195284b6dc703d0e2eb7d0a00116e5c 100644 --- a/prebuilts/gradle/ActionBarCompat-Basic/Application/build.gradle +++ b/prebuilts/gradle/ActionBarCompat-Basic/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 7 diff --git a/prebuilts/gradle/ActionBarCompat-Basic/README.md b/prebuilts/gradle/ActionBarCompat-Basic/README.md index b3886689d0b6648440d640a25040514e87abd132..dd87b25ee919bebf23bbd399395845c1113064b7 100644 --- a/prebuilts/gradle/ActionBarCompat-Basic/README.md +++ b/prebuilts/gradle/ActionBarCompat-Basic/README.md @@ -36,7 +36,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -65,7 +65,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/Application/build.gradle b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/Application/build.gradle index e85cdbd0bb94e1e226ff635780fbb7b9b9ae49ad..12dcfb154195284b6dc703d0e2eb7d0a00116e5c 100644 --- a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/Application/build.gradle +++ b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 7 diff --git a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/README.md b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/README.md index 5799f0b36a00b7a8886cc85857d86260875a13cb..ab7dc2febc895a04a0d2590693ee67c9289fbaa4 100644 --- a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/README.md +++ b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/README.md @@ -18,7 +18,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -47,7 +47,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/ActionBarCompat-Styled/Application/build.gradle b/prebuilts/gradle/ActionBarCompat-Styled/Application/build.gradle index e85cdbd0bb94e1e226ff635780fbb7b9b9ae49ad..12dcfb154195284b6dc703d0e2eb7d0a00116e5c 100644 --- a/prebuilts/gradle/ActionBarCompat-Styled/Application/build.gradle +++ b/prebuilts/gradle/ActionBarCompat-Styled/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 7 diff --git a/prebuilts/gradle/ActionBarCompat-Styled/README.md b/prebuilts/gradle/ActionBarCompat-Styled/README.md index e84c5828945968d5943b684cb4446870764ee682..4cb16df3997081ed3c5d50998cbcc062cc9bad3b 100644 --- a/prebuilts/gradle/ActionBarCompat-Styled/README.md +++ b/prebuilts/gradle/ActionBarCompat-Styled/README.md @@ -24,7 +24,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -53,7 +53,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/ActiveNotifications/.google/packaging.yaml b/prebuilts/gradle/ActiveNotifications/.google/packaging.yaml index 5bc17de340b4cc6b5a1fc6ce7230ad0903f1ab5e..1d83dcd940e5bec1d0af7720cbe3510dd7a9da84 100644 --- a/prebuilts/gradle/ActiveNotifications/.google/packaging.yaml +++ b/prebuilts/gradle/ActiveNotifications/.google/packaging.yaml @@ -4,9 +4,9 @@ # This file is used by Google as part of our samples packaging process. # End users may safely ignore this file. It has no relevance to other systems. --- -status: DRAFT +status: PUBLISHED technologies: [Android] -categories: [Getting Started, Notification, Android N Preview] +categories: [Notification] languages: [Java] solutions: [Mobile] github: android-ActiveNotifications diff --git a/prebuilts/gradle/ActiveNotifications/Application/build.gradle b/prebuilts/gradle/ActiveNotifications/Application/build.gradle index 6d59159bfeda4f5da65d13c439e40c612d0bc708..be6a1a35449f407ca0231838ffb2c7732d75e58e 100644 --- a/prebuilts/gradle/ActiveNotifications/Application/build.gradle +++ b/prebuilts/gradle/ActiveNotifications/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 24 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 24 diff --git a/prebuilts/gradle/ActiveNotifications/Application/src/main/res/menu/main.xml b/prebuilts/gradle/ActiveNotifications/Application/src/main/res/menu/main.xml index b49c2c526f45cebfb5494270ecb2dfe00742726e..29c52f8ed465f69aa7326ab5b542e2d7ed547e3e 100644 --- a/prebuilts/gradle/ActiveNotifications/Application/src/main/res/menu/main.xml +++ b/prebuilts/gradle/ActiveNotifications/Application/src/main/res/menu/main.xml @@ -14,7 +14,9 @@ limitations under the License. --> - + diff --git a/prebuilts/gradle/ActiveNotifications/README.md b/prebuilts/gradle/ActiveNotifications/README.md index e7ccb7f09f8ff0965a9d9de11f68b3343392fa31..6f20204967f87324bc532d576c1faaa0feae1d89 100644 --- a/prebuilts/gradle/ActiveNotifications/README.md +++ b/prebuilts/gradle/ActiveNotifications/README.md @@ -32,7 +32,7 @@ Pre-requisites -------------- - Android SDK 24 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -61,7 +61,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/ActivityInstrumentation/Application/build.gradle b/prebuilts/gradle/ActivityInstrumentation/Application/build.gradle index e85cdbd0bb94e1e226ff635780fbb7b9b9ae49ad..12dcfb154195284b6dc703d0e2eb7d0a00116e5c 100644 --- a/prebuilts/gradle/ActivityInstrumentation/Application/build.gradle +++ b/prebuilts/gradle/ActivityInstrumentation/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 7 diff --git a/prebuilts/gradle/ActivityInstrumentation/README.md b/prebuilts/gradle/ActivityInstrumentation/README.md index e88fcaf93b872e7ca2b67830763a2e5284e2f1c7..b35798d5b4dbe835da00f7324c7d480080360fb0 100644 --- a/prebuilts/gradle/ActivityInstrumentation/README.md +++ b/prebuilts/gradle/ActivityInstrumentation/README.md @@ -9,7 +9,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Getting Started @@ -33,7 +33,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/ActivitySceneTransitionBasic/Application/build.gradle b/prebuilts/gradle/ActivitySceneTransitionBasic/Application/build.gradle index f74f7cb7bed3389ba2bea639fe2090dc0b48b587..90aaa702a673c606f5569372393266bcaf967a52 100644 --- a/prebuilts/gradle/ActivitySceneTransitionBasic/Application/build.gradle +++ b/prebuilts/gradle/ActivitySceneTransitionBasic/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -33,7 +33,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 21 diff --git a/prebuilts/gradle/ActivitySceneTransitionBasic/README.md b/prebuilts/gradle/ActivitySceneTransitionBasic/README.md index 21809913374a08b11b7343a11d761cae91da4525..93fe2b9b10c447fab3c63e3a26eeb8af0d5d95c4 100644 --- a/prebuilts/gradle/ActivitySceneTransitionBasic/README.md +++ b/prebuilts/gradle/ActivitySceneTransitionBasic/README.md @@ -20,7 +20,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -49,7 +49,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties index 693f20ff4e7bf85db3480c579674f78fd74b55a3..aa3b089c95baba4da4ce553d5bc3c26de45f0e50 100644 --- a/prebuilts/gradle/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AdvancedImmersiveMode/Application/build.gradle b/prebuilts/gradle/AdvancedImmersiveMode/Application/build.gradle index 6fbfa076c28dea29aee413250c88c74da7f858ea..19aaaded31477371a7696209da80fa77443e89ec 100644 --- a/prebuilts/gradle/AdvancedImmersiveMode/Application/build.gradle +++ b/prebuilts/gradle/AdvancedImmersiveMode/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 19 diff --git a/prebuilts/gradle/AdvancedImmersiveMode/Application/src/main/res/menu/main.xml b/prebuilts/gradle/AdvancedImmersiveMode/Application/src/main/res/menu/main.xml index b49c2c526f45cebfb5494270ecb2dfe00742726e..29c52f8ed465f69aa7326ab5b542e2d7ed547e3e 100644 --- a/prebuilts/gradle/AdvancedImmersiveMode/Application/src/main/res/menu/main.xml +++ b/prebuilts/gradle/AdvancedImmersiveMode/Application/src/main/res/menu/main.xml @@ -14,7 +14,9 @@ limitations under the License. --> - + diff --git a/prebuilts/gradle/AdvancedImmersiveMode/README.md b/prebuilts/gradle/AdvancedImmersiveMode/README.md index 5c2da27590e5868637beff3c39b08c54a3eabf1f..a03c8e63e06a22f9f86bf243368409235049e9f2 100644 --- a/prebuilts/gradle/AdvancedImmersiveMode/README.md +++ b/prebuilts/gradle/AdvancedImmersiveMode/README.md @@ -27,7 +27,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -56,7 +56,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties index 0e72654af0dc9af55d0950e175a82814d782e142..2d6fb9789600ffe304c2798d17ee4a2a44bc563c 100644 --- a/prebuilts/gradle/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AgendaData/Application/build.gradle b/prebuilts/gradle/AgendaData/Application/build.gradle index 96b8c6a6336f891573c22e93219a4e70a5e11982..d053b96bd2d2a19ee33eeb295e321f8cbf163137 100644 --- a/prebuilts/gradle/AgendaData/Application/build.gradle +++ b/prebuilts/gradle/AgendaData/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -21,8 +21,8 @@ dependencies { compile "com.android.support:cardview-v7:25.0.1" compile "com.android.support:appcompat-v7:25.0.1" compile 'com.android.support:design:24.0.0' - compile 'com.google.android.gms:play-services-wearable:10.0.1' - compile 'com.android.support:support-v13:25.0.1' + compile 'com.google.android.gms:play-services-wearable:10.2.4' + compile 'com.android.support:support-v13:25.3.1' wearApp project(':Wearable') } @@ -36,7 +36,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 18 diff --git a/prebuilts/gradle/AgendaData/README.md b/prebuilts/gradle/AgendaData/README.md index 1a510be9f8837a09350096f75878a1a4fa5fb9b7..c6c27e71be99f99a3670b45fb5ca75c4a65ef371 100644 --- a/prebuilts/gradle/AgendaData/README.md +++ b/prebuilts/gradle/AgendaData/README.md @@ -23,7 +23,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -52,7 +52,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AgendaData/Wearable/build.gradle b/prebuilts/gradle/AgendaData/Wearable/build.gradle index 89e2f842624d4e02967ee287e4bbb21d399401e8..780ff9b1682afa6b7316ed5016c1a21c3fe99e74 100644 --- a/prebuilts/gradle/AgendaData/Wearable/build.gradle +++ b/prebuilts/gradle/AgendaData/Wearable/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.1' } } @@ -17,12 +17,12 @@ dependencies { - compile 'com.google.android.gms:play-services-wearable:10.0.1' - compile 'com.android.support:support-v13:25.0.1' + compile 'com.google.android.gms:play-services-wearable:10.2.4' + compile 'com.android.support:support-v13:25.3.1' - provided 'com.google.android.wearable:wearable:2.0.0' + provided 'com.google.android.wearable:wearable:2.0.1' - compile 'com.google.android.support:wearable:2.0.0' + compile 'com.google.android.support:wearable:2.0.1' } @@ -37,7 +37,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { versionCode 1 diff --git a/prebuilts/gradle/AgendaData/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AgendaData/gradle/wrapper/gradle-wrapper.properties index 21a31e19f8d27e6dca1d6256367eba33d2153a18..7a7c4663bfb64c3b20b6769a825507d0ccb45df0 100644 --- a/prebuilts/gradle/AgendaData/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AgendaData/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AlwaysOn/README.md b/prebuilts/gradle/AlwaysOn/README.md index f0ae15df13a2dde1a64c17244b2d7132feadf6c9..e7d88f4688a5a476070b1ff5dbb7976de479ebca 100644 --- a/prebuilts/gradle/AlwaysOn/README.md +++ b/prebuilts/gradle/AlwaysOn/README.md @@ -25,7 +25,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -54,7 +54,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AlwaysOn/Wearable/build.gradle b/prebuilts/gradle/AlwaysOn/Wearable/build.gradle index f8586fb723685e0c2f389631b0ec45d630841b99..9b5c9341b38d2fcaf401c5fd27dd55b20dfa9a4c 100644 --- a/prebuilts/gradle/AlwaysOn/Wearable/build.gradle +++ b/prebuilts/gradle/AlwaysOn/Wearable/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.1' } } @@ -17,12 +17,12 @@ dependencies { - compile 'com.google.android.gms:play-services-wearable:10.0.1' - compile 'com.android.support:support-v13:25.0.1' + compile 'com.google.android.gms:play-services-wearable:10.2.4' + compile 'com.android.support:support-v13:25.3.1' - provided 'com.google.android.wearable:wearable:2.0.0' + provided 'com.google.android.wearable:wearable:2.0.1' - compile 'com.google.android.support:wearable:2.0.0' + compile 'com.google.android.support:wearable:2.0.1' } @@ -37,7 +37,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { versionCode 1 diff --git a/prebuilts/gradle/AlwaysOn/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AlwaysOn/gradle/wrapper/gradle-wrapper.properties index c8203a8d529d06e1bb63e0695d7b0489f9929d56..c2cde241478f19eac76dc8d5d7312cc2c0f4d2c1 100644 --- a/prebuilts/gradle/AlwaysOn/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AlwaysOn/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AppRestrictionEnforcer/Application/build.gradle b/prebuilts/gradle/AppRestrictionEnforcer/Application/build.gradle index 0c19226d192721a3863cbdad244096304d64daf7..a96785caaa11c3b842836b0c2fb8ac7e77e7b036 100644 --- a/prebuilts/gradle/AppRestrictionEnforcer/Application/build.gradle +++ b/prebuilts/gradle/AppRestrictionEnforcer/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 21 diff --git a/prebuilts/gradle/AppRestrictionEnforcer/README.md b/prebuilts/gradle/AppRestrictionEnforcer/README.md index 684c2c458b9ce5359f60d49933b880b85af0b6e4..c92a38999ddbd3bffca9ec98ac780c7caf859f99 100644 --- a/prebuilts/gradle/AppRestrictionEnforcer/README.md +++ b/prebuilts/gradle/AppRestrictionEnforcer/README.md @@ -28,7 +28,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -57,7 +57,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties index 2d393d40309beee1e9821370e67ec8fb1f982bc8..22aa0ef4b9a55a68a1f4d4cf855d99e3166f80bd 100644 --- a/prebuilts/gradle/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AppRestrictionSchema/Application/build.gradle b/prebuilts/gradle/AppRestrictionSchema/Application/build.gradle index 0c19226d192721a3863cbdad244096304d64daf7..a96785caaa11c3b842836b0c2fb8ac7e77e7b036 100644 --- a/prebuilts/gradle/AppRestrictionSchema/Application/build.gradle +++ b/prebuilts/gradle/AppRestrictionSchema/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 21 diff --git a/prebuilts/gradle/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java b/prebuilts/gradle/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java index 3f4cea012fbcb8a31749ed213a0cae21e593cd33..8d219656a63f905102b67b5be588bcd00489ec5b 100644 --- a/prebuilts/gradle/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java +++ b/prebuilts/gradle/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java @@ -16,7 +16,10 @@ package com.example.android.apprestrictionschema; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.RestrictionEntry; import android.content.RestrictionsManager; import android.os.Build; @@ -44,7 +47,7 @@ import java.util.List; public class AppRestrictionSchemaFragment extends Fragment implements View.OnClickListener { // Tag for the logger - private static final String TAG = "AppRestrictionSchemaFragment"; + private static final String TAG = "AppRestrictionSchema"; private static final String KEY_CAN_SAY_HELLO = "can_say_hello"; private static final String KEY_MESSAGE = "message"; @@ -60,6 +63,9 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli // Message to show when the button is clicked (String restriction) private String mMessage; + // Observes restriction changes + private BroadcastReceiver mBroadcastReceiver; + // UI Components private TextView mTextSayHello; private Button mButtonSayHello; @@ -96,6 +102,28 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli resolveRestrictions(); } + @Override + public void onStart() { + super.onStart(); + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + resolveRestrictions(); + } + }; + getActivity().registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)); + } + + @Override + public void onStop() { + super.onStop(); + if (mBroadcastReceiver != null) { + getActivity().unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + } + } + private void resolveRestrictions() { RestrictionsManager manager = (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE); diff --git a/prebuilts/gradle/AppRestrictionSchema/Application/src/main/res/menu/main.xml b/prebuilts/gradle/AppRestrictionSchema/Application/src/main/res/menu/main.xml index b49c2c526f45cebfb5494270ecb2dfe00742726e..29c52f8ed465f69aa7326ab5b542e2d7ed547e3e 100644 --- a/prebuilts/gradle/AppRestrictionSchema/Application/src/main/res/menu/main.xml +++ b/prebuilts/gradle/AppRestrictionSchema/Application/src/main/res/menu/main.xml @@ -14,7 +14,9 @@ limitations under the License. --> - + diff --git a/prebuilts/gradle/AppRestrictionSchema/README.md b/prebuilts/gradle/AppRestrictionSchema/README.md index 4bfbdd24c966747384b1e5033decfdf170573dc1..3f20773ce75cd8d816ec03f54a0612e88560f270 100644 --- a/prebuilts/gradle/AppRestrictionSchema/README.md +++ b/prebuilts/gradle/AppRestrictionSchema/README.md @@ -55,7 +55,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -84,7 +84,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties index e03dde33851cc8a24a905529d5df59fe39fa1704..819aeb4a5a8f789713b1efc0c55a7715db2a9752 100644 --- a/prebuilts/gradle/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AppRestrictions/Application/build.gradle b/prebuilts/gradle/AppRestrictions/Application/build.gradle index 4480abaec4536f203c77e015ff8d46c7ea26b304..60de96824fff2ebbebc121381809e0706d856afd 100644 --- a/prebuilts/gradle/AppRestrictions/Application/build.gradle +++ b/prebuilts/gradle/AppRestrictions/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 18 diff --git a/prebuilts/gradle/AppRestrictions/README.md b/prebuilts/gradle/AppRestrictions/README.md index f682572ea4224d6b79aa7702f3f71b1149c2efbc..ff6fe2872713803919d28f6ca69aee9c939accab 100644 --- a/prebuilts/gradle/AppRestrictions/README.md +++ b/prebuilts/gradle/AppRestrictions/README.md @@ -28,7 +28,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -57,7 +57,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AppRestrictions/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AppRestrictions/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/AppRestrictions/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AppRestrictions/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AppShortcuts/README.md b/prebuilts/gradle/AppShortcuts/README.md index ceda836e4ff24f647a453da2950a0e0ccb9d0284..61ec0d614dd80279544036e624118fbe63255e1d 100644 --- a/prebuilts/gradle/AppShortcuts/README.md +++ b/prebuilts/gradle/AppShortcuts/README.md @@ -40,7 +40,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -69,7 +69,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AppShortcuts/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AppShortcuts/gradle/wrapper/gradle-wrapper.properties index e46bcabe0bbb3c69b6bb0cd6dce5531506404bfa..a3a3b57b6067ad70f09ec4be0629c11437ada3ec 100644 --- a/prebuilts/gradle/AppShortcuts/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AppShortcuts/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AppUsageStatistics/.google/packaging.yaml b/prebuilts/gradle/AppUsageStatistics/.google/packaging.yaml index e7f685649dfffddd7c3bc5fe5a93ec1d4727af62..1e8b80553927608f1e358a436d6150457b58d27a 100644 --- a/prebuilts/gradle/AppUsageStatistics/.google/packaging.yaml +++ b/prebuilts/gradle/AppUsageStatistics/.google/packaging.yaml @@ -4,7 +4,7 @@ # This file is used by Google as part of our samples packaging process. # End users may safely ignore this file. It has no relevance to other systems. --- -status: DRAFT +status: PUBLISHED technologies: [Android] categories: [System] languages: [Java] diff --git a/prebuilts/gradle/AppUsageStatistics/Application/build.gradle b/prebuilts/gradle/AppUsageStatistics/Application/build.gradle index 409f68c3cecbe1477f579002deee080f2f5ceac8..e40e49d17a61ffffec01da8b23c32f4046f0103b 100644 --- a/prebuilts/gradle/AppUsageStatistics/Application/build.gradle +++ b/prebuilts/gradle/AppUsageStatistics/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -34,7 +34,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 21 diff --git a/prebuilts/gradle/AppUsageStatistics/README.md b/prebuilts/gradle/AppUsageStatistics/README.md index 94aafdc31f9c9d14c7456089582b7344e93f7cdc..db4677908cde02908c5118611b601de59044e20a 100644 --- a/prebuilts/gradle/AppUsageStatistics/README.md +++ b/prebuilts/gradle/AppUsageStatistics/README.md @@ -50,7 +50,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -79,7 +79,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties index 616889940bfdd7e2aa2a145a2f2d288fb8b87db1..4a39e6d5ae7a070b13645564ecdd4b225634c9e9 100644 --- a/prebuilts/gradle/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AsymmetricFingerprintDialog/Application/build.gradle b/prebuilts/gradle/AsymmetricFingerprintDialog/Application/build.gradle index 1e3585375be40e4c5ff191fb322ba7f0bf8fed8f..84996ed32bd71e7600489422737c6db30f594d14 100644 --- a/prebuilts/gradle/AsymmetricFingerprintDialog/Application/build.gradle +++ b/prebuilts/gradle/AsymmetricFingerprintDialog/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -36,7 +36,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 23 diff --git a/prebuilts/gradle/AsymmetricFingerprintDialog/README.md b/prebuilts/gradle/AsymmetricFingerprintDialog/README.md index 96f6b208038a36168cb188bb9e4d8246b52fea1e..ce9e94d7a1606c86f02d4dda1387844fc5fce3b2 100644 --- a/prebuilts/gradle/AsymmetricFingerprintDialog/README.md +++ b/prebuilts/gradle/AsymmetricFingerprintDialog/README.md @@ -39,7 +39,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -68,7 +68,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties index f4b47eaa910ed16a51a300f4d9c6c0c48788ec23..74d071fa1a75d449eae962a09d49930ab2e332ee 100644 --- a/prebuilts/gradle/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AutoBackupForApps/Application/build.gradle b/prebuilts/gradle/AutoBackupForApps/Application/build.gradle index f7ed452565c26813f4b8546bbf9dac111ab0b9f0..cdcf1063c730b94e00a9260f5cf29e4821552b4b 100644 --- a/prebuilts/gradle/AutoBackupForApps/Application/build.gradle +++ b/prebuilts/gradle/AutoBackupForApps/Application/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.2.0' } } @@ -32,7 +32,7 @@ List dirs = [ android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 23 diff --git a/prebuilts/gradle/AutoBackupForApps/README.md b/prebuilts/gradle/AutoBackupForApps/README.md index d1c2a510088a99fd509ad768e981c88e1d7e9ebd..6a11b3dc32768029e4c0f063ca1533b3861bf14e 100644 --- a/prebuilts/gradle/AutoBackupForApps/README.md +++ b/prebuilts/gradle/AutoBackupForApps/README.md @@ -27,7 +27,7 @@ Pre-requisites -------------- - Android SDK 25 -- Android Build Tools v25.0.2 +- Android Build Tools v25.0.3 - Android Support Repository Screenshots @@ -56,7 +56,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de License ------- -Copyright 2016 The Android Open Source Project, Inc. +Copyright 2017 The Android Open Source Project, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/prebuilts/gradle/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties index 1081cc34aa1bb994620d9df821344166b0fd6e51..4da4a5808fee2512de0bcbe3cd6b2bb866234b02 100644 --- a/prebuilts/gradle/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties +++ b/prebuilts/gradle/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/.google/packaging.yaml b/prebuilts/gradle/AutofillFramework/.google/packaging.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7ec2793a488fa26fb219fdde4d9e7f885b2c4a49 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/.google/packaging.yaml @@ -0,0 +1,19 @@ + +# GOOGLE SAMPLE PACKAGING DATA +# +# This file is used by Google as part of our samples packaging process. +# End users may safely ignore this file. It has no relevance to other systems. +--- +status: PUBLISHED +technologies: [Android] +categories: [Input, Android O Preview] +languages: [Java] +solutions: [Mobile] +github: android-AutofillFramework +level: ADVANCED +icon: screenshots/icon-web.png +apiRefs: + - android:android.view.View + - android:android.service.autofill.AutoFillService + - android:android.view.autofill.AutoFillManager +license: apache2 diff --git a/prebuilts/gradle/AutofillFramework/Application/build.gradle b/prebuilts/gradle/AutofillFramework/Application/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..35233ec8139bc6a57dd088f7f57a5e8b03ebf72f --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/build.gradle @@ -0,0 +1,59 @@ + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.2.0' + } +} + +apply plugin: 'com.android.application' + +repositories { + jcenter() +} + +dependencies { + compile "com.android.support:support-v4:25.0.1" + compile "com.android.support:support-v13:25.0.1" + compile "com.android.support:cardview-v7:25.0.1" + compile "com.android.support:appcompat-v7:25.0.1" +} + +// The sample build uses multiple directories to +// keep boilerplate and common code separate from +// the main sample code. +List dirs = [ + 'main', // main sample code; look here for the interesting stuff. + 'common', // components that are reused by multiple samples + 'template'] // boilerplate code that is generated by the sample template process + +android { + compileSdkVersion "android-O" + buildToolsVersion "25.0.3" + + defaultConfig { + minSdkVersion "O" + targetSdkVersion "O" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + sourceSets { + main { + dirs.each { dir -> + java.srcDirs "src/${dir}/java" + res.srcDirs "src/${dir}/res" + } + } + androidTest.setRoot('tests') + androidTest.java.srcDirs = ['tests/src'] + + } + +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/AutofillFramework/Application/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..28d9c0b506c76b514e405b6796748afd6a9c03ac --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/AndroidManifest.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..54049902d920b8757abf9cdbaeb7a42301dbe57d --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework; + +import android.os.Bundle; + +import java.util.Arrays; +import java.util.Set; + +public final class CommonUtil { + + public static final String TAG = "AutofillSample"; + + public static final String EXTRA_DATASET_NAME = "dataset_name"; + public static final String EXTRA_FOR_RESPONSE = "for_response"; + + private static void bundleToString(StringBuilder builder, Bundle data) { + final Set keySet = data.keySet(); + builder.append("[Bundle with ").append(keySet.size()).append(" keys:"); + for (String key : keySet) { + builder.append(' ').append(key).append('='); + Object value = data.get(key); + if ((value instanceof Bundle)) { + bundleToString(builder, (Bundle) value); + } else { + builder.append((value instanceof Object[]) + ? Arrays.toString((Object[]) value) : value); + } + } + builder.append(']'); + } + + public static String bundleToString(Bundle data) { + if (data == null) { + return "N/A"; + } + final StringBuilder builder = new StringBuilder(); + bundleToString(builder, data); + return builder.toString(); + } +} \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..a82de4c8a1491ab8fa919134656500aff2738dbe --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; + +import com.example.android.autofillframework.R; + +public class CreditCardActivity extends AppCompatActivity { + + private Spinner mCcExpirationDaySpinner; + private Spinner mCcExpirationMonthSpinner; + private Spinner mCcExpirationYearSpinner; + private Button mSubmitButton; + private Button mClearButton; + + public static Intent getStartActivityIntent(Context context) { + Intent intent = new Intent(context, CreditCardActivity.class); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.credit_card_activity); + + mSubmitButton = (Button) findViewById(R.id.submit); + mClearButton = (Button) findViewById(R.id.clear); + mCcExpirationDaySpinner = (Spinner) findViewById(R.id.expirationDay); + mCcExpirationMonthSpinner = (Spinner) findViewById(R.id.expirationMonth); + mCcExpirationYearSpinner = (Spinner) findViewById(R.id.expirationYear); + + // Create an ArrayAdapter using the string array and a default spinner layout + ArrayAdapter dayAdapter = ArrayAdapter.createFromResource + (this, R.array.day_array, android.R.layout.simple_spinner_item); + // Specify the layout to use when the list of choices appears + dayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + // Apply the adapter to the spinner + mCcExpirationDaySpinner.setAdapter(dayAdapter); + + ArrayAdapter monthAdapter = ArrayAdapter.createFromResource + (this, R.array.month_array, android.R.layout.simple_spinner_item); + monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mCcExpirationMonthSpinner.setAdapter(monthAdapter); + + ArrayAdapter yearAdapter = ArrayAdapter.createFromResource + (this, R.array.year_array, android.R.layout.simple_spinner_item); + yearAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mCcExpirationYearSpinner.setAdapter(yearAdapter); + + mSubmitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + submit(); + } + }); + mClearButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resetFields(); + } + }); + } + + private void resetFields() { + //TODO + } + + /** + * Launches new Activity and finishes, triggering an autofill save request if the user entered + * any new data. + */ + private void submit() { + Intent intent = WelcomeActivity.getStartActivityIntent(CreditCardActivity.this); + startActivity(intent); + finish(); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java new file mode 100644 index 0000000000000000000000000000000000000000..085f827c2638619b7427b51edf835bb3011e2e74 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewStructure; +import android.view.autofill.AutofillManager; +import android.view.autofill.AutofillValue; +import android.widget.EditText; +import android.widget.TextView; + +import com.example.android.autofillframework.R; + +import java.util.ArrayList; +import java.util.Arrays; + +import static com.example.android.autofillframework.CommonUtil.bundleToString; + + +/** + * Custom View with virtual child views for Username/Password text fields. + */ +public class CustomVirtualView extends View { + + private static final String TAG = "CustomView"; + + private static int nextId; + + private final ArrayList mLines = new ArrayList<>(); + private final SparseArray mItems = new SparseArray<>(); + private final AutofillManager mAfm; + + private Line mFocusedLine; + private Paint mTextPaint; + private int mTextHeight; + private int mTopMargin; + private int mLeftMargin; + private int mVerticalGap; + private int mLineLength; + private int mFocusedColor; + private int mUnfocusedColor; + + private Line mUsernameLine; + private Line mPasswordLine; + + public CustomVirtualView(Context context, AttributeSet attrs) { + super(context, attrs); + + mAfm = context.getSystemService(AutofillManager.class); + + mTextPaint = new Paint(); + + mUnfocusedColor = Color.BLACK; + mFocusedColor = Color.RED; + mTextPaint.setStyle(Style.FILL); + mTopMargin = 100; + mLeftMargin = 100; + mTextHeight = 90; + mVerticalGap = 10; + + mLineLength = mTextHeight + mVerticalGap; + mTextPaint.setTextSize(mTextHeight); + mUsernameLine = addLine("usernameField", context.getString(R.string.username_label), + new String[] {View.AUTOFILL_HINT_USERNAME}, " ", true); + mPasswordLine = addLine("passwordField", context.getString(R.string.password_label), + new String[] {View.AUTOFILL_HINT_PASSWORD}, " ", false); + + Log.d(TAG, "Text height: " + mTextHeight); + } + + @Override + public void autofill(SparseArray values) { + // User has just selected a Dataset from the list of Autofill suggestions and the Dataset's + // AutofillValue gets passed into this method. + Log.d(TAG, "autoFill(): " + values); + for (int i = 0; i < values.size(); i++) { + final int id = values.keyAt(i); + final AutofillValue value = values.valueAt(i); + final Item item = mItems.get(id); + if (item == null) { + Log.w(TAG, "No item for id " + id); + return; + } + if (!item.editable) { + Log.w(TAG, "Item for id " + id + " is not editable: " + item); + return; + } + // Set the item's text to the text wrapped in the AutofillValue. + item.text = value.getTextValue(); + } + postInvalidate(); + } + + @Override + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + // Build a ViewStructure to pack in AutoFillService requests. + structure.setClassName(getClass().getName()); + int childrenSize = mItems.size(); + Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = " + + childrenSize + ", extras: " + bundleToString(structure.getExtras())); + int index = structure.addChildCount(childrenSize); + for (int i = 0; i < childrenSize; i++) { + Item item = mItems.valueAt(i); + Log.d(TAG, "Adding new child at index " + index + ": " + item); + ViewStructure child = structure.newChild(index); + child.setAutofillId(structure, item.id); + child.setAutofillHints(item.hints); + child.setAutofillType(item.type); + child.setDataIsSensitive(!item.sanitized); + child.setText(item.text); + child.setAutofillValue(AutofillValue.forText(item.text)); + child.setFocused(item.focused); + child.setId(item.id, getContext().getPackageName(), null, item.line.idEntry); + child.setClassName(item.getClassName()); + index++; + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + Log.d(TAG, "onDraw: " + mLines.size() + " lines; canvas:" + canvas); + float x; + float y = mTopMargin + mLineLength; + for (int i = 0; i < mLines.size(); i++) { + x = mLeftMargin; + Line line = mLines.get(i); + Log.v(TAG, "Drawing '" + line + "' at " + x + "x" + y); + mTextPaint.setColor(line.fieldTextItem.focused ? mFocusedColor : mUnfocusedColor); + String readOnlyText = line.labelItem.text + ": ["; + String writeText = line.fieldTextItem.text + "]"; + // Paints the label first... + canvas.drawText(readOnlyText, x, y, mTextPaint); + // ...then paints the edit text and sets the proper boundary + float deltaX = mTextPaint.measureText(readOnlyText); + x += deltaX; + line.bounds.set((int) x, (int) (y - mLineLength), + (int) (x + mTextPaint.measureText(writeText)), (int) y); + Log.d(TAG, "setBounds(" + x + ", " + y + "): " + line.bounds); + canvas.drawText(writeText, x, y, mTextPaint); + y += mLineLength; + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int y = (int) event.getY(); + Log.d(TAG, "Touched: y=" + y + ", range=" + mLineLength + ", top=" + mTopMargin); + int lowerY = mTopMargin; + int upperY = -1; + for (int i = 0; i < mLines.size(); i++) { + upperY = lowerY + mLineLength; + Line line = mLines.get(i); + Log.d(TAG, "Line " + i + " ranges from " + lowerY + " to " + upperY); + if (lowerY <= y && y <= upperY) { + if (mFocusedLine != null) { + Log.d(TAG, "Removing focus from " + mFocusedLine); + mFocusedLine.changeFocus(false); + } + Log.d(TAG, "Changing focus to " + line); + mFocusedLine = line; + mFocusedLine.changeFocus(true); + invalidate(); + break; + } + lowerY += mLineLength; + } + return super.onTouchEvent(event); + } + + public CharSequence getUsernameText() { + return mUsernameLine.fieldTextItem.text; + } + + public CharSequence getPasswordText() { + return mPasswordLine.fieldTextItem.text; + } + + public void resetFields() { + mUsernameLine.reset(); + mPasswordLine.reset(); + postInvalidate(); + } + + private Line addLine(String idEntry, String label, String[] hints, String text, boolean sanitized) { + Line line = new Line(idEntry, label, hints, text, sanitized); + mLines.add(line); + mItems.put(line.labelItem.id, line.labelItem); + mItems.put(line.fieldTextItem.id, line.fieldTextItem); + return line; + } + + private static final class Item { + private final Line line; + private final int id; + private final boolean editable; + private final boolean sanitized; + private final String[] hints; + private final int type; + private CharSequence text; + private boolean focused = false; + + Item(Line line, int id, String[] hints, int type, CharSequence text, boolean editable, boolean sanitized) { + this.line = line; + this.id = id; + this.text = text; + this.editable = editable; + this.sanitized = sanitized; + this.hints = hints; + this.type = type; + } + + @Override + public String toString() { + return id + ": " + text + (editable ? " (editable)" : " (read-only)" + + (sanitized ? " (sanitized)" : " (sensitive")); + } + + public String getClassName() { + return editable ? EditText.class.getName() : TextView.class.getName(); + } + } + + private final class Line { + + // Boundaries of the text field, relative to the CustomView + final Rect bounds = new Rect(); + private Item labelItem; + private Item fieldTextItem; + private String idEntry; + + private Line(String idEntry, String label, String[] hints, String text, boolean sanitized) { + this.idEntry = idEntry; + this.labelItem = new Item(this, ++nextId, null, AUTOFILL_TYPE_NONE, label, false, true); + this.fieldTextItem = new Item(this, ++nextId, hints, AUTOFILL_TYPE_TEXT, text, true, sanitized); + } + + void changeFocus(boolean focused) { + fieldTextItem.focused = focused; + if (focused) { + final Rect absBounds = getAbsCoordinates(); + Log.d(TAG, "focus gained on " + fieldTextItem.id + "; absBounds=" + absBounds); + mAfm.notifyViewEntered(CustomVirtualView.this, fieldTextItem.id, absBounds); + } else { + Log.d(TAG, "focus lost on " + fieldTextItem.id); + mAfm.notifyViewExited(CustomVirtualView.this, fieldTextItem.id); + } + } + + private Rect getAbsCoordinates() { + // Must offset the boundaries so they're relative to the CustomView. + final int offset[] = new int[2]; + getLocationOnScreen(offset); + final Rect absBounds = new Rect(bounds.left + offset[0], + bounds.top + offset[1], + bounds.right + offset[0], bounds.bottom + offset[1]); + Log.v(TAG, "getAbsCoordinates() for " + fieldTextItem.id + ": bounds=" + bounds + + " offset: " + Arrays.toString(offset) + " absBounds: " + absBounds); + return absBounds; + } + + public void reset() { + fieldTextItem.text = " "; + } + + @Override + public String toString() { + return "Label: " + labelItem + " Text: " + fieldTextItem + " Focused: " + + fieldTextItem.focused; + } + } +} \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..3ec87da07d2051fcf53a2b3842629be40b921155 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.example.android.autofillframework.R; + +public class LoginActivity extends AppCompatActivity { + + private EditText mUsernameEditText; + private EditText mPasswordEditText; + private Button mLoginButton; + private Button mClearButton; + + public static Intent getStartActivityIntent(Context context) { + Intent intent = new Intent(context, LoginActivity.class); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.login_activity); + + mLoginButton = (Button) findViewById(R.id.login); + mClearButton = (Button) findViewById(R.id.clear); + mUsernameEditText = (EditText) findViewById(R.id.usernameField); + mPasswordEditText = (EditText) findViewById(R.id.passwordField); + mLoginButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + login(); + } + }); + mClearButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resetFields(); + } + }); + } + + private void resetFields() { + mUsernameEditText.setText(""); + mPasswordEditText.setText(""); + } + + /** + * Emulates a login action. + */ + private void login() { + String username = mUsernameEditText.getText().toString(); + String password = mPasswordEditText.getText().toString(); + boolean valid = isValidCredentials(username, password); + if (valid) { + Intent intent = WelcomeActivity.getStartActivityIntent(LoginActivity.this); + startActivity(intent); + finish(); + } else { + Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show(); + } + } + + /** + * Dummy implementation for demo purposes. A real service should use secure mechanisms to + * authenticate users. + */ + public boolean isValidCredentials(String username, String password) { + return username != null && password != null && username.equals(password); + } +} \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..4b27010b6a70e983b8d7587fbd9fba7513467318 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.example.android.autofillframework.R; + +/** + * This is used to launch sample activities that showcase autofill. + */ +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + findViewById(R.id.standardViewSignInButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + standardViewSignIn(); + } + }); + findViewById(R.id.virtualViewSignInButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + virtualViewSignIn(); + } + }); + findViewById(R.id.creditCardCheckoutButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + creditCardCheckout(); + } + }); + } + + private void creditCardCheckout() { + Intent intent = CreditCardActivity.getStartActivityIntent(this); + startActivity(intent); + } + + private void standardViewSignIn() { + Intent intent = LoginActivity.getStartActivityIntent(this); + startActivity(intent); + } + + private void virtualViewSignIn() { + Intent intent = VirtualLoginActivity.getStartActivityIntent(this); + startActivity(intent); + } +} \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..5a4a4f4e4944f75b132b506a6e58ba3fac939415 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.example.android.autofillframework.R; + + +public class VirtualLoginActivity extends AppCompatActivity { + + private CustomVirtualView mCustomVirtualView; + + public static Intent getStartActivityIntent(Context context) { + Intent intent = new Intent(context, VirtualLoginActivity.class); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.virtual_login_activity); + + mCustomVirtualView = (CustomVirtualView) findViewById(R.id.custom_view); + findViewById(R.id.login).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + login(); + } + }); + findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + resetFields(); + } + }); + } + + private void resetFields() { + mCustomVirtualView.resetFields(); + } + + /** + * Emulates a login action. + */ + private void login() { + String username = mCustomVirtualView.getUsernameText().toString(); + String password = mCustomVirtualView.getPasswordText().toString(); + boolean valid = isValidCredentials(username, password); + if (valid) { + Intent intent = WelcomeActivity.getStartActivityIntent(VirtualLoginActivity.this); + startActivity(intent); + } else { + Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show(); + } + } + + /** + * Dummy implementation for demo purposes. A real service should use secure mechanisms to + * authenticate users. + */ + public boolean isValidCredentials(String username, String password) { + return username != null && password != null && username.equals(password); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..fd3df2f626850a0330d763ba665fd145b7fda3d9 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.example.android.autofillframework.R; + +public class WelcomeActivity extends Activity { + + public static Intent getStartActivityIntent(Context context) { + return new Intent(context, WelcomeActivity.class); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.welcome_activity); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..768b2ee393f48289cbc1cec4e2090f1994b82273 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service; + +import android.app.Activity; +import android.app.PendingIntent; +import android.app.assist.AssistStructure; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; +import android.support.annotation.Nullable; +import android.text.Editable; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.example.android.autofillframework.R; +import com.example.android.autofillframework.service.datasource.LocalAutofillRepository; +import com.example.android.autofillframework.service.model.AutofillFieldsCollection; +import com.example.android.autofillframework.service.model.ClientFormData; +import com.example.android.autofillframework.service.settings.MyPreferences; + +import java.util.HashMap; + +import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE; +import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT; +import static com.example.android.autofillframework.CommonUtil.EXTRA_DATASET_NAME; +import static com.example.android.autofillframework.CommonUtil.EXTRA_FOR_RESPONSE; +import static com.example.android.autofillframework.CommonUtil.TAG; + +/** + * This Activity controls the UI for logging in to the Autofill service. + * It is launched when an Autofill Response or specific Dataset within the Response requires + * authentication to access. It bundles the result in an Intent. + */ +public class AuthActivity extends Activity { + + // Unique id for dataset intents. + private static int sDatasetPendingIntentId = 0; + + private EditText mMasterPassword; + private Button mCancel; + private Button mLogin; + private Intent mReplyIntent; + + static IntentSender getAuthIntentSenderForResponse(Context context) { + final Intent intent = new Intent(context, AuthActivity.class); + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) + .getIntentSender(); + } + + static IntentSender getAuthIntentSenderForDataset(Context context, String datasetName) { + final Intent intent = new Intent(context, AuthActivity.class); + intent.putExtra(EXTRA_DATASET_NAME, datasetName); + intent.putExtra(EXTRA_FOR_RESPONSE, false); + return PendingIntent.getActivity(context, ++sDatasetPendingIntentId, intent, + PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.auth_activity); + mCancel = findViewById(R.id.cancel); + mLogin = findViewById(R.id.login); + mMasterPassword = findViewById(R.id.master_password); + mLogin.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + login(); + } + + }); + + mCancel.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onFailure(); + AuthActivity.this.finish(); + } + }); + } + + private void login() { + Editable password = mMasterPassword.getText(); + if (password.toString() + .equals(MyPreferences.getInstance(AuthActivity.this).getMasterPassword())) { + onSuccess(); + } else { + Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show(); + onFailure(); + } + finish(); + } + + @Override + public void finish() { + if (mReplyIntent != null) { + setResult(RESULT_OK, mReplyIntent); + } else { + setResult(RESULT_CANCELED); + } + super.finish(); + } + + private void onFailure() { + Log.w(TAG, "Failed auth."); + mReplyIntent = null; + } + + private void onSuccess() { + Intent intent = getIntent(); + boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true); + AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE); + StructureParser parser = new StructureParser(structure); + parser.parse(); + AutofillFieldsCollection autofillFields = parser.getAutofillFields(); + int saveTypes = parser.getSaveTypes(); + mReplyIntent = new Intent(); + HashMap clientFormDataMap = + LocalAutofillRepository.getInstance(this).getClientFormData + (autofillFields.getFocusedHints(), autofillFields.getAllHints()); + if (forResponse) { + setResponseIntent(AutofillHelper.newResponse + (this, false, autofillFields, saveTypes, clientFormDataMap)); + } else { + String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME); + setDatasetIntent(AutofillHelper.newDataset + (this, autofillFields, clientFormDataMap.get(datasetName))); + } + } + + private void setResponseIntent(FillResponse fillResponse) { + mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse); + } + + private void setDatasetIntent(Dataset dataset) { + mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..460729e6db549dcb600e9346d1325e2021a7f6a1 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service; + +import android.content.Context; +import android.content.IntentSender; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; +import android.service.autofill.SaveInfo; +import android.util.Log; +import android.view.autofill.AutofillId; +import android.widget.RemoteViews; + +import com.example.android.autofillframework.R; +import com.example.android.autofillframework.service.model.AutofillFieldsCollection; +import com.example.android.autofillframework.service.model.ClientFormData; + +import java.util.HashMap; +import java.util.Set; + +import static com.example.android.autofillframework.CommonUtil.TAG; + +/** + * This is a class containing helper methods for building Autofill Datasets and Responses. + */ +public final class AutofillHelper { + + /** + * Wraps autofill data in a LoginCredential Dataset object which can then be sent back to the + * client View. + */ + public static Dataset newDataset(Context context, + AutofillFieldsCollection autofillFields, ClientFormData clientFormData) { + Dataset.Builder datasetBuilder = new Dataset.Builder + (newRemoteViews(context.getPackageName(), clientFormData.getDatasetName())); + boolean setValueAtLeastOnce = clientFormData.applyToFields(autofillFields, datasetBuilder); + if (setValueAtLeastOnce) { + return datasetBuilder.build(); + } else { + return null; + } + } + + public static RemoteViews newRemoteViews(String packageName, String remoteViewsText) { + RemoteViews presentation = new RemoteViews(packageName, R.layout.list_item); + presentation.setTextViewText(R.id.text1, remoteViewsText); + return presentation; + } + + /** + * Wraps autofill data in a Response object (essentially a series of Datasets) which can then + * be sent back to the client View. + */ + public static FillResponse newResponse(Context context, + boolean datasetAuth, AutofillFieldsCollection autofillFields, int saveType, + HashMap clientFormDataMap) { + FillResponse.Builder responseBuilder = new FillResponse.Builder(); + if (clientFormDataMap != null) { + Set datasetNames = clientFormDataMap.keySet(); + for (String datasetName : datasetNames) { + ClientFormData clientFormData = clientFormDataMap.get(datasetName); + if (datasetAuth) { + Dataset.Builder datasetBuilder = + new Dataset.Builder(newRemoteViews + (context.getPackageName(), clientFormData.getDatasetName())); + IntentSender sender = AuthActivity + .getAuthIntentSenderForDataset(context, clientFormData.getDatasetName()); + datasetBuilder.setAuthentication(sender); + responseBuilder.addDataset(datasetBuilder.build()); + } else { + Dataset dataset = newDataset(context, autofillFields, clientFormData); + if (dataset != null) { + responseBuilder.addDataset(dataset); + } + } + } + } + if (saveType != 0) { + AutofillId[] autofillIds = autofillFields.getAutofillIds(); + responseBuilder.setSaveInfo(new SaveInfo.Builder(saveType, autofillIds).build()); + return responseBuilder.build(); + } else { + Log.d(TAG, "These fields are not meant to be saved by autofill."); + return null; + } + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java new file mode 100644 index 0000000000000000000000000000000000000000..61e42050e64b328c7fbfbd9fb861a1df0f997d3a --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service; + +import android.app.assist.AssistStructure; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.service.autofill.AutofillService; +import android.service.autofill.FillCallback; +import android.service.autofill.FillContext; +import android.service.autofill.FillRequest; +import android.service.autofill.FillResponse; +import android.service.autofill.SaveCallback; +import android.service.autofill.SaveRequest; +import android.util.Log; +import android.widget.RemoteViews; + +import com.example.android.autofillframework.R; +import com.example.android.autofillframework.service.datasource.LocalAutofillRepository; +import com.example.android.autofillframework.service.model.AutofillFieldsCollection; +import com.example.android.autofillframework.service.model.ClientFormData; +import com.example.android.autofillframework.service.settings.MyPreferences; + +import java.util.HashMap; +import java.util.List; + +import static com.example.android.autofillframework.CommonUtil.TAG; +import static com.example.android.autofillframework.CommonUtil.bundleToString; + +public class MyAutofillService extends AutofillService { + + @Override + public void onFillRequest(AssistStructure assistStructure, Bundle bundle, int i, + CancellationSignal cancellationSignal, FillCallback fillCallback) { + /* Deprecated, ignore */ + } + + @Override + public void onSaveRequest(AssistStructure assistStructure, Bundle bundle, + SaveCallback saveCallback) { + /* Deprecated, ignore */ + } + + @Override + public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, + FillCallback callback) { + AssistStructure structure = request.getStructure(); + final Bundle data = request.getClientState(); + Log.d(TAG, "onFillRequest(): data=" + bundleToString(data)); + + // Temporary hack for disabling autofill for components in this autofill service. + // i.e. we don't want to autofill components in AuthActivity. + if (structure.getActivityComponent().toShortString() + .contains("com.example.android.autofillframework.service")) { + callback.onSuccess(null); + return; + } + cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() { + @Override + public void onCancel() { + Log.w(TAG, "Cancel autofill not implemented in this sample."); + } + }); + // Parse AutoFill data in Activity + StructureParser parser = new StructureParser(structure); + parser.parse(); + AutofillFieldsCollection autofillFields = parser.getAutofillFields(); + int saveTypes = parser.getSaveTypes(); + + FillResponse.Builder responseBuilder = new FillResponse.Builder(); + // Check user's settings for authenticating Responses and Datasets. + boolean responseAuth = MyPreferences.getInstance(this).isResponseAuth(); + if (responseAuth) { + // If the entire Autofill Response is authenticated, AuthActivity is used + // to generate Response. + IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this); + RemoteViews presentation = AutofillHelper + .newRemoteViews(getPackageName(), getString(R.string.autofill_sign_in_prompt)); + responseBuilder + .setAuthentication(autofillFields.getAutofillIds(), sender, presentation); + callback.onSuccess(responseBuilder.build()); + } else { + boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth(); + HashMap clientFormDataMap = + LocalAutofillRepository.getInstance(this).getClientFormData + (autofillFields.getFocusedHints(), autofillFields.getAllHints()); + FillResponse response = AutofillHelper.newResponse + (this, datasetAuth, autofillFields, saveTypes, clientFormDataMap); + callback.onSuccess(response); + } + } + + @Override + public void onSaveRequest(SaveRequest request, SaveCallback callback) { + List context = request.getFillContexts(); + final AssistStructure structure = context.get(context.size() - 1).getStructure(); + final Bundle data = request.getClientState(); + Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data)); + StructureParser parser = new StructureParser(structure); + parser.parse(); + ClientFormData clientFormData = parser.getClientFormData(); + LocalAutofillRepository.getInstance(this).saveClientFormData(clientFormData); + } + + @Override + public void onConnected() { + Log.d(TAG, "onConnected"); + } + + @Override + public void onDisconnected() { + Log.d(TAG, "onDisconnected"); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java new file mode 100644 index 0000000000000000000000000000000000000000..b6294449d8eb056d759435aa4ad73d3e9c306493 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service; + +import android.app.assist.AssistStructure; +import android.app.assist.AssistStructure.ViewNode; +import android.app.assist.AssistStructure.WindowNode; +import android.util.Log; + +import com.example.android.autofillframework.service.model.AutofillField; +import com.example.android.autofillframework.service.model.AutofillFieldsCollection; +import com.example.android.autofillframework.service.model.ClientFormData; +import com.example.android.autofillframework.service.model.SavedAutofillValue; + +import static com.example.android.autofillframework.CommonUtil.TAG; + +/** + * Parser for an AssistStructure object. This is invoked when the Autofill Service receives an + * AssistStructure from the client Activity, representing its View hierarchy. In this + * sample, it parses the hierarchy and records + */ +final class StructureParser { + private final AutofillFieldsCollection mAutofillFields = new AutofillFieldsCollection(); + private final AssistStructure mStructure; + private ClientFormData mClientFormData; + + StructureParser(AssistStructure structure) { + mStructure = structure; + + } + + /** + * Traverse AssistStructure and add ViewNode metadata to a flat list. + */ + void parse() { + Log.d(TAG, "Parsing structure for " + mStructure.getActivityComponent()); + int nodes = mStructure.getWindowNodeCount(); + mClientFormData = new ClientFormData(); + for (int i = 0; i < nodes; i++) { + WindowNode node = mStructure.getWindowNodeAt(i); + ViewNode view = node.getRootViewNode(); + parseLocked(view); + } + } + + private void parseLocked(ViewNode viewNode) { + if (viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) { + //TODO check to make sure hints are supported by service. + mAutofillFields.add(new AutofillField(viewNode)); + mClientFormData + .set(viewNode.getAutofillHints(), SavedAutofillValue.fromViewNode(viewNode)); + } + int childrenSize = viewNode.getChildCount(); + if (childrenSize > 0) { + for (int i = 0; i < childrenSize; i++) { + parseLocked(viewNode.getChildAt(i)); + } + } + } + + public AutofillFieldsCollection getAutofillFields() { + return mAutofillFields; + } + + public int getSaveTypes() { + return mAutofillFields.getSaveType(); + } + + public ClientFormData getClientFormData() { + return mClientFormData; + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..8de8b647eed78a600cb6b2baf1419b0b3b2ac644 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.datasource; + +import com.example.android.autofillframework.service.model.ClientFormData; + +import java.util.HashMap; +import java.util.List; + +public interface AutofillRepository { + + /** + * Gets saved ClientFormData that contains some objects that can autofill fields with these + * {@code autofillHints}. + */ + HashMap getClientFormData(List focusedAutofillHints, + List allAutofillHints); + + /** + * Saves LoginCredential under this datasetName. + */ + void saveClientFormData(ClientFormData clientFormData); + + /** + * Clears all data. + */ + void clear(); +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..8336fe1e38aefc68358472af2e439316fa9e84d5 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.datasource; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.ArraySet; + +import com.example.android.autofillframework.service.model.ClientFormData; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +/** + * Singleton autofill data repository, that stores autofill fields to SharedPreferences. + * DISCLAIMER, you should not store sensitive fields like user data unencrypted. This is only done + * here for simplicity and learning purposes. + */ +public class LocalAutofillRepository implements AutofillRepository { + private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service"; + private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets"; + private static final String DATASET_NUMBER_KEY = "datasetNumber"; + + private static LocalAutofillRepository sInstance; + + private final SharedPreferences mPrefs; + + // TODO prepend with autofill data set in Settings. + private LocalAutofillRepository(Context context) { + mPrefs = context.getApplicationContext() + .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE); + } + + public static LocalAutofillRepository getInstance(Context context) { + if (sInstance == null) { + sInstance = new LocalAutofillRepository(context); + } + return sInstance; + } + + @Override + public HashMap getClientFormData(List focusedAutofillHints, + List allAutofillHints) { + try { + // TODO use sqlite instead. + boolean hasDataForFocusedAutofillHints = false; + HashMap clientFormDataMap = new HashMap<>(); + Set clientFormDataStringSet = getAllAutofillDataStringSet(); + for (String clientFormDataString : clientFormDataStringSet) { + ClientFormData clientFormData = ClientFormData + .fromJson(new JSONObject(clientFormDataString)); + if (clientFormData != null) { + if (clientFormData.helpsWithHints(focusedAutofillHints)) { + hasDataForFocusedAutofillHints = true; + } + if (clientFormData.helpsWithHints(allAutofillHints)) { + clientFormDataMap.put(clientFormData.getDatasetName(), clientFormData); + } + } + } + if (hasDataForFocusedAutofillHints) { + return clientFormDataMap; + } else { + return null; + } + } catch (JSONException e) { + return null; + } + } + + @Override + public void saveClientFormData(ClientFormData clientFormData) { + //TODO use sqlite instead. + String datasetName = "dataset-" + getDatasetNumber(); + clientFormData.setDatasetName(datasetName); + Set allAutofillData = getAllAutofillDataStringSet(); + allAutofillData.add(clientFormData.toJson().toString()); + saveAllAutofillDataStringSet(allAutofillData); + incrementDatasetNumber(); + } + + @Override + public void clear() { + mPrefs.edit().remove(CLIENT_FORM_DATA_KEY).apply(); + } + + private Set getAllAutofillDataStringSet() { + return mPrefs.getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet()); + } + + private void saveAllAutofillDataStringSet(Set allAutofillDataStringSet) { + mPrefs.edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply(); + } + + /** + * For simplicity, datasets will be named in the form "dataset-X" where X means + * this was the Xth dataset saved. + */ + private int getDatasetNumber() { + return mPrefs.getInt(DATASET_NUMBER_KEY, 0); + } + + /** + * Every time a dataset is saved, this should be called to increment the dataset number. + * (only important for this service's dataset naming scheme). + */ + private void incrementDatasetNumber() { + mPrefs.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply(); + } +} \ No newline at end of file diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java new file mode 100644 index 0000000000000000000000000000000000000000..4d4de2bc532798b608d948fda7d148f96999e280 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.model; + +import android.app.assist.AssistStructure; +import android.service.autofill.SaveInfo; +import android.view.View; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; + +/** + * Class that represents a field that can be autofilled. It will contain a description + * (what type data the field holds), an AutoFillId (an ID unique to the rest of the ViewStructure), + * and a value (what data is currently in the field). + */ +public class AutofillField { + private int mSaveType = 0; + private String[] mHints; + private AutofillId mId; + private int mAutofillType; + private String[] mAutofillOptions; + private boolean mFocused; + + public AutofillField(AssistStructure.ViewNode view) { + mId = view.getAutofillId(); + setHints(view.getAutofillHints()); + mAutofillType = view.getAutofillType(); + mAutofillOptions = view.getAutofillOptions(); + mFocused = view.isFocused(); + } + + public String[] getHints() { + return mHints; + } + + public void setHints(String[] hints) { + mHints = hints; + updateSaveTypeFromHints(); + } + + public int getSaveType() { + return mSaveType; + } + + public AutofillId getId() { + return mId; + } + + public void setId(AutofillId id) { + mId = id; + } + + public int getAutofillType() { + return mAutofillType; + } + + public int getAutofillOptionIndex(String value) { + for (int i = 0; i < mAutofillOptions.length; i++) { + if (mAutofillOptions[i].equals(value)) { + return i; + } + } + return -1; + } + + public boolean isFocused() { + return mFocused; + } + + private void updateSaveTypeFromHints() { + mSaveType = 0; + if (mHints == null) { + return; + } + for (String hint : mHints) { + switch (hint) { + case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE: + case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY: + case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH: + case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR: + case View.AUTOFILL_HINT_CREDIT_CARD_NUMBER: + case View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD; + break; + case View.AUTOFILL_HINT_EMAIL_ADDRESS: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS; + break; + case View.AUTOFILL_HINT_PHONE: + case View.AUTOFILL_HINT_NAME: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_GENERIC; + break; + case View.AUTOFILL_HINT_PASSWORD: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_PASSWORD; + mSaveType &= ~SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS; + mSaveType &= ~SaveInfo.SAVE_DATA_TYPE_USERNAME; + break; + case View.AUTOFILL_HINT_POSTAL_ADDRESS: + case View.AUTOFILL_HINT_POSTAL_CODE: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_ADDRESS; + break; + case View.AUTOFILL_HINT_USERNAME: + mSaveType |= SaveInfo.SAVE_DATA_TYPE_USERNAME; + break; + } + } + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java new file mode 100644 index 0000000000000000000000000000000000000000..0354b989e1a333b4e948d87477cc48629d425bfe --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.model; + +import android.view.autofill.AutofillId; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public final class AutofillFieldsCollection { + + private final List mAutofillIds = new ArrayList<>(); + private final HashMap> mAutofillHintsToFieldsMap = new HashMap<>(); + private final List mAllAutofillHints = new ArrayList<>(); + private final List mFocusedAutofillHints = new ArrayList<>(); + private int size = 0; + private int mSaveType = 0; + + public void add(AutofillField autofillField) { + mSaveType |= autofillField.getSaveType(); + size++; + mAutofillIds.add(autofillField.getId()); + List hintsList = Arrays.asList(autofillField.getHints()); + mAllAutofillHints.addAll(hintsList); + if (autofillField.isFocused()) { + mFocusedAutofillHints.addAll(hintsList); + } + for (String hint : autofillField.getHints()) { + if (mAutofillHintsToFieldsMap.get(hint) == null) { + mAutofillHintsToFieldsMap.put(hint, new ArrayList()); + } + mAutofillHintsToFieldsMap.get(hint).add(autofillField); + } + } + + public int getSaveType() { + return mSaveType; + } + + public AutofillId[] getAutofillIds() { + return mAutofillIds.toArray(new AutofillId[size]); + } + + public List getFieldsForHint(String hint) { + return mAutofillHintsToFieldsMap.get(hint); + } + + public List getFocusedHints() { + return mFocusedAutofillHints; + } + + public List getAllHints() { + return mAllAutofillHints; + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java new file mode 100644 index 0000000000000000000000000000000000000000..aa57e9359e1ea9af009c3ed7ae0a7d990da8cc7e --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.model; + +import android.service.autofill.Dataset; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.View; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * ClientFormData is the model that holds all of the data on a client app's page, plus the dataset + * name associated with it. + */ +public final class ClientFormData { + private static final String TAG = "ClientFormData"; + private final HashMap hintMap; + private String datasetName; + + public ClientFormData() { + this(null, new HashMap()); + } + + public ClientFormData(String datasetName, HashMap hintMap) { + this.hintMap = hintMap; + this.datasetName = datasetName; + } + + public static ClientFormData fromJson(JSONObject jsonObject) { + HashMap hintMap = new HashMap<>(); + try { + String datasetName = jsonObject.has("datasetName") ? + jsonObject.getString("datasetName") : null; + JSONObject valuesJson = jsonObject.getJSONObject("values"); + Iterator hints = valuesJson.keys(); + while (hints.hasNext()) { + String hint = hints.next(); + JSONObject valueAsJson = valuesJson + .getJSONObject(hint); + if (valueAsJson != null) { + SavedAutofillValue savedAutofillValue = SavedAutofillValue.fromJson(valueAsJson); + hintMap.put(hint, savedAutofillValue); + } + } + return new ClientFormData(datasetName, hintMap); + } catch (JSONException e) { + Log.d(TAG, e.getMessage()); + return null; + } + } + + /** + * Returns the name of the {@link Dataset}. + */ + public String getDatasetName() { + return this.datasetName; + } + + /** + * Sets the {@link Dataset} name. + */ + public void setDatasetName(String datasetName) { + this.datasetName = datasetName; + } + + /** + * Sets values for a list of hints. + */ + public void set(@NonNull String[] autofillHints, @NonNull SavedAutofillValue autofillValue) { + if (autofillHints.length < 1) { + return; + } + for (int i = 0; i < autofillHints.length; i++) { + hintMap.put(autofillHints[i], autofillValue); + } + } + + /** + * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId} + * in a {@code AutofillFieldsCollection}. + */ + public boolean applyToFields(AutofillFieldsCollection autofillFieldsCollection, + Dataset.Builder datasetBuilder) { + boolean setValueAtLeastOnce = false; + List allHints = autofillFieldsCollection.getAllHints(); + for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) { + String hint = allHints.get(hintIndex); + List autofillFields = autofillFieldsCollection.getFieldsForHint(hint); + if (autofillFields == null) { + continue; + } + for (int autofillFieldIndex = 0; autofillFieldIndex < autofillFields.size(); autofillFieldIndex++) { + AutofillField autofillField = autofillFields.get(autofillFieldIndex); + AutofillId autofillId = autofillField.getId(); + int autofillType = autofillField.getAutofillType(); + SavedAutofillValue savedAutofillValue = hintMap.get(hint); + switch (autofillType) { + case View.AUTOFILL_TYPE_LIST: + int listValue = autofillField.getAutofillOptionIndex(savedAutofillValue.getTextValue()); + if (listValue != -1) { + datasetBuilder.setValue(autofillId, AutofillValue.forList(listValue)); + setValueAtLeastOnce = true; + } + break; + case View.AUTOFILL_TYPE_DATE: + long dateValue = savedAutofillValue.getDateValue(); + if (dateValue != -1) { + datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue)); + setValueAtLeastOnce = true; + } + break; + case View.AUTOFILL_TYPE_TEXT: + String textValue = savedAutofillValue.getTextValue(); + if (textValue != null) { + datasetBuilder.setValue(autofillId, AutofillValue.forText(textValue)); + setValueAtLeastOnce = true; + } + break; + case View.AUTOFILL_TYPE_TOGGLE: + if (savedAutofillValue.hasToggleValue()) { + boolean toggleValue = savedAutofillValue.getToggleValue(); + datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue)); + setValueAtLeastOnce = true; + } + break; + case View.AUTOFILL_TYPE_NONE: + default: + Log.w(TAG, "Invalid autofill type - " + autofillType); + break; + } + } + } + return setValueAtLeastOnce; + } + + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("datasetName", datasetName != null ? datasetName : JSONObject.NULL); + JSONObject jsonValues = new JSONObject(); + Set hints = hintMap.keySet(); + for (String hint : hints) { + SavedAutofillValue value = hintMap.get(hint); + jsonValues.put(hint, value != null ? value.toJson() : JSONObject.NULL); + } + jsonObject.put("values", jsonValues); + } catch (JSONException e) { + Log.e(TAG, e.getMessage()); + } + return jsonObject; + } + + public boolean helpsWithHints(List autofillHints) { + for (int i = 0; i < autofillHints.size(); i++) { + String autofillHint = autofillHints.get(i); + if (hintMap.get(autofillHint) != null && !hintMap.get(autofillHint).isNull()) { + return true; + } + } + return false; + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java new file mode 100644 index 0000000000000000000000000000000000000000..73e0c81eb0a1493bc5ace231208349da4324d050 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.model; + +import android.app.assist.AssistStructure; +import android.util.Log; +import android.view.autofill.AutofillValue; + +import org.json.JSONException; +import org.json.JSONObject; + +public class SavedAutofillValue { + private static final String TAG = "SavedAutofillValue"; + private String textValue = null; + private Long dateValue = -1L; + private Boolean toggleValue = false; + private boolean hasToggleValue = false; + + public static SavedAutofillValue fromJson(JSONObject jsonObject) { + if (jsonObject == null) { + return null; + } + try { + SavedAutofillValue savedAutofillValue = new SavedAutofillValue(); + + savedAutofillValue.textValue = + !jsonObject.isNull("textValue") ? jsonObject.getString("textValue") : null; + savedAutofillValue.dateValue = + !jsonObject.isNull("dateValue") ? jsonObject.getLong("dateValue") : null; + savedAutofillValue.setToggleValue + (!jsonObject.isNull("toggleValue") ? jsonObject.getBoolean("toggleValue") : null); + return savedAutofillValue; + } catch (JSONException e) { + Log.e(TAG, e.getMessage()); + return null; + } + } + + public static SavedAutofillValue fromViewNode(AssistStructure.ViewNode viewNode) { + SavedAutofillValue savedAutofillValue = new SavedAutofillValue(); + AutofillValue autofillValue = viewNode.getAutofillValue(); + if (autofillValue != null) { + if (autofillValue.isList()) { + String[] autofillOptions = viewNode.getAutofillOptions(); + int index = autofillValue.getListValue(); + if (autofillOptions != null && autofillOptions.length > 0) { + savedAutofillValue.textValue = autofillOptions[index]; + } + } else if (autofillValue.isDate()) { + savedAutofillValue.dateValue = autofillValue.getDateValue(); + } else if (autofillValue.isText()) { + // Using toString of AutofillValue.getTextValue in order to save it to + // SharedPreferences. + savedAutofillValue.textValue = autofillValue.getTextValue().toString(); + } + } + return savedAutofillValue; + } + + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("textValue", textValue != null ? textValue : JSONObject.NULL); + jsonObject.put("dateValue", dateValue != null ? dateValue : JSONObject.NULL); + jsonObject.put("toggleValue", toggleValue != null ? toggleValue : JSONObject.NULL); + return jsonObject; + } catch (JSONException e) { + Log.e(TAG, e.getMessage()); + return null; + } + } + + public String getTextValue() { + return textValue; + } + + public long getDateValue() { + return dateValue; + } + + + public boolean getToggleValue() { + return toggleValue; + } + + public void setToggleValue(Boolean toggleValue) { + this.toggleValue = toggleValue; + hasToggleValue = toggleValue != null; + } + + + public boolean isNull() { + return textValue == null && dateValue == -1L && !hasToggleValue; + } + + public boolean hasToggleValue() { + return hasToggleValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SavedAutofillValue that = (SavedAutofillValue) o; + + if (textValue != null ? !textValue.equals(that.textValue) : that.textValue != null) + return false; + if (dateValue != null ? !dateValue.equals(that.dateValue) : that.dateValue != null) + return false; + return toggleValue != null ? toggleValue.equals(that.toggleValue) : that.toggleValue == null; + + } + + @Override + public int hashCode() { + int result = textValue != null ? textValue.hashCode() : 0; + result = 31 * result + (dateValue != null ? dateValue.hashCode() : 0); + result = 31 * result + (toggleValue != null ? toggleValue.hashCode() : 0); + return result; + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java new file mode 100644 index 0000000000000000000000000000000000000000..3926530e3eb5b0f67610c4c58a50065f4533a9ab --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; +import android.support.annotation.NonNull; + +public class MyPreferences { + private static final String TAG = "MyPreferences"; + + private static final String RESPONSE_AUTH_KEY = "response_auth"; + private static final String DATASET_AUTH_KEY = "dataset_auth"; + private static final String MASTER_PASSWORD_KEY = "master_password"; + + private static MyPreferences sInstance; + private final SharedPreferences mPrefs; + + private MyPreferences(Context context) { + mPrefs = context.getApplicationContext().getSharedPreferences("my-settings", + Context.MODE_PRIVATE); + } + + public static MyPreferences getInstance(Context context) { + if (sInstance == null) { + sInstance = new MyPreferences(context); + } + return sInstance; + } + + /** + * Gets whether {@link FillResponse}s should require authentication. + */ + public boolean isResponseAuth() { + return mPrefs.getBoolean(RESPONSE_AUTH_KEY, false); + } + + /** + * Enables/disables authentication for the entire autofill {@link FillResponse}. + */ + public void setResponseAuth(boolean responseAuth) { + mPrefs.edit().putBoolean(RESPONSE_AUTH_KEY, responseAuth).apply(); + } + + /** + * Gets whether {@link Dataset}s should require authentication. + */ + public boolean isDatasetAuth() { + return mPrefs.getBoolean(DATASET_AUTH_KEY, false); + } + + /** + * Enables/disables authentication for individual autofill {@link Dataset}s. + */ + public void setDatasetAuth(boolean datasetAuth) { + mPrefs.edit().putBoolean(DATASET_AUTH_KEY, datasetAuth).apply(); + } + + /** + * Gets autofill master username. + */ + public String getMasterPassword() { + return mPrefs.getString(MASTER_PASSWORD_KEY, null); + } + + /** + * Sets autofill master password. + */ + public void setMasterPassword(@NonNull String masterPassword) { + mPrefs.edit().putString(MASTER_PASSWORD_KEY, masterPassword).apply(); + } + + public void clearCredentials() { + mPrefs.edit().remove(MASTER_PASSWORD_KEY).apply(); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..6387d36b844d93be76f3c1a4e7809e39e50abb21 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.settings; + +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.Switch; +import android.widget.TextView; + +import com.example.android.autofillframework.R; +import com.example.android.autofillframework.service.datasource.LocalAutofillRepository; + +public class SettingsActivity extends AppCompatActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.settings_activity); + final MyPreferences preferences = MyPreferences.getInstance(this); + setupSettingsSwitch(R.id.settings_auth_responses_container, + R.id.settings_auth_responses_label, + R.id.settings_auth_responses_switch, + preferences.isResponseAuth(), + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + preferences.setResponseAuth(b); + } + }); + setupSettingsSwitch(R.id.settings_auth_datasets_container, + R.id.settings_auth_datasets_label, + R.id.settings_auth_datasets_switch, + preferences.isDatasetAuth(), + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + preferences.setDatasetAuth(b); + } + }); + setupSettingsButton(R.id.settings_clear_data_container, + R.id.settings_clear_data_label, + R.id.settings_clear_data_icon, + new View.OnClickListener() { + @Override + public void onClick(View view) { + buildClearDataDialog().show(); + } + }); + + setupSettingsButton(R.id.settings_auth_credentials_container, + R.id.settings_auth_credentials_label, + R.id.settings_auth_credentials_icon, + new View.OnClickListener() { + @Override + public void onClick(View view) { + if (preferences.getMasterPassword() != null) { + buildCurrentCredentialsDialog().show(); + } else { + buildNewCredentialsDialog().show(); + } + } + }); + } + + private AlertDialog buildClearDataDialog() { + return new AlertDialog.Builder(SettingsActivity.this) + .setMessage(R.string.settings_clear_data_confirmation) + .setTitle(R.string.settings_clear_data_confirmation_title) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + LocalAutofillRepository.getInstance + (SettingsActivity.this).clear(); + MyPreferences.getInstance(SettingsActivity.this) + .clearCredentials(); + dialog.dismiss(); + } + }) + .create(); + } + + private AlertDialog.Builder prepareCredentialsDialog() { + return new AlertDialog.Builder(SettingsActivity.this) + .setTitle(R.string.settings_auth_change_credentials_title) + .setNegativeButton(R.string.cancel, null); + } + + private AlertDialog buildCurrentCredentialsDialog() { + final EditText currentPasswordField = LayoutInflater + .from(SettingsActivity.this) + .inflate(R.layout.settings_authentication_dialog, null) + .findViewById(R.id.master_password_field); + return prepareCredentialsDialog() + .setMessage(R.string.settings_auth_enter_current_password) + .setView(currentPasswordField) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String password = currentPasswordField.getText().toString(); + if (MyPreferences.getInstance(SettingsActivity.this).getMasterPassword() + .equals(password)) { + buildNewCredentialsDialog().show(); + dialog.dismiss(); + } + } + }) + .create(); + } + + private AlertDialog buildNewCredentialsDialog() { + final EditText newPasswordField = LayoutInflater + .from(SettingsActivity.this) + .inflate(R.layout.settings_authentication_dialog, null) + .findViewById(R.id.master_password_field); + return prepareCredentialsDialog() + .setMessage(R.string.settings_auth_enter_new_password) + .setView(newPasswordField) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String password = newPasswordField.getText().toString(); + MyPreferences.getInstance(SettingsActivity.this).setMasterPassword(password); + dialog.dismiss(); + } + }) + .create(); + } + + private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked, + CompoundButton.OnCheckedChangeListener checkedChangeListener) { + ViewGroup container = (ViewGroup) findViewById(containerId); + String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString(); + final Switch switchView = container.findViewById(switchId); + switchView.setContentDescription(switchLabel); + switchView.setChecked(checked); + container.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + switchView.performClick(); + } + }); + switchView.setOnCheckedChangeListener(checkedChangeListener); + } + + private void setupSettingsButton(int containerId, int labelId, int imageViewId, + final View.OnClickListener onClickListener) { + ViewGroup container = (ViewGroup) findViewById(containerId); + String buttonLabel = ((TextView) container.findViewById(labelId)).getText().toString(); + final ImageView imageView = container.findViewById(imageViewId); + imageView.setContentDescription(buttonLabel); + container.setOnClickListener(onClickListener); + } +} diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable-hdpi/tile.9.png b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable-hdpi/tile.9.png new file mode 100644 index 0000000000000000000000000000000000000000..135862883e26eddce2b19db021adf62e10357ad0 Binary files /dev/null and b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable-hdpi/tile.9.png differ diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..4d2afb01d50eb42f17352bd4217bd0e06e2d36c4 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml @@ -0,0 +1,20 @@ + + + + diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..6534d9f0ec4a2ecb5fee31815134e0202cc5b171 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml @@ -0,0 +1,20 @@ + + + + diff --git a/prebuilts/gradle/AutofillFramework/Application/src/main/res/layout/activity_main.xml b/prebuilts/gradle/AutofillFramework/Application/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..26d0657d2b846e047eb2a6fe95deedc8efcf92a1 --- /dev/null +++ b/prebuilts/gradle/AutofillFramework/Application/src/main/res/layout/activity_main.xml @@ -0,0 +1,46 @@ + + + + +