Merge "FP2-1321:[ST_2][Monkey] 'com.android.systemui' crash once." into fp2-dev-v2
diff --git a/Android.mk b/Android.mk
index 102dfcf..d81bdd9 100755
--- a/Android.mk
+++ b/Android.mk
@@ -209,6 +209,7 @@
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/IPermissionController.aidl \
core/java/android/os/IPowerManager.aidl \
+ core/java/android/os/IPrivacyImpactService.aidl \
core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/ISchedulingPolicyService.aidl \
core/java/android/os/IUpdateLock.aidl \
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9568897..781664f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3984,6 +3984,7 @@
this.startActivity(intent, null);
}
+
/**
* Launch a new activity. You will not receive any information about when
* the activity exits. This implementation overrides the base version,
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ce25983..bef76c7 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -46,6 +46,7 @@
import android.os.ServiceManager;
import android.os.StrictMode;
import android.service.voice.IVoiceInteractionSession;
+import android.os.IPrivacyImpactService;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -2401,6 +2402,33 @@
{
return mRemote;
}
+
+ private static final String PRIVACY_IMPACT_PACKAGE = "com.fairphone.privacyimpact";
+ private static final String PRIVACY_IMPACT_ACTIVITY = "com.fairphone.privacyimpact.GrantAccessActivity";
+
+ private static Intent wrapPrivacyImpact(Intent intent, Bundle options, int requestCode){
+ Intent newIntent = intent;
+ try {
+ IPrivacyImpactService pis = IPrivacyImpactService.Stub.asInterface(ServiceManager.getService("PrivacyImpact"));
+ if( intent != null &&
+ intent.getComponent() != null &&
+ pis.showPackagePrivacy(intent.getComponent().getPackageName())) {
+ Log.i(TAG_TIMELINE, "Timeline: Activity_first_launch "+
+ intent.getComponent().getPackageName() + " time:"
+ + SystemClock.uptimeMillis());
+ // create the new intent
+ Intent privacyIntent = new Intent();
+ privacyIntent.setComponent(new ComponentName(PRIVACY_IMPACT_PACKAGE, PRIVACY_IMPACT_ACTIVITY));
+ privacyIntent.putExtra("originalIntent", intent);
+ privacyIntent.putExtra("originalOptions", options);
+ privacyIntent.putExtra("originalRequestCode", requestCode);
+ newIntent = privacyIntent;
+ }
+ } catch (Exception e) {
+ Log.e("ActivityManager", "Failed to wrap intent "+intent+" for PrivacyImpact", e);
+ }
+ return newIntent;
+ }
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -2412,6 +2440,7 @@
Log.i(TAG_TIMELINE, "Timeline: Activity_launch_request id:"
+ intent.getComponent().getPackageName() + " time:"
+ SystemClock.uptimeMillis());
+ intent = wrapPrivacyImpact(intent, options, requestCode);
}
data.writeInterfaceToken(IActivityManager.descriptor);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cf14202..a576cf7 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -20,10 +20,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.Handler;
@@ -31,7 +34,9 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.os.ServiceManager;
import android.util.AndroidException;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -282,6 +287,7 @@
*/
public static PendingIntent getActivity(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags, @Nullable Bundle options) {
+
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
@@ -425,9 +431,9 @@
String packageName = context.getPackageName();
String[] resolvedTypes = new String[intents.length];
for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
intents[i].migrateExtraStreamToClipData();
intents[i].prepareToLeaveProcess();
- resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
try {
IIntentSender target =
@@ -451,9 +457,9 @@
String packageName = context.getPackageName();
String[] resolvedTypes = new String[intents.length];
for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
intents[i].migrateExtraStreamToClipData();
intents[i].prepareToLeaveProcess();
- resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
try {
IIntentSender target =
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 8e86824..7eb4b2f 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -221,10 +221,10 @@
int appWidgetId, int intentFlags, int requestCode, @Nullable Bundle options) {
try {
IntentSender intentSender = sService.createAppWidgetConfigIntentSender(
- mContextOpPackageName, appWidgetId, intentFlags);
+ mContextOpPackageName, appWidgetId);
if (intentSender != null) {
- activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0,
- options);
+ activity.startIntentSenderForResult(intentSender, requestCode, null, 0,
+ intentFlags, intentFlags, options);
} else {
throw new ActivityNotFoundException();
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 49291a7..2e3f2b9 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -205,14 +205,11 @@
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- try {
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
- CancellationSignal.fromTransport(cancellationSignal));
- }
- } catch (Exception e) {
- e.printStackTrace();
+ if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
+ CancellationSignal.fromTransport(cancellationSignal));
}
+
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 61a381c..24a7d33 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -553,9 +553,6 @@
attachCancellationSignal(cancellationSignal);
try {
nativeExecute(mConnectionPtr, statement.mStatementPtr);
- } catch (Exception e) {
- Log.e(TAG, "execute error: " + e);
- e.printStackTrace();
} finally {
detachCancellationSignal(cancellationSignal);
}
diff --git a/core/java/android/os/IPrivacyImpactService.aidl b/core/java/android/os/IPrivacyImpactService.aidl
new file mode 100644
index 0000000..de63781
--- /dev/null
+++ b/core/java/android/os/IPrivacyImpactService.aidl
@@ -0,0 +1,9 @@
+package android.os;
+
+/**
+* {@hide}
+*/
+interface IPrivacyImpactService {
+ boolean showPackagePrivacy(String packageName);
+ void disablePackagePrivacy(String packageName);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0efb666..543c3dc 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -575,7 +575,19 @@
int length = list.length;
StorageVolume[] result = new StorageVolume[length];
for (int i = 0; i < length; i++) {
- result[i] = (StorageVolume)list[i];
+ if (length == 3) {
+ if (list[i].toString().contains("emulated")) {
+ result[0] = (StorageVolume) list[i];
+ }
+ if (list[i].toString().contains("sdcard1")) {
+ result[1] = (StorageVolume) list[i];
+ }
+ if (list[i].toString().contains("usbotg")) {
+ result[2] = (StorageVolume) list[i];
+ }
+ } else {
+ result[i] = (StorageVolume) list[i];
+ }
}
return result;
} catch (RemoteException e) {
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 3fb812e..415883f 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -988,7 +988,7 @@
try {
callback.onWriteFailed(error, mSequence);
- } catch (RemoteException re) {
+ } catch (RemoteException | IllegalStateException re) {
Log.e(LOG_TAG, "Error calling onWriteFailed", re);
} finally {
destroy();
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f93d1d9..c1341e1 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -227,9 +227,7 @@
}
if ((mask & PHONE_NUMBERS) != 0) {
- gatherLinks(links, text, Patterns.PHONE,
- new String[] { "tel:" },
- sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);
+ gatherTelLinks(links, text);
}
if ((mask & MAP_ADDRESSES) != 0) {
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 008d38b..faac392 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -41,8 +41,7 @@
void deleteAllHosts();
RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId);
int[] getAppWidgetIdsForHost(String callingPackage, int hostId);
- IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId,
- int intentFlags);
+ IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId);
//
// for AppWidgetManager
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index d7eef6e..3079218 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -575,24 +575,33 @@
return NULL;
}
- SkBitmap* bitmap = new SkBitmap;
+ SkAutoTDelete<SkBitmap> bitmap(new SkBitmap);
- bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes);
+ if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes)) {
+ return NULL;
+ }
SkColorTable* ctable = NULL;
if (colorType == kIndex_8_SkColorType) {
int count = p->readInt32();
+ if (count < 0 || count > 256) {
+ // The data is corrupt, since SkColorTable enforces a value between 0 and 256,
+ // inclusive.
+ return NULL;
+ }
if (count > 0) {
size_t size = count * sizeof(SkPMColor);
const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
+ if (src == NULL) {
+ return NULL;
+ }
ctable = new SkColorTable(src, count);
}
}
- jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+ jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
if (NULL == buffer) {
SkSafeUnref(ctable);
- delete bitmap;
return NULL;
}
@@ -604,7 +613,6 @@
android::status_t status = p->readBlob(size, &blob);
if (status) {
doThrowRE(env, "Could not read bitmap from parcel blob.");
- delete bitmap;
return NULL;
}
@@ -614,8 +622,8 @@
blob.release();
- return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
- NULL, NULL, density);
+ return GraphicsJNI::createBitmap(env, bitmap.detach(), buffer,
+ getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f6a5787..9bd1094 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -442,12 +442,16 @@
</string-array>
<array name="sim_colors">
+ <item>@color/fp_blue</item>
+ <item>@color/fp_blue_dark_2</item>
+<!--
<item>@color/Teal_700</item>
<item>@color/Blue_700</item>
<item>@color/Indigo_700</item>
<item>@color/Purple_700</item>
<item>@color/Pink_700</item>
<item>@color/Red_700</item>
+-->
</array>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b9825c5..9392386 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -174,4 +174,13 @@
<color name="Pink_800">#ffad1457</color>
<color name="Red_700">#ffc53929</color>
<color name="Red_800">#ffb93221</color>
+
+ <!-- Fairphone Colors -->
+ <color name="fp_blue">#2aa8e0</color>
+ <color name="fp_blue_dark">#005599</color>
+ <color name="fp_blue_dark_2">#123c59</color>
+ <color name="fp_manufacturing_green">#287955</color>
+ <color name="fp_design_pink">#d7617f</color>
+ <color name="fp_life_cycle_green">#92b851</color>
+ <color name="fp_mining_yellow">#f5a833</color>
</resources>
diff --git a/packages/FairphonePrivacyImpact/.gitignore b/packages/FairphonePrivacyImpact/.gitignore
new file mode 100644
index 0000000..a82817d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/.gitignore
@@ -0,0 +1,45 @@
+# Ignore OS specific files #
+.DS_Store
+.directory
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+.metadata
+RemoteSystemsTempFiles/
+
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Ignore gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Ignore Eclipse files #
+.project
+.classpath
+.settings/
+.idea/
+*.iml
+# Proguard folder generated by Eclipse
+proguard/
+
+res/values/com_crashlytics_export_strings.xml
+assets/crashlytics-build.properties
+androidCleaner.py
diff --git a/packages/FairphonePrivacyImpact/Android.mk b/packages/FairphonePrivacyImpact/Android.mk
new file mode 100644
index 0000000..0fd95b8
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/Android.mk
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v13 android-support-v7-appcompat
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_SDK_VERSION := current
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/appcompat/res
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages android.support.v7.appcompat
+
+LOCAL_PACKAGE_NAME := FairphonePrivacyImpact
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+include $(BUILD_MULTI_PREBUILT)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/FairphonePrivacyImpact/AndroidManifest.xml b/packages/FairphonePrivacyImpact/AndroidManifest.xml
new file mode 100644
index 0000000..26ca98b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/AndroidManifest.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.fairphone.privacyimpact"
+ android:versionCode="1"
+ android:versionName="1.0"
+ coreApp="true"
+ android:sharedUserId="android.uid.system">
+
+ <uses-sdk
+ android:minSdkVersion="17"
+ android:targetSdkVersion="21" />
+
+ <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme">
+
+ <activity
+ android:name="com.fairphone.privacyimpact.ApplicationSampleLauncher"
+ android:label="@string/activity_sample_launcher"
+ android:screenOrientation="portrait">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="com.fairphone.privacyimpact.GrantAccessActivity"
+ android:autoRemoveFromRecents="true"
+ android:clearTaskOnLaunch="true"
+ android:excludeFromRecents="true"
+ android:label="@string/app_name"
+ android:launchMode="singleTop"
+ android:screenOrientation="portrait">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="com.fairphone.privacyimpact.PrivacyImpactPreferenceActivity"
+ android:autoRemoveFromRecents="true"
+ android:clearTaskOnLaunch="true"
+ android:excludeFromRecents="true"
+ android:label="@string/activity_set_preference_name"
+ android:icon="@drawable/ic_settings_black_48dp"
+ android:theme="@style/AppTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/packages/FairphonePrivacyImpact/build.gradle b/packages/FairphonePrivacyImpact/build.gradle
new file mode 100644
index 0000000..c854d7a
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/build.gradle
@@ -0,0 +1,71 @@
+// Top level gradle declarations
+
+buildscript {
+ repositories {
+ jcenter()
+ maven { url 'http://repo1.maven.org/maven2' }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.2.3'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+// Module level gradle declarations
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "com.fairphone.privacyimpact"
+ minSdkVersion 17
+ targetSdkVersion 21
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+
+ // Move the tests to tests/java, tests/res, etc...
+ instrumentTest.setRoot('tests')
+ androidTest.setRoot('tests')
+
+ // Move the build types to build-types/<type>
+ // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
+ // This moves them out of them default location under src/<type>/... which would
+ // conflict with src/ being used by the main source set.
+ // Adding new build types or product flavors should be accompanied
+ // by a similar customization.
+ debug.setRoot('build-types/debug')
+ release.setRoot('build-types/release')
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v13:21.+'
+ compile 'com.android.support:appcompat-v7:21.+'
+}
+
diff --git a/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.jar b/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.properties b/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/packages/FairphonePrivacyImpact/gradlew b/packages/FairphonePrivacyImpact/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/packages/FairphonePrivacyImpact/gradlew.bat b/packages/FairphonePrivacyImpact/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/FairphonePrivacyImpact/ic_launcher-web.png b/packages/FairphonePrivacyImpact/ic_launcher-web.png
new file mode 100644
index 0000000..421fd02
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/ic_launcher-web.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/libs/android-support-v4.jar b/packages/FairphonePrivacyImpact/libs/android-support-v4.jar
new file mode 100644
index 0000000..4ebdaa9
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/libs/android-support-v4.jar
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/proguard-project.txt b/packages/FairphonePrivacyImpact/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/packages/FairphonePrivacyImpact/res/color/button_blue_border_text_states.xml b/packages/FairphonePrivacyImpact/res/color/button_blue_border_text_states.xml
new file mode 100644
index 0000000..3ffe5d4
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/color/button_blue_border_text_states.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:color="@color/white" android:state_pressed="true"/>
+ <item android:color="@color/blue"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/color/simple_button_text_color.xml b/packages/FairphonePrivacyImpact/res/color/simple_button_text_color.xml
new file mode 100644
index 0000000..048cf67
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/color/simple_button_text_color.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:color="@color/text_grey_dark" android:state_pressed="true"/>
+ <item android:color="@color/blue"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/color/submenu_toggle_button_text_color.xml b/packages/FairphonePrivacyImpact/res/color/submenu_toggle_button_text_color.xml
new file mode 100644
index 0000000..2bb0f35
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/color/submenu_toggle_button_text_color.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:color="@color/blue" android:state_checked="true"/>
+ <item android:color="@color/text_grey_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..288b665
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_settings_black_48dp.png b/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_settings_black_48dp.png
new file mode 100644
index 0000000..3023ff8
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-hdpi/ic_settings_black_48dp.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_generic_permission.png b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_generic_permission.png
new file mode 100644
index 0000000..02d08f5
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_generic_permission.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_down.png b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_down.png
new file mode 100644
index 0000000..a1cfb59
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_down.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_up.png b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_up.png
new file mode 100644
index 0000000..8c7f2d9
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-hdpi/icon_reveal_arrow_up.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6ae570b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_settings_black_48dp.png b/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_settings_black_48dp.png
new file mode 100644
index 0000000..e84e188
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-mdpi/ic_settings_black_48dp.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_generic_permission.png b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_generic_permission.png
new file mode 100644
index 0000000..57497b4
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_generic_permission.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_down.png b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_down.png
new file mode 100644
index 0000000..a6009c0
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_down.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_up.png b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_up.png
new file mode 100644
index 0000000..36845cf
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-mdpi/icon_reveal_arrow_up.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d4fb7cd
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_settings_black_48dp.png b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_settings_black_48dp.png
new file mode 100644
index 0000000..476d5c9
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/ic_settings_black_48dp.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_generic_permission.png b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_generic_permission.png
new file mode 100644
index 0000000..55a2a26
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_generic_permission.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_down.png b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_down.png
new file mode 100644
index 0000000..f3ce1a0
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_down.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_up.png b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_up.png
new file mode 100644
index 0000000..211d42d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xhdpi/icon_reveal_arrow_up.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/app_permission_sub_menu_background.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/app_permission_sub_menu_background.png
new file mode 100644
index 0000000..190a179
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/app_permission_sub_menu_background.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_check.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_check.png
new file mode 100644
index 0000000..55f0712
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_check.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png
new file mode 100644
index 0000000..84575bd
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_settings_black_48dp.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_settings_black_48dp.png
new file mode 100644
index 0000000..8f2a8bc
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/ic_settings_black_48dp.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_generic_permission.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_generic_permission.png
new file mode 100644
index 0000000..cf8c0fb
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_generic_permission.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_header_back.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_header_back.png
new file mode 100644
index 0000000..09dfe2f
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_header_back.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_location_grey.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_location_grey.png
new file mode 100644
index 0000000..81d20d1
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_location_grey.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_down.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_down.png
new file mode 100644
index 0000000..3ca2d02
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_down.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_up.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_up.png
new file mode 100644
index 0000000..653a62b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/icon_reveal_arrow_up.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_blue.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_blue.png
new file mode 100644
index 0000000..1246e05
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_blue.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_green.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_green.png
new file mode 100644
index 0000000..a711586
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_green.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_grey.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_grey.png
new file mode 100644
index 0000000..a4b32d6
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_background_grey.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_blue.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_blue.png
new file mode 100644
index 0000000..5666752
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_blue.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_grey.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_grey.png
new file mode 100644
index 0000000..7f36852
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_small_background_grey.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_blue.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_blue.png
new file mode 100644
index 0000000..a691da6
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_blue.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_green.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_green.png
new file mode 100644
index 0000000..0c6fdb8
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_green.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_grey.png b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_grey.png
new file mode 100644
index 0000000..4d14731
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxhdpi/toggle_switch_thumb_grey.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/ic_settings_black_48dp.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/ic_settings_black_48dp.png
new file mode 100644
index 0000000..e4d24ea
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/ic_settings_black_48dp.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_generic_permission.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_generic_permission.png
new file mode 100644
index 0000000..be90e15
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_generic_permission.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_down.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_down.png
new file mode 100644
index 0000000..82bac50
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_down.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_up.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_up.png
new file mode 100644
index 0000000..19d2bb8
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/icon_reveal_arrow_up.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_left_down.9.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_left_down.9.png
new file mode 100644
index 0000000..56552f6
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_left_down.9.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_up.9.png b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_up.9.png
new file mode 100644
index 0000000..cdb1ce0
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable-xxxhdpi/oobe_popup_arrow_up.9.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/drawable/button_blue_border.xml b/packages/FairphonePrivacyImpact/res/drawable/button_blue_border.xml
new file mode 100644
index 0000000..13d7237
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/button_blue_border.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke android:width="1dp" android:color="@color/blue" />
+</shape>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/button_blue_border_states.xml b/packages/FairphonePrivacyImpact/res/drawable/button_blue_border_states.xml
new file mode 100644
index 0000000..e94782a
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/button_blue_border_states.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@color/blue" android:state_pressed="true"/>
+ <item android:drawable="@drawable/button_blue_border"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/button_blue_dark_grey_background.xml b/packages/FairphonePrivacyImpact/res/drawable/button_blue_dark_grey_background.xml
new file mode 100644
index 0000000..93dcd63
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/button_blue_dark_grey_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@color/grey_light" android:state_enabled="false"/>
+ <item android:drawable="@color/grey_dark" android:state_pressed="true"/>
+ <item android:drawable="@color/blue_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/button_blue_states.xml b/packages/FairphonePrivacyImpact/res/drawable/button_blue_states.xml
new file mode 100644
index 0000000..87a798b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/button_blue_states.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@color/blue_dark" android:state_pressed="true"/>
+ <item android:drawable="@color/blue"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/button_grey_background.xml b/packages/FairphonePrivacyImpact/res/drawable/button_grey_background.xml
new file mode 100644
index 0000000..57c942f
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/button_grey_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@color/grey_transparent" android:state_enabled="false"/>
+ <item android:drawable="@color/grey" android:state_pressed="true"/>
+ <item android:drawable="@color/grey_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/checkbox_button_blue.xml b/packages/FairphonePrivacyImpact/res/drawable/checkbox_button_blue.xml
new file mode 100644
index 0000000..37dadd8
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/checkbox_button_blue.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/checkbox_button_blue_check" android:state_checked="true"/>
+ <item android:drawable="@drawable/checkbox_button_blue_uncheck"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/privacy_high.xml b/packages/FairphonePrivacyImpact/res/drawable/privacy_high.xml
new file mode 100644
index 0000000..faee718
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/privacy_high.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/red_dark" android:state_pressed="true"/>
+ <item android:drawable="@color/orange_dark" />
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/privacy_low.xml b/packages/FairphonePrivacyImpact/res/drawable/privacy_low.xml
new file mode 100644
index 0000000..2cc039b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/privacy_low.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/green_dark" android:state_pressed="true" />
+ <item android:drawable="@color/green" />
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/privacy_medium.xml b/packages/FairphonePrivacyImpact/res/drawable/privacy_medium.xml
new file mode 100644
index 0000000..385b6bf
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/privacy_medium.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/orange_dark" android:state_pressed="true"/>
+ <item android:drawable="@color/orange" />
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/privacy_none.xml b/packages/FairphonePrivacyImpact/res/drawable/privacy_none.xml
new file mode 100644
index 0000000..16f4b6a
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/privacy_none.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/blue_dark" android:state_pressed="true"/>
+ <item android:drawable="@color/blue" />
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/submenu_toggle_button_background.xml b/packages/FairphonePrivacyImpact/res/drawable/submenu_toggle_button_background.xml
new file mode 100644
index 0000000..896370d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/submenu_toggle_button_background.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/app_permission_sub_menu_background" android:state_checked="true"/>
+ <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/switch_thumb.xml b/packages/FairphonePrivacyImpact/res/drawable/switch_thumb.xml
new file mode 100644
index 0000000..70fa353
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/switch_thumb.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/toggle_switch_thumb_blue"
+ android:state_checked="true"/>
+ <item android:drawable="@drawable/toggle_switch_thumb_grey"/>
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/switch_track.xml b/packages/FairphonePrivacyImpact/res/drawable/switch_track.xml
new file mode 100644
index 0000000..14376ab
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/switch_track.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/toggle_switch_small_background_blue" android:state_checked="true" />
+ <item android:drawable="@drawable/toggle_switch_small_background_grey"/>
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_blue_background.xml b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_blue_background.xml
new file mode 100644
index 0000000..a8cc7aa
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_blue_background.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/toggle_switch_thumb_blue" android:state_checked="true"/>
+ <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_green_background.xml b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_green_background.xml
new file mode 100644
index 0000000..0c562a5
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_green_background.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/toggle_switch_thumb_green" android:state_checked="true"/>
+ <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_grey_background.xml b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_grey_background.xml
new file mode 100644
index 0000000..06bcbf3
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/drawable/triple_toggle_button_grey_background.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/toggle_switch_thumb_grey" android:state_checked="true"/>
+ <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/layout/activity_application_sample_launcher.xml b/packages/FairphonePrivacyImpact/res/layout/activity_application_sample_launcher.xml
new file mode 100644
index 0000000..aa64b93
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/layout/activity_application_sample_launcher.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@+id/app_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="10dp"
+ android:background="#ffffff"></ListView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/layout/activity_grant_access.xml b/packages/FairphonePrivacyImpact/res/layout/activity_grant_access.xml
new file mode 100644
index 0000000..9061675
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/layout/activity_grant_access.xml
@@ -0,0 +1,311 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_light"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_white"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/app_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="41dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="60dp"
+ android:layout_height="60dp"
+ android:scaleType="fitXY" />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5.7dp"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-thin"
+ android:lines="1"
+ android:textColor="@color/text_grey"
+ android:textSize="34sp" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/privacy_group"
+ android:layout_width="match_parent"
+ android:layout_height="80dp"
+ android:layout_marginTop="30dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/privacy_level_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:lines="1"
+ android:text="@string/privacy_impact"
+ android:textAllCaps="true"
+ android:textColor="@color/white"
+ android:textSize="20dp" />
+
+ <TextView
+ android:id="@+id/privacy_level"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:lines="1"
+ android:text="@string/privacy_level_low"
+ android:textAllCaps="true"
+ android:textColor="@color/white"
+ android:textSize="20dp"
+ android:textStyle="bold" />
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:lines="1"
+ android:text="@string/what_does_this_mean"
+ android:textColor="@color/white"
+ android:textSize="12dp" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/notification_switch"
+ android:layout_width="match_parent"
+ android:layout_height="86dp"
+ android:background="@color/white">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_list_item_height" />
+
+ <LinearLayout
+ android:id="@+id/notification_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="42.5dp"
+ android:layout_marginRight="221.5dp"
+ android:layout_marginTop="30.3dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif"
+ android:includeFontPadding="false"
+ android:maxLines="1"
+ android:text="@string/notifications"
+ android:textColor="@color/text_grey"
+ android:textSize="16sp" />
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4.1dp"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif"
+ android:maxLines="1"
+ android:text="@string/notifications_off"
+ android:textSize="13sp" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/triple_switch_group"
+ android:layout_width="@dimen/notification_triple_switch_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginRight="46.7dp">
+
+ <View
+ android:id="@+id/toggle_background"
+ android:layout_width="@dimen/notification_triple_switch_background_width"
+ android:layout_height="@dimen/notification_triple_switch_background_height"
+ android:layout_marginLeft="@dimen/notification_triple_switch_background_margin_left"
+ android:layout_marginTop="@dimen/notification_triple_switch_background_margin_top"
+ android:background="@drawable/toggle_switch_background_grey" />
+
+ <LinearLayout
+ android:layout_width="@dimen/notification_triple_switch_width"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ToggleButton
+ android:id="@+id/off_toggle_button"
+ android:layout_width="@dimen/notification_triple_switch_toggle_size"
+ android:layout_height="@dimen/notification_triple_switch_toggle_size"
+ android:background="@drawable/triple_toggle_button_grey_background"
+ android:checked="true"
+ android:textOff=""
+ android:textOn="" />
+
+ <ToggleButton
+ android:id="@+id/on_toggle_button"
+ android:layout_width="@dimen/notification_triple_switch_toggle_size"
+ android:layout_height="@dimen/notification_triple_switch_toggle_size"
+ android:layout_marginLeft="@dimen/notification_triple_switch_toggle_gap"
+ android:layout_marginRight="@dimen/notification_triple_switch_toggle_gap"
+ android:background="@drawable/triple_toggle_button_blue_background"
+ android:textOff=""
+ android:textOn="" />
+
+ <ToggleButton
+ android:id="@+id/priority_toggle_button"
+ android:layout_width="@dimen/notification_triple_switch_toggle_size"
+ android:layout_height="@dimen/notification_triple_switch_toggle_size"
+ android:background="@drawable/triple_toggle_button_green_background"
+ android:textOff=""
+ android:textOn="" />
+ </LinearLayout>
+ </FrameLayout>
+ </FrameLayout>
+
+ <CheckBox
+ android:id="@+id/hide_privacy_impact_checkbox"
+ style="@style/CheckBoxBlue"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="42.5dp"
+ android:layout_marginRight="32dp"
+ android:layout_marginTop="23.7dp"
+ android:text="@string/dismiss_privacy_impact" />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/take_me_back_button"
+ style="@style/button_blue_border"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginLeft="@dimen/notification_horizontal_margin"
+ android:layout_marginRight="@dimen/notification_horizontal_margin"
+ android:text="@string/take_me_back"
+ android:textAllCaps="true" />
+
+ <TextView
+ android:id="@+id/start_the_app_button"
+ style="@style/button_blue"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginLeft="@dimen/notification_horizontal_margin"
+ android:layout_marginRight="@dimen/notification_horizontal_margin"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="15.7dp"
+ android:text="@string/start_the_app"
+ android:textAllCaps="true" />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/oobe_group"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clickable="true">
+
+ <LinearLayout
+ android:id="@+id/privacy_oobe_popup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="9.3dp"
+ android:layout_marginRight="9.3dp"
+ android:layout_marginTop="254dp"
+ android:background="@drawable/oobe_popup_arrow_up"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/oobe_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="27.3dp"
+ android:layout_marginRight="27.3dp"
+ android:layout_marginTop="15dp"
+ android:text="@string/oobe_privacy_impact_title" />
+
+ <TextView
+ style="@style/oobe_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="27.3dp"
+ android:layout_marginRight="27.3dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/oobe_privacy_impact_text" />
+
+ <TextView
+ android:id="@+id/privacy_got_it"
+ style="@style/button_blue_border"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="60.7dp"
+ android:layout_marginRight="60.7dp"
+ android:layout_marginTop="20dp"
+ android:text="@string/got_it" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/notifications_oobe_popup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="9.3dp"
+ android:layout_marginRight="9.3dp"
+ android:layout_marginTop="25dp"
+ android:background="@drawable/oobe_popup_arrow_left_down"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <TextView
+ style="@style/oobe_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="27.3dp"
+ android:layout_marginRight="27.3dp"
+ android:layout_marginTop="15dp"
+ android:text="@string/oobe_notifications_title" />
+
+ <TextView
+ style="@style/oobe_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="27.3dp"
+ android:layout_marginRight="27.3dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/oobe_notifications_text" />
+
+ <TextView
+ android:id="@+id/notifications_got_it"
+ style="@style/button_blue_border"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="60.7dp"
+ android:layout_marginRight="60.7dp"
+ android:layout_marginTop="20dp"
+ android:text="@string/got_it" />
+ </LinearLayout>
+ </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/layout/activity_grant_access_list_item.xml b/packages/FairphonePrivacyImpact/res/layout/activity_grant_access_list_item.xml
new file mode 100644
index 0000000..6307730
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/layout/activity_grant_access_list_item.xml
@@ -0,0 +1,60 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/permission_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="18.7dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:scaleType="centerInside"
+ android:src="@drawable/icon_location_grey" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="22.3dp"
+ android:layout_weight="0.95"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:fontFamily="sans-serif"
+ android:textColor="@color/blue"
+ android:textSize="16dp" />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.05" />
+
+ <ImageView
+ android:id="@+id/reveal_description_arrow"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:scaleType="centerInside"
+ android:src="@drawable/icon_reveal_arrow_down" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/details_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_vertical_margin"
+ android:layout_marginLeft="42.3dp"
+ android:layout_marginRight="39.3dp"
+ android:ellipsize="end"
+ android:fontFamily="sans-serif"
+ android:maxLines="5"
+ android:textColor="@color/grey_light"
+ android:textSize="12.7dp"
+ android:visibility="gone" />
+
+</LinearLayout>
diff --git a/packages/FairphonePrivacyImpact/res/layout/activity_launcher_list_item.xml b/packages/FairphonePrivacyImpact/res/layout/activity_launcher_list_item.xml
new file mode 100644
index 0000000..14babe4
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/layout/activity_launcher_list_item.xml
@@ -0,0 +1,37 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/launch_app_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="#ffffff"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginLeft="30dp"
+ android:layout_marginRight="29dp"
+ android:layout_marginBottom="10dp"
+ android:src="@drawable/icon_location_grey"
+ android:scaleType="centerInside"/>
+
+
+ <TextView
+ android:id="@+id/name"
+ style="@style/text_light_16_grey"
+ android:layout_width="0dp"
+ android:lines="1"
+ android:ellipsize="end"
+ android:layout_marginRight="@dimen/notification_horizontal_margin"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/FairphonePrivacyImpact/res/layout/privacy_impact_popup.xml b/packages/FairphonePrivacyImpact/res/layout/privacy_impact_popup.xml
new file mode 100644
index 0000000..a32cd93
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/layout/privacy_impact_popup.xml
@@ -0,0 +1,146 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp">
+
+ <TextView
+ android:id="@+id/privacy_impact_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:text="@string/privacy_impact"
+ android:textAllCaps="true"
+ android:textColor="@color/blue"
+ android:textSize="24dp" />
+
+ <TextView
+ android:id="@+id/privacy_impact_level_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-10dp"
+ android:fontFamily="sans-serif"
+ android:gravity="center"
+ android:textAllCaps="true"
+ android:textColor="@color/blue"
+ android:textSize="34dp"
+ android:textStyle="bold" />
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginLeft="15dp"
+ android:layout_marginRight="15dp"
+ android:layout_marginTop="5dp"
+ android:layout_weight="1"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/privacy_impact_detailed_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif"
+ android:gravity="center"
+ android:text="@string/privacy_impact_detailed_info"
+ android:textColor="@color/grey_light"
+ android:textSize="14dp" />
+
+ <TextView
+ android:id="@+id/privacy_impact_info_link"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif"
+ android:gravity="center"
+ android:textColor="@color/blue"
+ android:textColorLink="@color/blue"
+ android:textSize="14dp" />
+
+ <LinearLayout
+ android:id="@+id/no_permissions_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25.7dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center"
+ android:text="@string/no_permissons_required"
+ android:textAllCaps="true"
+ android:textColor="@color/grey"
+ android:textSize="16dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="7.3dp"
+ android:fontFamily="sans-serif"
+ android:gravity="center"
+ android:text="@string/privacy_level_none_description"
+ android:textColor="@color/grey_light"
+ android:textSize="14dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/permissions_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/privacy_level_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif"
+ android:gravity="center"
+ android:text="@string/privacy_level_low_description"
+ android:textColor="@color/grey_light"
+ android:textSize="14dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25.7dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center"
+ android:text="@string/this_app_is_able_to"
+ android:textAllCaps="true"
+ android:textColor="@color/grey"
+ android:textSize="16dp" />
+
+ <LinearLayout
+ android:id="@+id/permissions_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="21dp"
+ android:orientation="vertical" />
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
+
+ <TextView
+ android:id="@+id/confirmation_yes_button"
+ style="@style/button_blue"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginTop="10dp"
+ android:text="@string/close" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/mipmap-hdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..9deffda
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/mipmap-mdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..2785e7c
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/mipmap-xhdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..451bfd6
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/mipmap-xxhdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..91ddb39
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/mipmap-xxxhdpi/ic_launcher.png b/packages/FairphonePrivacyImpact/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..ac95969
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/FairphonePrivacyImpact/res/values/colors.xml b/packages/FairphonePrivacyImpact/res/values/colors.xml
new file mode 100644
index 0000000..61cedce
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/colors.xml
@@ -0,0 +1,40 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+--><resources>
+
+ <!-- Fairphone -->
+ <color name="transparent">#00000000</color>
+ <color name="background_white">#F7F7F7</color>
+ <color name="background_light">#F0F0F0</color>
+ <color name="text_grey">#666666</color>
+ <color name="text_grey_dark">#333333</color>
+ <color name="text_grey_light">#B3B3B3</color>
+ <color name="blue">#2AA9E0</color>
+ <color name="green">#92B851</color>
+ <color name="green_dark">#297a55</color>
+ <color name="white">#FFFFFF</color>
+ <color name="orange">#f5a833</color>
+ <color name="orange_dark">#d36928</color>
+ <color name="red_dark">#80394a</color>
+ <color name="grey_light">#999999</color>
+ <color name="grey_dark">#333333</color>
+ <color name="blue_dark">#123C59</color>
+ <color name="grey">#5b5b5b</color>
+ <color name="grey_transparent">#66999999</color>
+</resources>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/values/config.xml b/packages/FairphonePrivacyImpact/res/values/config.xml
new file mode 100644
index 0000000..8c740ff
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="privacy_impact_info_link" translatable="false">https://www.fairphone.com/</string>
+</resources>
diff --git a/packages/FairphonePrivacyImpact/res/values/dimens.xml b/packages/FairphonePrivacyImpact/res/values/dimens.xml
new file mode 100644
index 0000000..58150f4
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="notification_horizontal_margin">16dp</dimen>
+ <dimen name="notification_vertical_margin">16dp</dimen>
+
+ <dimen name="notification_list_item_height">72dp</dimen>
+
+ <dimen name="notification_switch_width">60dp</dimen>
+ <dimen name="notification_triple_switch_width">64dp</dimen>
+ <dimen name="notification_triple_switch_background_width">54dp</dimen>
+ <dimen name="notification_triple_switch_background_height">14dp</dimen>
+ <dimen name="notification_triple_switch_background_margin_top">7dp</dimen>
+ <dimen name="notification_triple_switch_background_margin_left">5dp</dimen>
+ <dimen name="notification_triple_switch_toggle_size">31dp</dimen>
+ <dimen name="notification_triple_switch_toggle_gap">-14dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/values/ids.xml b/packages/FairphonePrivacyImpact/res/values/ids.xml
new file mode 100644
index 0000000..3d41add
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/ids.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item type="id" name="all_details">false</item>
+ <item type="id" name="app_snippet">false</item>
+ <item type="id" name="operations_section">false</item>
+ <item type="id" name="op_icon">false</item>
+ <item type="id" name="op_name">false</item>
+ <item type="id" name="op_time">false</item>
+ <item type="id" name="switchWidget">false</item>
+ <item type="id" name="app_icon">false</item>
+ <item type="id" name="app_name">false</item>
+ <item type="id" name="pager">false</item>
+ <item type="id" name="tabs">false</item>
+ <item type="id" name="app_on_sdcard">false</item>
+ <item type="id" name="app_size">false</item>
+ <item type="id" name="app_disabled">false</item>
+</resources>
diff --git a/packages/FairphonePrivacyImpact/res/values/strings.xml b/packages/FairphonePrivacyImpact/res/values/strings.xml
new file mode 100644
index 0000000..b8ad97f
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/strings.xml
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+
+ <string name="app_name">Privacy Impact</string>
+ <string name="activity_set_preference_name">Privacy Preference</string>
+ <string name="activity_sample_launcher">Privacy Impact Tester</string>
+ <string name="notifications">Notifications</string>
+ <string name="no_description_available">No description available</string>
+ <string name="privacy_impact">privacy impact</string>
+ <string name="privacy_level_low">low</string>
+ <string name="privacy_level_medium">medium</string>
+ <string name="privacy_level_high">high</string>
+ <string name="privacy_level_none">none</string>
+ <string name="what_does_this_mean">What does this mean?</string>
+ <string name="notifications_off">Off</string>
+ <string name="notifications_on">On</string>
+ <string name="notifications_priority">Priority</string>
+ <string name="dismiss_privacy_impact">Don\'t show this screen again. You can always change this in the settings</string>
+ <string name="take_me_back">Take me back</string>
+ <string name="start_the_app">Start the app</string>
+ <string name="close">close</string>
+ <string name="privacy_impact_detailed_info">With Fairphone Privacy Impact we want to create privacy awareness. To learn more about Privacy Impact and how the impact is calculated, please visit</string>
+ <string name="privacy_impact_info_link_text">Fairphone Privacy Impact detailed info</string>
+ <string name="no_permissons_required">NO PERMISSIONS REQUIRED</string>
+ <string name="privacy_level_none_description">This app does not require any permissions and is not able to access any of your personal data, therefore it should be safe to use and without any privacy consequences.</string>
+ <string name="privacy_level_low_description">This app needs at least one permission to be able to work. Review the permission(s) below. If you trust the app developer, it should be safe to use and without any privacy consequences.</string>
+ <string name="privacy_level_medium_description">This app needs access to a great deal of personal data on your device in order to work. Please double check the permissions below. If you do not trust the app developer, we recommend you not to use it and look for alternative apps.</string>
+ <string name="privacy_level_high_description">This app needs access to a great deal of personal data on your device in order to work. Please double check the permissions below. If you do not trust the app developer, we highly recommend you not to use it and look for alternative apps.</string>
+ <string name="this_app_is_able_to">THIS APP IS ABLE TO</string>
+ <string name="oobe_privacy_impact_title">Fairphone Privacy Impact Analysis</string>
+ <string name="oobe_privacy_impact_text">It shows the impact a particular app may have on your device based on the permissions it requires. Certain app permissions have direct access to your personal data.</string>
+ <string name="got_it">got it</string>
+ <string name="oobe_notifications_title">App Notifications</string>
+ <string name="oobe_notifications_text">Choose if you want to receive notifications from the app. Notifications appear on the top of the screen. If you select Priority, app notifications will stay on top of the list.</string>
+ <string name="notifications_on_description">Show notifications from this app.</string>
+ <string name="notifications_off_description">Never show notifications from this app.</string>
+ <string name="notifications_priority_description">Show notifications at the top of the list and keep them coming when the device is set to priority interruptions only.</string>
+ <string name="privacy_popup_checkbox_title">Disable Privacy Impact popup</string>
+ <string name="privacy_popup_checkbox_description">Disables the Privacy Impact popup shown on the first app launch</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/res/values/styles.xml b/packages/FairphonePrivacyImpact/res/values/styles.xml
new file mode 100644
index 0000000..97d3e77
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/values/styles.xml
@@ -0,0 +1,162 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+
+ </style>
+
+ <!-- NOTIFICATIONS -->
+ <style name="notifications_back_button">
+ <item name="android:src">@drawable/icon_header_back</item>
+ <item name="android:background">@color/transparent</item>
+ </style>
+
+ <style name="notifications_button_blue">
+ <item name="android:gravity">center</item>
+ <item name="android:padding">0dp</item>
+ <item name="android:textColor">@color/white</item>
+ <item name="android:background">@color/blue</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="notifications_button_text_blue">
+ <item name="android:gravity">center</item>
+ <item name="android:padding">0dp</item>
+ <item name="android:textColor">@color/simple_button_text_color</item>
+ <item name="android:background">@color/transparent</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="notifications_header_text">
+ <item name="android:textSize">20sp</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textColor">@color/blue</item>
+ </style>
+
+ <style name="notifications_menu_toggle_button">
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textColor">@color/submenu_toggle_button_text_color</item>
+ <item name="android:background">@drawable/submenu_toggle_button_background</item>
+ </style>
+
+ <style name="small_switch">
+ <item name="android:textOff"></item>
+ <item name="android:textOn"></item>
+ <item name="android:switchMinWidth">@dimen/notification_switch_width</item>
+ <item name="android:thumb">@drawable/switch_thumb</item>
+ <item name="android:track">@drawable/switch_track</item>
+ <item name="android:layout_gravity">center_vertical|right</item>
+ </style>
+
+ <style name="text_thin_34_blue">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ <item name="android:textSize">34sp</item>
+ <item name="android:textColor">@color/blue</item>
+ </style>
+
+ <style name="text_light_24_grey_dark">
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:textColor">@color/text_grey_dark</item>
+ </style>
+
+ <style name="text_light_16_grey_light">
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/text_grey_light</item>
+ </style>
+ <style name="text_light_16_grey">
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/text_grey</item>
+ </style>
+
+ <style name="text_regular_13_grey_light">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textSize">13sp</item>
+ <item name="android:textColor">@color/text_grey_light</item>
+ </style>
+
+ <style name="text_regular_14_grey_light">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/text_grey_light</item>
+ </style>
+
+ <style name="text_regular_16_grey">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/text_grey</item>
+ </style>
+
+ <style name="text_regular_16_blue">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/blue</item>
+ </style>
+
+ <style name="CheckBoxBlue" parent="@android:style/Widget.CompoundButton.CheckBox">
+ <item name="android:button">@drawable/checkbox_button_blue</item>
+ <item name="android:textColor">@color/blue</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:gravity">top|left</item>
+ <item name="android:textSize">12sp</item>
+ <item name="android:paddingLeft">8dp</item>
+ </style>
+
+ <style name="ButtonSmall" parent="@android:style/Widget.Button">
+ <item name="android:background">@drawable/button_grey_background</item>
+ <item name="android:textColor">@color/white</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:padding">15dp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="ButtonSmallBlueDarkGrey" parent="@style/ButtonSmall">
+ <item name="android:background">@drawable/button_blue_dark_grey_background</item>
+ <item name="android:textColor">@color/white</item>
+ </style>
+
+ <style name="oobe_title">
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">24dp</item>
+ <item name="android:textColor">@color/white</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="oobe_description">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textSize">16dp</item>
+ <item name="android:textColor">@color/white</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="button_blue_border">
+ <item name="android:gravity">center</item>
+ <item name="android:padding">0dp</item>
+ <item name="android:textColor">@color/button_blue_border_text_states</item>
+ <item name="android:background">@drawable/button_blue_border_states</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="button_blue">
+ <item name="android:gravity">center</item>
+ <item name="android:padding">0dp</item>
+ <item name="android:textColor">@color/white</item>
+ <item name="android:background">@drawable/button_blue_states</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+</resources>
diff --git a/packages/FairphonePrivacyImpact/res/xml/privacy_impact_preference.xml b/packages/FairphonePrivacyImpact/res/xml/privacy_impact_preference.xml
new file mode 100644
index 0000000..f20e8c7
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/res/xml/privacy_impact_preference.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:key="privacy_impact_preference_screen"
+ android:title="">
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="hide_privacy_impact_preference"
+ android:summary="@string/privacy_popup_checkbox_description"
+ android:title="@string/privacy_popup_checkbox_title" />
+</PreferenceScreen>
\ No newline at end of file
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ApplicationSampleLauncher.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ApplicationSampleLauncher.java
new file mode 100644
index 0000000..6e2b50b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ApplicationSampleLauncher.java
@@ -0,0 +1,127 @@
+package com.fairphone.privacyimpact;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.fairphone.privacyimpact.appdata.AppDataUtils;
+import com.fairphone.privacyimpact.appdata.AppInfo;
+
+import java.util.List;
+
+/**
+ * Created by Tiago Costa on 17/03/15.
+ */
+public class ApplicationSampleLauncher extends Activity {
+
+ private final static String TAG = ApplicationSampleLauncher.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // setup the layout
+ setupLayout();
+ }
+
+ private void setupLayout() {
+ setContentView(R.layout.activity_application_sample_launcher);
+
+ ListView mAppList = (ListView) findViewById(R.id.app_list);
+
+ List<AppInfo> installedApps = AppDataUtils.getAllInstalledApps(this, false);
+ mAppList.setAdapter(new AppListAdapter(this, installedApps));
+
+ }
+
+ private void LaunchApplication(String packageName, String activityName) {
+
+ Intent newIntent = new Intent();
+ newIntent.setComponent(new ComponentName(this, GrantAccessActivity.class));
+
+ Intent originalIntent = new Intent();
+ originalIntent.setComponent(new ComponentName(packageName, activityName));
+
+ newIntent.putExtra("originalIntent", originalIntent);
+ newIntent.putExtra("originalOptions", new Bundle());
+
+ startActivity(newIntent);
+ }
+
+ public class AppListAdapter extends ArrayAdapter<AppInfo> {
+
+ private final PackageManager mPm;
+
+ public AppListAdapter(Context context, int textViewResourceId) {
+ super(context, textViewResourceId);
+
+ mPm = getContext().getPackageManager();
+ }
+
+ public AppListAdapter(Context context, List<AppInfo> items) {
+ super(context, R.layout.activity_launcher_list_item, items);
+
+ mPm = getContext().getPackageManager();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ View v = convertView;
+
+ if(v == null) {
+
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ v = inflater.inflate(R.layout.activity_launcher_list_item, parent, false);
+ }
+ ImageView icon = (ImageView) v.findViewById(R.id.icon);
+ TextView name = (TextView)v.findViewById(R.id.name);
+ View button = v.findViewById(R.id.launch_app_button);
+
+ final AppInfo p = getItem(position);
+
+ if(p != null) {
+ icon.setImageDrawable(p.getIcon());
+ name.setText(p.getAppName());
+
+
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ PackageManager pm = ApplicationSampleLauncher.this.getPackageManager();
+
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(p.getPackageName(), 0);
+
+ LaunchApplication(packageInfo.packageName, p.getClassName());
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+
+ }
+ });
+ }
+
+ return v;
+
+ }
+ }
+
+
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/GrantAccessActivity.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/GrantAccessActivity.java
new file mode 100644
index 0000000..f65212d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/GrantAccessActivity.java
@@ -0,0 +1,238 @@
+package com.fairphone.privacyimpact;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.fairphone.privacyimpact.appdata.OperationsManager;
+import com.fairphone.privacyimpact.appdata.PrivacyImpactCalculator;
+import com.fairphone.privacyimpact.database.AppSettingsDatabaseHelper;
+import com.fairphone.privacyimpact.ui.PrivacyImpactPopupDialog;
+import com.fairphone.privacyimpact.ui.TripleSwitchView;
+
+import java.util.List;
+
+public class GrantAccessActivity extends FragmentActivity {
+
+ private final static String TAG = GrantAccessActivity.class.getSimpleName();
+ private static final String PREFS_PRIVACY_IMPACT = "com.fairphone.privacyimpact.PREFS_PRIVACY_IMPACT";
+ private static final String SHOW_PRIVACY_IMPACT_OOBE = "com.fairphone.privacyimpact.SHOW_PRIVACY_OOBE";
+ private static final boolean DEBUG = false;
+ public static final String HIDE_PRIVACY_IMPACT_PREFERENCE = "hide_privacy_impact_preference";
+
+ private AppSettingsDatabaseHelper mDatabase;
+ private Intent mOriginalIntent;
+ private Bundle mOptions;
+
+ private PrivacyImpactCalculator.PRIVACY_LEVEL mPrivacyLevel;
+ private SharedPreferences mSharedPrefs;
+ private SharedPreferences mDefaultSharedPreferences;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ try {
+ // setup the database
+ mDatabase = new AppSettingsDatabaseHelper(this);
+
+ mDefaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ // treat the launch intent
+ processLaunchIntent(getIntent());
+
+ mSharedPrefs = getSharedPreferences(PREFS_PRIVACY_IMPACT, Context.MODE_PRIVATE);
+ // setup the layout
+ setupLayout();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to create Privacy Impact screen", e);
+ }
+ }
+
+ private void processLaunchIntent(Intent intent) {
+ Log.wtf(TAG, "GrantAccessActivity - start processLaunchIntent --------------------------------------");
+ Intent mLastPermissionsIntent = intent;
+
+ Bundle extras = mLastPermissionsIntent.getExtras();
+
+ Log.wtf(TAG, "GrantAccessActivity - Count : " + extras.size());
+
+ for (String key : extras.keySet()) {
+ String result = "";
+ if (extras.get(key) != null) {
+ result = extras.get(key).toString();
+ }
+
+ Log.wtf(TAG, "Key : " + key + " type " + result);
+ }
+
+
+ mOriginalIntent = (Intent) extras.get("originalIntent");
+
+ Log.wtf(TAG, "GrantAccessActivity - " + mOriginalIntent.getComponent().getPackageName());
+ ;
+
+ boolean hidePrivacyImpact = mDefaultSharedPreferences.getBoolean(HIDE_PRIVACY_IMPACT_PREFERENCE, false);
+ if (mDatabase.isPackageEnable(mOriginalIntent.getComponent().getPackageName()) ||
+ hidePrivacyImpact ||
+ (mOriginalIntent.resolveActivityInfo(getPackageManager(), 0).applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) {
+
+ Log.i(TAG, "GrantAccessActivity - Package is enabled ------------------------------ ");
+
+ mOptions = (Bundle) extras.get("originalOptions");
+
+ startApplication(mOriginalIntent, mOptions);
+ }
+
+ Log.wtf(TAG, "GrantAccessActivity - end processLaunchIntent --------------------------------------");
+ }
+
+ private void setupLayout() {
+ setContentView(R.layout.activity_grant_access);
+
+ final FrameLayout oobeGroup = (FrameLayout) findViewById(R.id.oobe_group);
+ if (mSharedPrefs.getBoolean(SHOW_PRIVACY_IMPACT_OOBE, true)) {
+ final View privacyOobePopup = findViewById(R.id.privacy_oobe_popup);
+ TextView privacyOobeButton = (TextView) findViewById(R.id.privacy_got_it);
+ final View notificationsOobePopup = findViewById(R.id.notifications_oobe_popup);
+ TextView notificationsOobeButton = (TextView) findViewById(R.id.notifications_got_it);
+
+ privacyOobeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ privacyOobePopup.setVisibility(View.GONE);
+ notificationsOobePopup.setVisibility(View.VISIBLE);
+ }
+ });
+
+ notificationsOobeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ notificationsOobePopup.setVisibility(View.GONE);
+ oobeGroup.setVisibility(View.GONE);
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(SHOW_PRIVACY_IMPACT_OOBE, false);
+ editor.apply();
+ }
+ });
+ } else {
+ oobeGroup.setVisibility(View.GONE);
+ }
+
+ final ComponentName component = mOriginalIntent.getComponent();
+
+ PackageManager pm = getPackageManager();
+
+ // setup the app icon
+ ImageView mAppIcon = (ImageView) findViewById(R.id.app_icon);
+
+ try {
+ mAppIcon.setImageDrawable(pm.getActivityIcon(mOriginalIntent));
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+
+ // setup the app name
+ TextView mAppName = (TextView) findViewById(R.id.app_name);
+ try {
+ mAppName.setText(pm.getActivityInfo(component, 0).loadLabel(pm));
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ TextView mLaunchApp = (TextView) findViewById(R.id.start_the_app_button);
+
+ mLaunchApp.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Launch Intent to App Settings
+ mDatabase.addPackageName(component.getPackageName());
+
+ startApplication(mOriginalIntent, mOptions);
+ }
+ });
+
+ TextView mTakeMeBackBtn = (TextView) findViewById(R.id.take_me_back_button);
+
+ mTakeMeBackBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = pm.getApplicationInfo(component.getPackageName(), PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ TripleSwitchView mNotificationSwitch = new TripleSwitchView(this, findViewById(R.id.notification_switch), appInfo);
+
+
+ final Pair<PackageInfo, List<String>> permissionInfo = OperationsManager.getValidPermissionListForPackage(pm, component.getPackageName());
+
+ Pair<PrivacyImpactCalculator.PRIVACY_LEVEL, Double> privacy = PrivacyImpactCalculator.calculateLevel(permissionInfo.second);
+ mPrivacyLevel = privacy.first;
+
+ View mPrivacyImpactGroup = findViewById(R.id.privacy_group);
+ mPrivacyImpactGroup.setBackgroundResource(PrivacyImpactCalculator.getPrivacyBackground(mPrivacyLevel));
+ TextView mPrivacyLevelText = (TextView) findViewById(R.id.privacy_level);
+ mPrivacyLevelText.setText(PrivacyImpactCalculator.getPrivacyName(this, mPrivacyLevel) + (DEBUG ? " -- " + privacy.second : ""));
+ mPrivacyImpactGroup.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //Toast.makeText(GrantAccessActivity.this, "Priority is " + PrivacyImpactCalculator.getPrivacyName(GrantAccessActivity.this, mPrivacyLevel), Toast.LENGTH_SHORT).show();
+ PrivacyImpactPopupDialog popupDialog = new PrivacyImpactPopupDialog();
+ popupDialog.setPrivacyLevel(mPrivacyLevel);
+ popupDialog.setPackageInfoAndPermissionList(permissionInfo.first, permissionInfo.second);
+ FragmentManager fm = getSupportFragmentManager();
+ popupDialog.show(fm, mPrivacyLevel.name());
+ }
+ });
+
+ CheckBox hidePrivacyImpactCheckbox = (CheckBox) findViewById(R.id.hide_privacy_impact_checkbox);
+
+ hidePrivacyImpactCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ SharedPreferences.Editor editor = mDefaultSharedPreferences.edit();
+ editor.putBoolean(HIDE_PRIVACY_IMPACT_PREFERENCE, isChecked);
+ editor.apply();
+ }
+ });
+ }
+
+ private void startApplication(Intent originalIntent, Bundle options) {
+ try {
+ originalIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (options != null) {
+ startActivityForResult(originalIntent, -1, options);
+ finish();
+ } else {
+ // Note we want to go through this call for compatibility with
+ // applications that may have overridden the method.
+ startActivityForResult(originalIntent, -1);
+ finish();
+ }
+ } catch (RuntimeException ren) {
+ Log.e(TAG, "Could not launch application", ren);
+ }
+ }
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/NotificationHandler.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/NotificationHandler.java
new file mode 100644
index 0000000..dd00657
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/NotificationHandler.java
@@ -0,0 +1,106 @@
+package com.fairphone.privacyimpact;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * Created by jpascoal on 15/07/2015.
+ */
+public class NotificationHandler {
+
+ private static final String TAG = NotificationHandler.class.getSimpleName();
+ private Class<?> iNotificationManager;
+ private Object iNotificationManagerObject;
+
+ public NotificationHandler(Context context) {
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ Method getServiceMethod = null;
+ iNotificationManagerObject = null;
+ iNotificationManager = null;
+ try {
+ getServiceMethod = notificationManager.getClass().getMethod("getService");
+ iNotificationManagerObject = getServiceMethod.invoke(notificationManager, null);
+ iNotificationManager = Class.forName("android.app.INotificationManager");
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NotificationHandler constructor:", e);
+ }
+// static INotificationManager sINM = INotificationManager.Stub.asInterface(
+// ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+
+ public boolean setNotificationsBanned(String pkg, int uid, boolean banned) {
+ try {
+ Method setNotificationsEnabledForPackage = iNotificationManager.getMethod("setNotificationsEnabledForPackage", String.class, Integer.TYPE, Boolean.TYPE);
+ setNotificationsEnabledForPackage.invoke(iNotificationManagerObject, pkg, uid, !banned);
+// sINM.setNotificationsEnabledForPackage(pkg, uid, !banned);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling setNotificationsBanned:", e);
+ return false;
+ }
+ }
+
+ public boolean getNotificationsBanned(String pkg, int uid) {
+ try {
+ Method areNotificationsEnabledForPackage = iNotificationManager.getMethod("areNotificationsEnabledForPackage", String.class, Integer.TYPE);
+ Object enabled = areNotificationsEnabledForPackage.invoke(iNotificationManagerObject, pkg, uid);
+ //final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid);
+ return !((Boolean) enabled).booleanValue();
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling getNotificationsBanned:", e);
+ return false;
+ }
+ }
+
+ public boolean getHighPriority(String pkg, int uid) {
+ try {
+ Method getPackagePriority = iNotificationManager.getDeclaredMethod("getPackagePriority", String.class, Integer.TYPE);
+ Object enabled = getPackagePriority.invoke(iNotificationManagerObject, pkg, uid);
+ return ((Integer) enabled).intValue() == Notification.PRIORITY_MAX;
+// return sINM.getPackagePriority(pkg, uid) == Notification.PRIORITY_MAX;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling getHighPriority:", e);
+ return false;
+ }
+ }
+
+ public boolean setHighPriority(String pkg, int uid, boolean highPriority) {
+ try {
+ Method setPackagePriority = iNotificationManager.getMethod("setPackagePriority", String.class, Integer.TYPE, Integer.TYPE);
+ setPackagePriority.invoke(iNotificationManagerObject, pkg, uid, highPriority ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
+// sINM.setPackagePriority(pkg, uid, highPriority ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling setHighPriority:", e);
+ return false;
+ }
+ }
+
+ public boolean getSensitive(String pkg, int uid) {
+ try {
+ Method getPackageVisibilityOverride = iNotificationManager.getMethod("getPackageVisibilityOverride", String.class, Integer.TYPE);
+ Object enabled = getPackageVisibilityOverride.invoke(iNotificationManagerObject, pkg, uid);
+ return ((Integer) enabled).intValue() == Notification.VISIBILITY_PRIVATE;
+// return sINM.getPackageVisibilityOverride(pkg, uid) == Notification.VISIBILITY_PRIVATE;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling getSensitive:", e);
+ return false;
+ }
+ }
+
+ public boolean setSensitive(String pkg, int uid, boolean sensitive) {
+ try {
+ Method setPackageVisibilityOverride = iNotificationManager.getMethod("setPackageVisibilityOverride", String.class, Integer.TYPE, Integer.TYPE);
+ //setPackageVisibilityOverride.invoke(iNotificationManagerObject, new Object[]{pkg, uid, sensitive ? Notification.VISIBILITY_PRIVATE : NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE});
+// sINM.setPackageVisibilityOverride(pkg, uid, sensitive ? Notification.VISIBILITY_PRIVATE : NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling setSensitive:", e);
+ return false;
+ }
+ }
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/PrivacyImpactPreferenceActivity.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/PrivacyImpactPreferenceActivity.java
new file mode 100644
index 0000000..acce3d5
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/PrivacyImpactPreferenceActivity.java
@@ -0,0 +1,24 @@
+package com.fairphone.privacyimpact;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+
+/**
+ * Created by jpascoal on 13/07/2015.
+ */
+public class PrivacyImpactPreferenceActivity extends PreferenceActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
+ }
+
+ public static class MyPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.privacy_impact_preference);
+ }
+ }
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/adapters/PermissionListFiller.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/adapters/PermissionListFiller.java
new file mode 100644
index 0000000..3cce87d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/adapters/PermissionListFiller.java
@@ -0,0 +1,125 @@
+package com.fairphone.privacyimpact.adapters;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.fairphone.privacyimpact.R;
+
+import java.util.List;
+
+/**
+ * Created by fp2builder on 27-04-2015.
+ */
+public class PermissionListFiller {
+
+ private static final String TAG = PermissionListFiller.class.getSimpleName();
+
+ public static void fillLayout(Context context, LinearLayout listContainer, List<String> permissionsList) {
+ Resources resources = context.getResources();
+ PackageManager packageManager = context.getPackageManager();
+ if (listContainer != null) {
+ listContainer.removeAllViews();
+ for (String permission : permissionsList) {
+
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflater.inflate(R.layout.activity_grant_access_list_item, listContainer, false);
+
+ LinearLayout permissionGroup = (LinearLayout) v.findViewById(R.id.permission_group);
+ permissionGroup.setOnClickListener(new View.OnClickListener() {
+ private Context lContext;
+ public View.OnClickListener setup(Context context){
+ lContext = context;
+ return this;
+ }
+ @Override
+ public void onClick(View v) {
+ ImageView hideRevealButton = (ImageView) v.findViewById(R.id.reveal_description_arrow);
+ TextView name = (TextView) v.findViewById(R.id.name);
+ TextView detailDescription = (TextView) v.findViewById(R.id.details_text);
+ boolean isOpen = detailDescription.getVisibility() == View.VISIBLE;
+ Resources resource = lContext.getResources();
+ if (isOpen) {
+ if (hideRevealButton != null) {
+ hideRevealButton.setImageDrawable(resource.getDrawable(R.drawable.icon_reveal_arrow_down));
+ }
+ name.setSingleLine(true);
+ detailDescription.setVisibility(View.GONE);
+ } else {
+ if (hideRevealButton != null) {
+ hideRevealButton.setImageDrawable(resource.getDrawable(R.drawable.icon_reveal_arrow_up));
+ }
+ name.setSingleLine(false);
+ detailDescription.setVisibility(View.VISIBLE);
+ }
+ }
+ }.setup(context)
+ );
+
+ ImageView icon = (ImageView) v.findViewById(R.id.icon);
+ TextView name = (TextView) v.findViewById(R.id.name);
+ TextView details = (TextView) v.findViewById(R.id.details_text);
+ ImageView revealDescriptionArrow = (ImageView) v.findViewById(R.id.reveal_description_arrow);
+
+ //reset state
+ if (revealDescriptionArrow != null) {
+ revealDescriptionArrow.setImageDrawable(resources.getDrawable(R.drawable.icon_reveal_arrow_down));
+ }
+ details.setVisibility(View.GONE);
+ boolean addView = false;
+
+ if (!TextUtils.isEmpty(permission)) {
+
+ try {
+ PermissionInfo info = packageManager.getPermissionInfo(permission, PackageManager.GET_META_DATA);
+
+ String group = info.group;
+
+ if (!TextUtils.isEmpty(group)) {
+ PermissionGroupInfo permissionGroupInfo = packageManager.getPermissionGroupInfo(group, 0);
+ icon.setImageDrawable(permissionGroupInfo.loadIcon(packageManager));
+ } else {
+ icon.setImageResource(R.drawable.icon_generic_permission);
+ }
+ String cap = capitalizeSentence(info.loadLabel(packageManager));
+ name.setText(cap);
+ name.setSingleLine(true);
+
+ CharSequence detailedDescription = info.loadDescription(packageManager);
+ if (!TextUtils.isEmpty(detailedDescription)) {
+ details.setText(detailedDescription);
+ } else {
+ details.setText(R.string.no_description_available);
+ }
+
+ addView = true;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Permission name not found: " + e.getLocalizedMessage());
+
+ }
+ if (addView) {
+ listContainer.addView(v);
+ }
+ } else {
+ Log.w(TAG, "Permission is null.");
+ }
+ }
+ }
+ }
+
+ private static String capitalizeSentence(CharSequence sequence) {
+ StringBuilder sb = new StringBuilder(sequence);
+ sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
+ return sb.toString();
+ }
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppDataUtils.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppDataUtils.java
new file mode 100644
index 0000000..f13c59d
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppDataUtils.java
@@ -0,0 +1,89 @@
+package com.fairphone.privacyimpact.appdata;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Tiago Costa on 20/03/15.
+ */
+public class AppDataUtils {
+
+ private static final String TAG = AppDataUtils.class.getSimpleName();
+
+ public static List<AppInfo> getAllInstalledApps(Context context, boolean getSystemApps) {
+ ArrayList<AppInfo> apps = getInstalledApps(context, getSystemApps); /* false = no system packages */
+ final int max = apps.size();
+
+ for (int i=0; i<max; i++) {
+ Log.d(TAG, apps.get(i).toString());
+ }
+
+ return apps;
+ }
+
+ private static ArrayList<AppInfo> getInstalledApps(Context context, boolean getSysPackages) {
+ ArrayList<AppInfo> res = new ArrayList<>();
+ List<PackageInfo> packs = context.getPackageManager().getInstalledPackages(0);
+
+ for(int i=0;i<packs.size();i++) {
+ PackageInfo p = packs.get(i);
+ if ((!getSysPackages) && (p.versionName == null)) {
+ continue ;
+ }
+
+ List<ResolveInfo> activitiesForPackage = findActivitiesForPackage(context, p.packageName);
+
+ for(ResolveInfo info : activitiesForPackage){
+
+ res.add(generateAppInfo(context, p, info));
+ }
+
+ }
+ return res;
+ }
+
+ private static AppInfo generateAppInfo(Context context, PackageInfo p, ResolveInfo info){
+ String appName = p.applicationInfo.loadLabel(context.getPackageManager()).toString();
+ String packageName = p.packageName;
+ String versionName = p.versionName;
+ int versionCode = p.versionCode;
+ String className = "";
+ if(info != null) {
+ className = info.activityInfo.name;
+ }
+ Drawable icon = p.applicationInfo.loadIcon(context.getPackageManager());
+
+ return new AppInfo(appName, packageName, className, icon, versionName, versionCode);
+ }
+
+ private static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
+ final PackageManager packageManager = context.getPackageManager();
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mainIntent.setPackage(packageName);
+
+ final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
+ return apps != null ? apps : new ArrayList<ResolveInfo>();
+ }
+
+ public static AppInfo getAppInformation(Context context, String packageName) {
+ AppInfo info = null;
+
+ try {
+ info = generateAppInfo(context, context.getPackageManager().getPackageInfo(packageName, 0), null);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ return info;
+ }
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppInfo.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppInfo.java
new file mode 100644
index 0000000..0c52e8c
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/AppInfo.java
@@ -0,0 +1,54 @@
+package com.fairphone.privacyimpact.appdata;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Created by Tiago Costa on 20/03/15.
+ */
+public class AppInfo {
+
+
+ private final String mAppName;
+ private final String mPackageName;
+ private final String mVersionName;
+ private final String mClassName;
+
+ private int mVersionCode = 0;
+
+ private final Drawable mIcon;
+
+ public AppInfo( String appName, String packageName, String className, Drawable icon, String versionName, int versionCode ){
+ mAppName = appName;
+ mPackageName = packageName;
+ mClassName = className;
+ mVersionName = versionName;
+ mVersionCode = versionCode;
+ mIcon = icon;
+ }
+
+ public String getAppName(){
+ return mAppName;
+ }
+
+ public String getPackageName(){
+ return mPackageName;
+ }
+
+ public String getVersionName(){
+ return mVersionName;
+ }
+
+ public String getClassName(){
+ return mClassName;
+ }
+
+ public Drawable getIcon(){
+ return mIcon;
+ }
+
+ @Override
+ public String toString() {
+ return mAppName + "\t" + mPackageName + "\t" + mClassName + "\t" + mVersionName + "\t" + mVersionCode ;
+ }
+
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/OperationsManager.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/OperationsManager.java
new file mode 100644
index 0000000..54ad112
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/OperationsManager.java
@@ -0,0 +1,68 @@
+package com.fairphone.privacyimpact.appdata;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Tiago Costa on 30/03/15.
+ */
+public class OperationsManager {
+ private static final String TAG = OperationsManager.class.getSimpleName();
+
+ public static Pair<PackageInfo, List<String>> getValidPermissionListForPackage(PackageManager packageManager, String packageName) {
+ PackageInfo packageInfo = null;
+ List<String> list = null;
+
+ if (!TextUtils.isEmpty(packageName) && packageManager != null) {
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
+ list = new ArrayList<>();
+
+ //Get Permissions
+ String[] requestedPermissions = packageInfo.requestedPermissions;
+
+ if (requestedPermissions != null) {
+ boolean isSystemPermission;
+ for (String perm : requestedPermissions) {
+ try {
+ PermissionInfo info = packageManager.getPermissionInfo(perm, PackageManager.GET_META_DATA);
+ if (isPermissionValid(packageManager, info)) {
+ isSystemPermission = true;
+ } else {
+ isSystemPermission = false;
+ Log.w(TAG, "Permission " + perm + " not added. Reason: Not recognized on AppOps");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Permission " + perm + " not added. Reason: " + e.getLocalizedMessage());
+ isSystemPermission = false;
+ }
+
+ if (isSystemPermission) {
+ list.add(perm);
+ }
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.wtf(TAG, "Unable to get package info for " + packageName + ". Reason " + e.getLocalizedMessage());
+ list = null;
+ packageInfo = null;
+ }
+ }
+ return new Pair<>(packageInfo, list);
+ }
+
+ private static boolean isPermissionValid(PackageManager packageManager, PermissionInfo info) {
+ String res = info == null ? "" : info.loadLabel(packageManager).toString();
+ res = res.toLowerCase().startsWith("com.") ? "" : res;
+ return !TextUtils.isEmpty(res);
+ }
+}
+
+
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/PrivacyImpactCalculator.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/PrivacyImpactCalculator.java
new file mode 100644
index 0000000..380727b
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/appdata/PrivacyImpactCalculator.java
@@ -0,0 +1,163 @@
+package com.fairphone.privacyimpact.appdata;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Pair;
+
+import com.fairphone.privacyimpact.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by jpascoal on 29/06/2015.
+ */
+public class PrivacyImpactCalculator {
+
+ public enum PRIVACY_LEVEL {
+ NONE,
+ LOW,
+ MEDIUM,
+ HIGH
+ }
+
+ private static List<Pair<Integer, List<String>>> permissionsCombinationList;
+
+ public static void setupPrivacyCombinations() {
+ if (permissionsCombinationList == null || permissionsCombinationList.isEmpty()) {
+ permissionsCombinationList = new ArrayList<>();
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.WRITE_EXTERNAL_STORAGE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_EXTERNAL_STORAGE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_PHONE_STATE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.ACCESS_WIFI_STATE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.ACCESS_COARSE_LOCATION"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.ACCESS_FINE_LOCATION"})));
+ permissionsCombinationList.add(new Pair<>(2, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.GET_ACCOUNTS"})));
+ permissionsCombinationList.add(new Pair<>(3, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.CAMERA"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.GET_TASKS"})));
+ permissionsCombinationList.add(new Pair<>(4, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_CONTACTS"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_HISTORY_BOOKMARKS"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_CALL_LOG"})));
+ permissionsCombinationList.add(new Pair<>(3, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.RECORD_AUDIO"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.READ_LOGS"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.USE_CREDENTIALS"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.SEND_SMS", "android.permission.READ_PHONE_STATE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.INTERNET", "android.permission.RECEIVE_SMS"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.SEND_SMS", "android.permission.ACCESS_NETWORK_STATE"})));
+ permissionsCombinationList.add(new Pair<>(1, Arrays.asList(new String[]{"android.permission.SEND_SMS", "android.permission.WRITE_EXTERNAL_STORAGE"})));
+ }
+ }
+
+ public static Pair<PRIVACY_LEVEL, Double> calculateLevel(List<String> permissionList) {
+ PRIVACY_LEVEL level = PRIVACY_LEVEL.NONE;
+ double appScore = 0.0;
+ if (permissionList != null && !permissionList.isEmpty()) {
+ setupPrivacyCombinations();
+ int privacyImpact = 0;
+ int privacySum = 0;
+ for (Pair<Integer, List<String>> permissionsCombination : permissionsCombinationList) {
+ if (permissionList.containsAll(permissionsCombination.second)) {
+ privacyImpact += permissionsCombination.first;
+ }
+ privacySum += permissionsCombination.first;
+ }
+
+ appScore = Math.round(((double) privacyImpact / privacySum) * 100.0) / 100.0;
+ if (appScore == 0.0) {
+ level = PRIVACY_LEVEL.NONE;
+ } else if (appScore < 0.45) {
+ level = PRIVACY_LEVEL.LOW;
+ } else if (appScore < 0.85) {
+ level = PRIVACY_LEVEL.MEDIUM;
+ } else {
+ level = PRIVACY_LEVEL.HIGH;
+ }
+ }
+ return new Pair<>(level, appScore);
+ }
+
+ public static int getPrivacyColor(PRIVACY_LEVEL level) {
+ int colorResourceId;
+ switch (level) {
+ case LOW:
+ colorResourceId = R.color.green;
+ break;
+ case MEDIUM:
+ colorResourceId = R.color.orange;
+ break;
+ case HIGH:
+ colorResourceId = R.color.orange_dark;
+ break;
+ case NONE:
+ default:
+ colorResourceId = R.color.blue;
+ break;
+ }
+ return colorResourceId;
+ }
+
+ public static int getPrivacyBackground(PRIVACY_LEVEL level) {
+ int colorResourceId;
+ switch (level) {
+ case LOW:
+ colorResourceId = R.drawable.privacy_low;
+ break;
+ case MEDIUM:
+ colorResourceId = R.drawable.privacy_medium;
+ break;
+ case HIGH:
+ colorResourceId = R.drawable.privacy_high;
+ break;
+ case NONE:
+ default:
+ colorResourceId = R.drawable.privacy_none;
+ break;
+ }
+ return colorResourceId;
+ }
+
+ public static String getPrivacyName(Context context, PRIVACY_LEVEL level) {
+ Resources resources = context.getResources();
+ String privacyName;
+ switch (level) {
+ case LOW:
+ privacyName = resources.getString(R.string.privacy_level_low);
+ break;
+ case MEDIUM:
+ privacyName = resources.getString(R.string.privacy_level_medium);
+ break;
+ case HIGH:
+ privacyName = resources.getString(R.string.privacy_level_high);
+ break;
+ case NONE:
+ default:
+ privacyName = resources.getString(R.string.privacy_level_none);
+ break;
+ }
+ return privacyName;
+ }
+
+ public static String getPrivacyDescription(Context context, PRIVACY_LEVEL level) {
+ Resources resources = context.getResources();
+ String privacyDescription;
+ switch (level) {
+ case LOW:
+ privacyDescription = resources.getString(R.string.privacy_level_low_description);
+ break;
+ case MEDIUM:
+ privacyDescription = resources.getString(R.string.privacy_level_medium_description);
+ break;
+ case HIGH:
+ privacyDescription = resources.getString(R.string.privacy_level_high_description);
+ break;
+ case NONE:
+ default:
+ privacyDescription = resources.getString(R.string.privacy_level_none_description);
+ break;
+ }
+ return privacyDescription;
+ }
+
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/database/AppSettingsDatabaseHelper.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/database/AppSettingsDatabaseHelper.java
new file mode 100644
index 0000000..d02c4b0
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/database/AppSettingsDatabaseHelper.java
@@ -0,0 +1,39 @@
+package com.fairphone.privacyimpact.database;
+
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.IPrivacyImpactService;
+import android.util.Log;
+
+
+public class AppSettingsDatabaseHelper {
+
+ private static final String TAG = AppSettingsDatabaseHelper.class.getName();
+
+ private final Context mContext;
+
+ public AppSettingsDatabaseHelper(Context context) {
+ mContext = context;
+ }
+
+ public boolean isPackageEnable(String packageName){
+ boolean result = true;
+ try {
+ IPrivacyImpactService pis = IPrivacyImpactService.Stub.asInterface(ServiceManager.getService("PrivacyImpact"));
+ result = !pis.showPackagePrivacy(packageName);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get package "+packageName+" info from service", e);
+ }
+ return result;
+ }
+
+ public void addPackageName(String packageName){
+ try {
+ IPrivacyImpactService pis = IPrivacyImpactService.Stub.asInterface(ServiceManager.getService("PrivacyImpact"));
+ pis.disablePackagePrivacy(packageName);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to set package "+packageName+" from service", e);
+ }
+ }
+}
+
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/PrivacyImpactPopupDialog.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/PrivacyImpactPopupDialog.java
new file mode 100644
index 0000000..c9f4ef7
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/PrivacyImpactPopupDialog.java
@@ -0,0 +1,116 @@
+package com.fairphone.privacyimpact.ui;
+
+import android.content.pm.PackageInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.text.Html;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.fairphone.privacyimpact.R;
+import com.fairphone.privacyimpact.adapters.PermissionListFiller;
+import com.fairphone.privacyimpact.appdata.PrivacyImpactCalculator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PrivacyImpactPopupDialog extends DialogFragment implements OnEditorActionListener {
+
+ private PrivacyImpactCalculator.PRIVACY_LEVEL mPrivacyLevel;
+ private List<String> mPermissionList;
+
+ public PrivacyImpactPopupDialog() {
+ // Empty constructor required for DialogFragment
+ super();
+ }
+
+ public void setPrivacyLevel(PrivacyImpactCalculator.PRIVACY_LEVEL level) {
+ mPrivacyLevel = level;
+ }
+
+ public void setPackageInfoAndPermissionList(PackageInfo packageInfo, List<String> permissionList) {
+ PackageInfo mPackageInfo = packageInfo;
+ mPermissionList = new ArrayList<>();
+ mPermissionList.addAll(permissionList);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ View view = inflater.inflate(R.layout.privacy_impact_popup, container);
+
+ Resources resources=getActivity().getResources();
+ TextView privacyText = (TextView) view.findViewById(R.id.privacy_impact_text);
+ privacyText.setTextColor(resources.getColor(PrivacyImpactCalculator.getPrivacyColor(mPrivacyLevel)));
+ TextView privacyLevelText = (TextView) view.findViewById(R.id.privacy_impact_level_text);
+ privacyLevelText.setText(PrivacyImpactCalculator.getPrivacyName(getActivity(), mPrivacyLevel));
+ privacyLevelText.setTextColor(resources.getColor(PrivacyImpactCalculator.getPrivacyColor(mPrivacyLevel)));
+
+ TextView link = (TextView) view.findViewById(R.id.privacy_impact_info_link);
+ link.setText(setupLink(resources));
+ link.setMovementMethod(LinkMovementMethod.getInstance());
+
+ View noPermissionsGroup = view.findViewById(R.id.no_permissions_group);
+ View permissionsGroup = view.findViewById(R.id.permissions_group);
+ if (mPermissionList.isEmpty()) {
+ permissionsGroup.setVisibility(View.GONE);
+ noPermissionsGroup.setVisibility(View.VISIBLE);
+ } else {
+ permissionsGroup.setVisibility(View.VISIBLE);
+ noPermissionsGroup.setVisibility(View.GONE);
+
+ TextView levelDescription = (TextView) view.findViewById(R.id.privacy_level_description);
+ levelDescription.setText(PrivacyImpactCalculator.getPrivacyDescription(getActivity(), mPrivacyLevel));
+
+ LinearLayout permissionsListView = (LinearLayout) view.findViewById(R.id.permissions_list_view);
+
+ PermissionListFiller.fillLayout(getActivity(), permissionsListView, mPermissionList);
+ }
+
+
+ TextView mOkButton = (TextView) view.findViewById(R.id.confirmation_yes_button);
+
+ mOkButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PrivacyImpactPopupDialog.this.dismiss();
+ }
+ });
+
+ return view;
+ }
+
+ private Spanned setupLink(Resources resources) {
+ StringBuilder link = new StringBuilder();
+ link.append("<a href=\"");
+ link.append(resources.getString(R.string.privacy_impact_info_link));
+ link.append("\">");
+ link.append(resources.getString(R.string.privacy_impact_info_link_text));
+ link.append("</a>");
+ return Html.fromHtml(link.toString());
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (EditorInfo.IME_ACTION_DONE == actionId) {
+ // Return input text to activity
+ this.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/TripleSwitchView.java b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/TripleSwitchView.java
new file mode 100644
index 0000000..e42eed2
--- /dev/null
+++ b/packages/FairphonePrivacyImpact/src/com/fairphone/privacyimpact/ui/TripleSwitchView.java
@@ -0,0 +1,176 @@
+package com.fairphone.privacyimpact.ui;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+
+import com.fairphone.privacyimpact.NotificationHandler;
+import com.fairphone.privacyimpact.R;
+
+/**
+ * Created by jpascoal on 29/06/2015.
+ */
+public class TripleSwitchView {
+ private static final String TAG = TripleSwitchStates.class.getSimpleName();
+ private final Context mContext;
+ private final FrameLayout mTripleSwitchGroup;
+ private final ApplicationInfo mAppInfo;
+ private final NotificationHandler mNotificationHandler;
+ private View mToggleBackground;
+ private ToggleButton mOffToggleButton;
+ private ToggleButton mOnToggleButton;
+ private ToggleButton mPriorityToggleButton;
+ private TextView mStatusText;
+
+ public enum TripleSwitchStates {
+ OFF,
+ ON,
+ PRIORITY
+ }
+
+ private TripleSwitchStates mCurrentTripleSwitchState = TripleSwitchStates.OFF;
+
+ public TripleSwitchView(Context context, View view, ApplicationInfo appInfo) {
+ mTripleSwitchGroup = (FrameLayout) view;
+ mContext = context;
+ mAppInfo = appInfo;
+ mNotificationHandler = new NotificationHandler(mContext);
+ setupViews();
+ }
+
+
+ private void setupViews() {
+ View mNotificationGroup = mTripleSwitchGroup.findViewById(R.id.notification_group);
+ mStatusText = (TextView) mTripleSwitchGroup.findViewById(R.id.status);
+ FrameLayout mTripleSwitch = (FrameLayout) mTripleSwitchGroup.findViewById(R.id.triple_switch_group);
+ mToggleBackground = mTripleSwitchGroup.findViewById(R.id.toggle_background);
+ mOffToggleButton = (ToggleButton) mTripleSwitchGroup.findViewById(R.id.off_toggle_button);
+ mOnToggleButton = (ToggleButton) mTripleSwitchGroup.findViewById(R.id.on_toggle_button);
+ mPriorityToggleButton = (ToggleButton) mTripleSwitchGroup.findViewById(R.id.priority_toggle_button);
+
+ setCurrentTripleSwitchState();
+
+ mOffToggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ changeTripleSwitchState();
+ }
+ });
+ mOnToggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ changeTripleSwitchState();
+ }
+ });
+ mPriorityToggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ changeTripleSwitchState();
+ }
+ });
+
+ mNotificationGroup.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Resources resources = mContext.getResources();
+ switch (mCurrentTripleSwitchState) {
+ case ON:
+ Toast.makeText(mContext, resources.getString(R.string.notifications_on).toUpperCase() + "\n" + resources.getString(R.string.notifications_on_description), Toast.LENGTH_SHORT).show();
+ break;
+ case OFF:
+ Toast.makeText(mContext, resources.getString(R.string.notifications_off).toUpperCase() + "\n" + resources.getString(R.string.notifications_off_description), Toast.LENGTH_SHORT).show();
+ break;
+ case PRIORITY:
+ Toast.makeText(mContext, resources.getString(R.string.notifications_priority).toUpperCase() + "\n" + resources.getString(R.string.notifications_priority_description), Toast.LENGTH_SHORT).show();
+ break;
+ default:
+ Log.wtf(TAG, "Unknow triple switch state: " + mCurrentTripleSwitchState);
+ break;
+ }
+ }
+ });
+ }
+
+ private void changeTripleSwitchState() {
+ Resources resources = mContext.getResources();
+
+ switch (mCurrentTripleSwitchState) {
+ case OFF:
+ setTripleSwitchOn(resources);
+
+ if (mAppInfo != null) {
+ mNotificationHandler.setNotificationsBanned(mAppInfo.packageName, mAppInfo.uid, false);
+ mNotificationHandler.setHighPriority(mAppInfo.packageName, mAppInfo.uid, false);
+ }
+ break;
+ case ON:
+ setTripleSwitchPriority(resources);
+
+ if (mAppInfo != null) {
+ mNotificationHandler.setNotificationsBanned(mAppInfo.packageName, mAppInfo.uid, false);
+ mNotificationHandler.setHighPriority(mAppInfo.packageName, mAppInfo.uid, true);
+ }
+ break;
+ case PRIORITY:
+ setTripleSwitchOff(resources);
+
+ if (mAppInfo != null) {
+ mNotificationHandler.setNotificationsBanned(mAppInfo.packageName, mAppInfo.uid, true);
+ mNotificationHandler.setHighPriority(mAppInfo.packageName, mAppInfo.uid, false);
+ }
+ break;
+ default:
+ Log.wtf(TAG, "Unknow triple switch state: " + mCurrentTripleSwitchState);
+ break;
+ }
+ }
+
+ private void setCurrentTripleSwitchState() {
+ Resources resources = mContext.getResources();
+
+ if ((!mNotificationHandler.getNotificationsBanned(mAppInfo.packageName, mAppInfo.uid)) &&
+ mNotificationHandler.getHighPriority(mAppInfo.packageName, mAppInfo.uid)) {
+ setTripleSwitchPriority(resources);
+ } else if (mNotificationHandler.getNotificationsBanned(mAppInfo.packageName, mAppInfo.uid)) {
+ setTripleSwitchOff(resources);
+ } else {
+ setTripleSwitchOn(resources);
+ }
+ }
+
+ private void setTripleSwitchOff(Resources resources) {
+ mToggleBackground.setBackgroundResource(R.drawable.toggle_switch_background_grey);
+ mOffToggleButton.setChecked(true);
+ mOnToggleButton.setChecked(false);
+ mPriorityToggleButton.setChecked(false);
+ mStatusText.setTextColor(resources.getColor(R.color.text_grey_dark));
+ mStatusText.setText(resources.getString(R.string.notifications_off));
+ mCurrentTripleSwitchState = TripleSwitchStates.OFF;
+ }
+
+ private void setTripleSwitchPriority(Resources resources) {
+ mToggleBackground.setBackgroundResource(R.drawable.toggle_switch_background_green);
+ mOffToggleButton.setChecked(false);
+ mOnToggleButton.setChecked(false);
+ mPriorityToggleButton.setChecked(true);
+ mStatusText.setTextColor(resources.getColor(R.color.green));
+ mStatusText.setText(resources.getString(R.string.notifications_priority));
+ mCurrentTripleSwitchState = TripleSwitchStates.PRIORITY;
+ }
+
+ private void setTripleSwitchOn(Resources resources) {
+ mToggleBackground.setBackgroundResource(R.drawable.toggle_switch_background_blue);
+ mOffToggleButton.setChecked(false);
+ mOnToggleButton.setChecked(true);
+ mPriorityToggleButton.setChecked(false);
+ mStatusText.setTextColor(resources.getColor(R.color.blue));
+ mStatusText.setText(resources.getString(R.string.notifications_on));
+ mCurrentTripleSwitchState = TripleSwitchStates.ON;
+ }
+}
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index 352317d..45a0cda 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -46,5 +46,10 @@
android:persistent="true"
android:supportsRtl="true">
+ <service
+ android:name="com.android.keyguard.ClockScreenService"
+ android:enabled="true"
+ android:exported="false" >
+ </service>
</application>
</manifest>
diff --git a/packages/Keyguard/res/drawable-hdpi/clock.png b/packages/Keyguard/res/drawable-hdpi/clock.png
new file mode 100755
index 0000000..d6d4067
--- /dev/null
+++ b/packages/Keyguard/res/drawable-hdpi/clock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/clock.png b/packages/Keyguard/res/drawable-mdpi/clock.png
new file mode 100755
index 0000000..81308aa
--- /dev/null
+++ b/packages/Keyguard/res/drawable-mdpi/clock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/clock.png b/packages/Keyguard/res/drawable-xhdpi/clock.png
new file mode 100755
index 0000000..98e521d
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xhdpi/clock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/alarm_icon.png b/packages/Keyguard/res/drawable-xxhdpi/alarm_icon.png
new file mode 100644
index 0000000..a2d27fc
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/alarm_icon.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_00.png b/packages/Keyguard/res/drawable-xxhdpi/battery_00.png
new file mode 100644
index 0000000..839b8e1
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_00.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_10.png b/packages/Keyguard/res/drawable-xxhdpi/battery_10.png
new file mode 100644
index 0000000..ae7b330
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_10.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_100.png b/packages/Keyguard/res/drawable-xxhdpi/battery_100.png
new file mode 100644
index 0000000..66b1a21
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_100.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_20.png b/packages/Keyguard/res/drawable-xxhdpi/battery_20.png
new file mode 100644
index 0000000..33e7fd9
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_20.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_30.png b/packages/Keyguard/res/drawable-xxhdpi/battery_30.png
new file mode 100644
index 0000000..5d77ad3
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_30.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_40.png b/packages/Keyguard/res/drawable-xxhdpi/battery_40.png
new file mode 100644
index 0000000..a2c779b
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_40.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_50.png b/packages/Keyguard/res/drawable-xxhdpi/battery_50.png
new file mode 100644
index 0000000..60538d6
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_50.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_60.png b/packages/Keyguard/res/drawable-xxhdpi/battery_60.png
new file mode 100644
index 0000000..bd2c9d8
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_60.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_70.png b/packages/Keyguard/res/drawable-xxhdpi/battery_70.png
new file mode 100644
index 0000000..4df2f7f
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_70.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_80.png b/packages/Keyguard/res/drawable-xxhdpi/battery_80.png
new file mode 100644
index 0000000..117fcfe
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_80.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_90.png b/packages/Keyguard/res/drawable-xxhdpi/battery_90.png
new file mode 100644
index 0000000..fd02500
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_90.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_00.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_00.png
new file mode 100644
index 0000000..3291ce0
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_00.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_10.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_10.png
new file mode 100644
index 0000000..6c72b90
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_10.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_100.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_100.png
new file mode 100644
index 0000000..bc01d99
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_100.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_20.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_20.png
new file mode 100644
index 0000000..d0132b0
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_20.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_30.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_30.png
new file mode 100644
index 0000000..9960c92
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_30.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_40.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_40.png
new file mode 100644
index 0000000..cc7d9b6
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_40.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_50.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_50.png
new file mode 100644
index 0000000..80fd7bd
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_50.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_60.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_60.png
new file mode 100644
index 0000000..cc48f13
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_60.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_70.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_70.png
new file mode 100644
index 0000000..8720550
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_70.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_80.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_80.png
new file mode 100644
index 0000000..e928413
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_80.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/battery_charging_90.png b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_90.png
new file mode 100644
index 0000000..4388712
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/battery_charging_90.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/clock.png b/packages/Keyguard/res/drawable-xxhdpi/clock.png
new file mode 100755
index 0000000..19ecc7d
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/clock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxhdpi/share_icon.png b/packages/Keyguard/res/drawable-xxhdpi/share_icon.png
new file mode 100644
index 0000000..dcb2c51
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxhdpi/share_icon.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xxxhdpi/clock.png b/packages/Keyguard/res/drawable-xxxhdpi/clock.png
new file mode 100755
index 0000000..7ed6dbe
--- /dev/null
+++ b/packages/Keyguard/res/drawable-xxxhdpi/clock.png
Binary files differ
diff --git a/packages/Keyguard/res/drawable/button_states_blue.xml b/packages/Keyguard/res/drawable/button_states_blue.xml
new file mode 100644
index 0000000..3471c42
--- /dev/null
+++ b/packages/Keyguard/res/drawable/button_states_blue.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@color/fairphone_blue" android:state_pressed="true"/>
+ <item android:drawable="@android:color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/Keyguard/res/layout/clock_widget_battery.xml b/packages/Keyguard/res/layout/clock_widget_battery.xml
new file mode 100644
index 0000000..0a256a8
--- /dev/null
+++ b/packages/Keyguard/res/layout/clock_widget_battery.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/battery_description"
+ style="@style/text_light_20sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="31.3dp"
+ android:gravity="left"
+ android:text="@string/battery_charge_will_last_until" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="78dp"
+ android:layout_marginLeft="31.5dp"
+ android:layout_marginTop="35dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/battery_level_image"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside"
+ android:src="@drawable/battery_10" />
+
+ <TextView
+ android:id="@+id/charged_text"
+ style="@style/text_condensed_bold_56sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="26dp"
+ android:layout_marginTop="10dp"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:text="@string/charged"
+ android:textAllCaps="true"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/battery_days_left"
+ style="@style/text_condensed_bold_67sp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="26dp"
+ android:layout_marginTop="-7dp"
+ android:gravity="left"
+ android:textAllCaps="true"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:id="@+id/battery_time_group"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="26dp"
+ android:layout_marginTop="-7dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/hours_text"
+ style="@style/text_condensed_bold_67sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left" />
+
+ <TextView
+ style="@style/text_light_67sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:paddingLeft="3dp"
+ android:paddingRight="3dp"
+ android:text="@string/colon" />
+
+ <TextView
+ android:id="@+id/minutes_text"
+ style="@style/text_condensed_bold_67sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="5dp"
+ android:layout_marginTop="15dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/day_indicator"
+ style="@style/text_condensed_bold_21sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|left"
+ android:gravity="left"
+ android:text="@string/tomorrow"
+ android:textAllCaps="true"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/battery_am_pm_indicator"
+ style="@style/text_thin_37sp"
+ android:textAllCaps="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|left"
+ android:layout_marginLeft="-3dp"
+ android:layout_marginTop="-10dp"
+ android:gravity="left" />
+ </LinearLayout>
+ </LinearLayout>
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/unplug_charger_text"
+ style="@style/text_condensed_21sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="58dp"
+ android:layout_marginTop="100dp"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:text="@string/unplug_your_charger"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/last_longer_button"
+ style="@style/text_condensed_bold_italic_18sp"
+ android:layout_width="200dp"
+ android:layout_height="32dp"
+ android:layout_gravity="right"
+ android:layout_marginLeft="160dp"
+ android:layout_marginTop="122dp"
+ android:background="@drawable/button_states_blue"
+ android:gravity="center"
+ android:text="@string/make_it_last_longer" />
+
+</RelativeLayout>
diff --git a/packages/Keyguard/res/layout/clock_widget_main.xml b/packages/Keyguard/res/layout/clock_widget_main.xml
new file mode 100644
index 0000000..a73f448
--- /dev/null
+++ b/packages/Keyguard/res/layout/clock_widget_main.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="180dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/alarm_text"
+ style="@style/text_regular_18sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@drawable/clock"
+ android:drawablePadding="8.7dp"
+ android:gravity="left"
+ android:textAllCaps="true"
+ android:visibility="invisible"
+ android:layout_marginLeft="30.7dp"
+ android:layout_alignParentStart="true"/>
+
+ <LinearLayout
+ android:id="@+id/hour_container"
+ android:layout_width="match_parent"
+ android:layout_height="119.7dp"
+ android:layout_marginLeft="25dp"
+ android:layout_marginTop="20dp"
+ android:orientation="horizontal">
+
+ <TextClock
+ style="@style/text_thin_90sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:format12Hour="@string/big_clock_12_hours_format"
+ android:format24Hour="@string/big_clock_24_hours_format"
+ android:gravity="left"
+ android:includeFontPadding="false" />
+
+ <TextView
+ android:id="@+id/ampm_text"
+ style="@style/text_thin_34sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:layout_marginLeft="7dp" />
+ </LinearLayout>
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="86.67dp"
+ android:layout_marginLeft="31.33dp"
+ android:layout_marginTop="100dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <TextClock
+ style="@style/text_regular_bold_48sp"
+ android:layout_width="55.3dp"
+ android:layout_height="56.3dp"
+ android:format12Hour="@string/day_number_format"
+ android:format24Hour="@string/day_number_format"
+ android:includeFontPadding="false"
+ android:gravity="center"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_marginLeft="6.67dp">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="18dp"
+ android:orientation="horizontal">
+
+ <TextClock
+ style="@style/text_medium_16sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:format12Hour="@string/month_format"
+ android:format24Hour="@string/month_format"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:textAllCaps="true"/>
+
+ <TextClock
+ style="@style/text_light_16sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:format12Hour="@string/year_format"
+ android:format24Hour="@string/year_format"
+ android:gravity="left"
+ android:textAllCaps="true"
+ android:includeFontPadding="false"
+ android:paddingLeft="8dp" />
+ </LinearLayout>
+ <TextClock
+ style="@style/text_regular_bold_22sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="36dp"
+ android:format12Hour="@string/day_name_format"
+ android:format24Hour="@string/day_name_format"
+ android:gravity="left"
+ android:singleLine="true"
+ android:textAllCaps="true"
+ android:includeFontPadding="false" />
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/clock_edit_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="-60dp"
+ android:layout_marginTop="9dp">
+ <TextView
+ style="@style/text_condensed_bold_italic_18sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/button_states_blue"
+ android:gravity="left"
+ android:padding="5dp"
+ android:text="@string/edit" />
+ </FrameLayout>
+ </LinearLayout>
+</RelativeLayout>
diff --git a/packages/Keyguard/res/layout/clock_widget_peace_of_mind.xml b/packages/Keyguard/res/layout/clock_widget_peace_of_mind.xml
new file mode 100644
index 0000000..3fa71dc
--- /dev/null
+++ b/packages/Keyguard/res/layout/clock_widget_peace_of_mind.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/text_light_20sp"
+ android:layout_marginTop="-5dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="0.9"
+ android:gravity="left"
+ android:paddingLeft="112dp"
+ android:text="@string/you_been_in_peace_for" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="-13dp"
+ android:layout_marginBottom="-5dp"
+ >
+
+ <TextView
+ android:id="@+id/text_pom_current"
+ style="@style/text_regular_bold_45sp"
+ android:layout_width="112dp"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:paddingRight="8dp"
+ android:singleLine="true"
+ />
+
+ <TextView
+ style="@style/text_condensed_bold_45sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:text="@string/minutes" />
+ </LinearLayout>
+
+ <TextView
+ style="@style/text_light_14sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:paddingLeft="112dp"
+ android:lineSpacingMultiplier="0.9"
+ android:text="@string/your_record_peace_of_mind" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="-5dp"
+ >
+
+ <TextView
+ android:id="@+id/text_pom_record"
+ style="@style/text_regular_bold_27sp"
+ android:layout_width="112dp"
+ android:layout_height="wrap_content"
+ android:paddingRight="8dp"
+ android:gravity="right"
+ android:singleLine="true"
+ />
+
+ <TextView
+ style="@style/text_condensed_bold_27sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:paddingRight="8dp"
+ android:text="@string/minutes" />
+
+ <TextView
+ android:id="@+id/peace_share_button"
+ style="@style/text_condensed_bold_italic_18sp"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:background="@drawable/button_states_blue"
+ android:drawablePadding="8dp"
+ android:drawableRight="@drawable/share_icon"
+ android:gravity="left"
+ android:padding="5dp"
+ android:layout_marginLeft="30dp"
+ android:text="@string/share" />
+
+ </LinearLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/Keyguard/res/layout/clock_widget_yours_since.xml b/packages/Keyguard/res/layout/clock_widget_yours_since.xml
new file mode 100644
index 0000000..b7df4eb
--- /dev/null
+++ b/packages/Keyguard/res/layout/clock_widget_yours_since.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/text_light_21sp"
+ android:textSize="20dp"
+ android:layout_width="match_parent"
+ android:layout_height="23.67dp"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:paddingLeft="31dp"
+ android:text="@string/your_fairphone_for" />
+
+ <LinearLayout
+ android:id="@+id/eleapsed_years_container"
+ android:layout_width="match_parent"
+ android:layout_height="46.67dp"
+ android:orientation="horizontal"
+ android:paddingLeft="31dp"
+ android:layout_marginTop="30.45dp">
+
+ <TextView
+ android:id="@+id/eleapsed_years_text"
+ style="@style/text_regular_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:paddingRight="3dp" />
+
+ <TextView
+ android:id="@+id/years_text"
+ style="@style/text_condensed_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:text="@string/years" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/eleapsed_months_container"
+ android:layout_width="match_parent"
+ android:layout_height="46.67dp"
+ android:orientation="horizontal"
+ android:paddingLeft="31dp"
+ android:layout_below="@id/eleapsed_years_container"
+ android:layout_marginTop="-8.67dp">
+
+ <TextView
+ android:id="@+id/eleapsed_months_text"
+ style="@style/text_regular_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:paddingRight="3dp" />
+
+ <TextView
+ android:id="@+id/months_text"
+ style="@style/text_condensed_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:text="@string/months" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/eleapsed_days_container"
+ android:layout_width="match_parent"
+ android:layout_height="46.67dp"
+ android:orientation="horizontal"
+ android:paddingLeft="31dp"
+ android:layout_below="@id/eleapsed_months_container"
+ android:layout_marginTop="-8.67dp">
+
+ <TextView
+ android:id="@+id/eleapsed_days_text"
+ style="@style/text_regular_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:paddingRight="3dp" />
+
+ <TextView
+ android:id="@+id/days_text"
+ style="@style/text_condensed_bold_45sp"
+ android:textSize="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:includeFontPadding="false"
+ android:text="@string/days" />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/yours_since_share_button"
+ android:layout_width="84.67dp"
+ android:layout_height="32dp"
+ android:background="@drawable/button_states_blue"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_marginTop="121.45dp"
+ android:layout_alignParentRight="true">
+ <TextView
+ style="@style/text_condensed_bold_italic_18sp"
+ android:textSize="17.67dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:layout_gravity="center_vertical"
+ android:text="@string/share"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:src="@drawable/share_icon"/>
+ </FrameLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 69f510e..552fe1a 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -21,6 +21,7 @@
<com.android.keyguard.KeyguardStatusView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ xmlns:fairphone_clock_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyguard_status_view"
android:orientation="vertical"
android:layout_width="match_parent"
@@ -34,6 +35,12 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical" >
+ <com.android.keyguard.FairphoneClockView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ fairphone_clock_view:showOnly="all"
+ android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
<TextClock
android:id="@+id/clock_view"
android:layout_width="wrap_content"
@@ -44,9 +51,12 @@
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+ android:layout_marginBottom="@dimen/bottom_text_spacing_digital"
+ android:visibility="gone" />
- <include layout="@layout/keyguard_status_area" />
+ <include
+ layout="@layout/keyguard_status_area"
+ android:visibility="gone" />
<TextView
android:id="@+id/owner_info"
android:layout_marginLeft="16dp"
diff --git a/packages/Keyguard/res/layout/widget_main.xml b/packages/Keyguard/res/layout/widget_main.xml
new file mode 100644
index 0000000..483bc7d
--- /dev/null
+++ b/packages/Keyguard/res/layout/widget_main.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="180dp">
+
+ <include layout="@layout/clock_widget_main"
+ android:id="@+id/clock_widget_main"
+ android:visibility="gone"/>
+ <include layout="@layout/clock_widget_battery"
+ android:id="@+id/clock_widget_battery"
+ android:visibility="gone"/>
+ <include layout="@layout/clock_widget_peace_of_mind"
+ android:id="@+id/clock_widget_peace_of_mind"
+ android:visibility="gone"/>
+ <include layout="@layout/clock_widget_yours_since"
+ android:id="@+id/clock_widget_yours_since"
+ android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
index 9100140..ab8d52b 100644
--- a/packages/Keyguard/res/values/attrs.xml
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -141,4 +141,15 @@
<declare-styleable name="CarrierText">
<attr name="allCaps" format="boolean" />
</declare-styleable>
+
+ <declare-styleable name="FairphoneClockView">
+ <attr name="showOnly">
+ <flag name="clock" value="0x1"/>
+ <flag name="pom" value="0x2"/>
+ <flag name="battery" value="0x4"/>
+ <flag name="yours" value="0x8"/>
+ <flag name="all" value="0xf" />
+ </attr>
+ </declare-styleable>
+
</resources>
diff --git a/packages/Keyguard/res/values/colors.xml b/packages/Keyguard/res/values/colors.xml
index 4e28eff..4891a9d 100644
--- a/packages/Keyguard/res/values/colors.xml
+++ b/packages/Keyguard/res/values/colors.xml
@@ -27,4 +27,6 @@
<!-- Clock -->
<color name="clock_white">#ffffffff</color>
<color name="clock_gray">#99ffffff</color>
+
+ <color name="fairphone_blue">#2aa8e0</color>
</resources>
diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml
index c576b61..8149044 100644
--- a/packages/Keyguard/res/values/config.xml
+++ b/packages/Keyguard/res/values/config.xml
@@ -38,4 +38,17 @@
<!-- display airplane mode in lock sreen when phone is in APM mode -->
<bool name="config_display_APM">false</bool>
+
+
+ <string name="big_clock_12_hours_format" translatable="false">h:mm</string>
+ <string name="big_clock_24_hours_format" translatable="false">kk:mm</string>
+ <string name="month_format" translatable="false">MMMM</string>
+ <string name="year_format" translatable="false">yyyy</string>
+ <string name="day_name_format" translatable="false">EEEE</string>
+ <string name="day_number_format" translatable="false">dd</string>
+ <string name="alarm_clock_12h_format" translatable="false">EEE, h:mm</string>
+ <string name="alarm_clock_24h_format" translatable="false">EEE, kk:mm</string>
+ <string name="time_am_default" translatable="false">AM</string>
+ <string name="time_pm_default" translatable="false">PM</string>
+ <string name="colon" translate="false">:</string>
</resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index a5797e9..a0b9c16 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -469,4 +469,37 @@
<item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item>
<item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
</plurals>
+
+ <string name="you_been_in_peace_for">You\'ve been in\npeace of mind for</string>
+ <string name="your_record_peace_of_mind">Your record\npeace of mind was</string>
+ <string name="minutes">minutes</string>
+ <string name="share">share</string>
+ <string name="battery_charge_will_last_until">Battery charge will last until</string>
+ <string name="battery_will_be_charged_at">Battery will be charged at</string>
+ <string name="battery_is_fully">Battery charge complete</string>
+ <string name="make_it_last_longer">Make it last longer</string>
+ <string name="your_fairphone_for">Your Fairphone for</string>
+ <string name="years">years</string>
+ <string name="months">months</string>
+ <string name="days">days</string>
+ <string name="charged">charged</string>
+ <string name="year">year</string>
+ <string name="month">month</string>
+ <string name="day">day</string>
+ <string name="hours">hours</string>
+ <string name="hour">hour</string>
+ <string name="weeks">weeks</string>
+ <string name="week">week</string>
+ <string name="been_in_peace_for">I\'ve been in peace of mind for</string>
+ <string name="peace_record_text">A new personal best!</string>
+ <string name="my_fairphone_is">My Fairphone is</string>
+ <string name="old">old!</string>
+ <string name="edit">Edit</string>
+ <string name="share_to">Share to</string>
+ <string name="battery_charge_will_last">Battery charge will last</string>
+ <string name="battery_will_be_charged_in">Battery will be charged in</string>
+ <string name="unplug_your_charger">Unplug your charger</string>
+ <string name="tomorrow">tomorrow</string>
+
+
</resources>
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
index 592be3e..a211fa5 100644
--- a/packages/Keyguard/res/values/styles.xml
+++ b/packages/Keyguard/res/values/styles.xml
@@ -87,4 +87,327 @@
<item name="android:colorControlNormal">#80ffffff</item>
<item name="android:colorControlActivated">#80ffffff</item>
</style>
+
+<style name="ButtonBar">
+ <item name="android:paddingLeft">2dp</item>
+ <item name="android:paddingTop">5dp</item>
+ <item name="android:paddingRight">2dp</item>
+ <item name="android:paddingBottom">0dp</item>
+ <item name="android:background">@android:drawable/bottom_bar</item>
+ </style>
+
+ <style name="ButtonBarButton" />
+
+ <!-- Button styles -->
+ <style name="SubTitle">
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">18dp</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:includeFontPadding">false</item>
+ <item name="android:shadowColor">#33000000</item>
+ <item name="android:shadowDx">1</item>
+ <item name="android:shadowDy">1</item>
+ <item name="android:shadowRadius">1</item>
+ </style>
+
+ <style name="label_widget">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_white_21sp">
+ <item name="android:textSize">21sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_light_21sp" parent="text_white_21sp">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="text_regular_21sp" parent="text_white_21sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_21sp_spacing" parent="text_regular_21sp">
+ <item name="android:letterSpacing">-0.01</item>
+ </style>
+
+ <style name="text_white_93sp">
+ <item name="android:textSize">93sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_thin_93sp" parent="text_white_93sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_thin_93sp_spacing" parent="text_thin_93sp">
+ <item name="android:letterSpacing">-0.01</item>
+ </style>
+
+ <style name="text_white_102sp">
+ <item name="android:textSize">102sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_thin_102sp" parent="text_white_102sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_white_90sp">
+ <item name="android:textSize">90sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_thin_90sp" parent="text_white_90sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_white_53sp">
+ <item name="android:textSize">53sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_thin_53sp" parent="text_white_53sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_condensed_53sp" parent="text_white_53sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_thin_53sp_spacing" parent="text_thin_53sp">
+ <item name="android:alpha">0.5</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:letterSpacing">-0.01</item>
+ </style>
+
+ <style name="text_white_56sp">
+ <item name="android:textSize">56sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_condensed_bold_56sp" parent="text_white_56sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_white_20sp">
+ <item name="android:textSize">20sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_medium_20sp" parent="text_white_20sp">
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_light_20sp" parent="text_white_20sp">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+
+ <style name="text_white_34sp">
+ <item name="android:textSize">34sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_34sp" parent="text_white_34sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_bold_34sp" parent="text_regular_34sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_white_48sp">
+ <item name="android:textSize">48sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_48sp" parent="text_white_48sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_bold_48sp" parent="text_regular_48sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_thin_34sp" parent="text_white_34sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_condensed_34sp" parent="text_white_34sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_thin_34sp_spacing" parent="text_thin_34sp">
+ <item name="android:alpha">0.5</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:letterSpacing">-0.01</item>
+ </style>
+
+ <style name="text_white_67sp">
+ <item name="android:textSize">67sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_67sp" parent="text_white_67sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_bold_67sp" parent="text_regular_67sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_condensed_67sp" parent="text_white_67sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_condensed_bold_67sp" parent="text_condensed_67sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_light_67sp" parent="text_white_67sp">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="text_white_45sp">
+ <item name="android:textSize">45sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_45sp" parent="text_white_45sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_bold_45sp" parent="text_regular_45sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_condensed_45sp" parent="text_white_45sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_condensed_bold_45sp" parent="text_condensed_45sp">
+ <item name="android:textStyle">bold</item>
+ <item name="android:alpha">0.5</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_white_16sp">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_medium_16sp" parent="text_white_16sp">
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="text_regular_16sp" parent="text_white_16sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_white_22sp">
+ <item name="android:textSize">22sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_22sp" parent="text_white_22sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_white_14sp">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_light_16sp" parent="text_white_16sp">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="text_regular_bold_16sp" parent="text_regular_16sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_regular_bold_22sp" parent="text_regular_22sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+ <style name="text_light_14sp" parent="text_white_14sp">
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="text_white_27sp">
+ <item name="android:textSize">27sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_regular_27sp" parent="text_white_27sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_regular_bold_27sp" parent="text_regular_27sp">
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="text_condensed_27sp" parent="text_white_27sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_condensed_bold_27sp" parent="text_condensed_27sp">
+ <item name="android:textStyle">bold</item>
+ <item name="android:alpha">0.5</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_white_18sp">
+ <item name="android:textSize">18sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_condensed_18sp" parent="text_white_18sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_condensed_16sp" parent="text_white_16sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_regular_18sp" parent="text_white_18sp">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="text_condensed_bold_italic_18sp" parent="text_condensed_18sp">
+ <item name="android:textStyle">bold|italic</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_condensed_bold_italic_16sp" parent="text_condensed_16sp">
+ <item name="android:textStyle">bold|italic</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_condensed_21sp" parent="text_white_21sp">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ </style>
+
+ <style name="text_condensed_bold_21sp" parent="text_condensed_21sp">
+ <item name="android:textStyle">bold</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="text_white_37sp">
+ <item name="android:textSize">37sp</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="text_thin_37sp" parent="text_white_37sp">
+ <item name="android:fontFamily">sans-serif-thin</item>
+ </style>
+
+ <style name="text_thin_37sp_alpha" parent="text_thin_37sp">
+ <item name="android:alpha">0.5</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/ClockScreenService.java b/packages/Keyguard/src/com/android/keyguard/ClockScreenService.java
new file mode 100644
index 0000000..5a6d841
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/ClockScreenService.java
@@ -0,0 +1,190 @@
+package com.android.keyguard;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.Calendar;
+
+//import com.fairphone.clock.widget.ClockWidget;
+
+public class ClockScreenService extends Service
+{
+
+ private static final String TAG = ClockScreenService.class.getSimpleName();
+
+ public static final String ACTION_ALARM_CHANGED_V18 = "android.intent.action.ALARM_CHANGED";
+ public static final String ACTION_ALARM_CHANGED = "android.app.action.NEXT_ALARM_CLOCK_CHANGED";
+ public static final String ACTION_CLOCK_UPDATE = "com.fairphone.clock.widget.ClockWidget.CLOCK_UPDATE";
+
+ public static final long MINUTES_IN_MILIS = 60000L;
+
+ private PendingIntent clockUpdateIntent;
+ private BroadcastReceiver mBroadcastReceiver;
+
+ private long mScreenOffTimestamp = -1;
+
+ public ClockScreenService()
+ {
+ Log.wtf(TAG, "ClockScreenService");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId)
+ {
+ Log.wtf(TAG, "onStartCommand");
+ super.onStartCommand(intent, flags, startId);
+
+ setupAMPMManager();
+ setupBroadcastReceiver();
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ Log.wtf(TAG, "onDestroy");
+ super.onDestroy();
+
+ unregisterReceiver(mBroadcastReceiver);
+ clearAMPMManager();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent)
+ {
+ return null;
+ }
+
+// --Commented out by Inspection START (26/08/15 20:57):
+// private static String getBatteryStatusAsString(int status) {
+// String desc = "Unknown: ";
+// switch (status) {
+// case BatteryManager.BATTERY_STATUS_CHARGING:
+// desc = "BATTERY_STATUS_CHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_FULL:
+// desc = "BATTERY_STATUS_FULL";
+// break;
+// case BatteryManager.BATTERY_STATUS_DISCHARGING:
+// desc = "BATTERY_STATUS_DISCHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+// desc = "BATTERY_STATUS_NOT_CHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_UNKNOWN:
+// desc = "BATTERY_STATUS_UNKNOWN";
+// break;
+// default:
+// desc += status;
+// break;
+// }
+// return desc;
+// }
+// --Commented out by Inspection STOP (26/08/15 20:57)
+
+ private void setupBroadcastReceiver()
+ {
+ mBroadcastReceiver = new BroadcastReceiver()
+ {
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ // lets sort this by occurrence
+ if (intent == null)
+ {
+ return;
+ }
+ String action = intent.getAction();
+ if (action.equals(ACTION_CLOCK_UPDATE))
+ {
+ FairphoneClockData.sendUpdateBroadcast(context);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_CHANGED))
+ {
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+// String debug = "Battery Level: " + level + "\nBattery Status: " + getBatteryStatusAsString(status) + "\nBattery Scale: " + scale;
+// Toast.makeText(context, debug, Toast.LENGTH_LONG).show();
+ FairphoneClockData.updateBatteryPreferences(context, level, status, scale);
+ FairphoneClockData.sendUpdateBroadcast(context);
+ }
+ else if (action.equals(Intent.ACTION_SCREEN_OFF))
+ {
+ mScreenOffTimestamp = System.currentTimeMillis();
+ }
+ else if (action.equals(Intent.ACTION_SCREEN_ON))
+ {
+ if (mScreenOffTimestamp != -1)
+ {
+ long pomInMinutes = (System.currentTimeMillis() - mScreenOffTimestamp) / MINUTES_IN_MILIS;
+ FairphoneClockData.setPeaceOfMindCurrent(context, pomInMinutes);
+ long pomRecord = FairphoneClockData.getPeaceOfMindRecord(context);
+ if (pomInMinutes > pomRecord)
+ {
+ FairphoneClockData.setPeaceOfMindRecord(context, pomInMinutes);
+ }
+ }
+ // always update
+ FairphoneClockData.sendUpdateBroadcast(context);
+ }
+ else if (action.equals(ACTION_ALARM_CHANGED) ||
+ action.equals(ACTION_ALARM_CHANGED_V18) ||
+ action.equals(Intent.ACTION_TIME_CHANGED) ||
+ action.equals(Intent.ACTION_TIMEZONE_CHANGED))
+ {
+ FairphoneClockData.sendUpdateBroadcast(context);
+ }
+ }
+ };
+ IntentFilter actions = new IntentFilter(ACTION_ALARM_CHANGED);
+ actions.addAction(ACTION_ALARM_CHANGED_V18);
+ actions.addAction(ACTION_CLOCK_UPDATE);
+ actions.addAction(Intent.ACTION_TIME_CHANGED);
+ actions.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ actions.addAction(Intent.ACTION_BATTERY_CHANGED);
+ actions.addAction(Intent.ACTION_SCREEN_OFF);
+ actions.addAction(Intent.ACTION_SCREEN_ON);
+ registerReceiver(mBroadcastReceiver, actions);
+ }
+
+ private PendingIntent getUpdateIntent()
+ {
+ if (clockUpdateIntent == null)
+ {
+ Intent intent = new Intent(ACTION_CLOCK_UPDATE);
+ clockUpdateIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+ return clockUpdateIntent;
+ }
+
+ private void setupAMPMManager()
+ {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.add(Calendar.MINUTE, 60 - calendar.get(Calendar.MINUTE));
+ calendar.add(Calendar.SECOND, 60 - calendar.get(Calendar.SECOND));
+ AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 3600000, getUpdateIntent());
+ }
+
+
+ private void clearAMPMManager()
+ {
+ if (clockUpdateIntent != null)
+ {
+ AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.cancel(getUpdateIntent());
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/FairphoneClockData.java b/packages/Keyguard/src/com/android/keyguard/FairphoneClockData.java
new file mode 100644
index 0000000..ffe7e5c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/FairphoneClockData.java
@@ -0,0 +1,146 @@
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+/**
+ * Created by rrocha on 8/25/15.
+ */
+public class FairphoneClockData
+{
+ private static final String FAIRPHONE_CLOCK_PREFERENCES = "com.fairphone.clock.FAIRPHONE_CLOCK_PREFERENCES";
+ private static final String PREFERENCE_BATTERY_LEVEL = "com.fairphone.clock.PREFERENCE_BATTERY_LEVEL";
+ private static final String PREFERENCE_ACTIVE_LAYOUT = "com.fairphone.clock.PREFERENCE_ACTIVE_LAYOUT";
+ private static final String PREFERENCE_BATTERY_STATUS = "com.fairphone.clock.PREFERENCE_BATTERY_STATUS";
+ private static final String PREFERENCE_POM_CURRENT = "com.fairphone.clock.PREFERENCE_POM_CURRENT";
+ private static final String PREFERENCE_POM_RECORD = "com.fairphone.clock.PREFERENCE_POM_RECORD";
+ private static final String PREFERENCE_YOUR_FAIRPHONE_SINCE = "com.fairphone.clock.PREFERENCE_YOUR_FAIRPHONE_SINCE";
+ private static final String PREFERENCE_BATTERY_CHANGED_TIMESTAMP = "com.fairphone.clock.PREFERENCE_BATTERY_CHANGED_TIMESTAMP";
+ private static final String PREFERENCE_BATTERY_TIME_UNTIL_DISCHARGED = "com.fairphone.clock.PREFERENCE_BATTERY_TIME_UNTIL_DISCHARGED";
+ private static final String PREFERENCE_BATTERY_TIME_UNTIL_CHARGED = "com.fairphone.clock.PREFERENCE_BATTERY_TIME_UNTIL_CHARGED";
+
+ public static final String VIEW_UPDATE = "com.fairphone.fairphoneclock.UPDATE";
+
+ private static SharedPreferences getSharedPrefs(Context context)
+ {
+ return context.getSharedPreferences(FAIRPHONE_CLOCK_PREFERENCES, Context.MODE_PRIVATE);
+ }
+
+ public static long getPeaceOfMindCurrent(Context context)
+ {
+ return getSharedPrefs(context).getLong(PREFERENCE_POM_CURRENT, 0);
+ }
+
+ public static void setPeaceOfMindCurrent(Context context, long value)
+ {
+ getSharedPrefs(context).edit().putLong(PREFERENCE_POM_CURRENT, value).commit();
+ }
+
+ public static long getPeaceOfMindRecord(Context context)
+ {
+ return getSharedPrefs(context).getLong(PREFERENCE_POM_RECORD, 0);
+ }
+
+ public static void setPeaceOfMindRecord(Context context, long value)
+ {
+ getSharedPrefs(context).edit().putLong(PREFERENCE_POM_RECORD, value).commit();
+ }
+
+ public static int getBatteryLevel(Context context)
+ {
+ return getSharedPrefs(context).getInt(PREFERENCE_BATTERY_LEVEL, 0);
+ }
+
+ public static void setBatteryLevel(Context context, int value)
+ {
+ getSharedPrefs(context).edit().putInt(PREFERENCE_BATTERY_LEVEL, value).commit();
+ }
+
+ public static int getBatteryStatus(Context context)
+ {
+ return getSharedPrefs(context).getInt(PREFERENCE_BATTERY_STATUS, 0);
+ }
+
+ public static void setBatteryStatus(Context context, int value)
+ {
+ getSharedPrefs(context).edit().putInt(PREFERENCE_BATTERY_STATUS, value).commit();
+ }
+
+ public static long getBatteryTimeUntilCharged(Context context)
+ {
+ return getSharedPrefs(context).getLong(PREFERENCE_BATTERY_TIME_UNTIL_CHARGED, 0);
+ }
+
+ public static void setBatteryTimeUntilCharged(Context context, long value)
+ {
+ getSharedPrefs(context).edit().putLong(PREFERENCE_BATTERY_TIME_UNTIL_CHARGED, value).commit();
+ }
+
+ public static long getBatteryTimeUntilDischarged(Context context)
+ {
+ return getSharedPrefs(context).getLong(PREFERENCE_BATTERY_TIME_UNTIL_DISCHARGED, 0);
+ }
+
+ public static void setBatteryTimeUntilDischarged(Context context, long value)
+ {
+ getSharedPrefs(context).edit().putLong(PREFERENCE_BATTERY_TIME_UNTIL_DISCHARGED, value).commit();
+ }
+
+ public static int getCurrentLayoutIdx(Context context)
+ {
+ return getSharedPrefs(context).getInt(PREFERENCE_ACTIVE_LAYOUT, 0);
+ }
+
+ public static void setCurrentLayoutIdx(Context context, int value)
+ {
+ getSharedPrefs(context).edit().putInt(PREFERENCE_ACTIVE_LAYOUT, value).commit();
+ }
+
+ public static long getFairphoneSince(Context context)
+ {
+ return getSharedPrefs(context).getLong(PREFERENCE_YOUR_FAIRPHONE_SINCE, 0);
+ }
+
+ public static void setFairphoneSince(Context context, long value)
+ {
+ getSharedPrefs(context).edit().putLong(PREFERENCE_YOUR_FAIRPHONE_SINCE, value).commit();
+ }
+
+ public static void updateBatteryPreferences(Context context, int level, int status, int scale)
+ {
+ int currentLevel = getBatteryLevel(context);
+ int currentStatus = getBatteryStatus(context);
+
+ if (currentLevel != level || currentStatus != status)
+ {
+ setBatteryLevel(context, level);
+ setBatteryStatus(context, status);
+ updateBatteryDurationTimes(context, level, scale);
+ }
+
+ }
+
+ private static void updateBatteryDurationTimes(Context context, int level, int scale)
+ {
+ SharedPreferences prefs = getSharedPrefs(context);
+ long now = System.currentTimeMillis();
+ long lasttime = prefs.getLong(PREFERENCE_BATTERY_CHANGED_TIMESTAMP, now);
+ long timeUntilCharge = (now - lasttime) * Math.abs(scale - level);
+ long timeUntilDischarge = (now - lasttime) * (level);
+ prefs.edit().putLong(PREFERENCE_BATTERY_CHANGED_TIMESTAMP, now).commit();
+ setBatteryTimeUntilCharged(context, timeUntilCharge);
+ setBatteryTimeUntilDischarged(context, timeUntilDischarge);
+ }
+
+ public static void sendLastLongerBroadcast(Context context)
+ {
+ FairphoneClockView.dismissKeyguardOnNextActivity();
+ context.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ public static void sendUpdateBroadcast(Context context)
+ {
+ context.sendBroadcast(new Intent(VIEW_UPDATE));
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/FairphoneClockView.java b/packages/Keyguard/src/com/android/keyguard/FairphoneClockView.java
new file mode 100644
index 0000000..9b88e35
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/FairphoneClockView.java
@@ -0,0 +1,698 @@
+package com.android.keyguard;
+
+import android.annotation.TargetApi;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManagerGlobal;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by rrocha on 8/24/15.
+ */
+public class FairphoneClockView extends LinearLayout
+{
+ private static final String TAG = FairphoneClockView.class.getSimpleName();
+ private ViewGroup[] CLOCK_WIDGET_VIEWS = null;
+
+ private TextView mAmPmText, mAlarmText, mBatteryDaysLeft, mBatteryAmPmIndicator, mHoursText,
+ mMinutesText, mBatteryDescriptionText, mPomCurrentText, mPomRecordText, mElapsedYearsText,
+ mYearsText, mElapsedMonthsText, mMonthsText, mElapsedDaysText, mDaysText;
+ private View mDayIndicator, mBatteryTimeGroup, mLastLongerButton, mChargedText, mUnplugChargerText;
+ private ImageView mBatteryLevelImage;
+ private BroadcastReceiver mReceiver;
+ Intent serviceIntent;
+ private final OnClickListener viewClickListener = new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ FairphoneClockData.getCurrentLayoutIdx(getContext());
+ int currentLayoutIdx = (1 + FairphoneClockData.getCurrentLayoutIdx(getContext())) % CLOCK_WIDGET_VIEWS.length;
+ FairphoneClockData.setCurrentLayoutIdx(getContext(), currentLayoutIdx);
+ update(currentLayoutIdx);
+ }
+ };
+
+ private static final int ATTR_CLOCK = 0x1;
+ private static final int ATTR_POM = 0x2;
+ private static final int ATTR_BATTERY = 0x4;
+ private static final int ATTR_YOURS = 0x8;
+ private static final int ATTR_ALL = 0xf;
+
+ private int[] CLOCK_WIDGET_LAYOUTS = {R.id.clock_widget_main, R.id.clock_widget_peace_of_mind, R.id.clock_widget_battery, R.id.clock_widget_yours_since};
+
+ @Override
+ protected void onAttachedToWindow()
+ {
+ Log.w(TAG, "onAttachedToWindow");
+ super.onAttachedToWindow();
+ mReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ update();
+ }
+ };
+ getContext().registerReceiver(mReceiver, new IntentFilter(FairphoneClockData.VIEW_UPDATE));
+ serviceIntent = new Intent(getContext(), ClockScreenService.class);
+ getContext().startService(serviceIntent);
+ }
+
+ @Override
+ protected void onDetachedFromWindow()
+ {
+ Log.w(TAG, "onDetachedFromWindow");
+ getContext().unregisterReceiver(mReceiver);
+ getContext().stopService(serviceIntent);
+ super.onDetachedFromWindow();
+ }
+
+ private final OnClickListener editClickListener = new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ Intent launchIntent = Intent.makeMainActivity(new ComponentName("com.android.deskclock", "com.android.deskclock.DeskClock"));
+ dismissKeyguardOnNextActivity();
+ UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
+ getContext().startActivityAsUser(launchIntent, null, user);
+ }
+ };
+
+ private final OnClickListener shareClickListener = new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ int active_layout = CLOCK_WIDGET_LAYOUTS[FairphoneClockData.getCurrentLayoutIdx(getContext())];
+ String shareText = null;
+ if (active_layout == R.id.clock_widget_peace_of_mind) {
+ shareText = getPeaceOfMindShareText();
+ }
+ else if (active_layout == R.id.clock_widget_yours_since) {
+ shareText = getYourFairphoneSinceShareText();
+ }
+ else {
+ Log.w(TAG, "Unknown Share button: " + active_layout);
+ }
+ if (!TextUtils.isEmpty(shareText))
+ {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType("text/plain");
+
+ sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
+ sendIntent = Intent.createChooser(sendIntent, getResources().getString(R.string.share_to));
+ sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ dismissKeyguardOnNextActivity();
+ UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
+ getContext().startActivityAsUser(sendIntent, null, user);
+ }
+ }
+ };
+
+ private String getPeaceOfMindShareText()
+ {
+ long pom_current = FairphoneClockData.getPeaceOfMindCurrent(getContext());
+ long pom_record = FairphoneClockData.getPeaceOfMindRecord(getContext());
+ String shareText = String.format("%s %d %s!", getResources().getString(R.string.been_in_peace_for), pom_current, getResources().getString(R.string.minutes));
+ if (pom_current == pom_record)
+ {
+ shareText += " " + getResources().getString(R.string.peace_record_text);
+ }
+ return shareText;
+ }
+
+ private String getYourFairphoneSinceShareText()
+ {
+ Resources resources = getResources();
+ Period p = getPeriod();
+ StringBuilder s = new StringBuilder();
+ if (p.diffYears != 0)
+ {
+ s.append(String.format("%02d", p.diffYears));
+ s.append(p.diffYears == 1 ? resources.getString(R.string.year) : resources.getString(R.string.years));
+ s.append(", ");
+ s.append(String.format("%02d", p.diffMonths));
+ s.append(p.diffMonths == 1 ? resources.getString(R.string.month) : resources.getString(R.string.months));
+ s.append(", ");
+ s.append(String.format("%02d", p.diffMonthDays));
+ s.append(p.diffMonthDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ }
+ else if (p.diffMonths != 0)
+ {
+ s.append(String.format("%02d", p.diffMonths));
+ s.append(p.diffMonths == 1 ? resources.getString(R.string.month) : resources.getString(R.string.months));
+ s.append(", ");
+ s.append(String.format("%02d", p.diffWeeks));
+ s.append(p.diffWeeks == 1 ? resources.getString(R.string.week) : resources.getString(R.string.weeks));
+ s.append(", ");
+ s.append(String.format("%02d", p.diffMonthWeekDays));
+ s.append(p.diffMonthWeekDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ }
+ else
+ {
+ mElapsedYearsText.setText(String.format("%02d", p.diffWeeks));
+ mYearsText.setText(p.diffWeeks == 1 ? resources.getString(R.string.week) : resources.getString(R.string.weeks));
+ s.append(", ");
+ mElapsedMonthsText.setText(String.format("%02d", p.diffMonthWeekDays));
+ mMonthsText.setText(p.diffMonthWeekDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ s.append(", ");
+ mElapsedDaysText.setText(String.format("%02d", p.diffHours));
+ mDaysText.setText(p.diffHours == 1 ? resources.getString(R.string.hour) : resources.getString(R.string.hours));
+ }
+ return String.format("%s %s %s", getResources().getString(R.string.my_fairphone_is), s.toString(), getResources().getString(R.string.old));
+ }
+
+ public FairphoneClockView(Context context)
+ {
+ super(context);
+ init(null);
+ }
+
+ public FairphoneClockView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init(attrs);
+ }
+
+ public FairphoneClockView(Context context, AttributeSet attrs, int defStyleAttr)
+ {
+ super(context, attrs, defStyleAttr);
+ init(attrs);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public FairphoneClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
+ {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(attrs);
+ }
+
+
+ private void init(AttributeSet attrs)
+ {
+ if (attrs != null)
+ {
+ TypedArray a = getContext().getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.FairphoneClockView,
+ 0, 0);
+
+ int showOnly = ATTR_ALL;
+ try
+ {
+ showOnly = a.getInteger(R.styleable.FairphoneClockView_showOnly, ATTR_ALL);
+ }
+ finally
+ {
+ a.recycle();
+ }
+ if (showOnly != ATTR_ALL)
+ {
+ List<Integer> availableLayouts = new ArrayList<>();
+ if ((showOnly&ATTR_CLOCK) == ATTR_CLOCK)
+ {
+ availableLayouts.add(R.id.clock_widget_main);
+ }
+ if ((showOnly&ATTR_POM) == ATTR_POM)
+ {
+ availableLayouts.add(R.id.clock_widget_peace_of_mind);
+ }
+ if ((showOnly&ATTR_BATTERY) == ATTR_BATTERY)
+ {
+ availableLayouts.add(R.id.clock_widget_battery);
+ }
+ if ((showOnly&ATTR_YOURS) == ATTR_YOURS)
+ {
+ availableLayouts.add(R.id.clock_widget_yours_since);
+ }
+ CLOCK_WIDGET_LAYOUTS = new int[availableLayouts.size()];
+ Iterator<Integer> iterator = availableLayouts.iterator();
+ for (int i=0; i < CLOCK_WIDGET_LAYOUTS.length; i++)
+ {
+ CLOCK_WIDGET_LAYOUTS[i] = iterator.next();
+ }
+ int currentIdx = FairphoneClockData.getCurrentLayoutIdx(getContext());
+ if (currentIdx >= CLOCK_WIDGET_LAYOUTS.length)
+ {
+ // somehow, the config changed. Let's reset
+ FairphoneClockData.setCurrentLayoutIdx(getContext(), 0);
+ }
+ }
+ }
+
+ ViewGroup mRootView = (ViewGroup) inflate(getContext(), R.layout.widget_main, this);
+ CLOCK_WIDGET_VIEWS = new ViewGroup[CLOCK_WIDGET_LAYOUTS.length];
+ for (int i = 0; i < CLOCK_WIDGET_LAYOUTS.length; ++i)
+ {
+ CLOCK_WIDGET_VIEWS[i] = (ViewGroup) mRootView.findViewById(CLOCK_WIDGET_LAYOUTS[i]);
+ CLOCK_WIDGET_VIEWS[i].setOnClickListener(viewClickListener);
+ }
+ mAmPmText = (TextView) mRootView.findViewById(R.id.ampm_text);
+ mAlarmText = (TextView) mRootView.findViewById(R.id.alarm_text);
+ mDayIndicator = mRootView.findViewById(R.id.day_indicator);
+ mBatteryTimeGroup = mRootView.findViewById(R.id.battery_time_group);
+ mBatteryDaysLeft = (TextView) mRootView.findViewById(R.id.battery_days_left);
+ mBatteryAmPmIndicator = (TextView) mRootView.findViewById(R.id.battery_am_pm_indicator);
+ mHoursText = (TextView) mRootView.findViewById(R.id.hours_text);
+ mMinutesText = (TextView) mRootView.findViewById(R.id.minutes_text);
+ mBatteryLevelImage = (ImageView) mRootView.findViewById(R.id.battery_level_image);
+ mBatteryDescriptionText = (TextView) mRootView.findViewById(R.id.battery_description);
+ mLastLongerButton = mRootView.findViewById(R.id.last_longer_button);
+ mChargedText = mRootView.findViewById(R.id.charged_text);
+ mUnplugChargerText = mRootView.findViewById(R.id.unplug_charger_text);
+ mPomCurrentText = (TextView) mRootView.findViewById(R.id.text_pom_current);
+ mPomRecordText = (TextView) mRootView.findViewById(R.id.text_pom_record);
+
+ mElapsedYearsText = (TextView) mRootView.findViewById(R.id.eleapsed_years_text);
+ mYearsText = (TextView) mRootView.findViewById(R.id.years_text);
+ mElapsedMonthsText = (TextView) mRootView.findViewById(R.id.eleapsed_months_text);
+ mMonthsText = (TextView) mRootView.findViewById(R.id.months_text);
+ mElapsedDaysText = (TextView) mRootView.findViewById(R.id.eleapsed_days_text);
+ mDaysText = (TextView) mRootView.findViewById(R.id.days_text);
+
+ mRootView.findViewById(R.id.clock_edit_button).setOnClickListener(editClickListener);
+
+ mRootView.findViewById(R.id.peace_share_button).setOnClickListener(shareClickListener);
+ mRootView.findViewById(R.id.yours_since_share_button).setOnClickListener(shareClickListener);
+ mLastLongerButton.setOnClickListener(new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ FairphoneClockData.sendLastLongerBroadcast(getContext());
+ }
+ });
+
+ update();
+ }
+
+ public void update()
+ {
+ int currentLayoutIdx = FairphoneClockData.getCurrentLayoutIdx(getContext());
+ update(currentLayoutIdx);
+ }
+
+ private void update(int currentLayoutIdx)
+ {
+ makeViewgroupVisible(CLOCK_WIDGET_VIEWS[currentLayoutIdx]);
+ setupActiveView(currentLayoutIdx);
+ }
+
+ private void makeViewgroupVisible(ViewGroup viewGroup)
+ {
+ for (ViewGroup vg : CLOCK_WIDGET_VIEWS)
+ {
+ vg.setVisibility(vg.equals(viewGroup) ? VISIBLE : GONE);
+ }
+ }
+
+ private void setupActiveView(int currentLayoutIdx)
+ {
+ int active_layout = CLOCK_WIDGET_LAYOUTS[currentLayoutIdx];
+ if (active_layout == R.id.clock_widget_main) {
+ setClockAmPm();
+ setNextScheduledAlarm();
+ } else if (active_layout == R.id.clock_widget_peace_of_mind) {
+ setupPeaceOfMind();
+ } else if (active_layout == R.id.clock_widget_battery) {
+ setupBatteryLayout();
+ } else if (active_layout == R.id.clock_widget_yours_since) {
+ setYourFairphoneSince();
+ } else {
+ Log.wtf(TAG, "Unknown layout: " + active_layout);
+ }
+ }
+
+ private void setClockAmPm()
+ {
+ if (DateFormat.is24HourFormat(getContext()))
+ {
+ mAmPmText.setVisibility(View.GONE);
+ }
+ else
+ {
+ mAmPmText.setVisibility(View.VISIBLE);
+ Calendar currentCalendar = Calendar.getInstance();
+
+ int hour = currentCalendar.get(Calendar.HOUR_OF_DAY);
+
+ if (hour < 12)
+ {
+ mAmPmText.setText(getContext().getResources().getString(R.string.time_am_default));
+ }
+ else
+ {
+ mAmPmText.setText(getContext().getResources().getString(R.string.time_pm_default));
+ }
+ }
+ }
+
+ private void setNextScheduledAlarm()
+ {
+ String nextAlarm = getNextAlarm(getContext());
+
+ if (TextUtils.isEmpty(nextAlarm))
+ {
+ mAlarmText.setVisibility(View.INVISIBLE);
+ }
+ else
+ {
+ mAlarmText.setText(nextAlarm);
+ mAlarmText.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private static String getNextAlarm(Context context)
+ {
+ String nextAlarm = "";
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ {
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ if (am != null && am.getNextAlarmClock() != null)
+ {
+ String amPmMarker = "";
+ SimpleDateFormat sdf;
+ boolean is24hFormat = DateFormat.is24HourFormat(context);
+ long alarmTriggerTime = am.getNextAlarmClock().getTriggerTime();
+
+ if (is24hFormat)
+ {
+ sdf = new SimpleDateFormat(context.getResources().getString(R.string.alarm_clock_24h_format));
+ }
+ else
+ {
+ sdf = new SimpleDateFormat(context.getResources().getString(R.string.alarm_clock_12h_format));
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(alarmTriggerTime);
+
+ if (cal.get(Calendar.HOUR_OF_DAY) < 12)
+ {
+ amPmMarker = " " + context.getResources().getString(R.string.time_am_default);
+ }
+ else
+ {
+ amPmMarker = " " + context.getResources().getString(R.string.time_pm_default);
+ }
+ }
+ nextAlarm = sdf.format(am.getNextAlarmClock().getTriggerTime()) + amPmMarker;
+ }
+ }
+ else
+ {
+ nextAlarm = Settings.System.getString(context.getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED);
+ }
+ return nextAlarm;
+ }
+
+ private void setupBatteryLayout()
+ {
+ int batteryLevel = FairphoneClockData.getBatteryLevel(getContext());
+ int batteryStatus = FairphoneClockData.getBatteryStatus(getContext());
+ long chargingTime = FairphoneClockData.getBatteryTimeUntilCharged(getContext());
+ long remainingTime = FairphoneClockData.getBatteryTimeUntilDischarged(getContext());
+ updateBatteryStatusAndLevel(batteryLevel, batteryStatus, remainingTime, chargingTime);
+ }
+
+ private void setupPeaceOfMind()
+ {
+ long pom_current = FairphoneClockData.getPeaceOfMindCurrent(getContext());
+ long pom_record = FairphoneClockData.getPeaceOfMindRecord(getContext());
+ mPomCurrentText.setText(Long.toString(pom_current));
+ mPomRecordText.setText(Long.toString(pom_record));
+ }
+
+ private void updateBatteryLevel(int level, boolean isCharging)
+ {
+ if (level <= 5)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_00 : R.drawable.battery_00);
+ }
+ else if (level <= 10)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_10 : R.drawable.battery_10);
+ }
+ else if (level <= 20)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_20 : R.drawable.battery_20);
+ }
+ else if (level <= 30)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_30 : R.drawable.battery_30);
+ }
+ else if (level <= 40)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_40 : R.drawable.battery_40);
+ }
+ else if (level <= 50)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_50 : R.drawable.battery_50);
+ }
+ else if (level <= 60)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_60 : R.drawable.battery_60);
+ }
+ else if (level <= 70)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_70 : R.drawable.battery_70);
+ }
+ else if (level <= 80)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_80 : R.drawable.battery_80);
+ }
+ else if (level <= 90)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_90 : R.drawable.battery_90);
+ }
+ else if (level <= 100)
+ {
+ mBatteryLevelImage.setImageResource(isCharging ? R.drawable.battery_charging_100 : R.drawable.battery_100);
+ }
+ }
+
+ private void updateBatteryStatusAndLevel(int level, int status, long remainingTime, long chargingTime)
+ {
+ Resources resources = getResources();
+ switch (status)
+ {
+ case BatteryManager.BATTERY_STATUS_CHARGING:
+ updateBatteryLevel(level, true);
+ mChargedText.setVisibility(View.GONE);
+ mUnplugChargerText.setVisibility(View.GONE);
+ mLastLongerButton.setVisibility(View.INVISIBLE);
+
+ getRemainingTime(chargingTime, true);
+ break;
+ case BatteryManager.BATTERY_STATUS_FULL:
+ mBatteryDescriptionText.setText(resources.getString(R.string.battery_is_fully));
+ mBatteryLevelImage.setImageResource(R.drawable.battery_charging_100);
+ mLastLongerButton.setVisibility(View.INVISIBLE);
+ mBatteryTimeGroup.setVisibility(View.GONE);
+ mChargedText.setVisibility(View.VISIBLE);
+ mUnplugChargerText.setVisibility(View.VISIBLE);
+ break;
+ case BatteryManager.BATTERY_STATUS_DISCHARGING:
+ case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+ case BatteryManager.BATTERY_STATUS_UNKNOWN:
+ default:
+
+ updateBatteryLevel(level, false);
+ mChargedText.setVisibility(View.GONE);
+ mUnplugChargerText.setVisibility(View.GONE);
+ mLastLongerButton.setVisibility(View.VISIBLE);
+
+ getRemainingTime(remainingTime, false);
+ break;
+ }
+ }
+
+ private void getRemainingTime(long remainingTime, boolean isCharging)
+ {
+ Calendar currentTime = Calendar.getInstance();
+ Calendar endTime = Calendar.getInstance();
+ endTime.add(Calendar.MILLISECOND, (int) remainingTime);
+
+ if (endTime.get(Calendar.DAY_OF_MONTH) <= currentTime.get(Calendar.DAY_OF_MONTH) + 1)
+ {
+ mDayIndicator.setVisibility((endTime.get(Calendar.DAY_OF_MONTH) != currentTime.get(Calendar.DAY_OF_MONTH)) ? VISIBLE : INVISIBLE);
+ mBatteryDescriptionText.setText(getResources().getString(isCharging ? R.string.battery_will_be_charged_at : R.string.battery_charge_will_last_until));
+ mBatteryTimeGroup.setVisibility(View.VISIBLE);
+ mBatteryDaysLeft.setVisibility(View.GONE);
+ if (DateFormat.is24HourFormat(getContext()))
+ {
+ mHoursText.setText(String.format("%02d", endTime.get(Calendar.HOUR_OF_DAY)));
+ mBatteryAmPmIndicator.setText("");
+ }
+ else
+ {
+ mHoursText.setText(String.format("%d", endTime.get(Calendar.HOUR)));
+ mBatteryAmPmIndicator.setText(getResources().getString(endTime.get(Calendar.AM_PM) == Calendar.AM ? R.string.time_am_default : R.string.time_pm_default));
+ }
+ mMinutesText.setText(String.format("%02d", endTime.get(Calendar.MINUTE)));
+ }
+ else
+ {
+ mBatteryDescriptionText.setText(getResources().getString(isCharging ? R.string.battery_will_be_charged_in : R.string.battery_charge_will_last));
+ long diff = currentTime.getTimeInMillis() - endTime.getTimeInMillis();
+ long days = TimeUnit.MILLISECONDS.toDays(diff);
+ mBatteryDaysLeft.setText(String.format("%d %s", days, getResources().getString(days == 1 ? R.string.day : R.string.days)));
+ mBatteryTimeGroup.setVisibility(View.GONE);
+ mBatteryDaysLeft.setVisibility(View.VISIBLE);
+ }
+ }
+
+ //
+// public static String getBatteryStatusAsString(int status) {
+// String desc = "Unknown: ";
+// switch (status) {
+// case BatteryManager.BATTERY_STATUS_CHARGING:
+// desc = "BATTERY_STATUS_CHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_FULL:
+// desc = "BATTERY_STATUS_FULL";
+// break;
+// case BatteryManager.BATTERY_STATUS_DISCHARGING:
+// desc = "BATTERY_STATUS_DISCHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+// desc = "BATTERY_STATUS_NOT_CHARGING";
+// break;
+// case BatteryManager.BATTERY_STATUS_UNKNOWN:
+// desc = "BATTERY_STATUS_UNKNOWN";
+// break;
+// default:
+// desc += status;
+// break;
+// }
+// return desc;
+// }
+//
+ private void setYourFairphoneSince()
+ {
+ Resources resources = getResources();
+ Period p = getPeriod();
+
+ if (p.diffYears != 0)
+ {
+ mElapsedYearsText.setText(String.format("%02d", p.diffYears));
+ mYearsText.setText(p.diffYears == 1 ? resources.getString(R.string.year) : resources.getString(R.string.years));
+ mElapsedMonthsText.setText(String.format("%02d", p.diffMonths));
+ mMonthsText.setText(p.diffMonths == 1 ? resources.getString(R.string.month) : resources.getString(R.string.months));
+ mElapsedDaysText.setText(String.format("%02d", p.diffMonthDays));
+ mDaysText.setText(p.diffMonthDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ }
+ else if (p.diffMonths != 0)
+ {
+ mElapsedYearsText.setText(String.format("%02d", p.diffMonths));
+ mYearsText.setText(p.diffMonths == 1 ? resources.getString(R.string.month) : resources.getString(R.string.months));
+ mElapsedMonthsText.setText(String.format("%02d", p.diffWeeks));
+ mMonthsText.setText(p.diffWeeks == 1 ? resources.getString(R.string.week) : resources.getString(R.string.weeks));
+ mElapsedDaysText.setText(String.format("%02d", p.diffMonthWeekDays));
+ mDaysText.setText(p.diffMonthWeekDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ }
+ else
+ {
+ mElapsedYearsText.setText(String.format("%02d", p.diffWeeks));
+ mYearsText.setText(p.diffWeeks == 1 ? resources.getString(R.string.week) : resources.getString(R.string.weeks));
+ mElapsedMonthsText.setText(String.format("%02d", p.diffMonthWeekDays));
+ mMonthsText.setText(p.diffMonthWeekDays == 1 ? resources.getString(R.string.day) : resources.getString(R.string.days));
+ mElapsedDaysText.setText(String.format("%02d", p.diffHours));
+ mDaysText.setText(p.diffHours == 1 ? resources.getString(R.string.hour) : resources.getString(R.string.hours));
+ }
+ }
+
+ private Period getPeriod() {
+ Period p = new Period();
+ long startTime = FairphoneClockData.getFairphoneSince(getContext());
+ if (startTime == 0L)
+ {
+ startTime = System.currentTimeMillis();
+ FairphoneClockData.setFairphoneSince(getContext(),startTime);
+ }
+ Calendar start = Calendar.getInstance();
+ start.setTimeInMillis(startTime);
+ Calendar now = Calendar.getInstance();
+
+ p.diffHours = now.get(Calendar.HOUR_OF_DAY)-start.get(Calendar.HOUR_OF_DAY);
+
+ p.diffMonthDays = now.get(Calendar.DAY_OF_MONTH)-start.get(Calendar.DAY_OF_MONTH);
+ if (p.diffHours < 0) {
+ p.diffMonthDays -= 1;
+ p.diffHours = 24 + p.diffHours;
+ }
+
+ p.diffMonths = now.get(Calendar.MONTH)-start.get(Calendar.MONTH);
+ if (p.diffMonthDays < 0){
+ p.diffMonths -= 1;
+ Calendar c = Calendar.getInstance();
+ c.set(now.get(Calendar.YEAR), now.get(Calendar.MONTH), 0);
+ p.diffMonthDays = c.get(Calendar.DAY_OF_MONTH) + p.diffMonthDays;
+ }
+
+ p.diffYears = now.get(Calendar.YEAR)-start.get(Calendar.YEAR);
+ if (p.diffMonths < 0) {
+ p.diffYears -= 1;
+ p.diffMonths = 12 + p.diffMonths;
+ }
+
+ p.diffWeeks = p.diffMonthDays / 7;
+ p.diffMonthWeekDays = p.diffMonthDays % 7;
+
+ return p;
+ }
+
+ private static class Period {
+ public int diffHours = 0;
+ public int diffMonthDays = 0;
+ public int diffMonthWeekDays = 0;
+ public int diffWeeks = 0;
+ public int diffMonths = 0;
+ public int diffYears = 0;
+// --Commented out by Inspection START (26/08/15 20:58):
+// public String print() {
+// return
+// "diffYears "+diffYears+", "+
+// "diffMonths "+diffMonths+", "+
+// "diffWeeks "+diffWeeks+", "+
+// "diffMonthWeekDays "+diffMonthWeekDays+", "+
+// "diffMonthDays "+diffMonthDays+", "+
+// "diffHours "+diffHours;
+// }
+// --Commented out by Inspection STOP (26/08/15 20:58)
+ }
+
+ public static void dismissKeyguardOnNextActivity() {
+ try {
+ WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
+ } catch (Exception e) {
+ Log.e(TAG, "Error dismissing keyguard", e);
+ }
+ }
+}
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index abb2a56..c7cf61a 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -62,7 +62,6 @@
<activity
android:name=".ui.PrintActivity"
android:configChanges="orientation|screenSize"
- android:screenOrientation="portrait"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:theme="@style/PrintActivity">
<intent-filter>
diff --git a/packages/ProgrammableButton/.gitignore b/packages/ProgrammableButton/.gitignore
new file mode 100644
index 0000000..e924446
--- /dev/null
+++ b/packages/ProgrammableButton/.gitignore
@@ -0,0 +1,44 @@
+# Ignore OS specific files #
+.DS_Store
+.directory
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+.metadata
+RemoteSystemsTempFiles/
+
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Ignore gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Ignore Eclipse files #
+.project
+.classpath
+.settings/
+.idea/
+*.iml
+# Proguard folder generated by Eclipse
+proguard/
+
+res/values/com_crashlytics_export_strings.xml
+assets/crashlytics-build.properties
diff --git a/packages/ProgrammableButton/Android.mk b/packages/ProgrammableButton/Android.mk
new file mode 100644
index 0000000..915651f
--- /dev/null
+++ b/packages/ProgrammableButton/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-common android-support-v4
+#android-support-v7-appcompat libphonenumber
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := ProgrammableButton
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+include $(BUILD_MULTI_PREBUILT)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/ProgrammableButton/AndroidManifest.xml b/packages/ProgrammableButton/AndroidManifest.xml
new file mode 100644
index 0000000..343146a
--- /dev/null
+++ b/packages/ProgrammableButton/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.fairphone.programmablebutton"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="21"
+ android:targetSdkVersion="21" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <uses-feature android:name="android.hardware.camera" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".ProgrammableButton"
+ android:label="@string/title_activity_main" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <receiver
+ android:name=".CameraButtonReceiver"
+ android:enabled="true"
+ android:exported="true">
+ <intent-filter
+ android:priority="999">
+ <action android:name="android.intent.action.CAMERA_BUTTON"></action>
+ <category android:name="android.intent.category.DEFAULT"></category>
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/packages/ProgrammableButton/build.gradle b/packages/ProgrammableButton/build.gradle
new file mode 100644
index 0000000..fac5db5
--- /dev/null
+++ b/packages/ProgrammableButton/build.gradle
@@ -0,0 +1,69 @@
+// Top level gradle declarations
+
+buildscript {
+ repositories {
+ jcenter()
+ maven { url 'http://repo1.maven.org/maven2' }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.0'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+// Module level gradle declarations
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "com.fairphone.programmablebutton"
+ minSdkVersion 17
+ targetSdkVersion 21
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+
+ // Move the tests to tests/java, tests/res, etc...
+ instrumentTest.setRoot('tests')
+ androidTest.setRoot('tests')
+
+ // Move the build types to build-types/<type>
+ // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
+ // This moves them out of them default location under src/<type>/... which would
+ // conflict with src/ being used by the main source set.
+ // Adding new build types or product flavors should be accompanied
+ // by a similar customization.
+ debug.setRoot('build-types/debug')
+ release.setRoot('build-types/release')
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:21.0.+'
+}
diff --git a/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.jar b/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.properties b/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/packages/ProgrammableButton/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/packages/ProgrammableButton/gradlew b/packages/ProgrammableButton/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/packages/ProgrammableButton/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/packages/ProgrammableButton/gradlew.bat b/packages/ProgrammableButton/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/packages/ProgrammableButton/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/ProgrammableButton/libs/android-support-v4.jar b/packages/ProgrammableButton/libs/android-support-v4.jar
new file mode 100644
index 0000000..1437d24
--- /dev/null
+++ b/packages/ProgrammableButton/libs/android-support-v4.jar
Binary files differ
diff --git a/packages/ProgrammableButton/proguard-project.txt b/packages/ProgrammableButton/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/packages/ProgrammableButton/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/packages/ProgrammableButton/project.properties b/packages/ProgrammableButton/project.properties
new file mode 100644
index 0000000..00cf62b
--- /dev/null
+++ b/packages/ProgrammableButton/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-22
diff --git a/packages/ProgrammableButton/res/drawable-hdpi/ic_launcher.png b/packages/ProgrammableButton/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/packages/ProgrammableButton/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/ProgrammableButton/res/drawable-mdpi/ic_launcher.png b/packages/ProgrammableButton/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/packages/ProgrammableButton/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/ProgrammableButton/res/drawable-xhdpi/ic_launcher.png b/packages/ProgrammableButton/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/packages/ProgrammableButton/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/ProgrammableButton/res/layout/activity_main.xml b/packages/ProgrammableButton/res/layout/activity_main.xml
new file mode 100644
index 0000000..9ca0e4e
--- /dev/null
+++ b/packages/ProgrammableButton/res/layout/activity_main.xml
@@ -0,0 +1,33 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context="com.fairphone.programmablebutton.ProgrammableButton">
+
+ <TextView
+ android:id="@+id/currentIntent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="15dp"
+ android:gravity="center_horizontal"
+ android:textSize="16sp"
+ />
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ <ListView
+ android:id="@+id/intentList"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:textAlignment="center"/>
+ </ScrollView>
+
+</LinearLayout>
diff --git a/packages/ProgrammableButton/res/layout/intent_list_item.xml b/packages/ProgrammableButton/res/layout/intent_list_item.xml
new file mode 100644
index 0000000..5b74874
--- /dev/null
+++ b/packages/ProgrammableButton/res/layout/intent_list_item.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall" />
\ No newline at end of file
diff --git a/packages/ProgrammableButton/res/values-v11/styles.xml b/packages/ProgrammableButton/res/values-v11/styles.xml
new file mode 100644
index 0000000..3c02242
--- /dev/null
+++ b/packages/ProgrammableButton/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/packages/ProgrammableButton/res/values-v14/styles.xml b/packages/ProgrammableButton/res/values-v14/styles.xml
new file mode 100644
index 0000000..a91fd03
--- /dev/null
+++ b/packages/ProgrammableButton/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/packages/ProgrammableButton/res/values-w820dp/dimens.xml b/packages/ProgrammableButton/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/packages/ProgrammableButton/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/packages/ProgrammableButton/res/values/dimens.xml b/packages/ProgrammableButton/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/packages/ProgrammableButton/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/packages/ProgrammableButton/res/values/strings.xml b/packages/ProgrammableButton/res/values/strings.xml
new file mode 100644
index 0000000..c2db849
--- /dev/null
+++ b/packages/ProgrammableButton/res/values/strings.xml
@@ -0,0 +1,6 @@
+<resources>
+
+ <string name="app_name">ProgrammableButton</string>
+ <string name="title_activity_main">ProgrammableButton</string>
+
+</resources>
diff --git a/packages/ProgrammableButton/res/values/styles.xml b/packages/ProgrammableButton/res/values/styles.xml
new file mode 100644
index 0000000..6ce89c7
--- /dev/null
+++ b/packages/ProgrammableButton/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/packages/ProgrammableButton/src/com/fairphone/programmablebutton/CameraButtonReceiver.java b/packages/ProgrammableButton/src/com/fairphone/programmablebutton/CameraButtonReceiver.java
new file mode 100644
index 0000000..5128695
--- /dev/null
+++ b/packages/ProgrammableButton/src/com/fairphone/programmablebutton/CameraButtonReceiver.java
@@ -0,0 +1,26 @@
+package com.fairphone.programmablebutton;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.provider.MediaStore;
+import android.widget.Toast;
+
+public class CameraButtonReceiver extends BroadcastReceiver {
+ public CameraButtonReceiver() {}
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SharedPreferences preferences = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
+ String intent_action = preferences.getString(ProgrammableButton.CAMERA_BUTTON_INTENT_PREF, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ if (!intent_action.equals(ProgrammableButton.ACTION_IGNORE)){
+ Intent launch = new Intent(intent_action);
+ launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (intent.resolveActivity(context.getPackageManager()) != null) {
+ context.startActivity(launch);
+ abortBroadcast();
+ }
+ }
+ }
+}
diff --git a/packages/ProgrammableButton/src/com/fairphone/programmablebutton/ProgrammableButton.java b/packages/ProgrammableButton/src/com/fairphone/programmablebutton/ProgrammableButton.java
new file mode 100644
index 0000000..31fdb08
--- /dev/null
+++ b/packages/ProgrammableButton/src/com/fairphone/programmablebutton/ProgrammableButton.java
@@ -0,0 +1,71 @@
+package com.fairphone.programmablebutton;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+
+public class ProgrammableButton extends Activity {
+
+ public static final String ACTION_IGNORE = "com.fairphone.programmablebutton.ACTION_IGNORE";
+ public static final String CAMERA_BUTTON_INTENT_PREF = "CameraButtonIntent";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ SharedPreferences preferences = getSharedPreferences(getPackageName(), Context.MODE_PRIVATE);
+ String intentAction = preferences.getString(CAMERA_BUTTON_INTENT_PREF, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+
+ TextView currentIntent = (TextView) findViewById(R.id.currentIntent);
+ currentIntent.setText(intentAction);
+
+ ListView intentList = (ListView) findViewById(R.id.intentList);
+
+ String[] items = {
+ ACTION_IGNORE,
+ MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA,
+ MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE,
+ MediaStore.INTENT_ACTION_VIDEO_CAMERA,
+ Settings.ACTION_SETTINGS,
+ Settings.ACTION_WIRELESS_SETTINGS,
+ Settings.ACTION_AIRPLANE_MODE_SETTINGS,
+ Settings.ACTION_WIFI_SETTINGS,
+ Settings.ACTION_APN_SETTINGS,
+ Settings.ACTION_BLUETOOTH_SETTINGS,
+ Settings.ACTION_DATE_SETTINGS,
+ Settings.ACTION_LOCALE_SETTINGS,
+ Settings.ACTION_INPUT_METHOD_SETTINGS,
+ Settings.ACTION_DISPLAY_SETTINGS,
+ Settings.ACTION_SECURITY_SETTINGS,
+ Settings.ACTION_LOCATION_SOURCE_SETTINGS,
+ Settings.ACTION_INTERNAL_STORAGE_SETTINGS,
+ Settings.ACTION_MEMORY_CARD_SETTINGS,
+ };
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+ R.layout.intent_list_item, items);
+
+ intentList.setAdapter(adapter);
+ intentList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String intentAction = ((String)parent.getItemAtPosition(position));
+ TextView currentIntent = (TextView) findViewById(R.id.currentIntent);
+ SharedPreferences.Editor preferencesEditor = getSharedPreferences(getPackageName(), Context.MODE_PRIVATE).edit();
+ preferencesEditor.putString(CAMERA_BUTTON_INTENT_PREF, ((String) parent.getItemAtPosition(position)));
+ preferencesEditor.commit();
+ currentIntent.setText(intentAction);
+ }
+ });
+ }
+
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6222ce3..210108f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -90,6 +90,7 @@
//Maximum number of phones
private static final int MAX_PHONE_COUNT = 3;
+ private static final int DEFAULT_NETWORK_TYPE =9;
static {
mValidTables.add(TABLE_SYSTEM);
@@ -2717,7 +2718,7 @@
}
String val = Integer.toString(type);
for (int phoneId = 1; phoneId < phoneCount; phoneId++) {
- val = val + "," + type;
+ val = val + "," + DEFAULT_NETWORK_TYPE;
}
loadSetting(stmt, Settings.Global.PREFERRED_NETWORK_MODE, val);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1bfc6a8..2e2f298 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -377,5 +377,11 @@
android:exported="true"
android:singleUser="true"
android:permission="android.permission.BIND_DREAM_SERVICE" />
+
+ <service
+ android:name="com.android.keyguard.ClockScreenService"
+ android:enabled="true"
+ android:exported="false" >
+ </service>
</application>
</manifest>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index cfe8d07..023bad6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -62,7 +62,7 @@
* during closing the detail panel, this already returns the smaller height.
*/
public int getDesiredHeight() {
- if (mQSPanel.isClosingDetail()) {
+ if (mQSPanel.isClosingDetail() && mHeightOverride != -1) {
return mQSPanel.getGridHeight() + getPaddingTop() + getPaddingBottom();
} else {
return getMeasuredHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d54b2d6..3922abd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3272,8 +3272,7 @@
protected void dismissKeyguardThenExecute(final OnDismissAction action,
boolean afterKeyguardGone) {
if (mStatusBarKeyguardViewManager.isShowing()) {
- if (/*UnlockMethodCache.getInstance(mContext).isMethodInsecure()
- &&*/ mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
+ if (UnlockMethodCache.getInstance(mContext).isCurrentlyInsecure() && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
action.onDismiss();
mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 3a0d2d5..d21dc50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -236,13 +236,13 @@
}
}
- if (level <= 20) {
+ if (level <= 10) {
return context.getResources().getDrawable(R.drawable.pink_gradient_20);
- } else if (level <= 40) {
+ } else if (level <= 30) {
return context.getResources().getDrawable(R.drawable.yellow_gradient_40);
- } else if (level <= 60) {
+ } else if (level <= 50) {
return context.getResources().getDrawable(R.drawable.green_gradient_60);
- } else if (level <= 80) {
+ } else if (level <= 75) {
return context.getResources().getDrawable(R.drawable.emerald_gradient_80);
} else {
return context.getResources().getDrawable(R.drawable.blue_gradient_100);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7623514..1545557 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -673,8 +673,7 @@
}
@Override
- public IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId,
- int intentFlags) {
+ public IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
@@ -704,7 +703,6 @@
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setComponent(provider.info.configure);
- intent.setFlags(intentFlags);
// All right, create the sender.
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 99baf06..6ffa7d2 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8140,8 +8140,9 @@
}
}
if (!allowed) {
+ //FP2-1080: Modify for CTS fail case.
Slog.w(TAG, caller + ": caller " + callingUid
- + " does not hold GET_TASKS; limiting output");
+ + " does not hold REAL_GET_TASKS; limiting output");
}
return allowed;
}
@@ -12282,16 +12283,26 @@
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
+
+ // FP2-1080: Modify for CTS fail case.
+ final int callingUid = Binder.getCallingUid();
+
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
+ // FP2-1080: Modify for CTS fail case.
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
- Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
- int userId = UserHandle.getUserId(Binder.getCallingUid());
+ callingUid) == PackageManager.PERMISSION_GRANTED;
+ final int userId = UserHandle.getUserId(callingUid);
+ final boolean allUids = isGetTasksAllowed(
+ "getRunningAppProcesses", Binder.getCallingPid(), callingUid);
+
synchronized (this) {
// Iterate across all processes
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
- if (!allUsers && app.userId != userId) {
+ // FP2-1080: Modify for CTS fail case.
+ if ((!allUsers && app.userId != userId)
+ || (!allUids && app.uid != callingUid)) {
continue;
}
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
@@ -12315,7 +12326,8 @@
//Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
- runList = new ArrayList<ActivityManager.RunningAppProcessInfo>();
+ // FP2-1080: Modify for CTS fail case.
+ runList = new ArrayList<>();
}
runList.add(currApp);
}
diff --git a/services/core/java/com/android/server/am/PrivacyImpactService.java b/services/core/java/com/android/server/am/PrivacyImpactService.java
new file mode 100644
index 0000000..78b4648
--- /dev/null
+++ b/services/core/java/com/android/server/am/PrivacyImpactService.java
@@ -0,0 +1,92 @@
+package com.android.server.am;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.IPrivacyImpactService;
+import android.util.Log;
+
+public class PrivacyImpactService extends IPrivacyImpactService.Stub {
+ private static final String TAG = "PrivacyImpactService";
+
+ public static final String TABLE_NAME = "popup";
+ public static final String COLUMN_ID = "_id";
+ public static final String COLUMN_PACKAGE_NAME = "name";
+ private static final String DB_NAME = "fairphone_privacy_impact.db";
+ private static final int DB_VERSION = 1;
+
+ private final Context mContext;
+ private final SQLiteOpenHelper mHelper;
+
+ public PrivacyImpactService(Context context) {
+ super();
+ mContext = context;
+ Log.wtf(TAG, "Starting PrivacyImpactService");
+
+ mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(
+ "create table "
+ + TABLE_NAME + "("
+ + COLUMN_ID + " integer primary key autoincrement, "
+ + COLUMN_PACKAGE_NAME + " text not null"
+ + ");"
+ );
+ }
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
+ onCreate(db);
+ }
+ };
+ Log.wtf(TAG, "Started PrivacyImpactService");
+ }
+
+
+ public boolean showPackagePrivacy(String packageName) {
+ Log.wtf(TAG, "Querying Package name: " + packageName);
+ boolean show = false;
+
+ // Don't show Privacy Impact for system apps
+ boolean isSystemApp = false;
+ try {
+ isSystemApp = (mContext.getPackageManager().getApplicationInfo(packageName, 0).flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Can't determine if "+packageName+" is a system app.", e);
+ }
+
+ if (!isSystemApp) {
+ SQLiteDatabase db = mHelper.getReadableDatabase();
+ Cursor cursor = db.query(
+ TABLE_NAME,
+ new String[] { COLUMN_ID, COLUMN_PACKAGE_NAME },
+ COLUMN_PACKAGE_NAME + " =?",
+ new String[]{ packageName },
+ null,
+ null,
+ null
+ );
+
+ cursor.moveToFirst();
+ show = cursor.getCount() == 0;
+ cursor.close();
+ }
+ return show;
+ }
+
+ public void disablePackagePrivacy(String packageName) {
+ Log.wtf(TAG, "Adding Package name: " + packageName);
+ SQLiteDatabase db = mHelper.getWritableDatabase();
+
+ // Create a new map of values, where column names are the keys
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_PACKAGE_NAME, packageName);
+
+ db.insert(TABLE_NAME, null, values);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 87f78c1..864453c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -651,7 +651,7 @@
* Returns HTTP response code.
*/
private int isCaptivePortal() {
- if (!mIsCaptivePortalCheckEnabled) return 204;
+ if (mIsCaptivePortalCheckEnabled) return 204;
HttpURLConnection urlConnection = null;
int httpResponseCode = 599;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5ae50f4..cc99a7b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -379,13 +379,13 @@
static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE;
- static final int DEF_MINIMUM_PASSWORD_LETTERS = 1;
+ static final int DEF_MINIMUM_PASSWORD_LETTERS = 0;
int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LETTERS;
- static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1;
+ static final int DEF_MINIMUM_PASSWORD_NUMERIC = 0;
int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC;
- static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1;
+ static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 0;
int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS;
static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e6bbb31..b59f13a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -62,6 +62,7 @@
import com.android.server.accounts.AccountManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.BatteryStatsService;
+import com.android.server.am.PrivacyImpactService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.content.ContentService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -1015,6 +1016,13 @@
mSystemServiceManager.startService(MediaProjectionManagerService.class);
}
+ try {
+ Slog.i(TAG, "PrivacyImpactService");
+ ServiceManager.addService( "PrivacyImpact", new PrivacyImpactService(context));
+ } catch (Throwable e) {
+ reportWtf("starting PrivacyImpactService", e);
+ }
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();