1c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright/*
2c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Copyright (C) 2014 The Android Open Source Project
3c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright *
4c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Licensed under the Apache License, Version 2.0 (the "License");
5c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * you may not use this file except in compliance with the License.
6c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * You may obtain a copy of the License at
7c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright *
8c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright *      http://www.apache.org/licenses/LICENSE-2.0
9c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright *
10c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * Unless required by applicable law or agreed to in writing, software
11c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * distributed under the License is distributed on an "AS IS" BASIS,
12c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * See the License for the specific language governing permissions and
14c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright * limitations under the License.
15c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright */
16c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
17c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightpackage com.android.systemui.media;
18c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
19752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roosimport android.app.Activity;
20c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.app.AlertDialog;
21c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.DialogInterface;
22c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.Intent;
23c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.pm.ApplicationInfo;
24c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.content.pm.PackageManager;
2558950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.graphics.Typeface;
26c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.media.projection.IMediaProjection;
27c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport android.media.projection.IMediaProjectionManager;
28c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport android.media.projection.MediaProjectionManager;
29c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.Bundle;
30c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.IBinder;
31c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.RemoteException;
32c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.os.ServiceManager;
3358950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.text.BidiFormatter;
3458950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.text.SpannableString;
3558950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.text.TextPaint;
3658950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.text.TextUtils;
3758950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wrightimport android.text.style.StyleSpan;
38c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.util.Log;
39752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roosimport android.view.WindowManager;
40c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.widget.CheckBox;
41c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport android.widget.CompoundButton;
42c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
43c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wrightimport com.android.systemui.R;
44c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
45752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roospublic class MediaProjectionPermissionActivity extends Activity
46752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener,
47752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        DialogInterface.OnCancelListener {
48c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private static final String TAG = "MediaProjectionPermissionActivity";
4958950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright    private static final float MAX_APP_NAME_SIZE_PX = 500f;
5058950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright    private static final String ELLIPSIS = "\u2026";
51c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
52c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private boolean mPermanentGrant;
53c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private String mPackageName;
54c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private int mUid;
55c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private IMediaProjectionManager mService;
56c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
57752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    private AlertDialog mDialog;
58752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
59c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    @Override
60c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    public void onCreate(Bundle icicle) {
61c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        super.onCreate(icicle);
62c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
63c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        mPackageName = getCallingPackage();
64c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
65c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        mService = IMediaProjectionManager.Stub.asInterface(b);
66c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
67c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        if (mPackageName == null) {
68c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            finish();
69c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            return;
70c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        }
71c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
72c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        PackageManager packageManager = getPackageManager();
73c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        ApplicationInfo aInfo;
74c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        try {
75c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            aInfo = packageManager.getApplicationInfo(mPackageName, 0);
76c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            mUid = aInfo.uid;
77c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        } catch (PackageManager.NameNotFoundException e) {
78c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            Log.e(TAG, "unable to look up package name", e);
79c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            finish();
80c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            return;
81c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        }
82c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
83c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        try {
84c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            if (mService.hasProjectionPermission(mUid, mPackageName)) {
85c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName,
86c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                        false /*permanentGrant*/));
87c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                finish();
88c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                return;
89c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            }
90c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        } catch (RemoteException e) {
91c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            Log.e(TAG, "Error checking projection permissions", e);
92c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            finish();
93c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            return;
94c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        }
95c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
9658950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        TextPaint paint = new TextPaint();
9758950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        paint.setTextSize(42);
9858950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
9958950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        String label = aInfo.loadLabel(packageManager).toString();
10058950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
10158950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        // If the label contains new line characters it may push the security
10258950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        // message below the fold of the dialog. Labels shouldn't have new line
10358950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        // characters anyways, so just truncate the message the first time one
10458950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        // is seen.
10558950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        final int labelLength = label.length();
10658950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        int offset = 0;
10758950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        while (offset < labelLength) {
10858950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            final int codePoint = label.codePointAt(offset);
10958950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            final int type = Character.getType(codePoint);
11058950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            if (type == Character.LINE_SEPARATOR
11158950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                    || type == Character.CONTROL
11258950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                    || type == Character.PARAGRAPH_SEPARATOR) {
11358950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                label = label.substring(0, offset) + ELLIPSIS;
11458950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                break;
11558950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            }
11658950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            offset += Character.charCount(codePoint);
11758950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        }
11858950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
11958950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        if (label.isEmpty()) {
12058950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            label = mPackageName;
12158950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        }
12258950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
12358950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        String unsanitizedAppName = TextUtils.ellipsize(label,
12458950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
12558950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
12658950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
12758950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        String actionText = getString(R.string.media_projection_dialog_text, appName);
12858950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        SpannableString message = new SpannableString(actionText);
12958950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright
13058950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        int appNameIndex = actionText.indexOf(appName);
13158950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        if (appNameIndex >= 0) {
13258950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright            message.setSpan(new StyleSpan(Typeface.BOLD),
13358950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                    appNameIndex, appNameIndex + appName.length(), 0);
13458950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright        }
135c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
136752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        mDialog = new AlertDialog.Builder(this)
137752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .setIcon(aInfo.loadIcon(packageManager))
13858950a9476070b47bc0f2ae93e7a24ab0444c3f3Michael Wright                .setMessage(message)
139752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .setPositiveButton(R.string.media_projection_action_text, this)
140752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .setNegativeButton(android.R.string.cancel, this)
141752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .setView(R.layout.remember_permission_checkbox)
142752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .setOnCancelListener(this)
143752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos                .create();
144752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
145752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        mDialog.create();
146b0cf7ea65df8aad45556d2336e9ebae7d6da94b4Dongwon Kang        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
147752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
148752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        ((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this);
149752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
150752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
151752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        mDialog.show();
152752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    }
153752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
154752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    @Override
155752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    protected void onDestroy() {
156752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        super.onDestroy();
1573d332f4e67a484abe597576bf329d89655afe720John Spurlock        if (mDialog != null) {
1583d332f4e67a484abe597576bf329d89655afe720John Spurlock            mDialog.dismiss();
1593d332f4e67a484abe597576bf329d89655afe720John Spurlock        }
160c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    }
161c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
162c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    @Override
163c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    public void onClick(DialogInterface dialog, int which) {
164c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        try {
165c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            if (which == AlertDialog.BUTTON_POSITIVE) {
166c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                setResult(RESULT_OK, getMediaProjectionIntent(
167c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                        mUid, mPackageName, mPermanentGrant));
168c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            }
169c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        } catch (RemoteException e) {
170c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            Log.e(TAG, "Error granting projection permission", e);
171c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            setResult(RESULT_CANCELED);
172c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        } finally {
1733d332f4e67a484abe597576bf329d89655afe720John Spurlock            if (mDialog != null) {
1743d332f4e67a484abe597576bf329d89655afe720John Spurlock                mDialog.dismiss();
1753d332f4e67a484abe597576bf329d89655afe720John Spurlock            }
176c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            finish();
177c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        }
178c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    }
179c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
180c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    @Override
181c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
182c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        mPermanentGrant = isChecked;
183c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    }
184c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright
185c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    private Intent getMediaProjectionIntent(int uid, String packageName, boolean permanentGrant)
186c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright            throws RemoteException {
187c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        IMediaProjection projection = mService.createProjection(uid, packageName,
188c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, permanentGrant);
189c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        Intent intent = new Intent();
190c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder());
191c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright        return intent;
192c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright    }
193752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos
194752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    @Override
195752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    public void onCancel(DialogInterface dialog) {
196752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos        finish();
197752aee8a6f868011dea1d1bb4f582a2531d76d47Adrian Roos    }
198c39d47a8e7c74bd539104b0efab898ef6fc43ddfMichael Wright}
199