diff --git a/packages/phone_log/CHANGELOG.md b/packages/phone_log/CHANGELOG.md new file mode 100644 index 0000000..2bb0f3c --- /dev/null +++ b/packages/phone_log/CHANGELOG.md @@ -0,0 +1,5 @@ +## [0.0.2] - June 11th, 2018. +Update README and several fixes. + +## [0.0.1] - June 11th, 2018. +Initial the plugin package. diff --git a/packages/phone_log/LICENSE b/packages/phone_log/LICENSE new file mode 100644 index 0000000..8abfae5 --- /dev/null +++ b/packages/phone_log/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Google Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/phone_log/README.md b/packages/phone_log/README.md new file mode 100644 index 0000000..c43db9d --- /dev/null +++ b/packages/phone_log/README.md @@ -0,0 +1,41 @@ +# phone_log + +A flutter plugin project to access the device phone log (Android only). + +Currently only fetching the following 5 fields: + CallLog.Calls.CACHED_FORMATTED_NUMBER + CallLog.Calls.CACHED_MATCHED_NUMBER + CallLog.Calls.TYPE + CallLog.Calls.DATE + CallLog.Calls.DURATION + +For iOS it is not possible to extract the call log programmatically. Apple officially does not expose any public API to access the call log. So all the method will return null on iOS. + +## Getting Started + +Make sure you add the permission below to your Android Manifest Permission: + +```xml + +``` +## Usage in Dart + +Import the relevant file: + +``` +import 'package:phone_log/phone_log.dart'; +``` + +### Methods +```dart +/// Check phone log permission and return a [Future] with the result +Future checkPermission(Permission permission); + +/// Request phone log permission and return a [Future] with the result +Future requestPermission(Permission permission); + +/// Fetch the call log from Android device with a [startDate] and a +/// minimum [duration]. If startDate == null, all the data will be +/// fetched out. If duration == null, there will be no duration limit. +Future> getPhoneLogs(); +``` diff --git a/packages/phone_log/android/build.gradle b/packages/phone_log/android/build.gradle new file mode 100644 index 0000000..82b011b --- /dev/null +++ b/packages/phone_log/android/build.gradle @@ -0,0 +1,34 @@ +group 'com.jiajiabingcheng.phonelog' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.0.1' + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 27 + + defaultConfig { + minSdkVersion 16 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } +} diff --git a/packages/phone_log/android/gradle.properties b/packages/phone_log/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/packages/phone_log/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/packages/phone_log/android/gradle/wrapper/gradle-wrapper.properties b/packages/phone_log/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..eeb2f7e --- /dev/null +++ b/packages/phone_log/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jun 13 15:42:51 PDT 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/packages/phone_log/android/settings.gradle b/packages/phone_log/android/settings.gradle new file mode 100644 index 0000000..633553f --- /dev/null +++ b/packages/phone_log/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'phone_log' diff --git a/packages/phone_log/android/src/main/AndroidManifest.xml b/packages/phone_log/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..dc1cf43 --- /dev/null +++ b/packages/phone_log/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/CallRecord.java b/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/CallRecord.java new file mode 100644 index 0000000..fba0e48 --- /dev/null +++ b/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/CallRecord.java @@ -0,0 +1,35 @@ +package com.jiajiabingcheng.phonelog; + +import java.util.HashMap; + +class CallRecord { + + CallRecord() {} + + String formattedNumber; + String number; + String callType; + int dateYear; + int dateMonth; + int dateDay; + int dateHour; + int dateMinute; + int dateSecond; + long duration; + + HashMap toMap() { + HashMap recordMap = new HashMap<>(); + recordMap.put("formattedNumber", formattedNumber); + recordMap.put("number", number); + recordMap.put("callType", callType); + recordMap.put("dateYear", dateYear); + recordMap.put("dateMonth", dateMonth); + recordMap.put("dateDay", dateDay); + recordMap.put("dateHour", dateHour); + recordMap.put("dateMinute", dateMinute); + recordMap.put("dateSecond", dateSecond); + recordMap.put("duration", duration); + + return recordMap; + } +} diff --git a/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/PhoneLogPlugin.java b/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/PhoneLogPlugin.java new file mode 100644 index 0000000..0b8eccc --- /dev/null +++ b/packages/phone_log/android/src/main/java/com/jiajiabingcheng/phonelog/PhoneLogPlugin.java @@ -0,0 +1,187 @@ +package com.jiajiabingcheng.phonelog; + +import android.Manifest; +import android.annotation.TargetApi; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.os.Build; +import android.provider.CallLog; +import android.util.Log; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.PluginRegistry.Registrar; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; + +/** + * PhoneLogPlugin + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class PhoneLogPlugin implements MethodCallHandler, + PluginRegistry.RequestPermissionsResultListener { + private final Registrar registrar; + private Result pendingResult; + + private PhoneLogPlugin(Registrar registrar) { + this.registrar = registrar; + } + + public static void registerWith(Registrar registrar) { + final MethodChannel channel = + new MethodChannel(registrar.messenger(), "github.com/jiajiabingcheng/phone_log"); + PhoneLogPlugin phoneLogPlugin = new PhoneLogPlugin(registrar); + channel.setMethodCallHandler(phoneLogPlugin); + registrar.addRequestPermissionsResultListener(phoneLogPlugin); + } + + @Override + public void onMethodCall(MethodCall call, Result result) { + if (pendingResult != null) { + pendingResult.error("multiple_requests", "Cancelled by a second request.", null); + pendingResult = null; + } + pendingResult = result; + switch (call.method) { + case "checkPermission": + pendingResult.success(checkPermission()); + pendingResult = null; + break; + case "requestPermission": + requestPermission(); + break; + case "getPhoneLogs": + String startDate = call.argument("startDate"); + String duration = call.argument("duration"); + fetchCallRecords(startDate, duration); + break; + default: + result.notImplemented(); + } + } + + private void requestPermission() { + Log.i("PhoneLogPlugin", "Requesting permission : " + Manifest.permission.READ_CALL_LOG); + String[] perm = {Manifest.permission.READ_CALL_LOG}; + registrar.activity().requestPermissions(perm, 0); + } + + private boolean checkPermission() { + Log.i("PhoneLogPlugin", "Checking permission : " + Manifest.permission.READ_CALL_LOG); + return PackageManager.PERMISSION_GRANTED + == registrar.activity().checkSelfPermission(Manifest.permission.READ_CALL_LOG); + } + + @Override + public boolean onRequestPermissionsResult(int requestCode, + String[] strings, + int[] grantResults) { + boolean res = false; + if (requestCode == 0 && grantResults.length > 0) { + res = grantResults[0] == PackageManager.PERMISSION_GRANTED; + pendingResult.success(res); + pendingResult = null; + } + return res; + } + + private static final String[] PROJECTION = + {CallLog.Calls.CACHED_FORMATTED_NUMBER, + CallLog.Calls.CACHED_MATCHED_NUMBER, + CallLog.Calls.TYPE, + CallLog.Calls.DATE, + CallLog.Calls.DURATION,}; + + @TargetApi(Build.VERSION_CODES.M) + private void fetchCallRecords(String startDate, String duration) { + if (registrar.activity().checkSelfPermission(Manifest.permission.READ_CALL_LOG) + == PackageManager.PERMISSION_GRANTED) { + String selectionCondition = null; + if (startDate != null) { + selectionCondition = CallLog.Calls.DATE + "> " + startDate; + } + if (duration != null) { + String durationSelection = CallLog.Calls.DURATION + "> " + duration; + if (selectionCondition != null) { + selectionCondition = selectionCondition + " AND " + durationSelection; + } else { + selectionCondition = durationSelection; + } + } + Cursor cursor = registrar.context().getContentResolver().query( + CallLog.Calls.CONTENT_URI, PROJECTION, + selectionCondition, + null, CallLog.Calls.DATE + " DESC"); + + try { + ArrayList> records = getCallRecordMaps(cursor); + pendingResult.success(records); + pendingResult = null; + } catch (Exception e) { + Log.e("PhoneLog", "Error on fetching call record" + e); + pendingResult.error("PhoneLog", e.getMessage(), null); + pendingResult = null; + } finally { + if (cursor != null) { + cursor.close(); + } + } + + } else { + pendingResult.error("PhoneLog", "Permission is not granted", null); + pendingResult = null; + } + } + + + /** + * Builds the list of call record maps from the cursor + * + * @param cursor + * @return the list of maps + */ + private ArrayList> getCallRecordMaps(Cursor cursor) { + ArrayList> records = new ArrayList<>(); + + while (cursor != null && cursor.moveToNext()) { + CallRecord record = new CallRecord(); + record.formattedNumber = cursor.getString(0); + record.number = cursor.getString(1); + record.callType = getCallType(cursor.getInt(2)); + + Date date = new Date(cursor.getLong(3)); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + record.dateYear = cal.get(Calendar.YEAR); + record.dateMonth = cal.get(Calendar.MONTH); + record.dateDay = cal.get(Calendar.DAY_OF_MONTH); + record.dateHour = cal.get(Calendar.HOUR_OF_DAY); + record.dateMinute = cal.get(Calendar.MINUTE); + record.dateSecond = cal.get(Calendar.SECOND); + record.duration = cursor.getLong(4); + + records.add(record.toMap()); + } + return records; + } + + private String getCallType(int anInt) { + switch (anInt) { + case CallLog.Calls.INCOMING_TYPE: + return "INCOMING_TYPE"; + case CallLog.Calls.OUTGOING_TYPE: + return "OUTGOING_TYPE"; + case CallLog.Calls.MISSED_TYPE: + return "MISSED_TYPE"; + default: + break; + } + return null; + } +} diff --git a/packages/phone_log/example/.metadata b/packages/phone_log/example/.metadata new file mode 100644 index 0000000..8cab361 --- /dev/null +++ b/packages/phone_log/example/.metadata @@ -0,0 +1,8 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 44b7e7d3f42f050a79712daab253af06e9daf530 + channel: beta diff --git a/packages/phone_log/example/README.md b/packages/phone_log/example/README.md new file mode 100644 index 0000000..debe0a3 --- /dev/null +++ b/packages/phone_log/example/README.md @@ -0,0 +1,8 @@ +# phone_log_example + +Demonstrates how to use the phone_log plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.io/). diff --git a/packages/phone_log/example/android/app/build.gradle b/packages/phone_log/example/android/app/build.gradle new file mode 100644 index 0000000..00b0bad --- /dev/null +++ b/packages/phone_log/example/android/app/build.gradle @@ -0,0 +1,51 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 27 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.phonelogexample" + minSdkVersion 16 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' +} diff --git a/packages/phone_log/example/android/app/src/main/AndroidManifest.xml b/packages/phone_log/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0c2fb2b --- /dev/null +++ b/packages/phone_log/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/packages/phone_log/example/android/app/src/main/java/com/example/phonelogexample/MainActivity.java b/packages/phone_log/example/android/app/src/main/java/com/example/phonelogexample/MainActivity.java new file mode 100644 index 0000000..1c73ade --- /dev/null +++ b/packages/phone_log/example/android/app/src/main/java/com/example/phonelogexample/MainActivity.java @@ -0,0 +1,14 @@ +package com.example.phonelogexample; + +import android.os.Bundle; + +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/packages/phone_log/example/android/app/src/main/res/drawable/launch_background.xml b/packages/phone_log/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..5782842 --- /dev/null +++ b/packages/phone_log/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,4 @@ + + + + diff --git a/packages/phone_log/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/phone_log/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/packages/phone_log/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/phone_log/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/phone_log/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/packages/phone_log/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/phone_log/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/phone_log/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/packages/phone_log/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/phone_log/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/phone_log/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/packages/phone_log/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/phone_log/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/phone_log/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/packages/phone_log/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/phone_log/example/android/app/src/main/res/values/styles.xml b/packages/phone_log/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..00fa441 --- /dev/null +++ b/packages/phone_log/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/phone_log/example/android/build.gradle b/packages/phone_log/example/android/build.gradle new file mode 100644 index 0000000..4476887 --- /dev/null +++ b/packages/phone_log/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.0.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/phone_log/example/android/gradle.properties b/packages/phone_log/example/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/packages/phone_log/example/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/packages/phone_log/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/phone_log/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..aa901e1 --- /dev/null +++ b/packages/phone_log/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/packages/phone_log/example/android/settings.gradle b/packages/phone_log/example/android/settings.gradle new file mode 100644 index 0000000..5a2f14f --- /dev/null +++ b/packages/phone_log/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/phone_log/example/ios/Flutter/AppFrameworkInfo.plist b/packages/phone_log/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..6c2de80 --- /dev/null +++ b/packages/phone_log/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 8.0 + + diff --git a/packages/phone_log/example/ios/Flutter/Debug.xcconfig b/packages/phone_log/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..e8efba1 --- /dev/null +++ b/packages/phone_log/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/phone_log/example/ios/Flutter/Release.xcconfig b/packages/phone_log/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..399e934 --- /dev/null +++ b/packages/phone_log/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/phone_log/example/ios/Runner.xcodeproj/project.pbxproj b/packages/phone_log/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4caa8bc --- /dev/null +++ b/packages/phone_log/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,497 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 8E7A2CADC98BA27A5BFE6C9A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E46AD22DB085EB141B1C3 /* libPods-Runner.a */; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A93E46AD22DB085EB141B1C3 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 8E7A2CADC98BA27A5BFE6C9A /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, + 3B80C3931E831B6300D905FE /* App.framework */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + B2320E75BCE5DD82766C5A28 /* Pods */, + B26DCFF71A1389B9A2E55687 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + B2320E75BCE5DD82766C5A28 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = ""; + }; + B26DCFF71A1389B9A2E55687 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A93E46AD22DB085EB141B1C3 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + D3FAE181685D1E546EE2E398 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + AF907A40BFCA7C891761DC2B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AF907A40BFCA7C891761DC2B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/.symlinks/flutter/ios/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + D3FAE181685D1E546EE2E398 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneLogExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.phoneLogExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/phone_log/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/phone_log/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/packages/phone_log/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/phone_log/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/phone_log/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..1263ac8 --- /dev/null +++ b/packages/phone_log/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/phone_log/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/phone_log/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/phone_log/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/phone_log/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/phone_log/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/packages/phone_log/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/phone_log/example/ios/Runner/AppDelegate.h b/packages/phone_log/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..cf210d2 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/phone_log/example/ios/Runner/AppDelegate.m b/packages/phone_log/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..112becd --- /dev/null +++ b/packages/phone_log/example/ios/Runner/AppDelegate.m @@ -0,0 +1,12 @@ +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..3d43d11 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/phone_log/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/phone_log/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/phone_log/example/ios/Runner/Base.lproj/Main.storyboard b/packages/phone_log/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/phone_log/example/ios/Runner/Info.plist b/packages/phone_log/example/ios/Runner/Info.plist new file mode 100644 index 0000000..910bcd8 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + phone_log_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/phone_log/example/ios/Runner/main.m b/packages/phone_log/example/ios/Runner/main.m new file mode 100644 index 0000000..0ccc450 --- /dev/null +++ b/packages/phone_log/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/phone_log/example/lib/main.dart b/packages/phone_log/example/lib/main.dart new file mode 100644 index 0000000..f80cb37 --- /dev/null +++ b/packages/phone_log/example/lib/main.dart @@ -0,0 +1,109 @@ +import 'dart:async'; + +import 'package:fixnum/fixnum.dart'; +import 'package:flutter/material.dart'; +import 'package:phone_log/phone_log.dart'; + +void main() => runApp(new MyApp()); + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => new _MyAppState(); +} + +class _MyAppState extends State { + Iterable _callRecords; + var phoneLog = new PhoneLog(); + + Future fetchCallLogs() async { + var callLogs = await phoneLog.getPhoneLogs( + // startDate: 20180605, duration: 15 seconds + startDate: new Int64(1525590000000), + duration: new Int64(13)); + setState(() { + _callRecords = callLogs; + }); + } + + + requestPermission() async { + bool res = await phoneLog.requestPermission(); + print("permission request result is " + res.toString()); + } + + checkPermission() async { + bool res = await phoneLog.checkPermission(); + print("permission is " + res.toString()); + } + + @override + Widget build(BuildContext context) { + var children = [ + new Padding( + padding: const EdgeInsets.all(8.0), + child: new RaisedButton( + onPressed: checkPermission, child: new Text("Check permission")), + ), + new Padding( + padding: const EdgeInsets.all(8.0), + child: new RaisedButton( + onPressed: requestPermission, + child: new Text("Request permission")), + ), + new Padding( + padding: const EdgeInsets.all(8.0), + child: new RaisedButton( + onPressed: fetchCallLogs, child: new Text("Fetch phone log"))), + ]; + + for (CallRecord call in _callRecords ?? []) { + children.addAll([ + new Container( + height: 16.0, + ), + new Row( + children: [ + new Text(call.formattedNumber ?? call.number ?? 'unknow'), + new Padding( + child: new Text(call.callType), + padding: const EdgeInsets.only(left: 8.0), + ), + ], + crossAxisAlignment: CrossAxisAlignment.center, + ), + new Row( + children: [ + new Padding( + child: new Text(call.dateYear.toString() + + '-' + + call.dateMonth.toString() + + '-' + + call.dateDay.toString() + + ' ' + + call.dateHour.toString() + + ': ' + + call.dateMinute.toString() + + ': ' + + call.dateSecond.toString()), + padding: const EdgeInsets.only(left: 8.0), + ), + new Padding( + child: new Text(call.duration.toString() + 'seconds'), + padding: const EdgeInsets.only(left: 8.0)) + ], + crossAxisAlignment: CrossAxisAlignment.center, + ) + ]); + } + + return new MaterialApp( + home: new Scaffold( + appBar: new AppBar(title: new Text('PhoneLog plugin example')), + body: new Center( + child: new Column(children: children), + ), + ), + ); + } +} + diff --git a/packages/phone_log/example/pubspec.yaml b/packages/phone_log/example/pubspec.yaml new file mode 100644 index 0000000..f890813 --- /dev/null +++ b/packages/phone_log/example/pubspec.yaml @@ -0,0 +1,60 @@ +name: phone_log_example +description: Demonstrates how to use the phone_log plugin. + +dependencies: + flutter: + sdk: flutter + fixnum: ^0.10.7 + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + phone_log: + path: ../ + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.io/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.io/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.io/custom-fonts/#from-packages diff --git a/packages/phone_log/ios/Assets/.gitkeep b/packages/phone_log/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/phone_log/ios/Classes/PhoneLogPlugin.h b/packages/phone_log/ios/Classes/PhoneLogPlugin.h new file mode 100644 index 0000000..c253019 --- /dev/null +++ b/packages/phone_log/ios/Classes/PhoneLogPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface PhoneLogPlugin : NSObject +@end diff --git a/packages/phone_log/ios/Classes/PhoneLogPlugin.m b/packages/phone_log/ios/Classes/PhoneLogPlugin.m new file mode 100644 index 0000000..16fd6df --- /dev/null +++ b/packages/phone_log/ios/Classes/PhoneLogPlugin.m @@ -0,0 +1,22 @@ +#import "PhoneLogPlugin.h" + +@implementation PhoneLogPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"github.com/jiajiabingcheng/phone_log" + binaryMessenger:[registrar messenger]]; + PhoneLogPlugin* instance = [[PhoneLogPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([@"checkPermission" isEqualToString:call.method] || + [@"requestPermission" isEqualToString:call.method] || + [@"getPhoneLogs" isEqualToString:call.method]) { + result(nil); + } else { + result(FlutterMethodNotImplemented); + } +} + +@end diff --git a/packages/phone_log/ios/phone_log.podspec b/packages/phone_log/ios/phone_log.podspec new file mode 100644 index 0000000..31587fc --- /dev/null +++ b/packages/phone_log/ios/phone_log.podspec @@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'phone_log' + s.version = '0.0.1' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC +A new flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + + s.ios.deployment_target = '8.0' +end + diff --git a/packages/phone_log/lib/phone_log.dart b/packages/phone_log/lib/phone_log.dart new file mode 100644 index 0000000..f678709 --- /dev/null +++ b/packages/phone_log/lib/phone_log.dart @@ -0,0 +1,76 @@ +import 'dart:async'; + +import 'package:fixnum/fixnum.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +/// Provide methods to access and fetch the phone log. +class PhoneLog { + final MethodChannel _channel; + + static final PhoneLog _instance = new PhoneLog.private( + const MethodChannel('github.com/jiajiabingcheng/phone_log')); + + /// Provides an instance of this class. + factory PhoneLog() => _instance; + + @visibleForTesting + PhoneLog.private(MethodChannel platformChannel):_channel = platformChannel; + + /// Check a [permission] and return a [Future] with the result + Future checkPermission() async { + final bool isGranted = await _channel.invokeMethod("checkPermission", null); + return isGranted; + } + + /// Request a [permission] and return a [Future] with the result + Future requestPermission() async { + final bool isGranted = + await _channel.invokeMethod("requestPermission", null); + return isGranted; + } + + ///Fetches phone logs + /// + ///The unit of [startDate] is the Milliseconds of date. + ///The unit of [duration] is second. + Future> getPhoneLogs( + {Int64 startDate, Int64 duration}) async { + var _startDate = startDate?.toString(); + var _duration = duration?.toString(); + Iterable records = await _channel.invokeMethod( + 'getPhoneLogs', {"startDate": _startDate, "duration": _duration}); + return records?.map((m) => new CallRecord.fromMap(m)); + } +} + +/// The class that carries all the data for one call history entry. +class CallRecord { + CallRecord({ + this.formattedNumber, + this.number, + this.callType, + this.dateYear, + this.dateMonth, + this.dateHour, + this.dateMinute, + this.dateSecond, + this.duration, + }); + + String formattedNumber, number, callType; + int dateYear, dateMonth, dateDay, dateHour, dateMinute, dateSecond, duration; + + CallRecord.fromMap(Map m) { + formattedNumber = m['formattedNumber']; + number = m['number']; + callType = m['callType']; + dateYear = m['dateYear']; + dateMonth = m['dateMonth']; + dateDay = m['dateDay']; + dateHour = m['dateHour']; + dateMinute = m['dateMinute']; + dateSecond = m['dateSecond']; + duration = m['duration']; + } +} diff --git a/packages/phone_log/pubspec.yaml b/packages/phone_log/pubspec.yaml new file mode 100644 index 0000000..e8fe5e3 --- /dev/null +++ b/packages/phone_log/pubspec.yaml @@ -0,0 +1,24 @@ +name: phone_log +description: A new flutter plugin project. +version: 0.0.2 +author: Jiaming Cheng +homepage: https://github.com/jiajiabingcheng/phone_log + +environment: + sdk: ">=1.19.0 <2.0.0" + +dependencies: + flutter: + sdk: flutter + fixnum: ^0.10.7 + +# The following section is specific to Flutter. +flutter: + plugin: + androidPackage: com.jiajiabingcheng.phonelog + pluginClass: PhoneLogPlugin + +dev_dependencies: + mockito: ^2.0.2 + flutter_test: + sdk: flutter diff --git a/packages/phone_log/test/phone_log_test.dart b/packages/phone_log/test/phone_log_test.dart new file mode 100644 index 0000000..60fc5cd --- /dev/null +++ b/packages/phone_log/test/phone_log_test.dart @@ -0,0 +1,86 @@ +import 'dart:async'; + +import 'package:fixnum/fixnum.dart'; +import 'package:flutter/services.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'package:phone_log/phone_log.dart'; + +void main() { + String invokedMethod; + dynamic arguments; + MockPlatformChannel mockChannel; + MockPlatformChannel mockChannelForGetLogs; + + setUp(() { + mockChannel = new MockPlatformChannel(); + mockChannelForGetLogs = new MockPlatformChannel(); + + when(mockChannel.invokeMethod(typed(any), any)) + .thenAnswer((Invocation invocation) { + invokedMethod = invocation.positionalArguments[0]; + arguments = invocation.positionalArguments[1]; + }); + + when(mockChannelForGetLogs.invokeMethod('getPhoneLogs', any)) + .thenReturn(new Future(() => [ + { + 'formattedNumber': '123 123 1234', + 'number': '1231231234', + 'callType': 'INCOMING_TYPE', + 'dateYear': 2018, + 'dateMonth': 6, + 'dateDay': 15, + 'dateHour': 3, + 'dateMinute': 16, + 'dateSecond': 23, + 'duration': 123 + } + ])); + }); + + group('Phone log plugin', () { + test('fetch phone log', () async { + var phoneLog = new PhoneLog.private(mockChannelForGetLogs); + + var records = await phoneLog.getPhoneLogs( + startDate: new Int64(123456789), duration: new Int64(12)); + + print(records); + var record = records.first; + + expect(record.formattedNumber, '123 123 1234'); + expect(record.callType, 'INCOMING_TYPE'); + expect(record.number, '1231231234'); + expect(record.dateYear, 2018); + expect(record.duration, 123); + + var phoneLogMethod = new PhoneLog.private(mockChannel); + await phoneLogMethod.getPhoneLogs( + startDate: new Int64(123456789), duration: new Int64(12)); + expect(invokedMethod, 'getPhoneLogs'); + expect(arguments, {'startDate': '123456789', 'duration': '12'}); + }); + + test('check permission', () async { + var phoneLog = new PhoneLog.private(mockChannel); + + await phoneLog.checkPermission(); + + expect(invokedMethod, 'checkPermission'); + expect(arguments, null); + }); + + test('request permission', () async { + var phoneLog = new PhoneLog.private(mockChannel); + + await phoneLog.requestPermission(); + + expect(invokedMethod, 'requestPermission'); + expect(arguments, null); + }); + }); +} + +class MockPlatformChannel extends Mock implements MethodChannel {}