From 5d3838ae3213f63823cca49a4e37becda53ff8d0 Mon Sep 17 00:00:00 2001 From: Mark Murphy Date: Mon, 7 Aug 2017 07:44:21 -0400 Subject: [PATCH] updated for runtime permissions --- Contacts/CallLog/app/build.gradle | 13 ++- .../CallLog/app/src/main/AndroidManifest.xml | 9 +- .../consumer/AbstractPermissionActivity.java | 105 ++++++++++++++++++ .../consumer/CallLogConsumerActivity.java | 21 +++- .../app/src/main/res/values-v21/styles.xml | 9 ++ .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/strings.xml | 2 +- .../app/src/main/res/values/styles.xml | 6 + Contacts/CallLog/build.gradle | 3 +- Contacts/Inserter/app/build.gradle | 9 +- .../Inserter/app/src/main/AndroidManifest.xml | 10 +- .../app/src/main/res/values-v21/styles.xml | 9 ++ .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/styles.xml | 6 + Contacts/Inserter/build.gradle | 2 +- Contacts/Spinners/app/build.gradle | 13 ++- .../Spinners/app/src/main/AndroidManifest.xml | 24 ++-- .../spinners/AbstractPermissionActivity.java | 105 ++++++++++++++++++ .../contacts/spinners/ContactSpinners.java | 23 +++- .../app/src/main/res/values-v21/styles.xml | 9 ++ .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/strings.xml | 3 +- .../app/src/main/res/values/styles.xml | 6 + Contacts/Spinners/build.gradle | 3 +- 24 files changed, 364 insertions(+), 44 deletions(-) create mode 100644 Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/AbstractPermissionActivity.java create mode 100644 Contacts/CallLog/app/src/main/res/values-v21/styles.xml create mode 100644 Contacts/CallLog/app/src/main/res/values/colors.xml create mode 100644 Contacts/CallLog/app/src/main/res/values/styles.xml create mode 100644 Contacts/Inserter/app/src/main/res/values-v21/styles.xml create mode 100644 Contacts/Inserter/app/src/main/res/values/colors.xml create mode 100644 Contacts/Inserter/app/src/main/res/values/styles.xml create mode 100644 Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/AbstractPermissionActivity.java create mode 100644 Contacts/Spinners/app/src/main/res/values-v21/styles.xml create mode 100644 Contacts/Spinners/app/src/main/res/values/colors.xml create mode 100644 Contacts/Spinners/app/src/main/res/values/styles.xml diff --git a/Contacts/CallLog/app/build.gradle b/Contacts/CallLog/app/build.gradle index 10680dd0d..f2b430939 100644 --- a/Contacts/CallLog/app/build.gradle +++ b/Contacts/CallLog/app/build.gradle @@ -1,6 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 - buildToolsVersion "25.0.3" + compileSdkVersion 26 + buildToolsVersion "26.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 26 + } } + +dependencies { + compile 'com.android.support:support-compat:26.0.0' +} \ No newline at end of file diff --git a/Contacts/CallLog/app/src/main/AndroidManifest.xml b/Contacts/CallLog/app/src/main/AndroidManifest.xml index 5e3e7eba5..9c5040597 100644 --- a/Contacts/CallLog/app/src/main/AndroidManifest.xml +++ b/Contacts/CallLog/app/src/main/AndroidManifest.xml @@ -4,15 +4,12 @@ android:versionCode="1" android:versionName="1.0"> - - - + + android:label="@string/app_name" + android:theme="@style/Theme.Apptheme"> diff --git a/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/AbstractPermissionActivity.java b/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/AbstractPermissionActivity.java new file mode 100644 index 000000000..8e73d841e --- /dev/null +++ b/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/AbstractPermissionActivity.java @@ -0,0 +1,105 @@ +/*** + Copyright (c) 2015-2016 CommonsWare, LLC + 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. + + Covered in detail in the book _The Busy Coder's Guide to Android Development_ + https://commonsware.com/Android + */ + +package com.commonsware.android.calllog.consumer; + +import android.app.ListActivity; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import java.util.ArrayList; + +abstract public class AbstractPermissionActivity + extends ListActivity { + abstract protected String[] getDesiredPermissions(); + abstract protected void onPermissionDenied(); + abstract protected void onReady(); + + private static final int REQUEST_PERMISSION=61125; + private static final String STATE_IN_PERMISSION="inPermission"; + private boolean isInPermission=false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState!=null) { + isInPermission= + savedInstanceState.getBoolean(STATE_IN_PERMISSION, false); + } + + if (hasAllPermissions(getDesiredPermissions())) { + onReady(); + } + else if (!isInPermission) { + isInPermission=true; + + ActivityCompat + .requestPermissions(this, + netPermissions(getDesiredPermissions()), + REQUEST_PERMISSION); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String[] permissions, + int[] grantResults) { + isInPermission=false; + + if (requestCode==REQUEST_PERMISSION) { + if (hasAllPermissions(getDesiredPermissions())) { + onReady(); + } + else { + onPermissionDenied(); + } + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putBoolean(STATE_IN_PERMISSION, isInPermission); + } + + private boolean hasAllPermissions(String[] perms) { + for (String perm : perms) { + if (!hasPermission(perm)) { + return(false); + } + } + + return(true); + } + + private boolean hasPermission(String perm) { + return(ContextCompat.checkSelfPermission(this, perm)== + PackageManager.PERMISSION_GRANTED); + } + + private String[] netPermissions(String[] wanted) { + ArrayList result=new ArrayList(); + + for (String perm : wanted) { + if (!hasPermission(perm)) { + result.add(perm); + } + } + + return(result.toArray(new String[result.size()])); + } +} diff --git a/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/CallLogConsumerActivity.java b/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/CallLogConsumerActivity.java index e280713d1..7f726782b 100644 --- a/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/CallLogConsumerActivity.java +++ b/Contacts/CallLog/app/src/main/java/com/commonsware/android/calllog/consumer/CallLogConsumerActivity.java @@ -14,7 +14,7 @@ package com.commonsware.android.calllog.consumer; -import android.app.ListActivity; +import android.Manifest; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; @@ -25,18 +25,31 @@ import android.view.View; import android.widget.SimpleCursorAdapter; import android.widget.TextView; +import android.widget.Toast; -public class CallLogConsumerActivity extends ListActivity implements +public class CallLogConsumerActivity extends AbstractPermissionActivity implements LoaderManager.LoaderCallbacks, SimpleCursorAdapter.ViewBinder { + private static final String[] PERMS={Manifest.permission.READ_CALL_LOG}; private static final String[] PROJECTION=new String[] { CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.DATE }; private SimpleCursorAdapter adapter=null; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + protected String[] getDesiredPermissions() { + return(PERMS); + } + @Override + protected void onPermissionDenied() { + Toast + .makeText(this, R.string.msg_no_perm, Toast.LENGTH_LONG) + .show(); + finish(); + } + + @Override + public void onReady() { adapter= new SimpleCursorAdapter(this, R.layout.row, null, new String[] { CallLog.Calls.NUMBER, CallLog.Calls.DATE }, new int[] { diff --git a/Contacts/CallLog/app/src/main/res/values-v21/styles.xml b/Contacts/CallLog/app/src/main/res/values-v21/styles.xml new file mode 100644 index 000000000..730d6221b --- /dev/null +++ b/Contacts/CallLog/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Contacts/CallLog/app/src/main/res/values/colors.xml b/Contacts/CallLog/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..73b8bbd1a --- /dev/null +++ b/Contacts/CallLog/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3f51b5 + #1a237e + #ffee58 + \ No newline at end of file diff --git a/Contacts/CallLog/app/src/main/res/values/strings.xml b/Contacts/CallLog/app/src/main/res/values/strings.xml index 44bdd2061..c50621664 100644 --- a/Contacts/CallLog/app/src/main/res/values/strings.xml +++ b/Contacts/CallLog/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ CallLog Consumer - + Sorry! This demo cannot run without permission! \ No newline at end of file diff --git a/Contacts/CallLog/app/src/main/res/values/styles.xml b/Contacts/CallLog/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..5aa425476 --- /dev/null +++ b/Contacts/CallLog/app/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Contacts/CallLog/build.gradle b/Contacts/CallLog/build.gradle index ea98e44f3..a0725be17 100644 --- a/Contacts/CallLog/build.gradle +++ b/Contacts/CallLog/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,5 +15,6 @@ buildscript { allprojects { repositories { jcenter() + maven { url 'https://maven.google.com' } } } diff --git a/Contacts/Inserter/app/build.gradle b/Contacts/Inserter/app/build.gradle index 10680dd0d..04aa29d8b 100644 --- a/Contacts/Inserter/app/build.gradle +++ b/Contacts/Inserter/app/build.gradle @@ -1,6 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 - buildToolsVersion "25.0.3" + compileSdkVersion 26 + buildToolsVersion "26.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 26 + } } diff --git a/Contacts/Inserter/app/src/main/AndroidManifest.xml b/Contacts/Inserter/app/src/main/AndroidManifest.xml index 6b21394dc..ae85cab57 100644 --- a/Contacts/Inserter/app/src/main/AndroidManifest.xml +++ b/Contacts/Inserter/app/src/main/AndroidManifest.xml @@ -4,16 +4,10 @@ android:versionCode="1" android:versionName="1.0"> - - + android:label="@string/app_name" + android:theme="@style/Theme.Apptheme"> diff --git a/Contacts/Inserter/app/src/main/res/values-v21/styles.xml b/Contacts/Inserter/app/src/main/res/values-v21/styles.xml new file mode 100644 index 000000000..730d6221b --- /dev/null +++ b/Contacts/Inserter/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Contacts/Inserter/app/src/main/res/values/colors.xml b/Contacts/Inserter/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..73b8bbd1a --- /dev/null +++ b/Contacts/Inserter/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3f51b5 + #1a237e + #ffee58 + \ No newline at end of file diff --git a/Contacts/Inserter/app/src/main/res/values/styles.xml b/Contacts/Inserter/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..5aa425476 --- /dev/null +++ b/Contacts/Inserter/app/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Contacts/Inserter/build.gradle b/Contacts/Inserter/build.gradle index ea98e44f3..596601301 100644 --- a/Contacts/Inserter/build.gradle +++ b/Contacts/Inserter/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Contacts/Spinners/app/build.gradle b/Contacts/Spinners/app/build.gradle index 10680dd0d..f2b430939 100644 --- a/Contacts/Spinners/app/build.gradle +++ b/Contacts/Spinners/app/build.gradle @@ -1,6 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 - buildToolsVersion "25.0.3" + compileSdkVersion 26 + buildToolsVersion "26.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 26 + } } + +dependencies { + compile 'com.android.support:support-compat:26.0.0' +} \ No newline at end of file diff --git a/Contacts/Spinners/app/src/main/AndroidManifest.xml b/Contacts/Spinners/app/src/main/AndroidManifest.xml index 7c749c570..a7288bc93 100644 --- a/Contacts/Spinners/app/src/main/AndroidManifest.xml +++ b/Contacts/Spinners/app/src/main/AndroidManifest.xml @@ -1,17 +1,21 @@ - + - + - - - - - - + + - - + + diff --git a/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/AbstractPermissionActivity.java b/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/AbstractPermissionActivity.java new file mode 100644 index 000000000..de35136bb --- /dev/null +++ b/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/AbstractPermissionActivity.java @@ -0,0 +1,105 @@ +/*** + Copyright (c) 2015-2016 CommonsWare, LLC + 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. + + Covered in detail in the book _The Busy Coder's Guide to Android Development_ + https://commonsware.com/Android + */ + +package com.commonsware.android.contacts.spinners; + +import android.app.ListActivity; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import java.util.ArrayList; + +abstract public class AbstractPermissionActivity + extends ListActivity { + abstract protected String[] getDesiredPermissions(); + abstract protected void onPermissionDenied(); + abstract protected void onReady(); + + private static final int REQUEST_PERMISSION=61125; + private static final String STATE_IN_PERMISSION="inPermission"; + private boolean isInPermission=false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState!=null) { + isInPermission= + savedInstanceState.getBoolean(STATE_IN_PERMISSION, false); + } + + if (hasAllPermissions(getDesiredPermissions())) { + onReady(); + } + else if (!isInPermission) { + isInPermission=true; + + ActivityCompat + .requestPermissions(this, + netPermissions(getDesiredPermissions()), + REQUEST_PERMISSION); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String[] permissions, + int[] grantResults) { + isInPermission=false; + + if (requestCode==REQUEST_PERMISSION) { + if (hasAllPermissions(getDesiredPermissions())) { + onReady(); + } + else { + onPermissionDenied(); + } + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putBoolean(STATE_IN_PERMISSION, isInPermission); + } + + private boolean hasAllPermissions(String[] perms) { + for (String perm : perms) { + if (!hasPermission(perm)) { + return(false); + } + } + + return(true); + } + + private boolean hasPermission(String perm) { + return(ContextCompat.checkSelfPermission(this, perm)== + PackageManager.PERMISSION_GRANTED); + } + + private String[] netPermissions(String[] wanted) { + ArrayList result=new ArrayList(); + + for (String perm : wanted) { + if (!hasPermission(perm)) { + result.add(perm); + } + } + + return(result.toArray(new String[result.size()])); + } +} diff --git a/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/ContactSpinners.java b/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/ContactSpinners.java index 222f48e79..227e0ac9c 100644 --- a/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/ContactSpinners.java +++ b/Contacts/Spinners/app/src/main/java/com/commonsware/android/contacts/spinners/ContactSpinners.java @@ -14,7 +14,7 @@ package com.commonsware.android.contacts.spinners; -import android.app.ListActivity; +import android.Manifest; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; @@ -25,13 +25,14 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter; import android.widget.Spinner; +import android.widget.Toast; -public class ContactSpinners extends ListActivity implements +public class ContactSpinners extends AbstractPermissionActivity implements LoaderManager.LoaderCallbacks, AdapterView.OnItemSelectedListener { + private static final String[] PERMS={Manifest.permission.READ_CONTACTS}; private static final int LOADER_NAMES=0; private static final int LOADER_NAMES_NUMBERS=1; private static final String[] PROJECTION_NAMES=new String[] { @@ -68,8 +69,20 @@ public class ContactSpinners extends ListActivity implements }; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + protected String[] getDesiredPermissions() { + return(PERMS); + } + + @Override + protected void onPermissionDenied() { + Toast + .makeText(this, R.string.msg_no_perm, Toast.LENGTH_LONG) + .show(); + finish(); + } + + @Override + public void onReady() { setContentView(R.layout.main); Spinner spin=(Spinner)findViewById(R.id.spinner); diff --git a/Contacts/Spinners/app/src/main/res/values-v21/styles.xml b/Contacts/Spinners/app/src/main/res/values-v21/styles.xml new file mode 100644 index 000000000..730d6221b --- /dev/null +++ b/Contacts/Spinners/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Contacts/Spinners/app/src/main/res/values/colors.xml b/Contacts/Spinners/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..73b8bbd1a --- /dev/null +++ b/Contacts/Spinners/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3f51b5 + #1a237e + #ffee58 + \ No newline at end of file diff --git a/Contacts/Spinners/app/src/main/res/values/strings.xml b/Contacts/Spinners/app/src/main/res/values/strings.xml index 773655058..686d38ef8 100644 --- a/Contacts/Spinners/app/src/main/res/values/strings.xml +++ b/Contacts/Spinners/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ - ContactSpinners + Contact Spinners + Sorry! This demo cannot run without permission! \ No newline at end of file diff --git a/Contacts/Spinners/app/src/main/res/values/styles.xml b/Contacts/Spinners/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..5aa425476 --- /dev/null +++ b/Contacts/Spinners/app/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Contacts/Spinners/build.gradle b/Contacts/Spinners/build.gradle index ea98e44f3..a0725be17 100644 --- a/Contacts/Spinners/build.gradle +++ b/Contacts/Spinners/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.3.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,5 +15,6 @@ buildscript { allprojects { repositories { jcenter() + maven { url 'https://maven.google.com' } } }