Commit ef9f915c4082368868fa20393c16b24dc4c111ea

Authored by 朱显松
1 parent 7c5bdd74

add permissions test

app/build.gradle
@@ -46,10 +46,17 @@ dependencies { @@ -46,10 +46,17 @@ dependencies {
46 }) 46 })
47 compile 'com.android.support:appcompat-v7:26.+' 47 compile 'com.android.support:appcompat-v7:26.+'
48 compile 'com.android.support:recyclerview-v7:26.+' 48 compile 'com.android.support:recyclerview-v7:26.+'
  49 + compile 'com.android.support:support-v13:26.+'
49 compile 'com.android.support.constraint:constraint-layout:1.0.2' 50 compile 'com.android.support.constraint:constraint-layout:1.0.2'
50 testCompile 'junit:junit:4.12' 51 testCompile 'junit:junit:4.12'
51 // TODO 本地酷使用说明 代码仓库标识 52 // TODO 本地酷使用说明 代码仓库标识
52 // compile 'com.cnlive.libs:base:1.0.0@aar' 53 // compile 'com.cnlive.libs:base:1.0.0@aar'
53 // compile 'com.cnlive:cnlive:0.2.0' 54 // compile 'com.cnlive:cnlive:0.2.0'
  55 +
  56 + compile("com.github.hotchemi:permissionsdispatcher:2.4.0") {
  57 + // if you don't use android.app.Fragment you can exclude support for them
  58 + exclude module: "support-v13"
  59 + }
  60 + annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:2.4.0"
54 compile project(':cnlive') 61 compile project(':cnlive')
55 } 62 }
app/src/main/AndroidManifest.xml
@@ -2,6 +2,10 @@ @@ -2,6 +2,10 @@
2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.cnlive.libs.demo"> 3 package="com.cnlive.libs.demo">
4 4
  5 + <uses-permission android:name="android.permission.CAMERA"/>
  6 + <uses-permission android:name="android.permission.READ_CONTACTS" />
  7 + <uses-permission android:name="android.permission.WRITE_CONTACTS" />
  8 +
5 <application 9 <application
6 android:name=".App" 10 android:name=".App"
7 android:allowBackup="true" 11 android:allowBackup="true"
@@ -23,6 +27,7 @@ @@ -23,6 +27,7 @@
23 </intent-filter> 27 </intent-filter>
24 </activity> 28 </activity>
25 <activity android:name=".mvptest.ui.activity.LceTestActivity"></activity> 29 <activity android:name=".mvptest.ui.activity.LceTestActivity"></activity>
  30 + <activity android:name=".sample.permissions.PermissionsActivity"></activity>
26 </application> 31 </application>
27 32
28 </manifest> 33 </manifest>
29 \ No newline at end of file 34 \ No newline at end of file
app/src/main/java/com/cnlive/libs/demo/MainActivity.java
@@ -2,9 +2,11 @@ package com.cnlive.libs.demo; @@ -2,9 +2,11 @@ package com.cnlive.libs.demo;
2 2
3 import android.content.Intent; 3 import android.content.Intent;
4 import android.os.Bundle; 4 import android.os.Bundle;
  5 +import android.support.annotation.NonNull;
5 import android.support.v7.app.AppCompatActivity; 6 import android.support.v7.app.AppCompatActivity;
6 7
7 import com.cnlive.libs.demo.mvptest.ui.activity.LceTestActivity; 8 import com.cnlive.libs.demo.mvptest.ui.activity.LceTestActivity;
  9 +import com.cnlive.libs.demo.sample.permissions.PermissionsActivity;
