├── Android.mk ├── AndroidManifest.xml ├── README.md ├── proguard.flags ├── res ├── drawable-hdpi │ ├── ic_launcher.png │ ├── ic_notify_screen_recorder.png │ ├── ic_notify_screen_recorder_error.png │ ├── ic_show_touch_disabled.png │ ├── ic_show_touch_enabled.png │ └── ic_stop_recording.png ├── drawable-mdpi │ ├── ic_launcher.png │ ├── ic_notify_screen_recorder.png │ ├── ic_notify_screen_recorder_error.png │ ├── ic_show_touch_disabled.png │ ├── ic_show_touch_enabled.png │ └── ic_stop_recording.png ├── drawable-xhdpi │ ├── ic_launcher.png │ ├── ic_notify_screen_recorder.png │ ├── ic_notify_screen_recorder_error.png │ ├── ic_show_touch_disabled.png │ ├── ic_show_touch_enabled.png │ └── ic_stop_recording.png ├── drawable-xxhdpi │ ├── ic_launcher.png │ ├── ic_notify_screen_recorder.png │ ├── ic_notify_screen_recorder_error.png │ ├── ic_show_touch_disabled.png │ ├── ic_show_touch_enabled.png │ └── ic_stop_recording.png ├── values-el │ └── strings.xml ├── values-sk │ └── strings.xml └── values │ ├── config.xml │ └── strings.xml └── src └── org └── chameleonos └── screenrecorder ├── ScreenRecordReceiver.java └── ScreenRecorderService.java /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE_TAGS := optional 5 | 6 | LOCAL_SRC_FILES := $(call all-java-files-under, src) 7 | 8 | LOCAL_PACKAGE_NAME := ScreenRecorder 9 | LOCAL_CERTIFICATE := platform 10 | LOCAL_PRIVILEGED_MODULE := true 11 | 12 | LOCAL_PROGUARD_FLAG_FILES := proguard.flags 13 | 14 | include $(BUILD_PACKAGE) 15 | 16 | # Use the folloing include to make our test apk. 17 | include $(call all-makefiles-under,$(LOCAL_PATH)) 18 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple system app that contains a service which interfaces with the ScreenRecorder 2 | class to record video of the device display. 3 | -------------------------------------------------------------------------------- /proguard.flags: -------------------------------------------------------------------------------- 1 | # Keep all Fragments in this package, which are used by reflection. 2 | -keep class org.chameleonos.screenrecorder.* 3 | 4 | # Keep click responders 5 | 6 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_notify_screen_recorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_notify_screen_recorder.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_notify_screen_recorder_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_notify_screen_recorder_error.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_show_touch_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_show_touch_disabled.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_show_touch_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_show_touch_enabled.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_stop_recording.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-hdpi/ic_stop_recording.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_notify_screen_recorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_notify_screen_recorder.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_notify_screen_recorder_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_notify_screen_recorder_error.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_show_touch_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_show_touch_disabled.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_show_touch_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_show_touch_enabled.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_stop_recording.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-mdpi/ic_stop_recording.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_notify_screen_recorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_notify_screen_recorder.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_notify_screen_recorder_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_notify_screen_recorder_error.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_show_touch_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_show_touch_disabled.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_show_touch_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_show_touch_enabled.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_stop_recording.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xhdpi/ic_stop_recording.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_notify_screen_recorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_notify_screen_recorder.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_notify_screen_recorder_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_notify_screen_recorder_error.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_show_touch_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_show_touch_disabled.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_show_touch_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_show_touch_enabled.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_stop_recording.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChameleonOS/android_packages_apps_ScreenRecorder/6ecbef01f584e45992958ea124d74cf65efa9f4b/res/drawable-xxhdpi/ic_stop_recording.png -------------------------------------------------------------------------------- /res/values-el/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ScreenRecorder 5 | 6 | Η καταγραφή οθόνης βρίσκεται σε εξέλιξη 7 | 8 | Η καταγραφή αποθηκεύτηκε 9 | Πατήστε για να δείτε %s 10 | 11 | Σφάλμα 12 | %s 13 | 14 | Δεν ήταν δυνατή η δημιουργία φακέλου αποθήκευσης. 15 | Δεν υπάρχει εξωτερικός χώρος αποθήκευσης. 16 | Δεν είναι δυνατή η εκκίνηση της καταγραφής. 17 | 18 | 19 | -------------------------------------------------------------------------------- /res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ScreenRecorder 5 | 6 | Prebieha nahrávanie obrazovky 7 | 8 | Nahrávka uložená 9 | Kliknite pre zobrazenie %s 10 | 11 | Chyba 12 | %s 13 | 14 | Nepodarilo sa vytvoriť adresár. 15 | Externé uložisko nie je dostupné. 16 | Nie je možné spustiť nahrávanie. 17 | 18 | 19 | -------------------------------------------------------------------------------- /res/values/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 720x1280 6 | 7 | 8 | 4000000 9 | 10 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ScreenRecorder 5 | 6 | Screen recording in progress 7 | 8 | Screen recording saved 9 | Touch to view %s 10 | 11 | Screen recorder error 12 | %s 13 | 14 | Processing recorded video 15 | 16 | Stop recording 17 | Show touch 18 | 19 | Unable to create output directory. 20 | No external storage available. 21 | Unable to start recording. 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/org/chameleonos/screenrecorder/ScreenRecordReceiver.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, The ChameleonOS 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 org.chameleonos.screenrecorder; 18 | 19 | import android.app.NotificationManager; 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.net.Uri; 24 | 25 | import java.io.File; 26 | 27 | import static org.chameleonos.screenrecorder.ScreenRecorderService.ACTION_NOTIFY_DELETE_SCREENRECORD; 28 | import static org.chameleonos.screenrecorder.ScreenRecorderService.ACTION_NOTIFY_RECORD_SERVICE; 29 | import static org.chameleonos.screenrecorder.ScreenRecorderService.NOTIFICATION_ID; 30 | import static org.chameleonos.screenrecorder.ScreenRecorderService.SCREENRECORD_PATH; 31 | 32 | public class ScreenRecordReceiver extends BroadcastReceiver { 33 | 34 | @Override 35 | public void onReceive(Context context, Intent intent) { 36 | if (ACTION_NOTIFY_RECORD_SERVICE.equals(intent.getAction())) { 37 | Intent serviceIntent = new Intent(context, ScreenRecorderService.class); 38 | serviceIntent.setAction(ACTION_NOTIFY_RECORD_SERVICE); 39 | context.startService(serviceIntent); 40 | } else if (ACTION_NOTIFY_DELETE_SCREENRECORD.equals(intent.getAction())) { 41 | File screenRecord = new File(intent.getExtras().getString(SCREENRECORD_PATH)); 42 | if (screenRecord.exists()) screenRecord.delete(); 43 | context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, 44 | Uri.fromFile(screenRecord))); 45 | NotificationManager notificationManager = 46 | (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 47 | notificationManager.cancel(NOTIFICATION_ID); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/org/chameleonos/screenrecorder/ScreenRecorderService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, The ChameleonOS 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 org.chameleonos.screenrecorder; 18 | 19 | import android.app.IntentService; 20 | import android.app.Notification; 21 | import android.app.NotificationManager; 22 | import android.app.PendingIntent; 23 | import android.content.ContentResolver; 24 | import android.content.Context; 25 | import android.content.Intent; 26 | import android.content.res.Resources; 27 | import android.graphics.Bitmap; 28 | import android.graphics.Point; 29 | import android.media.MediaActionSound; 30 | import android.media.MediaMetadataRetriever; 31 | import android.media.screenrecorder.ScreenRecorder; 32 | import android.media.screenrecorder.ScreenRecorder.ScreenRecorderCallbacks; 33 | import android.net.Uri; 34 | import android.os.Environment; 35 | import android.provider.Settings; 36 | import android.text.TextUtils; 37 | import android.util.Log; 38 | import android.view.Display; 39 | import android.view.Surface; 40 | import android.view.WindowManager; 41 | 42 | import java.io.File; 43 | import java.text.SimpleDateFormat; 44 | import java.util.Date; 45 | 46 | public class ScreenRecorderService extends IntentService 47 | implements ScreenRecorderCallbacks { 48 | public static final String ACTION_NOTIFY_RECORD_SERVICE 49 | = "org.chameleonos.action.NOTIFY_RECORD_SERVICE"; 50 | public static final String ACTION_NOTIFY_DELETE_SCREENRECORD 51 | = "org.chameleonos.action.NOTIFY_DELETE_SCREENRECORD"; 52 | public static final String ACTION_NOTIFY_TOGGLE_SHOW_TOUCHES 53 | = "org.chameleonos.action.NOTIFY_TOGGLE_SHOW_TOUCHES"; 54 | public static final String SCREENRECORD_PATH 55 | = "org.chameleonos.screenrecorder.SCREENRECORD_PATH"; 56 | 57 | private static final String TAG = "ScreenRecorderService"; 58 | private static final String RECORDER_FOLDER = "ScreenRecorder"; 59 | private static final String RECORDER_PATH = 60 | Environment.getExternalStorageDirectory().getAbsolutePath() 61 | + File.separator + RECORDER_FOLDER; 62 | 63 | public static final int NOTIFICATION_ID = 0xD34D; 64 | 65 | // videos will be saved using SCR_[date]_[time].mp4 66 | private static final String FILE_NAME_FORMAT = "SCR_%s.mp4"; 67 | private static final String DATE_TIME_FORMAT = "yyyyMMdd_HHmmss"; 68 | private static final String SCREENRECORD_SHARE_SUBJECT_TEMPLATE = "ScreenRecord (%s)"; 69 | 70 | private static String sCurrentFileName; 71 | private static ScreenRecorder sScreenRecorder; 72 | private static MediaActionSound sActionSound = new MediaActionSound(); 73 | 74 | public ScreenRecorderService() { 75 | super(TAG); 76 | } 77 | 78 | private void startOrStopRecording() { 79 | if (sScreenRecorder == null) { 80 | sScreenRecorder = ScreenRecorder.getInstance(); 81 | sScreenRecorder.setScreenRecorderCallbacks(this); 82 | } 83 | final int state = sScreenRecorder.getState(); 84 | if (state != ScreenRecorder.STATE_RECORDING) { 85 | startRecording(); 86 | } else { 87 | sActionSound.play(MediaActionSound.STOP_VIDEO_RECORDING); 88 | sScreenRecorder.stop(); 89 | postProcessingNotification(); 90 | } 91 | } 92 | 93 | private static String getVideoFileName() { 94 | final String dateTime; 95 | SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT); 96 | dateTime = sdf.format(new Date()).toString(); 97 | 98 | return String.format(FILE_NAME_FORMAT, dateTime); 99 | } 100 | 101 | private void startRecording() { 102 | // make sure external storage is available first 103 | if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 104 | postRecordingErrorNotification(getString(R.string.error_external_storage_unavailable)); 105 | return; 106 | } 107 | 108 | WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 109 | final Display display = wm.getDefaultDisplay(); 110 | final int rotation = display.getRotation(); 111 | final ContentResolver resolver = getContentResolver(); 112 | final Resources res = getResources(); 113 | 114 | final int[] dimensions = getVideoDimensions(res, display); 115 | final int bitRate = Settings.System.getInt(resolver, 116 | Settings.System.SCREEN_RECORDER_BITRATE, 117 | res.getInteger(R.integer.config_screenRecorderFramerate)); 118 | final boolean recordAudio = Settings.System.getInt(resolver, 119 | Settings.System.SCREEN_RECORDER_RECORD_AUDIO, 0) == 1; 120 | 121 | sScreenRecorder.init(rotation, dimensions[0], dimensions[1], bitRate, 0, recordAudio); 122 | File f = new File(RECORDER_PATH); 123 | if (!f.exists()) { 124 | if (!f.mkdir()) { 125 | Log.e(TAG, "Unable to create output directory " + RECORDER_PATH); 126 | postRecordingErrorNotification( 127 | getString(R.string.error_unable_to_create_directory)); 128 | return; 129 | } 130 | } 131 | sCurrentFileName = getVideoFileName(); 132 | final String fileName = RECORDER_PATH + File.separator + sCurrentFileName; 133 | Log.d(TAG, "Start recording screen to " + fileName); 134 | try { 135 | if (!sScreenRecorder.start(fileName)) { 136 | postRecordingErrorNotification(getString(R.string.error_unable_to_start)); 137 | } 138 | } catch (IllegalStateException e) { 139 | postRecordingErrorNotification(getString(R.string.error_unable_to_start)); 140 | } 141 | } 142 | 143 | /* Screen recorder callbacks */ 144 | @Override 145 | public void onRecordingStarted() { 146 | final boolean recordAudio = Settings.System.getInt(getContentResolver(), 147 | Settings.System.SCREEN_RECORDER_RECORD_AUDIO, 0) == 1; 148 | // only play the start recording sound when only video is being recorded 149 | // otherwise the sound will be picked up by the mic and included which 150 | // isn't really desirable. 151 | if (!recordAudio) sActionSound.play(MediaActionSound.START_VIDEO_RECORDING); 152 | postRecordingNotification(); 153 | } 154 | 155 | @Override 156 | public void onRecordingFinished() { 157 | stopForeground(true); 158 | postRecordingFinishedNotification(); 159 | } 160 | 161 | @Override 162 | public void onRecordingError(String error) { 163 | postRecordingErrorNotification(error); 164 | } 165 | 166 | private void postRecordingNotification() { 167 | // set an action for stopping recording 168 | Intent intent = new Intent(ACTION_NOTIFY_RECORD_SERVICE); 169 | PendingIntent stopIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 170 | intent = new Intent(this, ScreenRecorderService.class); 171 | 172 | // set an action for toggling show touches 173 | intent.setAction(ACTION_NOTIFY_TOGGLE_SHOW_TOUCHES); 174 | PendingIntent showTouchesIntent = PendingIntent.getService(this, 0, 175 | intent, 0); 176 | final boolean showTouches = Settings.System.getInt(getContentResolver(), 177 | Settings.System.SHOW_TOUCHES, 0) == 1; 178 | NotificationManager nm = 179 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 180 | Notification notice = new Notification.Builder(this) 181 | .setAutoCancel(false) 182 | .setOngoing(true) 183 | .setContentTitle(getString(R.string.notification_recording_title)) 184 | .setSmallIcon(R.drawable.ic_notify_screen_recorder) 185 | .setWhen(System.currentTimeMillis()) 186 | .setPriority(Integer.MAX_VALUE) 187 | .addAction(R.drawable.ic_stop_recording, 188 | getString(R.string.notification_action_stop_recording), stopIntent) 189 | .addAction(showTouches ? R.drawable.ic_show_touch_enabled 190 | : R.drawable.ic_show_touch_disabled, 191 | getString(R.string.notification_action_show_touch), showTouchesIntent) 192 | .build(); 193 | nm.notify(NOTIFICATION_ID, notice); 194 | } 195 | 196 | private void postProcessingNotification() { 197 | NotificationManager nm = 198 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 199 | Notification notice = new Notification.Builder(this) 200 | .setAutoCancel(true) 201 | .setOngoing(false) 202 | .setContentTitle(getString(R.string.notification_video_processing_title)) 203 | .setSmallIcon(R.drawable.ic_notify_screen_recorder) 204 | .setWhen(System.currentTimeMillis()) 205 | .build(); 206 | nm.notify(NOTIFICATION_ID, notice); 207 | } 208 | 209 | private void postRecordingFinishedNotification() { 210 | long screenRecordTime = System.currentTimeMillis(); 211 | Uri uri = Uri.fromFile(new File(RECORDER_PATH + File.separator + sCurrentFileName)); 212 | String subjectDate = new SimpleDateFormat("hh:mma, MMM dd, yyyy") 213 | .format(new Date(screenRecordTime)); 214 | String subject = String.format(SCREENRECORD_SHARE_SUBJECT_TEMPLATE, subjectDate); 215 | Intent sharingIntent = new Intent(Intent.ACTION_SEND); 216 | sharingIntent.setType("video/mp4"); 217 | sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); 218 | sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); 219 | 220 | Intent chooserIntent = Intent.createChooser(sharingIntent, null); 221 | chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK 222 | | Intent.FLAG_ACTIVITY_NEW_TASK); 223 | 224 | Intent deleteIntent = new Intent(ACTION_NOTIFY_DELETE_SCREENRECORD); 225 | deleteIntent.putExtra(SCREENRECORD_PATH, RECORDER_PATH + File.separator + sCurrentFileName); 226 | 227 | Intent intent = new Intent(Intent.ACTION_VIEW); 228 | intent.setDataAndType(uri, "video/mp4"); 229 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 230 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0); 231 | NotificationManager nm = 232 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 233 | Notification.Builder b = new Notification.Builder(this) 234 | .setAutoCancel(true) 235 | .setOngoing(false) 236 | .setContentTitle(getString(R.string.notification_recording_finished_title)) 237 | .setContentText(getString(R.string.notification_recording_finished_text, 238 | sCurrentFileName)) 239 | .setSmallIcon(R.drawable.ic_notify_screen_recorder) 240 | .setWhen(screenRecordTime) 241 | .setContentIntent(contentIntent) 242 | .addAction(com.android.internal.R.drawable.ic_menu_share, 243 | getString(com.android.internal.R.string.share), 244 | PendingIntent.getActivity(this, 0, chooserIntent, 245 | PendingIntent.FLAG_CANCEL_CURRENT)) 246 | .addAction(com.android.internal.R.drawable.ic_menu_delete, 247 | getString(com.android.internal.R.string.delete), 248 | PendingIntent.getBroadcast(this, 0, deleteIntent, 249 | PendingIntent.FLAG_CANCEL_CURRENT)); 250 | 251 | // try and grab a frame as a preview 252 | MediaMetadataRetriever mmr = new MediaMetadataRetriever(); 253 | mmr.setDataSource(RECORDER_PATH + File.separator + sCurrentFileName); 254 | Bitmap thumbnail = mmr.getFrameAtTime(); 255 | if (thumbnail != null) { 256 | Notification.BigPictureStyle style = 257 | new Notification.BigPictureStyle() 258 | .bigPicture(thumbnail) 259 | .setSummaryText(getString(R.string.notification_recording_finished_text, 260 | sCurrentFileName)); 261 | b.setStyle(style); 262 | } 263 | 264 | nm.notify(NOTIFICATION_ID, b.build()); 265 | } 266 | 267 | private void postRecordingErrorNotification(String error) { 268 | NotificationManager nm = 269 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 270 | Notification notice = new Notification.Builder(this) 271 | .setAutoCancel(true) 272 | .setOngoing(false) 273 | .setContentTitle(getString(R.string.notification_recording_error_title)) 274 | .setContentText(getString(R.string.notification_recording_error_text, error)) 275 | .setSmallIcon(R.drawable.ic_notify_screen_recorder_error) 276 | .setWhen(System.currentTimeMillis()) 277 | .build(); 278 | nm.notify(NOTIFICATION_ID, notice); 279 | } 280 | 281 | private int[] getVideoDimensions(Resources res, Display display) { 282 | String dimensionString = Settings.System.getString(getContentResolver(), 283 | Settings.System.SCREEN_RECORDER_OUTPUT_DIMENSIONS); 284 | if (TextUtils.isEmpty(dimensionString)) { 285 | dimensionString = res.getString(R.string.config_screenRecorderOutputDimensions); 286 | } 287 | int[] dimensions = parseDimensions(dimensionString); 288 | if (dimensions == null) { 289 | dimensions = new int[] {720, 1280}; 290 | } 291 | 292 | // if rotation is Surface.ROTATION_0 and width>height swap 293 | final Point p = new Point(); 294 | display.getRealSize(p); 295 | if (display.getRotation() == Surface.ROTATION_0 && (p.x > p.y)) { 296 | int tmp = dimensions[0]; 297 | dimensions[0] = dimensions[1]; 298 | dimensions[1] = tmp; 299 | } 300 | 301 | return dimensions; 302 | } 303 | 304 | private static int[] parseDimensions(String dimensions) { 305 | String[] tmp = dimensions.split("x"); 306 | if (tmp.length < 2) return null; 307 | int[] dims = new int[2]; 308 | try { 309 | dims[0] = Integer.valueOf(tmp[0]); 310 | dims[1] = Integer.valueOf(tmp[1]); 311 | } catch (NumberFormatException e) { 312 | return null; 313 | } 314 | 315 | return dims; 316 | } 317 | 318 | @Override 319 | protected void onHandleIntent(Intent intent) { 320 | final String action = intent.getAction(); 321 | if (ACTION_NOTIFY_RECORD_SERVICE.equals(action)) { 322 | startOrStopRecording(); 323 | } else if (ACTION_NOTIFY_TOGGLE_SHOW_TOUCHES.equals(action)) { 324 | final boolean showTouches = Settings.System.getInt(getContentResolver(), 325 | Settings.System.SHOW_TOUCHES, 0) == 1; 326 | Settings.System.putInt(getContentResolver(), 327 | Settings.System.SHOW_TOUCHES, showTouches ? 0 : 1); 328 | // call postRecordingNotification so the icon gets updated based on this change 329 | postRecordingNotification(); 330 | } 331 | } 332 | } 333 | --------------------------------------------------------------------------------