Commit ef9f915c4082368868fa20393c16b24dc4c111ea
1 parent
7c5bdd74
add permissions test
Showing
16 changed files
with
760 additions
and
10 deletions
app/build.gradle
| ... | ... | @@ -46,10 +46,17 @@ dependencies { |
| 46 | 46 | }) |
| 47 | 47 | compile 'com.android.support:appcompat-v7:26.+' |
| 48 | 48 | compile 'com.android.support:recyclerview-v7:26.+' |
| 49 | + compile 'com.android.support:support-v13:26.+' | |
| 49 | 50 | compile 'com.android.support.constraint:constraint-layout:1.0.2' |
| 50 | 51 | testCompile 'junit:junit:4.12' |
| 51 | 52 | // TODO 本地酷使用说明 代码仓库标识 |
| 52 | 53 | // compile 'com.cnlive.libs:base:1.0.0@aar' |
| 53 | 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 | 61 | compile project(':cnlive') |
| 55 | 62 | } | ... | ... |
app/src/main/AndroidManifest.xml
| ... | ... | @@ -2,6 +2,10 @@ |
| 2 | 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | 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 | 9 | <application |
| 6 | 10 | android:name=".App" |
| 7 | 11 | android:allowBackup="true" |
| ... | ... | @@ -23,6 +27,7 @@ |
| 23 | 27 | </intent-filter> |
| 24 | 28 | </activity> |
| 25 | 29 | <activity android:name=".mvptest.ui.activity.LceTestActivity"></activity> |
| 30 | + <activity android:name=".sample.permissions.PermissionsActivity"></activity> | |
| 26 | 31 | </application> |
| 27 | 32 | |
| 28 | 33 | </manifest> |
| 29 | 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 | 2 | |
| 3 | 3 | import android.content.Intent; |
| 4 | 4 | import android.os.Bundle; |
| 5 | +import android.support.annotation.NonNull; | |
| 5 | 6 | import android.support.v7.app.AppCompatActivity; |
| 6 | 7 | |
| 7 | 8 | import com.cnlive.libs.demo.mvptest.ui.activity.LceTestActivity; |
| 9 | +import com.cnlive.libs.demo.sample.permissions.PermissionsActivity; | |
| 8 | 10 | |
| 9 | 11 | public class MainActivity extends AppCompatActivity { |
| 10 | 12 | |
| ... | ... | @@ -20,5 +22,13 @@ public class MainActivity extends AppCompatActivity { |
| 20 | 22 | Java8Test.test(this); |
| 21 | 23 | |
| 22 | 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
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 | 10 | android:id="@+id/textView" |
| 11 | 11 | android:layout_width="wrap_content" |
| 12 | 12 | android:layout_height="wrap_content" |
| 13 | - android:text="Hello World!" | |
| 14 | 13 | android:padding="8dp" |
| 14 | + android:text="Hello World!" | |
| 15 | 15 | app:layout_constraintBottom_toBottomOf="parent" |
| 16 | 16 | app:layout_constraintLeft_toLeftOf="parent" |
| 17 | 17 | app:layout_constraintRight_toRightOf="parent" |
| ... | ... | @@ -21,12 +21,29 @@ |
| 21 | 21 | android:id="@+id/lce_test" |
| 22 | 22 | android:layout_width="368dp" |
| 23 | 23 | android:layout_height="wrap_content" |
| 24 | - android:text="Lce Test" | |
| 25 | - app:layout_constraintTop_toTopOf="parent" | |
| 26 | - android:layout_marginTop="8dp" | |
| 27 | 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 | 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 | 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 | 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 | 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 | 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
app/src/main/res/values/strings.xml
| 1 | 1 | <resources> |
| 2 | 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 | 21 | </resources> | ... | ... |
cnlive/build.gradle
| ... | ... | @@ -74,7 +74,7 @@ ext { |
| 74 | 74 | siteUrl = 'http://bj.gitlab.cnlive.com/cnlive-team/CNVideoDemo' |
| 75 | 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 | 79 | developerId = 'CNlive' |
| 80 | 80 | developerName = 'Granzon' | ... | ... |