8 10
9 public class MainActivity extends AppCompatActivity { 11 public class MainActivity extends AppCompatActivity {
10 12
@@ -20,5 +22,13 @@ public class MainActivity extends AppCompatActivity { @@ -20,5 +22,13 @@ public class MainActivity extends AppCompatActivity {
20 Java8Test.test(this); 22 Java8Test.test(this);
21 23
22 findViewById(R.id.lce_test).setOnClickListener(view -> startActivity(new Intent(MainActivity.this, LceTestActivity.class))); 24 findViewById(R.id.lce_test).setOnClickListener(view -> startActivity(new Intent(MainActivity.this, LceTestActivity.class)));
  25 + findViewById(R.id.permissions_test).setOnClickListener(view -> startActivity(new Intent(MainActivity.this, PermissionsActivity.class)));
  26 +
23 } 27 }
  28 +
  29 + @Override
  30 + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  31 + super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  32 + }
  33 +
24 } 34 }
app/src/main/java/com/cnlive/libs/demo/mvptest/model/ExpertLibraryListBean.java
1 package com.cnlive.libs.demo.mvptest.model; 1 package com.cnlive.libs.demo.mvptest.model;
2 2
3 3
4 -import android.os.Parcel;  
5 -import android.os.Parcelable;  
6 -  
7 -import com.cnlive.libs.demo.BR;  
8 import com.cnlive.libs.demo.data.model.BaseInfo; 4 import com.cnlive.libs.demo.data.model.BaseInfo;
9 5
10 import java.util.List; 6 import java.util.List;
app/src/main/java/com/cnlive/libs/demo/sample/permissions/PermissionsActivity.java 0 → 100644
  1 +package com.cnlive.libs.demo.sample.permissions;
  2 +
  3 +import android.Manifest;
  4 +import android.content.DialogInterface;
  5 +import android.os.Bundle;
  6 +import android.support.annotation.NonNull;
  7 +import android.support.annotation.StringRes;
  8 +import android.support.v7.app.AlertDialog;
  9 +import android.support.v7.app.AppCompatActivity;
  10 +import android.view.View;
  11 +import android.widget.Toast;
  12 +
  13 +import com.cnlive.libs.demo.R;
  14 +import com.cnlive.libs.demo.sample.permissions.camera.CameraPreviewFragment;
  15 +import com.cnlive.libs.demo.sample.permissions.contacts.ContactsFragment;
  16 +
  17 +import permissions.dispatcher.NeedsPermission;
  18 +import permissions.dispatcher.OnNeverAskAgain;
  19 +import permissions.dispatcher.OnPermissionDenied;
  20 +import permissions.dispatcher.OnShowRationale;
  21 +import permissions.dispatcher.PermissionRequest;
  22 +import permissions.dispatcher.RuntimePermissions;
  23 +
  24 +@RuntimePermissions
  25 +public class PermissionsActivity extends AppCompatActivity implements View.OnClickListener {
  26 +
  27 + @Override
  28 + protected void onCreate(Bundle savedInstanceState) {
  29 + super.onCreate(savedInstanceState);
  30 + setContentView(R.layout.activity_permissions);
  31 + findViewById(R.id.button_camera).setOnClickListener(this);
  32 + findViewById(R.id.button_contacts).setOnClickListener(this);
  33 + }
  34 +
  35 + @Override
  36 + public void onClick(@NonNull View v) {
  37 + switch (v.getId()) {
  38 + case R.id.button_camera:
  39 + // NOTE: delegate the permission handling to generated method
  40 + PermissionsActivityPermissionsDispatcher.showCameraWithCheck(this);
  41 + break;
  42 + case R.id.button_contacts:
  43 + // NOTE: delegate the permission handling to generated method
  44 + PermissionsActivityPermissionsDispatcher.showContactsWithCheck(this);
  45 + break;
  46 + }
  47 + }
  48 +
  49 + @Override
  50 + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  51 + super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  52 + // NOTE: delegate the permission handling to generated method
  53 + PermissionsActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
  54 + }
  55 +
  56 +
  57 +
  58 + @NeedsPermission({Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS })
  59 + void showContacts() {
  60 + // NOTE: Perform action that requires the permission.
  61 + // If this is run by PermissionsDispatcher, the permission will have been granted
  62 + getSupportFragmentManager().beginTransaction()
  63 + .replace(R.id.sample_content_fragment, ContactsFragment.newInstance())
  64 + .addToBackStack("contacts")
  65 + .commitAllowingStateLoss();
  66 + }
  67 +
  68 +
  69 + @OnShowRationale({Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS})
  70 + void showRationaleForContact(PermissionRequest request) {
  71 + // NOTE: Show a rationale to explain why the permission is needed, e.g. with a dialog.
  72 + // Call proceed() or cancel() on the provided PermissionRequest to continue or abort
  73 + showRationaleDialog(R.string.permission_contacts_rationale, request);
  74 + }
  75 +
  76 +
  77 +
  78 + @OnPermissionDenied({Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS})
  79 + void onContactDenied() {
  80 + // NOTE: Deal with a denied permission, e.g. by showing specific UI
  81 + // or disabling certain functionality
  82 + Toast.makeText(this, "拒绝", Toast.LENGTH_SHORT).show();
  83 + }
  84 +
  85 + @OnNeverAskAgain({Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS})
  86 + void onContactNeverAskAgain() {
  87 + Toast.makeText(this, "永久拒绝", Toast.LENGTH_SHORT).show();
  88 + }
  89 +
  90 + public void onBackClick(View view) {
  91 + getSupportFragmentManager().popBackStack();
  92 + }
  93 +
  94 + private void showRationaleDialog(@StringRes int messageResId, final PermissionRequest request) {
  95 + new AlertDialog.Builder(this)
  96 + .setPositiveButton(R.string.button_allow, new DialogInterface.OnClickListener() {
  97 + @Override
  98 + public void onClick(@NonNull DialogInterface dialog, int which) {
  99 + request.proceed();
  100 + }
  101 + })
  102 + .setNegativeButton(R.string.button_deny, new DialogInterface.OnClickListener() {
  103 + @Override
  104 + public void onClick(@NonNull DialogInterface dialog, int which) {
  105 + request.cancel();
  106 + }
  107 + })
  108 + .setCancelable(false)
  109 + .setMessage(messageResId)
  110 + .show();
  111 + }
  112 +
  113 +
  114 +
  115 + @NeedsPermission(Manifest.permission.CAMERA)
  116 + void showCamera() {
  117 + // NOTE: Perform action that requires the permission. If this is run by PermissionsDispatcher, the permission will have been granted
  118 + getSupportFragmentManager().beginTransaction()
  119 + .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
  120 + .addToBackStack("camera")
  121 + .commitAllowingStateLoss();
  122 +// Log.e("DemoView","成功````");
  123 + }
  124 +
  125 + @OnShowRationale(Manifest.permission.CAMERA)
  126 + void showRationaleForCamera(PermissionRequest request) {
  127 + // NOTE: Show a rationale to explain why the permission is needed, e.g. with a dialog.
  128 + // Call proceed() or cancel() on the provided PermissionRequest to continue or abort
  129 + showRationaleDialog(R.string.permission_camera_rationale, request);
  130 + }
  131 +
  132 + @OnPermissionDenied(Manifest.permission.CAMERA)
  133 + void onCameraDenied() {
  134 + // NOTE: Deal with a denied permission, e.g. by showing specific UI
  135 + // or disabling certain functionality
  136 + Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
  137 + }
  138 +
  139 + @OnNeverAskAgain(Manifest.permission.CAMERA)
  140 + void onCameraNeverAskAgain() {
  141 + Toast.makeText(this, R.string.permission_camera_never_askagain, Toast.LENGTH_SHORT).show();
  142 + }
  143 +
  144 +}
