diff --git a/samples/OptionalPermissions/Android.mk b/samples/OptionalPermissions/Android.mk new file mode 100644 index 000000000..758a7ea41 --- /dev/null +++ b/samples/OptionalPermissions/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := samples tests + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := OptionalPermissions + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) diff --git a/samples/OptionalPermissions/AndroidManifest.xml b/samples/OptionalPermissions/AndroidManifest.xml new file mode 100644 index 000000000..668de5b36 --- /dev/null +++ b/samples/OptionalPermissions/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/OptionalPermissions/res/values/strings.xml b/samples/OptionalPermissions/res/values/strings.xml new file mode 100644 index 000000000..66889475a --- /dev/null +++ b/samples/OptionalPermissions/res/values/strings.xml @@ -0,0 +1,68 @@ + + + + + + + Permissions Demo + + Vibrate + Internet + Phone state & identity + All + + + Vibrate + Vibrate Android + Prompt + Prompt for the VIBRATE permission + Status + + Get time from google.com + Get current time from https://www.google.com + Prompt + Prompt for the INTERNET permission + Status + + Show phone number + Read this Android\'s phone number + Prompt + Prompt for the READ_PHONE_STATE permission + Status + + Prompt + Prompt for all of the above permissions + + Granted + Denied + Revoked + + + Vibrate feature not available on this Android + + + + Telephony feature not available on this Android + + + Exception caught + + Getting current time from Google… + + GRANTED + NOT GRANTED + + diff --git a/samples/OptionalPermissions/res/xml/preference_headers.xml b/samples/OptionalPermissions/res/xml/preference_headers.xml new file mode 100644 index 000000000..4f9a64d71 --- /dev/null +++ b/samples/OptionalPermissions/res/xml/preference_headers.xml @@ -0,0 +1,19 @@ + + + +
+ diff --git a/samples/OptionalPermissions/res/xml/preferences.xml b/samples/OptionalPermissions/res/xml/preferences.xml new file mode 100644 index 000000000..7fe6dd334 --- /dev/null +++ b/samples/OptionalPermissions/res/xml/preferences.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/OptionalPermissions/src/com/example/android/permdemo/MainActivity.java b/samples/OptionalPermissions/src/com/example/android/permdemo/MainActivity.java new file mode 100644 index 000000000..c68689ccc --- /dev/null +++ b/samples/OptionalPermissions/src/com/example/android/permdemo/MainActivity.java @@ -0,0 +1,53 @@ +/* +** Copyright 2013, 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.permdemo; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.preference.PreferenceActivity; + +public class MainActivity extends Activity { + private static final int REQUEST_CODE_PREFS = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + startActivityForResult( + new Intent(this, PrefsActivity.class) + .putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true) + .putExtra( + PreferenceActivity.EXTRA_SHOW_FRAGMENT, + PrefsFragment.class.getName()), + REQUEST_CODE_PREFS); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PREFS: + // The PrefsActivity we launched has finished. + finish(); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + +} diff --git a/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsActivity.java b/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsActivity.java new file mode 100644 index 000000000..eff4d28ae --- /dev/null +++ b/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsActivity.java @@ -0,0 +1,37 @@ +/* +** Copyright 2013, 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.permdemo; + +import android.os.Bundle; +import android.preference.PreferenceActivity; + +import java.util.List; + +public class PrefsActivity extends PreferenceActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * Populate the activity with the top-level headers. + */ + @Override + public void onBuildHeaders(List
target) { + loadHeadersFromResource(R.xml.preference_headers, target); + } +} diff --git a/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsFragment.java b/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsFragment.java new file mode 100644 index 000000000..d37f6fae1 --- /dev/null +++ b/samples/OptionalPermissions/src/com/example/android/permdemo/PrefsFragment.java @@ -0,0 +1,331 @@ +/* +** Copyright 2013, 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.permdemo; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.Bundle; +import android.os.Vibrator; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.telephony.TelephonyManager; +import android.text.format.DateFormat; +import android.widget.Toast; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Date; + +public class PrefsFragment extends PreferenceFragment { + + private static final int REQUEST_CODE_PROMPT_PERMISSIONS = 1; + + private static final String[] ALL_PERMISSIONS = { + android.Manifest.permission.VIBRATE, + android.Manifest.permission.INTERNET, + android.Manifest.permission.READ_PHONE_STATE, + }; + + private Preference mVibrateStatusPreference; + private Preference mInternetStatusPreference; + private Preference mPhoneStateStatusPreference; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preferences); + + findPreference("vibrate_do").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onVibrateActionClicked(); + return false; + } + }); + findPreference("vibrate_prompt").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onPromptPermissionsClicked(android.Manifest.permission.VIBRATE); + return false; + } + }); + + findPreference("internet_do").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onInternetActionClicked(); + return false; + } + }); + findPreference("internet_prompt").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onPromptPermissionsClicked(android.Manifest.permission.INTERNET); + return false; + } + }); + + findPreference("phone_state_do").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onReadPhoneStateActionClicked(); + return false; + } + }); + findPreference("phone_state_prompt").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onPromptPermissionsClicked(android.Manifest.permission.READ_PHONE_STATE); + return false; + } + }); + + findPreference("all_prompt").setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + onPromptPermissionsClicked(ALL_PERMISSIONS); + return false; + } + }); + + mVibrateStatusPreference = findPreference("vibrate_status"); + mInternetStatusPreference = findPreference("internet_status"); + mPhoneStateStatusPreference = findPreference("phone_state_status"); + refreshPermissionsStatus(); + } + + private void onVibrateActionClicked() { + Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE); + if (vibrator == null) { + Toast.makeText( + getActivity(), + R.string.vibrate_feature_not_available, + Toast.LENGTH_LONG).show(); + return; + } + try { + vibrator.vibrate(300); + } catch (Exception e) { + displayException(e); + } + } + + private void onInternetActionClicked() { + new AsyncTask() { + private ProgressDialog mProgressDialog; + + @Override + protected void onPreExecute() { + mProgressDialog = ProgressDialog.show( + getActivity(), + null, + getString(R.string.get_time_progress_dialog_message), + true, // indeterminate progress + true, // cancelable + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + // Cancel this AsyncTask + cancel(true); + } + }); + super.onPreExecute(); + } + + @Override + protected Object doInBackground(Void... params) { + HttpURLConnection connection = null; + Date date; + try { + connection = + (HttpURLConnection) new URL("https://www.google.com").openConnection(); + connection.setDefaultUseCaches(false); + connection.setInstanceFollowRedirects(false); + connection.setRequestMethod("HEAD"); + + // Force the request to fail if there's no Internet connectivity + connection.getResponseCode(); + + long timeMillis = connection.getDate(); + if (timeMillis == 0) { + throw new IOException("No time returned by the server"); + } + date = new Date(timeMillis); + } catch (Throwable t) { + t.printStackTrace(); + return t; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + return date; + } + + @Override + protected void onPostExecute(Object result) { + if (getActivity().isFinishing()) { + // The Activity is already finishing/finished -- no need to display the date + return; + } + + dismissProgressDialog(); + + if (result instanceof Throwable) { + displayException((Throwable) result); + return; + } + + Date date = (Date) result; + String formattedDate = DateFormat.getDateFormat(getActivity()).format(date) + + " " + DateFormat.getTimeFormat(getActivity()).format(date); + new AlertDialog.Builder(getActivity()) + .setMessage(formattedDate) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + @Override + protected void onCancelled() { + dismissProgressDialog(); + super.onCancelled(); + } + + private void dismissProgressDialog() { + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + + }.execute((Void[]) null); + } + + private void onReadPhoneStateActionClicked() { + TelephonyManager telephonyManager = + (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager == null) { + Toast.makeText( + getActivity(), + R.string.telephony_feature_not_available, + Toast.LENGTH_LONG).show(); + return; + } + + String phoneNumber; + try { + phoneNumber = telephonyManager.getLine1Number(); + } catch (Exception e) { + displayException(e); + return; + } + + new AlertDialog.Builder(getActivity()) + .setMessage("Phone number: " + phoneNumber) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + private void onPromptPermissionsClicked(String... permissions) { + Intent i = getActivity().getApplication() + .getPackageManager().buildPermissionRequestIntent(permissions); + startActivityForResult(i, REQUEST_CODE_PROMPT_PERMISSIONS); + } + + private void onGrantPermissionsResult(boolean granted) { + if (granted) { + displayPermissionsGrantApprovedToast(); + } else { + displayPermissionsGrantDeniedToast(); + } + refreshPermissionsStatus(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PROMPT_PERMISSIONS: + onGrantPermissionsResult(resultCode == Activity.RESULT_OK); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + + private void displayException(Throwable exception) { + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.exception_dialog_title) + .setMessage(exception.toString()) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + private void displayPermissionsGrantApprovedToast() { + Toast.makeText( + getActivity(), + R.string.toast_permission_grant_approved, + Toast.LENGTH_SHORT).show(); + } + + private void displayPermissionsGrantDeniedToast() { + Toast.makeText( + getActivity(), + R.string.toast_permission_grant_denied, + Toast.LENGTH_SHORT).show(); + } + + private void refreshPermissionsStatus() { + mVibrateStatusPreference.setSummary( + hasPermission(android.Manifest.permission.VIBRATE) + ? R.string.permission_status_granted : R.string.permission_status_not_granted); + mInternetStatusPreference.setSummary( + hasPermission(android.Manifest.permission.INTERNET) + ? R.string.permission_status_granted : R.string.permission_status_not_granted); + mPhoneStateStatusPreference.setSummary( + hasPermission( + android.Manifest.permission.READ_PHONE_STATE) + ? R.string.permission_status_granted : R.string.permission_status_not_granted); + } + + private boolean hasPermission(String permission) { + long identity = Binder.clearCallingIdentity(); + try { + return getActivity().getApplicationContext() + .checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; + } finally { + Binder.restoreCallingIdentity(identity); + } + } +}