app/src/main/java/com/cnlive/libs/demo/sample/permissions/camera/CameraPreview.java 0 → 100644
  1 +/*
  2 +* Copyright 2015 The Android Open Source Project
  3 +*
  4 +* Licensed under the Apache License, Version 2.0 (the "License");
  5 +* you may not use this file except in compliance with the License.
  6 +* You may obtain a copy of the License at
  7 +*
  8 +* http://www.apache.org/licenses/LICENSE-2.0
  9 +*
  10 +* Unless required by applicable law or agreed to in writing, software
  11 +* distributed under the License is distributed on an "AS IS" BASIS,
  12 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +* See the License for the specific language governing permissions and
  14 +* limitations under the License.
  15 +*/
  16 +
  17 +package com.cnlive.libs.demo.sample.permissions.camera;
  18 +
  19 +import android.content.Context;
  20 +import android.hardware.Camera;
  21 +import android.util.Log;
  22 +import android.view.Surface;
  23 +import android.view.SurfaceHolder;
  24 +import android.view.SurfaceView;
  25 +
  26 +import java.io.IOException;
  27 +
  28 +/**
  29 + * Camera preview that displays a {@link Camera}.
  30 + *
  31 + * Handles basic lifecycle methods to display and stop the preview.
  32 + * <p>
  33 + * Implementation is based directly on the documentation at
  34 + * http://developer.android.com/guide/topics/media/camera.html
  35 + */
  36 +public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
  37 +
  38 + private static final String TAG = "CameraPreview";
  39 + private SurfaceHolder mHolder;
  40 + private Camera mCamera;
  41 + private Camera.CameraInfo mCameraInfo;
  42 + private int mDisplayOrientation;
  43 +
  44 + public CameraPreview(Context context, Camera camera, Camera.CameraInfo cameraInfo,
  45 + int displayOrientation) {
  46 + super(context);
  47 +
  48 + // Do not initialise if no camera has been set
  49 + if (camera == null || cameraInfo == null) {
  50 + return;
  51 + }
  52 + setCamera(camera, cameraInfo, displayOrientation);
  53 +
  54 + // Install a SurfaceHolder.Callback so we get notified when the
  55 + // underlying surface is created and destroyed.
  56 + mHolder = getHolder();
  57 + mHolder.addCallback(this);
  58 + }
  59 +
  60 + public void surfaceCreated(SurfaceHolder holder) {
  61 + // The Surface has been created, now tell the camera where to draw the preview.
  62 + try {
  63 + mCamera.setPreviewDisplay(holder);
  64 + mCamera.startPreview();
  65 + Log.d(TAG, "Camera preview started.");
  66 + } catch (IOException e) {
  67 + Log.d(TAG, "Error setting camera preview: " + e.getMessage());
  68 + }
  69 + }
  70 +
  71 + public void surfaceDestroyed(SurfaceHolder holder) {
  72 + // empty. Take care of releasing the Camera preview in your activity.
  73 + }
  74 +
  75 + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
  76 + // If your preview can change or rotate, take care of those events here.
  77 + // Make sure to stop the preview before resizing or reformatting it.
  78 +
  79 + if (mHolder.getSurface() == null) {
  80 + // preview surface does not exist
  81 + Log.d(TAG, "Preview surface does not exist");
  82 + return;
  83 + }
  84 +
  85 + // stop preview before making changes
  86 + try {
  87 + mCamera.stopPreview();
  88 + Log.d(TAG, "Preview stopped.");
  89 + } catch (Exception e) {
  90 + // ignore: tried to stop a non-existent preview
  91 + Log.d(TAG, "Error starting camera preview: " + e.getMessage());
  92 + }
  93 +
  94 + int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);
  95 + mCamera.setDisplayOrientation(orientation);
  96 +
  97 + try {
  98 + mCamera.setPreviewDisplay(mHolder);
  99 + mCamera.startPreview();
  100 + Log.d(TAG, "Camera preview started.");
  101 + } catch (Exception e) {
  102 + Log.d(TAG, "Error starting camera preview: " + e.getMessage());
  103 + }
  104 + }
  105 +
  106 + /**
  107 + * Calculate the correct orientation for a {@link Camera} preview that is displayed on screen.
  108 + *
  109 + * Implementation is based on the sample code provided in
  110 + * {@link Camera#setDisplayOrientation(int)}.
  111 + */
  112 + public static int calculatePreviewOrientation(Camera.CameraInfo info, int rotation) {
  113 + int degrees = 0;
  114 +
  115 + switch (rotation) {
  116 + case Surface.ROTATION_0:
  117 + degrees = 0;
  118 + break;
  119 + case Surface.ROTATION_90:
  120 + degrees = 90;
  121 + break;
  122 + case Surface.ROTATION_180:
  123 + degrees = 180;
  124 + break;
  125 + case Surface.ROTATION_270:
  126 + degrees = 270;
  127 + break;
  128 + }
  129 +
  130 + int result;
  131 + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  132 + result = (info.orientation + degrees) % 360;
  133 + result = (360 - result) % 360; // compensate the mirror
  134 + } else { // back-facing
  135 + result = (info.orientation - degrees + 360) % 360;
  136 + }
  137 +
  138 + return result;
  139 + }
  140 +
  141 + public void setCamera(Camera camera, Camera.CameraInfo cameraInfo, int displayOrientation) {
  142 + // Do not reinitialise if no camera has been set
  143 + if (camera == null || cameraInfo == null) {
  144 + return;
  145 + }
  146 + mCamera = camera;
  147 + mCameraInfo = cameraInfo;
  148 + mDisplayOrientation = displayOrientation;
  149 + }
  150 +}
app/src/main/java/com/cnlive/libs/demo/sample/permissions/camera/CameraPreviewFragment.java 0 → 100644
  1 +/*
  2 +* Copyright 2015 The Android Open Source Project
  3 +*
  4 +* Licensed under the Apache License, Version 2.0 (the "License");
  5 +* you may not use this file except in compliance with the License.
  6 +* You may obtain a copy of the License at
  7 +*
  8 +* http://www.apache.org/licenses/LICENSE-2.0
  9 +*
  10 +* Unless required by applicable law or agreed to in writing, software
  11 +* distributed under the License is distributed on an "AS IS" BASIS,
  12 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +* See the License for the specific language governing permissions and
  14 +* limitations under the License.
  15 +*/
  16 +
  17 +package com.cnlive.libs.demo.sample.permissions.camera;
  18 +
  19 +import android.hardware.Camera;
  20 +import android.os.Bundle;
  21 +import android.support.annotation.Nullable;
  22 +import android.support.v4.app.Fragment;
  23 +import android.util.Log;
  24 +import android.view.LayoutInflater;
  25 +import android.view.View;
  26 +import android.view.ViewGroup;
  27 +import android.widget.FrameLayout;
  28 +
  29 +import com.cnlive.libs.demo.R;
  30 +
  31 +
  32 +/**
  33 + * Displays a {@link CameraPreview} of the first {@link Camera}.
  34 + * An error message is displayed if the Camera is not available.
  35 + * <p>
  36 + * This Fragment is only used to illustrate that access to the Camera API has been granted (or
  37 + * denied) as part of the runtime permissions model. It is not relevant for the use of the
  38 + * permissions API.
  39 + * <p>
  40 + * Implementation is based directly on the documentation at
  41 + * http://developer.android.com/guide/topics/media/camera.html
  42 + */
  43 +public class CameraPreviewFragment extends Fragment {
  44 +
  45 + private static final String TAG = "CameraPreview";
  46 +
  47 + /**
  48 + * Id of the camera to access. 0 is the first camera.
  49 + */
  50 + private static final int CAMERA_ID = 0;
  51 +
  52 + private CameraPreview mPreview;
  53 + private Camera mCamera;
  54 +
  55 + public static CameraPreviewFragment newInstance() {
  56 + return new CameraPreviewFragment();
  57 + }
  58 +
  59 + @Override
  60 + public View onCreateView(LayoutInflater inflater, ViewGroup container,
  61 + Bundle savedInstanceState) {
  62 + return inflater.inflate(R.layout.fragment_camera, null);
  63 + }
  64 +
  65 + @Override
  66 + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
  67 + super.onViewCreated(view, savedInstanceState);
  68 + initCamera();
  69 + }
  70 +
  71 + private void initCamera() {
  72 + mCamera = getCameraInstance(CAMERA_ID);
  73 + Camera.CameraInfo cameraInfo = null;
  74 +
  75 + if (mCamera != null) {
  76 + // Get camera info only if the camera is available
  77 + cameraInfo = new Camera.CameraInfo();
  78 + Camera.getCameraInfo(CAMERA_ID, cameraInfo);
  79 + }
  80 +
  81 + // Get the rotation of the screen to adjust the preview image accordingly.
  82 + final int displayRotation = getActivity().getWindowManager().getDefaultDisplay()
  83 + .getRotation();
  84 +
  85 + if (getView() == null) {
  86 + return;
  87 + }
  88 +
  89 + FrameLayout preview = (FrameLayout) getView().findViewById(R.id.camera_preview);
  90 + preview.removeAllViews();
  91 +
  92 + if (mPreview == null) {
  93 + // Create the Preview view and set it as the content of this Activity.
  94 + mPreview = new CameraPreview(getActivity(), mCamera, cameraInfo, displayRotation);
  95 + } else {
  96 + mPreview.setCamera(mCamera, cameraInfo, displayRotation);
  97 + }
  98 +
  99 + preview.addView(mPreview);
  100 + }
  101 +
  102 + @Override
  103 + public void onResume() {
  104 + super.onResume();
  105 + if (mCamera == null) {
  106 + initCamera();
  107 + }
  108 + }
  109 +
  110 + @Override
  111 + public void onPause() {
  112 + super.onPause();
  113 + // Stop camera access
  114 + releaseCamera();
  115 + }
  116 +
  117 + /** A safe way to get an instance of the Camera object. */
  118 + public static Camera getCameraInstance(int cameraId) {
  119 + Camera c = null;
  120 + try {
  121 + c = Camera.open(cameraId); // attempt to get a Camera instance
  122 + } catch (Exception e) {
  123 + // Camera is not available (in use or does not exist)
  124 + Log.d(TAG, "Camera " + cameraId + " is not available: " + e.getMessage());
  125 + }
  126 + return c; // returns null if camera is unavailable
  127 + }
  128 +
  129 + private void releaseCamera() {
  130 + if (mCamera != null) {
  131 + mCamera.release(); // release the camera for other applications
  132 + mCamera = null;
  133 + }
  134 + }
  135 +}
app/src/main/java/com/cnlive/libs/demo/sample/permissions/contacts/ContactsFragment.java 0 → 100644
  1 +/*
  2 +* Copyright 2015 The Android Open Source Project
  3 +*
  4 +* Licensed under the Apache License, Version 2.0 (the "License");
  5 +* you may not use this file except in compliance with the License.
  6 +* You may obtain a copy of the License at
  7 +*
  8 +* http://www.apache.org/licenses/LICENSE-2.0
  9 +*
  10 +* Unless required by applicable law or agreed to in writing, software
  11 +* distributed under the License is distributed on an "AS IS" BASIS,
  12 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +* See the License for the specific language governing permissions and
  14 +* limitations under the License.
  15 +*/
  16 +
  17 +package com.cnlive.libs.demo.sample.permissions.contacts;
  18 +
  19 +import android.database.Cursor;
  20 +import android.os.Bundle;
  21 +import android.provider.ContactsContract;
  22 +import android.support.annotation.Nullable;
  23 +import android.support.v4.app.Fragment;
  24 +import android.support.v4.app.LoaderManager;
  25 +import android.support.v4.content.CursorLoader;
  26 +import android.support.v4.content.Loader;
  27 +import android.util.Log;
  28 +import android.view.LayoutInflater;
  29 +import android.view.View;
  30 +import android.view.ViewGroup;
  31 +import android.widget.Button;
  32 +import android.widget.TextView;
  33 +
  34 +import com.cnlive.libs.demo.R;
  35 +
  36 +/**
  37 + * Displays the first contact stored on the device and contains an option to add a dummy contact.
  38 + * <p>
  39 + * This Fragment is only used to illustrate that access to the Contacts ContentProvider API has
  40 + * been granted (or denied) as part of the runtime permissions model. It is not relevant for the
  41 + * use
  42 + * of the permissions API.
  43 + * <p>
  44 + * This fragments demonstrates a basic use case for accessing the Contacts Provider. The
  45 + * implementation is based on the training guide available here:
  46 + * https://developer.android.com/training/contacts-provider/retrieve-names.html
  47 + */
  48 +public class ContactsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
  49 +
  50 + private static final String TAG = "Contacts";
  51 + private TextView mMessageText = null;
  52 +
  53 + private static String DUMMY_CONTACT_NAME = "__DUMMY CONTACT from runtime permissions sample";
  54 +
  55 + /**
  56 + * Projection for the content provider query includes the id and primary name of a contact.
  57 + */
  58 + private static final String[] PROJECTION = {ContactsContract.Contacts._ID,
  59 + ContactsContract.Contacts.DISPLAY_NAME_PRIMARY};
  60 + /**
  61 + * Sort order for the query. Sorted by primary name in ascending order.
  62 + */
  63 + private static final String ORDER = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC";
  64 +
  65 +
  66 + /**
  67 + * Creates a new instance of a ContactsFragment.
  68 + */
  69 + public static ContactsFragment newInstance() {
  70 + return new ContactsFragment();
  71 + }
  72 +
  73 +
  74 + @Nullable
  75 + @Override
  76 + public View onCreateView(LayoutInflater inflater, ViewGroup container,
  77 + Bundle savedInstanceState) {
  78 + View rootView = inflater.inflate(R.layout.fragment_contacts, container, false);
  79 +
  80 + mMessageText = (TextView) rootView.findViewById(R.id.contact_message);
  81 +
  82 + // Register a listener to add a dummy contact when a button is clicked.
  83 + Button button = (Button) rootView.findViewById(R.id.contact_add);
  84 + button.setOnClickListener(new View.OnClickListener() {
  85 + @Override
  86 + public void onClick(View view) {
  87 + }
  88 + });
  89 +
  90 + // Register a listener to display the first contact when a button is clicked.
  91 + button = (Button) rootView.findViewById(R.id.contact_load);
  92 + button.setOnClickListener(new View.OnClickListener() {
  93 + @Override
  94 + public void onClick(View view) {
  95 + loadContact();
  96 + }
  97 + });
  98 + return rootView;
  99 + }
  100 +
  101 + /**
  102 + * Restart the Loader to query the Contacts content provider to display the first contact.
  103 + */
  104 + private void loadContact() {
  105 + getLoaderManager().restartLoader(0, null, this);
  106 + }
  107 +
  108 + /**
  109 + * Initialises a new {@link CursorLoader} that queries the {@link ContactsContract}.
  110 + */
  111 + @Override
  112 + public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
  113 + return new CursorLoader(getActivity(), ContactsContract.Contacts.CONTENT_URI, PROJECTION,
  114 + null, null, ORDER);
  115 + }
  116 +
  117 +
  118 + /**
  119 + * Dislays either the name of the first contact or a message.
  120 + */
  121 + @Override
  122 + public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
  123 +
  124 + if (cursor != null) {
  125 + final int totalCount = cursor.getCount();
  126 + if (totalCount > 0) {
  127 + cursor.moveToFirst();
  128 + String name = cursor
  129 + .getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
  130 + mMessageText.setText(
  131 + getResources().getString(R.string.contacts_string, totalCount, name));
  132 + Log.d(TAG, "First contact loaded: " + name);
  133 + Log.d(TAG, "Total number of contacts: " + totalCount);
  134 + Log.d(TAG, "Total number of contacts: " + totalCount);
  135 + } else {
  136 + Log.d(TAG, "List of contacts is empty.");
  137 + mMessageText.setText(R.string.contacts_empty);
  138 + }
  139 + }
  140 + }
  141 +
  142 + @Override
  143 + public void onLoaderReset(Loader<Cursor> loader) {
  144 + mMessageText.setText(R.string.contacts_empty);
  145 + }
  146 +
  147 +
  148 +}
app/src/main/res/layout/activity_main.xml
@@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
10 android:id="@+id/textView" 10 android:id="@+id/textView"
11 android:layout_width="wrap_content" 11 android:layout_width="wrap_content"
12 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
13 - android:text="Hello World!"  
14 android:padding="8dp" 13 android:padding="8dp"
  14 + android:text="Hello World!"
15 app:layout_constraintBottom_toBottomOf="parent" 15 app:layout_constraintBottom_toBottomOf="parent"
16 app:layout_constraintLeft_toLeftOf="parent" 16 app:layout_constraintLeft_toLeftOf="parent"
17 app:layout_constraintRight_toRightOf="parent" 17 app:layout_constraintRight_toRightOf="parent"
@@ -21,12 +21,29 @@ @@ -21,12 +21,29 @@
21 android:id="@+id/lce_test" 21 android:id="@+id/lce_test"
22 android:layout_width="368dp" 22 android:layout_width="368dp"
23 android:layout_height="wrap_content" 23 android:layout_height="wrap_content"
24 - android:text="Lce Test"  
25 - app:layout_constraintTop_toTopOf="parent"  
26 - android:layout_marginTop="8dp"  
27 android:layout_marginLeft="8dp" 24 android:layout_marginLeft="8dp"
  25 + android:layout_marginRight="8dp"
  26 + android:layout_marginTop="16dp"
  27 + android:text="Lce Test"
  28 + app:layout_constraintHorizontal_bias="0.0"
28 app:layout_constraintLeft_toLeftOf="parent" 29 app:layout_constraintLeft_toLeftOf="parent"
  30 + app:layout_constraintRight_toRightOf="parent"
  31 + app:layout_constraintTop_toTopOf="parent" />
  32 +
  33 + <Button
  34 + android:id="@+id/permissions_test"
  35 + android:layout_width="368dp"
  36 + android:layout_height="wrap_content"
  37 + android:layout_marginBottom="8dp"
  38 + android:layout_marginLeft="8dp"
29 android:layout_marginRight="8dp" 39 android:layout_marginRight="8dp"
30 - app:layout_constraintRight_toRightOf="parent" /> 40 + android:layout_marginTop="8dp"
  41 + android:text="Permissions Test"
  42 + app:layout_constraintBottom_toBottomOf="parent"
  43 + app:layout_constraintHorizontal_bias="0.0"
  44 + app:layout_constraintLeft_toLeftOf="parent"
  45 + app:layout_constraintRight_toRightOf="parent"
  46 + app:layout_constraintTop_toBottomOf="@+id/lce_test"
  47 + app:layout_constraintVertical_bias="0.011" />
31 48
32 </android.support.constraint.ConstraintLayout> 49 </android.support.constraint.ConstraintLayout>
app/src/main/res/layout/activity_permissions.xml 0 → 100644
  1 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:tools="http://schemas.android.com/tools"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:paddingLeft="@dimen/horizontal_page_margin"
  6 + android:paddingRight="@dimen/horizontal_page_margin"
  7 + android:paddingTop="@dimen/vertical_page_margin"
  8 + android:paddingBottom="@dimen/vertical_page_margin"
  9 + android:orientation="vertical" >
  10 +
  11 + <Button
  12 + android:layout_width="wrap_content"
  13 + android:layout_height="wrap_content"
  14 + android:text="@string/show_camera"
  15 + android:id="@+id/button_camera" />
  16 +
  17 + <Button
  18 + android:layout_width="wrap_content"
  19 + android:layout_height="wrap_content"
  20 + android:text="@string/show_contacts"
  21 + android:id="@+id/button_contacts" />
  22 +
  23 + <FrameLayout
  24 + android:id="@+id/sample_content_fragment"
  25 + android:layout_weight="2"
  26 + android:layout_width="match_parent"
  27 + android:layout_height="0px" />
  28 +
  29 +</LinearLayout>
app/src/main/res/layout/fragment_camera.xml 0 → 100644
  1 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + android:orientation="vertical"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent">
  5 + <Button
  6 + android:layout_width="wrap_content"
  7 + android:layout_height="wrap_content"
  8 + android:text="@string/back"
  9 + android:onClick="onBackClick"
  10 + android:layout_gravity="center_horizontal"/>
  11 +
  12 + <FrameLayout
  13 + android:id="@+id/camera_preview"
  14 + android:layout_width="match_parent"
  15 + android:layout_height="0dp"
  16 + android:layout_weight="1" />
  17 +</LinearLayout>
0 \ No newline at end of file 18 \ No newline at end of file
app/src/main/res/layout/fragment_contacts.xml 0 → 100644
  1 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + android:orientation="vertical"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:paddingLeft="@dimen/horizontal_page_margin"
  6 + android:paddingRight="@dimen/horizontal_page_margin"
  7 + android:paddingTop="@dimen/vertical_page_margin"
  8 + android:paddingBottom="@dimen/vertical_page_margin">
  9 +
  10 + <Button
  11 + android:layout_width="wrap_content"
  12 + android:layout_height="wrap_content"
  13 + android:text="@string/back"
  14 + android:onClick="onBackClick"
  15 + android:layout_gravity="center_horizontal"/>
  16 +
  17 + <TextView
  18 + android:layout_width="wrap_content"
  19 + android:layout_height="wrap_content"
  20 + android:text="@string/contacts_intro"/>
  21 +
  22 + <TextView
  23 + android:layout_width="wrap_content"
  24 + android:layout_height="wrap_content"
  25 + android:id="@+id/contact_message"/>
  26 +
  27 + <Button
  28 + android:layout_width="wrap_content"
  29 + android:layout_height="wrap_content"
  30 + android:text="@string/add_contact"
  31 + android:id="@+id/contact_add"/>
  32 +
  33 + <Button
  34 + android:layout_width="wrap_content"
  35 + android:layout_height="wrap_content"
  36 + android:text="@string/show_contact"
  37 + android:id="@+id/contact_load"/>
  38 +
  39 +</LinearLayout>
0 \ No newline at end of file 40 \ No newline at end of file
app/src/main/res/layout/fragment_main.xml 0 → 100644
  1 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:tools="http://schemas.android.com/tools"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:paddingLeft="@dimen/horizontal_page_margin"
  6 + android:paddingRight="@dimen/horizontal_page_margin"
  7 + android:paddingTop="@dimen/vertical_page_margin"
  8 + android:paddingBottom="@dimen/vertical_page_margin"
  9 + android:orientation="vertical"
  10 + tools:context=".MainActivity">
  11 +
  12 + <TextView
  13 + android:text="@string/main_introduction"
  14 + android:layout_width="wrap_content"
  15 + android:layout_height="wrap_content"/>
  16 +
  17 + <Button
  18 + android:layout_width="wrap_content"
  19 + android:layout_height="wrap_content"
  20 + android:text="@string/show_camera"
  21 + android:id="@+id/button_camera"/>
  22 +
  23 + <Button
  24 + android:layout_width="wrap_content"
  25 + android:layout_height="wrap_content"
  26 + android:text="@string/show_contacts"
  27 + android:id="@+id/button_contacts"/>
  28 +
  29 +</LinearLayout>
app/src/main/res/values/dimens.xml 0 → 100644
  1 +<resources>
  2 + <!-- Default screen margins, per the Android Design guidelines. -->
  3 + <dimen name="margin_medium">16dp</dimen>
  4 + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
  5 + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
  6 +</resources>
app/src/main/res/values/strings.xml
1 <resources> 1 <resources>
2 <string name="app_name">CNLiveLibs</string> 2 <string name="app_name">CNLiveLibs</string>
  3 +
  4 + <string name="contacts_string">Total number of contacts: %1$,d\nFirst contact:<b>%2$s</b></string>
  5 + <string name="contacts_empty">Contacts not loaded.</string>
  6 + <string name="add_contact">Add a contact</string>
  7 + <string name="show_contact">Show first contact</string>
  8 + <string name="contacts_intro">This fragment demonstrates access to the contact database on the device.\n
  9 + Clicking \"Add a contact\" adds a new contact named "__DUMMY ENTRY" directly into the database.\n
  10 + Clicking \"Show first contact\" accesses the contact database to display the name of the first contact.</string>
  11 + <string name="back">Back</string>
  12 + <string name="show_camera">Show camera preview</string>
  13 + <string name="show_contacts">Show and add contacts</string>
  14 + <string name="main_introduction">Click the buttons below to show a camera preview or access the contacts database.\nNote that the contacts option is only available on Android M to illustrate the use of optional, M-only permissions that are not requested on lower SDK platforms.</string>
  15 + <string name="permission_camera_rationale">Camera permission is needed to show the camera preview.</string>
  16 + <string name="permission_contacts_rationale">Contacts permissions are needed to demonstrate access to the contacts database.</string>
  17 + <string name="permission_camera_denied">Camera permission was denied. Please consider granting it in order to access the camera!</string>
  18 + <string name="permission_camera_never_askagain">Camera permission was denied with never ask again.</string>
  19 + <string name="button_allow">Allow</string>
  20 + <string name="button_deny">Deny</string>
3 </resources> 21 </resources>
cnlive/build.gradle
@@ -74,7 +74,7 @@ ext { @@ -74,7 +74,7 @@ ext {
74 siteUrl = 'http://bj.gitlab.cnlive.com/cnlive-team/CNVideoDemo' 74 siteUrl = 'http://bj.gitlab.cnlive.com/cnlive-team/CNVideoDemo'
75 gitUrl = 'http://bj.gitlab.cnlive.com/cnlive-team/CNVideoDemo.git' 75 gitUrl = 'http://bj.gitlab.cnlive.com/cnlive-team/CNVideoDemo.git'
76 76
77 - libraryVersion = '0.2.0' 77 + libraryVersion = '0.2.1'
78 78
79 developerId = 'CNlive' 79 developerId = 'CNlive'
80 developerName = 'Granzon' 80 developerName = 'Granzon'